It seems there are 3 ways of defining class methods (at least in common
usage):
Defining a method on self:
def self.method
Opening up self and defining the methods directly:
class << self
def method
…
end
Placing class methods into a module, and extending a class with that:
module ClassMethods
def method
…
end
extend ClassMethods
–
Which of these do you use and which do you prefer? Do you use all three?
Do
you think class << self is fugly and confusing?
I used to use class << self quite frequently and have been shifting to
using
modules when dealing with a large number of class methods. I can’t
really
say I’m a huge fan.
Which of these do you use and which do you prefer? Do you use all three? Do
you think class << self is fugly and confusing?
I use all three, depending on the situation. Generally, I use method 2
(class << self), but if I have an idea that I will be needing only
one, or two class methods (like Module.included) I use method 1 (def
self.foo). If it makes sense to separate the class methods from the
core definition of a class (like helper/utility functions), I define
them in a module in a separate file.
I do not find class << self to be ugly and confusing :).
I picked up the following pattern going through DataMapper’s code (or,
was it Ruby Best Practices?).
module Foo
def instance_method; puts “im”; end
module ClassMethods
def class_method1; puts “cm” end
end
def self.included(klass)
klass.extend(ClassMethods)
end
end
class Moo
include Foo
end
Foo.class_method #=> cm
Foo.new.instance_method #=> im
Foo.new.class_method #=> NoMethodError
A use case of this pattern for me was defining a Helper module for
Sinatra (modular) app where some helpers will be used in the
request-response context, while some in the context of the class that
inherits from Sinatra::Base.
It seems there are 3 ways of defining class methods (at least in common
usage):
Defining a method on self:
Opening up self and defining the methods directly:
Placing class methods into a module, and extending a class with that:
Explicit definition
def C.m
…
end
i usually use this one since
1 it’s visibly specified. The definition matches the use.
2 don’t need to press pgup just to see who is self
3 if i change class name, it self checks itself since i need to retype.
4 less prone to self abuse
It seems there are 3 ways of defining class methods (at least in common
usage):
There’s lots of ways to add a class method; Ruby is not a very
prescriptive language. Two more interesting examples that are used:
there’s instance_eval on a Class instance:
X = Class.new
X.instance_eval do
def foo; “calling #foo!”; end
end
X.foo
=> “calling #foo!”
or #class_eval on the class of your Class instance (which will be Class):
X.class.class_eval do
def bar; “calling #bar!”; end
end
X.bar
=> “calling #bar!”
It’s not very complicated, but class_eval on a Class instance is
typically used only in some rather advanced (and/or cryptic)
metaprogramming, so you don’t see it floating around a lot.
I don’t mean it as a put-down; I suspect the only person I’m putting
down is myself. But I don’t find that class methods actually come up
much in my Ruby coding; when I find myself coding one I tend to stop and
think hard about whether I actually need it.
And when I do use one it’s always your type (1). Why would I want to
define a class method anywhere else but in the class? I do understand
that for DSLs, all the rest of this syntax is really useful. But –
showing my ignorance here – is it really of any practical value
elsewhere?
Indeed. Class methods in Ruby are a code smell: not always wrong, but
you should always think about why you’re choosing a class-level method
instead of instance-level.
I don’t know if I’d go so far as to say they’re a code smell. Without
class methods a number of very useful Ruby DSLs wouldn’t exist in
their current form (RSpec, Cucumber, Rails, etc., just to name a few).
I agree that they can be abused or misused, but that’s true of pretty
much any construct in any language, isn’t it?
I’d probably arrange my skepticism of a class method from lowest to
highest in this way, depending on its features:
part of a DSL (describe, Before, callback { ... }, etc.)
repository pattern (Widget.find(…))
factory pattern (Widget.orange # => )
method takes more than a parameter or two
non-factory class method with variable args
instance of the class method’s class is in the arguments/params
list (often very questionable)
On Thu, Feb 17, 2011 at 4:41 AM, Shadowfirebird [email protected] wrote:
I don’t mean it as a put-down; I suspect the only person I’m putting down is
myself. But I don’t find that class methods actually come up much in my Ruby
coding; when I find myself coding one I tend to stop and think hard about whether
I actually need it.
Indeed. Class methods in Ruby are a code smell: not always wrong, but
you should always think about why you’re choosing a class-level method
instead of instance-level.
I think most of the class/module-level methods I write are “macros” -
e.g. methods along the lines of Ruby’s #attr_accessor.
It depends on whether and how you want to allow methods to be
overridden. Using #extend and ‘def self.xxx’ are not the same, viz:
module A
def run
puts "A"
end
end
class Foo
extend A
end
class Bar
extend A
end
Foo.run # => "A"
Bar.run # => "A"
module B
def run
puts "B"
end
end
class Foo
def self.run
puts "foo"
end
end
Foo.extend(B)
Bar.extend(B)
Foo.run # => "foo" - extend does not
override a def self.method
Bar.run # => “B” - but it does override a
previously defined extension
class Foo
class << self
def run
puts "bar"
end
end
end
Foo.run # => "bar" - using 'class << self;
On Thu, Feb 17, 2011 at 2:41 AM, Shadowfirebird [email protected]wrote:
And when I do use one it’s always your type (1). Why would I want to define
a class method anywhere else but in the class? I do understand that for
DSLs, all the rest of this syntax is really useful. But – showing my
ignorance here – is it really of any practical value elsewhere?
Well, more specific than DSLs: metaprogramming, and DSLs that generate
code.
Some of the more abject design patterns, such as the factory pattern,
can
often be avoid thanks to a combination of Ruby’s dynamism and class
methods.
The class is also a great place to stick state shared by all instances
of
that class, such as the configuration details for accessing a particular
network service.
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.