Defining class methods

It seems there are 3 ways of defining class methods (at least in common
usage):

  1. Defining a method on self:

def self.method

  1. Opening up self and defining the methods directly:

class << self
def method

end

  1. 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.

On Thu, Feb 17, 2011 at 1:17 AM, Tony A. [email protected]
wrote:

def method
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 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.

On Thu, Feb 17, 2011 at 3:47 AM, Tony A. [email protected]
wrote:

It seems there are 3 ways of defining class methods (at least in common
usage):

  1. Defining a method on self:
  2. Opening up self and defining the methods directly:
  3. Placing class methods into a module, and extending a class with that:
  1. 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 :slight_smile:
3 if i change class name, it self checks itself since i need to retype.
4 less prone to self abuse :slight_smile:

best regards -botp

best regards -botp

On Wed, Feb 16, 2011 at 4:37 PM, Anurag P.
[email protected] wrote:

  1. Defining a method on self:

def self.method

This one. That way you can always tell simply by looking at the method
definition that it is a class/module-level method.

On Wed, Feb 16, 2011 at 9:55 PM, botp [email protected] wrote:

  1. Explicit definition

def C.m

end

I avoid this one because it makes class renames more of a chore.

On Thu, Feb 17, 2011 at 11:14 AM, Avdi G. [email protected]d
wrote:

I avoid this one because it makes class renames more of a chore.

Indeed. and that’s what i like i about it. makes me think more on the
value of naming :slight_smile:

best regards -botp

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.

~ jf

John F.
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/

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)

~ jf

John F.
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/

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.

On Wed, Feb 16, 2011 at 7:47 PM, Tony A. [email protected]
wrote:

def method
extend ClassMethods

Tony A.
Medioh! Kudelski

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;

def method’ … same as ‘def self.method’

Personally, I’ve come to prefer using #extend.

Regards,
Sean

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.

| Privacy Policy | Terms of Service | Remote Ruby Jobs