Property
Wrap model properties into a single database column and declare properties from within the model.
website: zenadmin.org/635
license: MIT
Status: Beta
The gem works fine, even though it still needs some more features like property definition changes detections and migrations.
Usage
You first need to create a migration to add a ‘text’ field named ‘properties’ to your model. Choose a text format that is really long (otherwize your data will be truncated and the property will fail to decode => error). Do something like this:
class AddPropertyToContact < ActiveRecord::Migration
def self.up
if ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] == 'mysql'
execute "ALTER TABLE contacts ADD COLUMN properties LONGTEXT"
else
add_column :contacts, :properties, :text
end
end
def self.down
remove_column :contacts, :properties
end
end
Once your database is ready, you need to declare the property columns:
class Contact < ActiveRecord::Base
include Property
property do |p|
p.string 'first_name', 'name', 'phone'
p.datetime 'contacted_at', :default => Proc.new {Time.now}
end
end
You can now read property values with:
@contact.prop['first_name']
@contact.first_name
And set them with:
@contact.update_attributes('first_name' => 'Mahatma')
@contact.prop['name'] = 'Gandhi'
@contact.name = 'Gandhi'
Roles
Properties would not be really fun if you could not add new properties to your instances depending on what the object does. First define the roles:
@picture = Property::Role.new do |p|
p.integer :width, :default => :get_width
p.integer :height, :default => :get_height
p.string 'camera'
p.string 'location'
p.actions do
# Define new methods to insert into model
def get_width
image.width
end
def get_height
image.height
end
def image
raise 'Missing file' unless @file
@image ||= ImageBuilder(@file)
end
end
end
And then, either when creating new pictures or updating them, you need to include the role:
@model.has_role @picture
The model now has the picture’s properties defined, with accessors like @model.camera, methods like @model.image, get_with, etc and default values will be fetched on save.
Note that you do not need to include a role just to read the data as long as you use the ‘prop’ accessor.
StoredRole
The dynamic nature of the Property gem goes to the point where you can store your property definitions in the database by using the StoredRole and StoredColumn modules.
External storage
You might need to define properties in a model but store them in another model (versioning). In this case you can simply use ‘store_properties_in’ class method:
class Contact < ActiveRecord::Base
include Property
store_properties_in :version
property do |p|
p.string 'name', 'first_name'
p.string 'childhood', :default => 'happy'
end
end
Doing so will not touch the storage class. All property definitions, validations and method definitions are executed on the ‘Contact’ class.
Indexing support
The property gem lets you very easily export content from the packed data to any kind of external table. Using a key/value tables:
class Contact < ActiveRecord::Base
include Property
property do |p|
p.string 'name', :indexed => true
p.string 'first_name', :index => Proc.new {|rec| { 'fullname' => rec.fullname }}
p.index(:string) do |record|
{
'fulltext' => "name:#{record.name} first_name:#{record.first_name}",
"name_#{record.lang}" => record.name
}
end
end
end
Using a custom indexer, you can group indexed values together in a single record. This can be interesting if you have some legacy code or queries that need direct access to some values:
class Contact < ActiveRecord::Base
include Property
property do |p|
p.string 'name'
p.string 'first_name'
p.index(ContactIndexer) do |record|
{
'name' => record.name,
'first_name' => record.first_name,
}
end
end
end
Please read the docs for details: zenadmin.org/635
Developping property and testing
As of 2015, you need ruby 1.8.7 and the following gems in order to run the test suite.
gem install rdoc-data; rdoc-data --install
gem install shoulda --version=2.10.3
gem install activerecord --version=2.3.18
gem install sqlite3 --version=1.3.5
gem install json --version=1.5.1
gem install jeweler --version=1.8.3