Note: Some of the features describe here will require the current talia_core development version. They will be available in the Core version 0.5.5 and later.
Using ActiveSource instead of ActiveRecord - how to build model classes that plays well with standard Rails forms
The base model class of Talia, TaliaCore::ActiveSource has a number of features to make it easier to use it as a replacement of a default ActiveRecord model. This is particularly important when you have forms and such and you need to create or update models from parameters that come from the net.
Default patterns with ActiveRecord
In Rails you will usually create a form using a helper like #form_for, and create input elements (e.g. text fields) for each "field" of the corresponding model object. This mechanism will expect that the model object has accessor methods for reading (and later assigning) "fields".
When the form is submitted, you will find the values inside the params hash. Rails will put an entry in that hash, with a key named after the model. Inside you will find key-value pairs with the values submitted through the form.
# Inside the controller class for the model "MyModel" def create # At this point you will have the values from the form # in params[:my_model], which could look like # params => { :my_model => { :title => "Entered in form, :value => "Entered in form" } } @my_model = MyModel.new(params[:my_model]) # Save, validate, etc. end
This means you will usually create new model objects with #new(hash_of_attributes) or update them with #update_attributes(hash_of_attributes) and the like.
Using the same pattern with ActiveSource
TaliaCore::ActiveSource already behaves like ActiveRecord in many respects. You may pass "semantic" attributes together with database attributes into #new and #update_attributes and the like:
TaliaCore::ActiveSource.new(N::DCT.title => "My new title", :uri => N::LOCAL.myNewThingy)
You also have the "semantic" accessors:
my_active_source.dct::title.first
However, these methods hardly play nice with the standard Rails forms; they don't provide the simple accessors that Rails needs.
Defining Accessors on ActiveSource classes
To make things easier for you, accessor methods can be defined on ActiveSource class for "semantic" properties. Take this example class:
class MyThingy < TaliaCore::ActiveSource singular_property :title, N::DCT.title multi_property :authors, N::DCT.creator singular_property :text_source, N::DCT.hasPart, :type => TaliaCore::ActiveSource multi_property :my_multi_prop, N::TALIA.multi_prop, :type => TaliaCore::Source, :dependent => :destroy end
Using the #singular_property and #multi_property statements in the class you define getters and setters for a semantic attribute. A #singular_property will behave like a "normal" getter/setter to which a string (or object relation) can be assigned. A #multi_property will make a getter/setter combination for a list of values.
It is possible to define the type of the possible values for the property by using the :type option. In the previous examples the MyThingy has a "text" property that can cotain a single value of any type of ActiveSource. This also means that this property cannot contain literals. Note: :type => TaliaCore::ActiveSource is equivalent to :force_relation => true as used in previous releases. the :force_relation option is now deprecated.
Note: The #multi_property does not provide the features of the ActiveRecord relations (e.g. belongs_to, has_many). This means that Rails features which depend on that functionality will not work correctly and must be replaced by explicit code. This is not likely to change before Talia is ported to Rails 3.x.
The class may then be used like this:
new_thingy = MyThingy.new(:title => "My Title", :authors => ["Danilo", "Simone"], :uri => "http://foobar.com") # This call is equivalent to new_thingy = MyThingy.new(N::DCT.title => ["My Title"], N::DCT.creator => ["Danilo", "Simone"]) # Accessor methods work as expected title = new_thingy.title # Will return "My Title", which is the same as title = new_thingy.dct::title.first # And for the "multi" properties authors = new_thingy.authors # is the same as authors = new_thingy.dct::creator # On the multi property, there is an assignment operator for lists # The following code will completely REPLACE the authors on the object new_thingy.authors = ["Michele", "Daniel"] # You may also manipulate the list in the standard ActiveSource way # The following will ADD an author to the existing list new_thingy.authors << "Federico"
Updating existing objects
In standard Rails, updating models is quite straightforward and done through #update_attributes and friends. In the semantic world, the problem gets a bit more interesting. Especially, we may need to preserve external data for an attribute. To this end, The ActiveSource provides two ways to update records:
new_thingy = MyThingy.new(:title => "Noob", :authors => ["Danilo", "Simone"], :uri => "http://foobar.com") # The following would "update" the semantic attributes by adding to them new_thingy.update_attributes(:authors => ["Arturo"]) # At this point, the "authors" would be: ["Danilo", "Simone", "Arturo"] # The other method completely replaces the attribute new_thingy.rewrite_attributes(:authors => ["Michele", "Daniel"]) # At this point "authors" would be ["Michele", "Daniel"] # In both cases, other attributes like "title" remain untouched
Options for the "fields"
You can set options for any relation using property_options. The options can also be supplied to #singular_property and #multi_property, and they can make life easier in many places.
Defining types for relations
In case you define a type with :type => SomeTaliaType, Talia will assume that any value you pass in is the URI of an existing source to which to relate. The source should also be of the given type, although this is not checked in the base classes at the moment. The type information can also be used by the user interface to present drop-down lists of elements of the given type, and the like.
So, for example (see the class definition above):
new_thingy.text_source = "http://foobar.com/barbar" # is the same as new_thingy.text_source = TaliaCore::ActiveSource.find("http://foobar.com/barbar")
This makes it easier to work with forms which return just plain strings, instead of objects.
Type information can also be provided for a predicate/property without defining a multi or single-property accessor, using the property_options method. Once the :type option is defined, it will take effect on all accesses for that accessor.
# This will convert the values to source objects new_thingy.my_multi_prop = ["http://one", "http://two"] # This will, too new_thingy.my_multi_prop << "http://one" # And also this new_thingy[N::TALIA.multi_prop] << "http://two"
Automatically Destroying dependent sources
The option :dependent => :destroy works in the same as for ActiveRecord relations. If some of the attached sources are removed from the object, they will be deleted. The option can either be given to the singular/multi accessor or specified on its own using property_options, like the :type options. This will also take effect on every operation with the property/predicate:
new_thingy.my_multi_prop = [@first, @second] # The following will trigger the removal of sources new_thingy.my_multi_prop = [@third] # At this point, @first and @second have been destroyed # The following will also trigger the removal of sources new_thingy.my_multi_prop.remove # And this will, too new_thingy[N::TALIA.multi_prop].remove # To remove the attached sources, simply set it to an emtpy array new_thingy.my_multi_pro = []
