You can sometimes remove a lot of duplication by generating methods dynamic. In this episode I will show you how to create a plugin which does exactly that.
Module#define_method and Object#instance_variable_set are advanced for me, your screencast show a good example how to use them,now I think I underground them,thanks !~
Is there any way around having to restart the server to get it to reload the plugin? Any special "i'm writing a plugin now, please reload it's files" mode you can run in?
We're all spoiled by the magic reloading that rails does for us :)
@Trueke, you can probably do this by overriding method_missing on NilClass, but I don't recommend it. This may break some things and changes the behavior of Ruby that other programs rely on. Instead, just do a nil check anytime you need to call a method on an object that could be nil.
@infrid, glad you enjoy the screencasts and thanks for the encouraging comments! I enjoy doing them. :)
Brilliant as usual. Thank you for putting this great resource out there. Your commitment to the RoR community and learning/teaching in general is to be highly commended.
Off topic, how are creating 'railscasts' while at RailsConf? Your ability to crank out high quality content at such a rapid pace never ceases to amaze me.
Thanks for another great one Ryan, I'm really impressed how quickly and regularly you release these at such high quality. I always get excited when I see the little (1) next to railscasts in my RSS reader.
You could use module_eval/class_eval instead of define_method, right? Might make the code a little more readable (i.e., no calls to instance_variable_set, write_attribute, etc.)
This was by far the best episode so far. This is a topic I was going to delve into very soon but you gave me a huge head start. One thing that might be neat to cover would be taking this a step further and adding a validates_X method to ActiveRecord::Base as well to validate the date string (validates_date_string or something like that). I would think of this as a more elegant solution than simply using the validate hook (though I guess this is a fairly straightforward process given the information already presented so maybe this isn't really necessary).
@Geoff, good point. I don't like putting code into a string and I didn't realize you could pass a block to class_eval and module_eval. You're right, that would be cleaner. Thanks for the suggestion. :)
Update: actually I need to define the name of the method, so I don't think it's possible to do this with module_eval without passing a string. I avoid that whenever possible so I guess define_method is the right way to go for me.
@Ross, thanks for the encouraging comments. Creating a validates_* method is a great idea. I'll try it out and consider creating an episode on it.
Great screencast! :)
Making plugins is actually easier than I thought :D (which doesn't surprise me all that much, considering the whole philosophy of rails).
One question though, why use write_attribute(name) and read_attribute(name) instead of send(name.to_sym) and send("#{name}=".to_sym)? That way if the user is further rewriting due_at and due_at= for some preprocessing they wouldn't loose there changes :)
(Maybe I'm missing on something about why it's not useful to do so, though)
I try to use plugins inside and outside of rails so putting the reference to ActiveRecord::Base at the bottom of the plugin/lib file rather than in the init.rb is a bit more portable.
Also, I agree with Nicolas about using either send or respond_to? rather than read/write_attribute. this de-couples the plugin from the database and makes it easier to unit test.
@Mike, although you can't do that with define_method, you can put the code in a big string to begin with and then use class_eval when you want to evaluate it. That way it's in a string so you can output it however you want. See Geoff's comment above for an example.
Just heard (at RailsConf up here) that the "require" statement isn't necessary in the init.rb file, so you can probably remove it.
I like this episode and hope you make screencast about plugin because it hard for me.
Module#define_method and Object#instance_variable_set are advanced for me, your screencast show a good example how to use them,now I think I underground them,thanks !~
Great episode.
Is there any way around having to restart the server to get it to reload the plugin? Any special "i'm writing a plugin now, please reload it's files" mode you can run in?
We're all spoiled by the magic reloading that rails does for us :)
BrianC, I think you can have plugins auto-reload by adding this line to your plugin's init.rb file:
Dependencies.load_once_paths.delete(lib_path)
See the post by technoweenie for details:
http://weblog.techno-weenie.net/2007/1/26/understanding-the-rails-plugin-initialization-process
Great screencast - I always wanted to know about plugins but couldn't find much information on them. This helped.
Hi!
Watching that webcast I got a question;
It's possible to extend nil object to make nil.anything returns nil?
I mean,
a = nil
b = a.anything
result: nil, not an error.
Did I explain myself?
found these today and am so overwhelmed that I put down tools and gave myself an afternoon of training matrix style.
WOW - almost every single one has been a gem (no pun intended).
great length, great topics, really elegant solutions. I'd happily pay to view longer screencasts, if you do them!
Thanks so much. Really really appreciate your work.
@Trueke, you can probably do this by overriding method_missing on NilClass, but I don't recommend it. This may break some things and changes the behavior of Ruby that other programs rely on. Instead, just do a nil check anytime you need to call a method on an object that could be nil.
@infrid, glad you enjoy the screencasts and thanks for the encouraging comments! I enjoy doing them. :)
Ryan,
Brilliant as usual. Thank you for putting this great resource out there. Your commitment to the RoR community and learning/teaching in general is to be highly commended.
Off topic, how are creating 'railscasts' while at RailsConf? Your ability to crank out high quality content at such a rapid pace never ceases to amaze me.
Keep up the great work.
Thanks for another great one Ryan, I'm really impressed how quickly and regularly you release these at such high quality. I always get excited when I see the little (1) next to railscasts in my RSS reader.
Great stuff!
You could use module_eval/class_eval instead of define_method, right? Might make the code a little more readable (i.e., no calls to instance_variable_set, write_attribute, etc.)
This was by far the best episode so far. This is a topic I was going to delve into very soon but you gave me a huge head start. One thing that might be neat to cover would be taking this a step further and adding a validates_X method to ActiveRecord::Base as well to validate the date string (validates_date_string or something like that). I would think of this as a more elegant solution than simply using the validate hook (though I guess this is a fairly straightforward process given the information already presented so maybe this isn't really necessary).
@Geoff, good point. I don't like putting code into a string and I didn't realize you could pass a block to class_eval and module_eval. You're right, that would be cleaner. Thanks for the suggestion. :)
Update: actually I need to define the name of the method, so I don't think it's possible to do this with module_eval without passing a string. I avoid that whenever possible so I guess define_method is the right way to go for me.
@Ross, thanks for the encouraging comments. Creating a validates_* method is a great idea. I'll try it out and consider creating an episode on it.
@Ryan, your screencasts are as great as ruby/rails language and framework! Keep Your good work :). Greetings from Poland.
Right, module_eval would require a string evaluation for this case, so if you're uncomfortable with that, define_method is indeed the way to go.
For comparison, here's a module_eval version:
module_eval <<-EOV
def #{name}_string
#{name}.to_s(:db)
end
EOV
The Rails source code uses module_eval/class_eval to define macros like acts_as_list, for whatever that's worth...
Great screencast! :)
Making plugins is actually easier than I thought :D (which doesn't surprise me all that much, considering the whole philosophy of rails).
One question though, why use write_attribute(name) and read_attribute(name) instead of send(name.to_sym) and send("#{name}=".to_sym)? That way if the user is further rewriting due_at and due_at= for some preprocessing they wouldn't loose there changes :)
(Maybe I'm missing on something about why it's not useful to do so, though)
Again, congrats on the great screencast :)
Excellent point Nicolás! Doing "send" is better because of the point that you mentioned. Thanks for bringing that up.
I try to use plugins inside and outside of rails so putting the reference to ActiveRecord::Base at the bottom of the plugin/lib file rather than in the init.rb is a bit more portable.
Also, I agree with Nicolas about using either send or respond_to? rather than read/write_attribute. this de-couples the plugin from the database and makes it easier to unit test.
thanks for your great work!
Ryan,
Is there a way to display the generated code from define_method ?
Mike
I didn't phrase that too well.
I mean display the code that define_method generates.
Mike
Display it where? Do you mean out put it as a string to the log or something? I don't think that's possible with define_method.
Yes, that's what I meant, i.e. access the generated code to output it anywhere.
Mike
@Mike, although you can't do that with define_method, you can put the code in a big string to begin with and then use class_eval when you want to evaluate it. That way it's in a string so you can output it however you want. See Geoff's comment above for an example.
Well actually with Geof's example the string is still holding the "source" / meta code.
What I want is the resulting "compiled" / generated code
Mike
@mike, the variables in the string are evaluated, so the string contains the resulting code. See this pastie for example:
http://pastie.caboo.se/78317
I am writing a plugin and would. My friends also would like to use it, however I haven't found any tutorials on how to "correctly" deploy a plugin.
Any pointers?
great screencast. If only i could think of a plugin to actually write.
Спасибо! За скреенкасты которые вы создаете мне нравиться главное не останавливайтесь на достигнутом! Россия рулеzzz =)
i didn't understand why *names is uses instead of names
This episode has been updated for Rails 5 as a blog post. Date Validation Gems in Rails 5