Saturday, October 20, 2007
Ruby: Defining Class Methods
There are several ways to define class methods in Ruby.
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.
I prefer this version. The syntax is concise and descriptive. When browsing a file of code, the use of
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
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.
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).
...
end
endThis 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
endI 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
endThis 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 # => 23This 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
endIt'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).
Labels: class methods, ruby
Friday, April 20, 2007
Ruby: Class Methods
It's very common when moving from Java or C# to be weary of Ruby's class methods. Many people have written about how static methods are evil in C# and Java, but do those arguments apply to Ruby?
Complaints
At this point I ran out of complaints, but it's been over a year since I've touched C#. If you have complaints of your own that support or oppose this entry, please drop them in the comments.
Another thought about static methods is that they tend not to encourage proper object design. For example, a method that removes characters from a string is probably best served on the string itself.
So what exactly is a class method?
In fact, you can define a class method various ways, but the result is always an instance method on the singleton class.
Thanks to James Lewis, Matt Deiters, and Mike Ward for help on this entry.
Complaints
- Static methods cannot participate in an Interface. Ruby: No interfaces, thus irrelevant.
- Static methods cannot be abstract. Ruby: No concept of abstract, also irrelevant.
- Static methods are not polymorphic. Ruby: doesn't seem like it applies since you don't declare type in Ruby. Also, you don't access class methods directly from an instance.
- Static methods are hard to test. Ruby: Class methods are as easy to test and mock as any instance method. Perhaps this is because they are simply instance methods of the sington class.
At this point I ran out of complaints, but it's been over a year since I've touched C#. If you have complaints of your own that support or oppose this entry, please drop them in the comments.
Another thought about static methods is that they tend not to encourage proper object design. For example, a method that removes characters from a string is probably best served on the string itself.
class StringYou see this type of class method much less in Ruby since Ruby has open classes. But, it's good to remember to put methods on the object whose data is being manipulated when possible.
# an example class method
def self.delete(string, characters)
...
end
# a better instance method
def delete(characters)
...
end
end
So what exactly is a class method?
class ParserFrom the example, you could say a class method is a singleton_method. Where do singleton methods live?
def self.process(script)
# ...
end
end
Parser.singleton_methods.inspect #=> ["process"]
Singleton (class) methods are actually all instance methods of the singleton class.
class Parser
def self.process(script)
# ...
end
def self.singleton
class << self; self; end
end
end
Parser.singleton.instance_methods(false).inspect
#=> ["singleton", "new", "superclass", "allocate", "process"]
In fact, you can define a class method various ways, but the result is always an instance method on the singleton class.
class ParserOf course, the process method is also inherited by subclasses and the singleton classes of subclasses.
def self.self_process(script)
# ...
end
def Parser.parser_process(script)
# ...
end
class << self
def singleton_process(script)
# ...
end
end
def self.singleton
class << self; self; end
end
end
Parser.singleton.instance_methods(false).inspect
#=> ["singleton_process", "parser_process", "new",
"superclass", "allocate", "singleton", "self_process"]
class ParserIn a later post I'll talk about where those methods are stored in the underlying C code.
def self.process(script)
# ...
end
end
class Object
def self.singleton
class << self; self; end
end
end
class HtmlParser < Parser
end
HtmlParser.singleton_methods.inspect
#=> ["singleton", "process"]
HtmlParser.singleton.instance_methods(false).inspect
#=> ["singleton", "new", "superclass", "allocate", "process"]
Thanks to James Lewis, Matt Deiters, and Mike Ward for help on this entry.
Labels: class methods, ruby, singleton class, singleton methods, static methods


