- 5.1 Configuration
- 5.2 Model classes
- 5.3 Properties
- 5.4 Associations
- 5.5 CRUD basics
- 5.6 Hooks
- 5.7 Plugins
- 5.8 Conclusion
5.2 Model classes
Model classes exist in the directory app/models. These classes typically have singular names and exist one class per file. Examples that may appear in a Merb blog application are Post in the file app/models/post.rb and FavoriteLink in app/models/favorite_link.rb. Organizing model classes in this way isn’t strictly necessary, though, since Merb by default includes everything inside app/models by recursive glob. In other words, so long as you make sure that your model class is somewhere in app/models, it will be available at boot. Nonetheless, our recommendation is that you do arrange your model classes in a reasonable way, so that both you and others can easily find them.
While various DataMapper modules can incorporate different functionality within your model class, the fundamental module that must be included in order for your class to work with DataMapper is DataMapper::Resource. Below we include this module and effectively set up our User class as a DataMapper model.
class User include DataMapper::Resource end
Let’s take a look at the source behind the Resource module to get a feel for how it affects the class in which it is included:
module DataMapper module Resource # ... # @api public def self.append_inclusions(*inclusions) extra_inclusions.concat inclusions true end def self.extra_inclusions @extra_inclusions ||= [] end # When Resource is included in a class this # method makes sure it gets all the methods # # - # @api private def self.included(model) model.extend Model model.extend ClassMethods if defined?(ClassMethods) model.const_set('Resource', self) unless model.const_defined?('Resource') extra_inclusions.each { |inclusion| model.send(:include, inclusion) } descendants << model class << model @_valid_model = false attr_reader :_valid_model end end # ... end end
Focusing on the last method listed above, we can see that the Resource module extends the class in which it is included (above called model). In the process, it principally extends it with the Model module, which contains the logic for property persistence and object retrieval. As we come to these methods later on, we’ll open up the Model class source as well. For now, however, take note of Resource’s ability to include extra modules through the class method append_inclusions. This method is used extensively by DataMapper plugins that need to extend the functionality of all DataMapper models. For instance, below is some of the source for dm-timestamps, which automatically sets timestamps on properties of particular names.
module DataMapper module Timestamp Resource.append_inclusions self # ... def self.included(model) model.before :create, :set_timestamps model.before :update, :set_timestamps model.extend ClassMethods end end end
The first line appends the module to all DataMapper models. Later on, using its own self.included, the module includes its own logic into the model. This cascading of modules is thus particularly effective for code as modular as DataMapper.