- 9.1 Scopes
- 9.2 Callbacks
- 9.3 Calculation Methods
- 9.4 Single-Table Inheritance (STI)
- 9.5 Abstract Base Model Classes
- 9.6 Polymorphic has_many Relationships
- 9.7 Enums
- 9.8 Foreign-Key Constraints
- 9.9 Modules for Reusing Common Behavior
- 9.10 Modifying Active Record Classes at Runtime
- 9.11 Using Value Objects
- 9.12 Nonpersisted Models
- 9.13 PostgreSQL Enhancements
- 9.14 Conclusion
9.12 Nonpersisted Models
In Rails 3, if one wanted to use a standard Ruby object with Action View helpers, such as form_for, the object had to “act” like an Active Record instance. This involved including/extending various Active Model module mixins and implementing the method persisted?. At a minimum, ActiveModel::Conversion should be included and ActiveModel::Naming extended. These two modules alone provide the object all the methods it needs for Rails to determine partial paths, routes, and naming. Optionally, extending ActiveModel::Translation adds internationalization support to your object, while including ActiveModel::Validations allows for validations to be defined. All modules are covered in detail in the Active Model API Reference.
To illustrate, let’s assume we have a Contact class that has attributes for name, email, and message. The following implementation is Action Pack and Action View compatible in both Rails 3 and 4:
1 class Contact
2 extend ActiveModel::Naming
3 extend ActiveModel::Translation
4 include ActiveModel::Conversion
5 include ActiveModel::Validations
6
7 attr_accessor :name, :email, :message
8
9 validates :name, presence: true
10 validates :email,
11 format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]
{2,})\z/ },
12 presence: true
13 validates :message, length: {maximum: 1000}, presence: true
14
15 def initialize(attributes = {})
16 attributes.each do |name, value|
17 send("#{name}=", value)
18 end
19 end
20
21 def persisted?
22 false
23 end
24 end
New to Rails 4 is the ActiveModel::Model, a module mixin that removes the drudgery of manually having to implement a compatible interface. It takes care of including/extending the modules mentioned earlier, defines an initializer to set all attributes on initialization, and sets persisted? to false by default. Using ActiveModel::Model, the Contact class can be implemented as follows:
1 class Contact
2 include ActiveModel::Model
3
4 attr_accessor :name, :email, :message
5
6 validates :name, presence: true
7 validates :email,
8 format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/ },
9 presence: true
10 validates :message, length: {maximum: 1000}, presence: true
11 end