Closed jgaskins closed 11 years ago
This is currently not exactly necessary and will become less necessary for Rails 4.0.
In current Rails (3.2) apps, you can put the following in the model:
class Article
if defined? ActiveModel
extend ActiveModel::Naming
include ActiveModel::Conversion
end
def persisted?
defined? @id # Mappers inject the @id ivar when inserting into/retrieving from the DB
end
end
This will give ActiveModel functionality to your models without worrying about it during unit tests (where ActiveModel isn't loaded). In Rails 4.0, you will simply need to include ActiveModel::Model
inside the conditional. I think it's best to keep the ActiveModel interface within the models rather than trying to load some Perpetuity-specific mixin. Models should ideally have no idea about the persistence mechanisms used and so shouldn't even know Perpetuity exists.
Unfortunately, this gets kinda weird with the persisted?
method since it has to know the criteria to determine whether or not it's been saved to the DB, but I think that's a failure of the API expected of models by Rails.
What do we need ActiveModel for?
Rails counts on a lot of ActiveModel methods in order to do things like
form_for
in views andredirect_to @foo
in controllers.How should it be done?
As always, several possibilities floating around in my head.
Using mappers as ActiveModel wrappers
Consider this gist. Unfortunately, this means that we need to make the mapper respond to all of the attributes (for
form_for
) and the id field (forredirect_to
). This is trivial to do, but feels weird.Include wrapper functionality into domain objects
Another possibility is to include the ActiveModel wrapper into the domain class (essentially just moving the
include Perpetuity::ActiveModelWrapper
call in the aforementioned gist). This would make the ActiveModel functionality expected inside those objects, but it means that the objects themselves aren't entirely decoupled from Rails. The primary idea behind Perpetuity is that there are no other dependencies other than those required for the domain objects to do their job.Create an entirely different class specifically for wrapping domain objects to use ActiveModel
This idea maintains the purity of the domain objects, keeps the domain-object calls outside of the mappers and still allows us to use ActiveModel functionality. This seems like it might be the best solution, but I'm not 100% sold on it yet. Here's an example. If we could come up with a smoother way of calling it, it might be better.