#219 Active Model
In Rails 3 the non-database functionality of Active Record is extracted out into Active Model. This allows you to cleanly add validations and other features to tableless models.
- Download:
- source code
- mp4
- m4v
- webm
- ogv
Very cool! I'd like to see more OM's in the NoSQL world to start using this, instead of cooking up their own!
Small caveat: ActiveModel defines before_validate and after_validate callbacks, but these aren't really usable: they're mixed up with the validations themselves, and invoked in the order they were defined. So, for example, if you declare a before_validate callback after a validates_presence_of, the before_validate will be called *after* the validation, which isn't very useful.
There's a change in edge now that moves before_validation and friends from ActiveRecord to ActiveModel to fix this, so expect that to make it to the release candidate.
Is it possible to just inherit from ActiveModel::Base?
If so, is there any good reason not to do this, instead of picking and choosing modules and having to dig through code to figure out which dependencies are missing?
If you don't want to dig through all the modules, use the default:
This is pretty handy, because it already extends Naming and Translation, and includes Validations and Conversion. And it comes with a nifty initializer that allows to pass attributes in Message.new(title: "...") and offers a persisted? method.
See: https://github.com/rails/rails/blob/master/activemodel/lib/active_model/model.rb
Would also like response to dhc question. From some angles it looks like Rails 3 has a lot of features that come as a box of d-i-y components which you have to know how to assemble youself.
Surely this wasn't the point of Rails?
I think this is very dangerous...
send("#{name}=", value)
Do you see any risk in using this method? Might allow for private methods to be called? How would you mitigate this risk?
instead of including ActiveModel::Conversion module, extending ActiveModel::Naming module, and defining the persisted? method...
Another option would have been to change the form_for call in the view. Just change "form_for @message" to "form_for :message, :url => messages_path"
@jonas, why? Rails does this in a similar way. Except all it does is check for protected attributes that shouldn't be mass assigned first and that the object responds to "#{key}=". If you want to add your own ability to have protected attributes, then go for it. I just don't think that's in the scope of this railscast.
http://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb#L1532
Just an idea, totally unrelated to the screencast.
You could make a good use of p2p and give torrent links among with the usual download links.
@dhc (Since people are asking): You could define an "ActiveModel::Base" class and include the functionality you want for your app. The nice thing about the modular architecture is that you can do exactly what you are looking to do, but it's not forced upon you. So just define your own base class and include the modules you want for all of your inheriting classes. Don't get this confused with ActiveRecord, as that a different set of classes and modules, that happen to leverage the ActiveModel classes/modules.
Very nice screencast, Ryan! I had the same problem with ActiveRecord on rails 2 which requires some hacks in order to make the model not db specific. Great work on introducing ActiveModel! You mentioned downloading the rails 3 codes and read it. I just found out a neat tool that makes it easier to read codes from a source control system on your browser, it's called CodeFaces, here is the restful link to read the rails code on GitHub: http://codefaces.org/http://github.com/rails/rails
I made a gem called basic_model, based on this approach:
http://github.com/petebrowne/basic_model
http://rubygems.org/gems/basic_model
Anyone using serializers? I can't get them to work. Active Model is great but need some more polishing in my opinion.
http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
Is it possible to use formtastic's semantic_form_for with a tableless model? Without using the Tableless gem?
It would be good to have an episode on tableless models with MongoID!
Hi Ryan,
Thanks again for the Railscast.
I'm upgrading our Rails 2.3.11 app straight to 3.1 and my old tableless models don't work anymore (as per screencast #193). This stuff in this railscast looks nice and clean but I'm a little confused why no one else is coming up against the problem of no longer getting access to associations and the SQL Column benefits of the old way.
We use things like:
column :option, :string, "Default"
and
has_many :areas
accepts_nested_attributes_for :areas
Is there some ActiveRecord goodness we can include/extend?
If not, the actual problem we have is that as of Rails 3.1 the tableless models are trying to hit the DB looking for "tablelesses" when initializing etc.
Thanks,
Matt
Hi all,
I've had a few people ask, just so you know I ended up having to implement Tableless like this to get it going with Rails 3.1 and working with associations.
Put this in your app/models dir and then extend it to get a tableless model that still supports all the associations like has_many, belongs_to etc, validations and basically smells like a real model.
We use it a fair bit for things like filter models, giving us very clean view and controller code.
https://gist.github.com/1101257
Enjoy.
//matt
This is great! So I created a model and I am having a separate issue. Since I use the @errors = ActiveModel::Errors.new(self) collection in my initializer and validators, when I generate JSON to store in my NOSQL database, it's including all kinds of stuff that I don't want to store:
{"errors"=>#, @username="jasdeep", @validation_context=nil, @firstname="Jasdeep", @lastname="Jaitla", @messages={}>, "username"=>"jasdeep", "email"=>... and so on
I've tried calling:
@user.as_json(only: [ :username, :email, :firstname, :lastname ])
as recommended in: api.rubyonrails.org -- ActiveModel::Serializers::JSON
but it has no effect, it still outputs every attribute and object. I have looked EVERYWHERE googling for hours upon hours and still can't find a solution for this.
I've tried overriding as_json in my Model class, but still no effect.
Any help or pointers are appreciated...
class User
include ActiveModel::Validations
include ActiveModel::Conversion
include ActiveModel::Serialization
extend ActiveModel::Callbacks
extend ActiveModel::Naming
attr_accessor :username, :email, :password, ... etc
def as_json(options={})
super(:only => [:username, :email])
end
Thanks!
Rails/ActiveModel 3.2.1
Ruby 1.9.3.p0
I found a workaround, but still curious if anyone who is also using ActiveModel was having issues with as_json/to_json methods. Maybe this is the wrong forum, if so, please feel free to delete!
Hey!
Following the activemodel source code, the initialize method should check if the attributes isn't nil.
Without it, I got errors running my specs
this thing happend to me too so i changed the code to look like yours but now all the validations pop up every time =(
and i cant get it to work as intended
Anyone trying translating ? I tried this, as mentioned in API docs:
http://api.rubyonrails.org/classes/ActiveModel/Translation.html
Actually what I am trying to do is translate the error messages. The message part like "can not be empty", etc. worked, but the attribute names still in english.
I'm facing the same issue, could you solve it?
Hi There, thanks for the rail cast I am wondering if anyone has had experience with using these in multi step forms like that of the #217 Rails Cast as it would be better if I could do this without the Active Record database creation, as well as possibly removing the sessions to be used in memcache instead, it seems there isn't a lot of info on this and would be very appreciative for any help someone can offer!
So after spending a bit of time looking at Active Model I realized that my main problem was with the persisted? method always being false (when your controller has new, and update methods like that of #217 this becomes a problem as it always thinks it is creating a new object rather then using the already created one) the solution I had for this was to use ids and change the persisted method to this:
Any idea on how to test the mailer for the contact form in Rails 4?
Hi ryan,
thanks for your article.
How to make pre-populate on the form?
Because currently My form always did not pre-populate.
Thanks
Hmm, nice. I'd like to know how I can keep on using belongs_to though. My tableless model is actually connected to ActiveRecord models, as in episode 193 with articles.
Any new way to do this with rails 4 for instance ?
Hi,
if i have
where :date is a datetime and :count is a integer, i have this error "NoMethodError (undefined method `data(1i)=' for".
Can i declare :date as a datetime?
Please note the new ActiveModel::Model available in Rails 4 which makes it much easier to create a tableless model. In fact, the code shown in this screencast for the model doesn't actually work fully with Rails 4 presumably because of this change (at least it didn't work for me). For info, see: http://blog.plataformatec.com.br/2012/03/barebone-models-to-use-with-actionpack-in-rails-4-0/
One comment: the
persisted?
public instance method defaults to returnfalse
.How i can get error messages when validation fails. Please someone help me to fix this. Any help will be appreciated. Thanks
nothing