I'm wondering about the total_price method on cart.rb. As I don't have a similar code here (hey, it's already late now), I'm wondering if we write the method this way
def total_price
line_items.inject(0) {|memo,item| memo += item.full_price}
end
can speed up things a little, avoiding the to_a and sum methods. What you think?
@youtubesearcher: "hacking" through activerecord was the point of the exercise. Lots of these vids aren't necessarily about the most efficient or best way to get X done, but teaching techniques and ways of doing things that lots of us find useful.
@ryan: excellent vid. I think lots of people know what they can do with self.included, sort of, but maybe not exactly why. Thanks for all your work.
True enough, but it also does not require hacking thru activerecord and exposing yourself to breakage when a new rails release or patch comes out...but hey...both work and it's just a matter of personal preference
@chivi, I'm using Final Cut Pro to edit them and Quicktime Pro to convert them.
@Zargony, right. You can also use "save(false)" to bypass validation. I often find myself using "create" more than "save" so it's convenient to have this plugin.
Great screencast, Ryan. You've put up a good summary of how to easily change the behavior of any class. Of course it does not only work with ActiveRecord, but with virtually every class.
To disable validations, so far I was using save_without_validation! instead of save! (AR itself uses alias_method_chain to add validations), however your plugin might come in handy to disable validations in a larger context.
Btw, you're publishing new screencasts faster than I can watch them :)
This is a jewel. I used most of these techniques before without fully understanding what's that self.included(base) thing is about. Now I know! And the example is well choosen. In a real app often it doesn't make sense to cramp the different aspects of a technique into one place. Here it seems quite reasonable.
Using the observer is a neat trick, but I can imagine it backfiring. Let's say you set up a rake task to import a bunch of users from a csv file. Well, all of those will receive an email as the User model is created. In some cases this may be what you want, but I prefer to have more control over when an email is sent. I don't want to spam someone accidentally. The act of sending an email seems more like a controller action to me.
The whole time I was wondering why you weren't using instance variables for the email, like in Acts As Authenticated, but you answered at the end.. I guess AAA is a bit out of date then?
Also, one thing I like about AAA is the user_observer where you can specify something like this
def after_create(user)
UserNotifier.deliver_signup_notification(user)
end
Seems like a more modular approach so you dont have to put it in multiple controllers. Any thoughts on this?
Ryan, Thanks for this! I have a question for some weird behavior I'm seeing:
In my controller I'm doing a model.reload as you've implemented it, and render a "conflict" action. In the controller, if I 'puts' on any of the model variables before and after the reload, I can see the change, but the form never reflects the change. Doing a 'puts' in the form writes out the pre-update model information.
My form is a bit different in that I'm using a form_tag and :id => model_name, but as I understand it, it should reload the model from the db, correct? I'm missing something simple.
Chuck, is it Mongrel that has the 60 sec timeout or Apache? I increased the timeout on Apache (I have several tasks for my app that can take a few hundred seconds, directory crawling, etc) and Mongrel seems to keep going until the task is done (but Apache may throw a "proxy mod error" if it goes too long, which is why I increased the time). And yes, its an internal website for logging people's work, so it's okay that it can take a long time on certain tasks, those tasks don't come up that often.
@macovsky, good question. I believe you can create your own custom helper module (helpers/foo_helper.rb) and include that using "helper :foo" in the UserMailer class. This is very similar to how you would set up custom helper modules in a controller. Untested.
ActiveMailer is not asynchronous so it does hang if the smtp is busy but not if it's denying requests. The popular example is sending mass emails through ActiveMailer using Mongrels as the app server (because Mongrel has a 60 second timeout).
The way around this is to either configure smtp to queue mails (the default for most MTAs), or to use something like ar_mailer by Eric Hodel if you don't control your MTA.
@martin
I was referring to possibility of connection timeouts and the length it would take to connect to, transfer and complete the transaction between your rails app and the SMTP server you're trying to send mail thru. If your SMTP server isn't responding quickly, your user could experience a lag time.
Where as if you went with the sendmail option for actionmailer, sendmail, postfix, whatever your MTA is would accept the email, then try to send it out for you - adding it to a background process que, freeing up your app to continue on (in theory).
I think for me the sendmail option is a better one, but then you've gotta configure your MTA yourself (at least in my case on a VPS).
I'm far from close to launching my app, so for dev's sake, I'm sticking with SMTP option for now.
I'm not familiar with ActionMailer (I'm still new to Ruby/Rails), but any sane e-mail client (ActionMailer, in this case) will throw an error if the SMTP server you're trying to connect to is unavailable.
Messages are not delivered in real time. The client (ActionMailer) will hand the message over to the SMTP server, which in turn is responsible for delivering the message. The client's involvement ends there. The SMTP server will add the e-mail to it's queue and deliver it as soon as possible. If it can't (i.e. the receiving server is unavailable) it will stop trying after some time (usually a few days) and notify the sender by e-mail.
Short story: A broken mail server won't hang your application :)
@dave, good question. I believe ActionMailer will open a socket to the mail server right there and send the email (using net/smtp). If the mail server takes a while to respond then it might hang. I don't know for sure though. The best thing to do is try it out and see.
It would be really cool if ActionMailer had a separate queue, that way if the smtp server is down or anything it would throw it in the queue for later delivery.
Just curious. If you sent email via SMTP - that process actually sends the mail at the time of request and doesn't cue it up for later delivery correct? So if the SMTP server you're mailing thru takes forever to answer/deliver - you're user is stuck waiting as well?
good point, and i guess dryness is mainly important in case you want to change something somewhere but forget to also change it somewhere else. i guess with tests that isn't a huge deal because the worst that'll happen is that some test will fail. i guess it's much worse when something that is shared by a lot of tests breaks them all at once (and possibly in different ways).
@vlad, true, it is less dry. But, I'm finding the DRY principle isn't nearly as important in testing. There are some cases keeping tests DRY can improve readability and maintainability, but other times it will lead to brittle tests which share too much. Tests should be self contained so changing one doesn't effect another.
Ryan, you are scary :) This is the third time I thought "Hmm... I need to learn about X" and then I look at iTunes and your screen cast JUST DOWNLOADED WITH THE EXACT SUBJECT I WAS GOING TO LOOK AT!!!
i agree that fixtures can be pretty brittle when there are a lot of interweaving dependencies between them, but testing without fixtures seems less DRY. seems like a case of choosing the lesser evil.
@dudzjosh, I normally use rSpec as well, and was tempted to use it for this screencast but decided to stick with what's built into Rails. You can apply basically the same principles no matter the framework.
While I do use rSpec, I still use Mocha for mocking/stubbing. I've had problems with their built in framework.
Also, I don't use mocking/stubbing nearly as much in the controller/view layers as they recommend. I find it's a lot of work with very little benefit. But that's more of my personal opinion so I probably won't make a screencast on it.
Hey really like the screen cast, just wondering what your thoughts are on Rspec vs Test::Unit.
I've pretty much started using only RSpec now and I really like the behavior driven approach.
Also it comes in built with stubbing and mocking.
When I used test unit I based some of my test structure on the Peepcode screencast on testing (really highly recommend). One of the things that it mentions to do is to stick class variables in to your test helper file with a hash of all values that will make a specific model valid. eg @@user_default_values = {:name => 'joe'}, this way you can make sure that these hashes are creating valid instances with a test in your test units and can reuse them in others tests such as functional tests or whatever. I suppose the only thing is the clarity issue with this technique where you have to look at a couple of files if you need to know exactly what your tests are doing.
This is fine approach, but it will display errors mixed with warnings in no particular order, and that is not good in my opinion. I prefer messages sorted in order of decreasing severity: [:error, :warning, :notice, :message]
I have some code that does it, but I written it at the very beginning of studying ruby/rails, so I will not posit it here, it is not pretty ;)
I'm using the will_paginate plugin myself for a while now and I really like it. However I've come across some problems while trying to paginate special queries (e.g. custom finder class-methods on your model). I've put up a summary in <a href="http://zargony.com/2007/07/21/paginating-special-queries/>this post in my blog</a>.
Ryan, nice example!
I'm wondering about the total_price method on cart.rb. As I don't have a similar code here (hey, it's already late now), I'm wondering if we write the method this way
def total_price
line_items.inject(0) {|memo,item| memo += item.full_price}
end
can speed up things a little, avoiding the to_a and sum methods. What you think?
Best regards.
THAT was gay ;)
@youtubesearcher: "hacking" through activerecord was the point of the exercise. Lots of these vids aren't necessarily about the most efficient or best way to get X done, but teaching techniques and ways of doing things that lots of us find useful.
@ryan: excellent vid. I think lots of people know what they can do with self.included, sort of, but maybe not exactly why. Thanks for all your work.
Hi ryan,
It was a good walk through... in a way also revising my OOPS concepts.. hehe
thanks for these screencasts.
i think it will be more ruby idiomatic if you did
def valid_with_disable_check?
return self.class.validation_disabled? ||
valid_without_disable_check?
end
rather than
def valid_with_disable_check?
if self.class.validation_disabled?
true
else
valid_without_disable_check?
end
end
check http://rubygarden.org/Ruby/page/show/RubyIdioms
regards and keep these gems
True enough, but it also does not require hacking thru activerecord and exposing yourself to breakage when a new rails release or patch comes out...but hey...both work and it's just a matter of personal preference
If I was gay I'd definitly would want to be your boyfriend!!!! Your casts are so seeeexy! For real!!!
@youtubesearcher, true. But this doesn't work for the convenient "create" and other methods related to saving models.
just pass false to model save, this saves without validations...like this
# will save person even though zip fails validation
person = Person.new
person.zip = "badzip"
person.save false
@brooke, the full quality feed should show all episodes on iTunes. The iPod version doesn't - I'll try to fix that.
Once again you have saved me hours of reading and days of trial and error! Bless you for your charity :-)
since people are talking about it here...
I'd love all the back issues in iTunes as well. Right now, i'm only seeing episode 33 on. Did the feed get reset to only allow the last 30 episodes?
Thanks for such a great screencast!
I have watched all your episodes so far and your hard work is greatly appreciated.
You have helped me greatly to jumpstart my web programming skills.
Your screencasts are simply the best I have found and their high quality makes every episode worth watching...
Keep up the great work!
Great screencast on extending AR!
My favorite way to modify this sort of behavior is using mocha to do a:
Product.any_instance.stubs(:valid?).returns(true)
I think flexmock has a similar new_instances method. Unfortunately rspec doesn't have this functionality yet which is why I use mocha.
@chivi, I'm using Final Cut Pro to edit them and Quicktime Pro to convert them.
@Zargony, right. You can also use "save(false)" to bypass validation. I often find myself using "create" more than "save" so it's convenient to have this plugin.
Ok, was my fault...
The database categories_posts shouldnt have an id field :)
Great screencast, Ryan. You've put up a good summary of how to easily change the behavior of any class. Of course it does not only work with ActiveRecord, but with virtually every class.
To disable validations, so far I was using save_without_validation! instead of save! (AR itself uses alias_method_chain to add validations), however your plugin might come in handy to disable validations in a larger context.
Btw, you're publishing new screencasts faster than I can watch them :)
Hi Ryan,
great job, congratulations!!!
Please, what program do you use for editing the video and export to .mov?
Im getting some problems...
Ive followed your tutorial step by step and the application tries to insert the 'id' field in categories_posts:
Mysql::Error: Duplicate entry '2' for key 1: INSERT INTO categories_posts (`post_id`, `id`, `category_id`) VALUES (19, 2, 2)
and it should be:
INSERT INTO categories_posts (`post_id`,`category_id`) VALUES (19,2)
Any help ?
Thanks
This is a jewel. I used most of these techniques before without fully understanding what's that self.included(base) thing is about. Now I know! And the example is well choosen. In a real app often it doesn't make sense to cramp the different aspects of a technique into one place. Here it seems quite reasonable.
Thank you!
Before your readers subscribe to this method, they should be aware of the performance issue.
http://m.onkey.org/2007/6/30/let-s-start-with-wtf
Apparently, nothing beats a good olf-fashioned block
Using the observer is a neat trick, but I can imagine it backfiring. Let's say you set up a rake task to import a bunch of users from a csv file. Well, all of those will receive an email as the User model is created. In some cases this may be what you want, but I prefer to have more control over when an email is sent. I don't want to spam someone accidentally. The act of sending an email seems more like a controller action to me.
The whole time I was wondering why you weren't using instance variables for the email, like in Acts As Authenticated, but you answered at the end.. I guess AAA is a bit out of date then?
Also, one thing I like about AAA is the user_observer where you can specify something like this
def after_create(user)
UserNotifier.deliver_signup_notification(user)
end
Seems like a more modular approach so you dont have to put it in multiple controllers. Any thoughts on this?
Ryan,
Ok, ignore me, found the problem, which is in my model definition.
Sometimes it just helps to talk. ;-)
Ryan, Thanks for this! I have a question for some weird behavior I'm seeing:
In my controller I'm doing a model.reload as you've implemented it, and render a "conflict" action. In the controller, if I 'puts' on any of the model variables before and after the reload, I can see the change, but the form never reflects the change. Doing a 'puts' in the form writes out the pre-update model information.
My form is a bit different in that I'm using a form_tag and :id => model_name, but as I understand it, it should reload the model from the db, correct? I'm missing something simple.
Thanks in advance for a great site!
Chuck, is it Mongrel that has the 60 sec timeout or Apache? I increased the timeout on Apache (I have several tasks for my app that can take a few hundred seconds, directory crawling, etc) and Mongrel seems to keep going until the task is done (but Apache may throw a "proxy mod error" if it goes too long, which is why I increased the time). And yes, its an internal website for logging people's work, so it's okay that it can take a long time on certain tasks, those tasks don't come up that often.
Great!
Thanks for the testing video. I really look forward to the next one. Maybe it'll even be rSpec?
Cheers,
@macovsky, good question. I believe you can create your own custom helper module (helpers/foo_helper.rb) and include that using "helper :foo" in the UserMailer class. This is very similar to how you would set up custom helper modules in a controller. Untested.
should have read the api docs before speaking. it's all right in there :-)
@ryan, what might be a helpful cast would be, how to send multi-part emails in Rails. Both plain text and HTML combined in one email.
Thx!
TYVM!
ActionMailer was the next thing on my TODO list also. ;)
I guess the path of learning things in Rails is quite similar for many people.
Very interesting! :D
@Ryan,
is there a way to share usual helper methods for emails' views? or to define them at one place?
@dave, @Ryan:
ActiveMailer is not asynchronous so it does hang if the smtp is busy but not if it's denying requests. The popular example is sending mass emails through ActiveMailer using Mongrels as the app server (because Mongrel has a 60 second timeout).
The way around this is to either configure smtp to queue mails (the default for most MTAs), or to use something like ar_mailer by Eric Hodel if you don't control your MTA.
http://blog.segment7.net/articles/2006/08/15/ar_mailer
Thanks Ryan,
Awesome tip, I've been using the instance variable technique for quite awhile now :D
@martin
I was referring to possibility of connection timeouts and the length it would take to connect to, transfer and complete the transaction between your rails app and the SMTP server you're trying to send mail thru. If your SMTP server isn't responding quickly, your user could experience a lag time.
Where as if you went with the sendmail option for actionmailer, sendmail, postfix, whatever your MTA is would accept the email, then try to send it out for you - adding it to a background process que, freeing up your app to continue on (in theory).
I think for me the sendmail option is a better one, but then you've gotta configure your MTA yourself (at least in my case on a VPS).
I'm far from close to launching my app, so for dev's sake, I'm sticking with SMTP option for now.
Thx Ryan!
@dave, @Ryan:
I'm not familiar with ActionMailer (I'm still new to Ruby/Rails), but any sane e-mail client (ActionMailer, in this case) will throw an error if the SMTP server you're trying to connect to is unavailable.
Messages are not delivered in real time. The client (ActionMailer) will hand the message over to the SMTP server, which in turn is responsible for delivering the message. The client's involvement ends there. The SMTP server will add the e-mail to it's queue and deliver it as soon as possible. If it can't (i.e. the receiving server is unavailable) it will stop trying after some time (usually a few days) and notify the sender by e-mail.
Short story: A broken mail server won't hang your application :)
@dave, good question. I believe ActionMailer will open a socket to the mail server right there and send the email (using net/smtp). If the mail server takes a while to respond then it might hang. I don't know for sure though. The best thing to do is try it out and see.
It would be really cool if ActionMailer had a separate queue, that way if the smtp server is down or anything it would throw it in the queue for later delivery.
Just curious. If you sent email via SMTP - that process actually sends the mail at the time of request and doesn't cue it up for later delivery correct? So if the SMTP server you're mailing thru takes forever to answer/deliver - you're user is stuck waiting as well?
good point, and i guess dryness is mainly important in case you want to change something somewhere but forget to also change it somewhere else. i guess with tests that isn't a huge deal because the worst that'll happen is that some test will fail. i guess it's much worse when something that is shared by a lot of tests breaks them all at once (and possibly in different ways).
@vlad, true, it is less dry. But, I'm finding the DRY principle isn't nearly as important in testing. There are some cases keeping tests DRY can improve readability and maintainability, but other times it will lead to brittle tests which share too much. Tests should be self contained so changing one doesn't effect another.
Ryan, you are scary :) This is the third time I thought "Hmm... I need to learn about X" and then I look at iTunes and your screen cast JUST DOWNLOADED WITH THE EXACT SUBJECT I WAS GOING TO LOOK AT!!!
Freeeeeeeeeeky.... but very, very, very cool :)
Thanks again
Mikel
i agree that fixtures can be pretty brittle when there are a lot of interweaving dependencies between them, but testing without fixtures seems less DRY. seems like a case of choosing the lesser evil.
@dudzjosh, I normally use rSpec as well, and was tempted to use it for this screencast but decided to stick with what's built into Rails. You can apply basically the same principles no matter the framework.
While I do use rSpec, I still use Mocha for mocking/stubbing. I've had problems with their built in framework.
Also, I don't use mocking/stubbing nearly as much in the controller/view layers as they recommend. I find it's a lot of work with very little benefit. But that's more of my personal opinion so I probably won't make a screencast on it.
Hey really like the screen cast, just wondering what your thoughts are on Rspec vs Test::Unit.
I've pretty much started using only RSpec now and I really like the behavior driven approach.
Also it comes in built with stubbing and mocking.
When I used test unit I based some of my test structure on the Peepcode screencast on testing (really highly recommend). One of the things that it mentions to do is to stick class variables in to your test helper file with a hash of all values that will make a specific model valid. eg @@user_default_values = {:name => 'joe'}, this way you can make sure that these hashes are creating valid instances with a test in your test units and can reuse them in others tests such as functional tests or whatever. I suppose the only thing is the clarity issue with this technique where you have to look at a couple of files if you need to know exactly what your tests are doing.
Cheers
Ryan. Yeh.
Sometimes I just ask weird questions :). Now I looked and got it. Edited paste too.
Without fixtures, code and understanding is clearer. Thanks for that railscast.
@Innu, I believe the counter cache column only works when records are saved to the database, so try using "create" instead of new/build.
How about this: http://pastie.caboo.se/81153
This is fine approach, but it will display errors mixed with warnings in no particular order, and that is not good in my opinion. I prefer messages sorted in order of decreasing severity: [:error, :warning, :notice, :message]
I have some code that does it, but I written it at the very beginning of studying ruby/rails, so I will not posit it here, it is not pretty ;)
Great screencast, thanks a lot.
I'm using the will_paginate plugin myself for a while now and I really like it. However I've come across some problems while trying to paginate special queries (e.g. custom finder class-methods on your model). I've put up a summary in <a href="http://zargony.com/2007/07/21/paginating-special-queries/>this post in my blog</a>.
http://pastie.caboo.se/81115
Well... I need a bit help with unit test. Testing counter_cache column.