#121 Non Active Record Model
Aug 04, 2008 | 12 minutes |
Models
This episode will show you how to make a model which isn't based on Active Record. You may want to do this if a resource isn't backed by the database.
- Download:
- source codeProject Files in Zip (96.7 KB)
- mp4Full Size H.264 Video (16.4 MB)
- m4vSmaller H.264 Video (12.1 MB)
- webmFull Size VP8 Video (33.9 MB)
- ogvFull Size Theora Video (23.1 MB)
Nice!
Note that if no model uses ActiveRecord in your app, you can add
<<<<<<<<<<<<<<
Rails::Initializer.run do |config|
config.frameworks -= [:active_record ]
end
>>>>>>>>>>>>>>>
to your environment.rb
And what about caching Letter.all with
<<<<<<<<<<<<<<
def self.all
@@all||=('A'..'Z').map { |c| new(c) }
end
>>>>>>>>>>>>>>>
?
Awesome as always!
How about using an anonymous scope for the products method. That way products would behave even more like an ar association.
Thanks for the great screencasts. Alway looking forward to Mondays.
Thanks, it is great. in fact i need it in my application WebVZ
@Eric, yep, caching the "all" method is a possibility. But I tend to not add this kind of caching up front unless I know for sure it is worth it.
Caching usually comes with some hidden costs. I couldn't, for example, cache the "products" method as well because that will result in all products staying in memory for my entire app.
If you determine adding a global cache is worth it (with @@all) then it's best to call "freeze" on each object in the cache. This way you ensure the state of the object doesn't change.
@Tim, that's a great idea. Using scopes instead of a simple find adds a lot of potential.
Good way to do it, just prompted me to update my activerecord-tableless-models plugin. Find the plugin at http://github.com/kennethkalmer/activerecord-tableless-models
@Kenneth,
Here's what I did for quick and dirty validations without DB backing:
http://pastie.org/247701
[Admin Edit: moved to pastie]
Great stuff & good timing for a current project :)
I'm looking forward to the day when ActiveModel (http://github.com/rails/rails/tree/master/activemodel) is complete and stable enough to use: it'll be good to create tableless models that can still use the cool stuff like validations and observers.
Until that day though...thanks for the screencast Ryan.
ps. oooh a preview button :D
@Rob, I'm looking forward to Active Model as well. I think it will have a big impact in how we work with these kinds of models. I was hesitating to make this episode yet for that very reason, but figured it will be a while before Active Model is released.
Finally ... I've seen questions regarding this stuff at Railsforum and WorkingWithRails. Now, instead of explaining it myself, I can link to this screencast! ;-)
Small suggestion: You override the to_param method which is really cool. However, if you do that, you might as well override the to_s method for the Letter class because I think it's a really underused feature in Rails (and probably Ruby in general).
@Clemens, I considered defining to_s here too, but decided not to. Although it may work well in this example, for the majority of cases I don't think it does. There are often many different ways to represent a model as a string.
I prefer to be explicit each time so there's no confusion as to how it's being displayed. For example, let's say we had a Month model which represented a specific month of the year. How would you display this as a string? The full month name? The abbreviated version? Would you include the year? etc.
I think this decision of how to represent a model as a string belongs in the view, not the model.
Ryan, thanks a bunch for this (and all screencasts).
I had a case similar to this in an app i'm working on and this is how i originally designed it.
But I was hesitant at first to have ActiveRecord methods inside a non-ActiveRecord class ... somehow it just didn't seem right. i thought the find methods should have been helpers in the controller, but this seems to make more sense.
I discovered a few weeks ago that if you use a form_for for these models, you need to define the "id" method or you get nasty-grams in your web server log file.
I think that the letters should be factored out of the 'all' method.
For example:
http://pastie.textmate.org/249561
What do you think Ryan?
@Andy, what is the reasoning behind moving the letters into their own method? Is it for self documentation? I think the "all" method is simple enough to not warrant further refactoring.
However, if you need to add more characters to the "all" method in addition to alphabetical letters, then I would say refactoring out just those letters is worth it.
@Ryan, it just feels a little more clear to me. To me, "all" isn't the dataset, the collection of letters is.
Also, I tend to preemptively factor things out that I know I'm going to refer to in a lot of different places.
Maybe, I'm getting ahead of the implementation but, I could easily see it becoming appropriate as the class grows. So why not just do it up front?
Because you should avoid premature optimization and premature refactoring. Maybe you'll need it, maybe you won't. In the former case, you can do it when you need it. In the latter case, you'll have wasted effort by doing premature work.
Ryan,
There is a missing '=' before 'render' in:
http://github.com/ryanb/railscasts-episodes/tree/master/episode-121/store/app/views/products/index.html.erb
so instead of:
<% render :partial => @products %>
it should read:
<%= render :partial => @products %>
I have also noticed that the 'log' folder is not present, so it needs to be created manually (not a big deal, but providing it would avoid an unnecessary error when trying to boot up the development server).
@Cassiano, thanks for pointing out this error! I'll update the code.
I just created a plugin called PassiveRecord that makes a lot of this stuff very easy. Check it out on Github: http://github.com/artofmission/passiverecord
Also very useful in this context (and a slightly different approach):
http://stackoverflow.com/questions/315850/rails-model-without-database
If you determine adding a global cache is worth it (with @@all) then it's best to call "freeze" on each object in the cache. This way you ensure the state of the object doesn't change.
I have searched for it a long time.
This episode has been updated to Rails 5 as a blog post Tableless Model in Rails 5
The above link is wrong. Here is the correct link Tableless Model in Rails 5
Wondering what version of rails was this railscast written for? I'm a newbie trying to learn using 4.2.6, there seem to be a lot of issues with all the railscasts I've tried. I can't see where they specify the rails version they were written for.