...
end
end
This is the version that recent Java/C# converts seem to prefer. I'm not a big fan of this version because it requires me to change all the class method definitions if I change the name of the class.
...
end
end
I prefer this version. The syntax is concise and descriptive. When browsing a file of code, the use of
self.
makes it very clear that the method is a class method. This is the version I use by default.
protected
...
end
end
end
This is the version I use when I need to make the method protected. For more information on why this is necessary you can check my previous entry: Protected Class Methods
# http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
name, &blk
(class << self; self; end).instance_eval { define_method name, &blk }
end
end
hash.each do |method_name, result|
meta_def method_name do
result
end
end
end
responses :success => 20, :unreachable => 23
end
Service.success # => 20
Service.unreachable # => 23
This version is by far the most complicated to write and read. The justification for using this version is that you can create class methods that declaratively define other class methods. This version requires the use of define_method instead of def because variables from the surrounding context are necessary. I tend to only use this version when I'm metaprogrammatically defining other class methods.
instance_eval do
...
end
end
end
It's also possible to define class methods inside an instance_eval sent to a class. For an explaination of why this works check out instance_eval and class_eval method definitions. As the linked entry states, I have run into this by accident, but I don't think I've ever actually used instance_eval to define class methods on purpose.
Are there ways that you define class methods that I've left off. If so, please let me know how and why in the comments (with an explanation inline or a link to a blog post on your site).
It looks mightily confusing when the syntax highlighting has hidden the underscores in "meta_def" and "instance_eval". For a minute I thought there was some trick I wasn't aware of, but the html source revealed that there indeed are underscores in both.
ReplyDeleteHi Jay,
ReplyDeleteOne other way could be putting your methods in a module and then in your class : extend ClassMethods
If you're looking for a better way for your readers to submit code may I suggest cross-posting to http://refactormycode.com/ and using the trackback feature.
Two more class_eval examples are http://snippets.dzone.com/posts/show/3378 and http://snippets.dzone.com/posts/show/2786
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteI prefer the singleton class way. Something about seeing 'def self.' over and over gets to me, but then again I try to use self as a receiver only when necessary.
ReplyDeleteIt also lets you do other things more naturally, like declaring some methods private (not protected, since that doesn't make sense for a class method), and creating aliases.
Hi Jay, I somehow seem to remember that the Person.class_meth form used to bomb when invoked via a subclass (sample code below). I tried it on Ruby 1.8.6 and it doesn't bomb anymore. Did it ever bomb in a previous Ruby version or am I day-dreaming??
ReplyDeleteEg:
class Person
def Person.my_class_meth
"hi"
end
def self.another_class_meth
"bye"
end
end
class Employee < Person
end
Employee.my_class_meth # Did this ever bomb with undefined method?!
Employee.another_class_meth # This always worked
As far as I know it always worked, but since I don't use that form I'm not the authority on it.
ReplyDeleteOn my first project we used Ruby 1.8.4 and it seemed to work fine...
Another drawback of the first form which the others avoid is that the class name occurs multiple times in the source for the class definition. If it needs to be changed for any reason (e.g. you realise it was a bad choice, or you want to clone the class as a start-point for a similar one) it has to be changed in multiple places. Not a huge effort, but irksome.
ReplyDeleteVery helpful information, thank you.
ReplyDelete"This [, Class.method,] is the version that recent Java/C# converts seem to prefer. I'm not a big fan of this version because it requires me to change all the class method definitions if I change the name of the class."
ReplyDeleteI think it's all a matter of perspective. Class.method seems less magical and makes more sense to me. Especially since Self changes so readily.
Since many Ruby purists frown on this I avoid doing it but I dislike Self.method as it is less clear to me. ESPECIALLY when refactoring; contrary to your complaint. If I were to use Class.method I would instantly know what class I'm refactoring and wouldn't have to worry about some recursive reference to Self that might or might not be in play. Using the class name is more precise while using self muddies the waters.
Also when you say, "...it requires me to change all the class method definitions if I change the name of the class." Is just plain silly and points to the weaknesses of using Textmate and other such editors instead of full-fledged IDE's. You're now in a mindset that refactoring is a chore when it should be the focus of good Behaviour-Driven Development. All you have to do is select your class, press the find/replace key combo, and swap out your class name with the new name. Easy.
That being said I actually do use Textmate for Ruby and Rails development simply because there currently are no good IDE's.