-4

I'm having trouble with nice and beautiful code. Basically, if I define a class that won't be instantiated at all, I'd do something like this:

class Foo
  def self.bar
    # Do something
  end
end

but this syntax bothers me (having to put self. in every method), so singleton solves my problem:

class Foo
  class << self
    def bar
      # Do something, but in better code
    end
  end
end

In both ways I can do Foo.bar and it'll do something, but the first one doesn't look good and in the second one I still don't get how singleton works exactly, so better be careful and not to use it.

So, in the end... What I'd like to ask: is there a way to do Foo.bar other than the mentioned above?

PuckXY
  • 59
  • 3
  • 3
    If the syntax of a totally normal part of the language “bothers” you then I suggest rethinking your position, because the language ain’t gonna change, and the people who write in that language ain’t gonna change, and millions of lines of code already written in that language ain’t gonna change. – anothermh Dec 18 '18 at 21:09
  • It's a valid point, but see, there may be many ways to do this, and I'd like to see them so I can decide which one is the best one for some situation, you know? Say in some situation putting self in the methods makes sense but there's a better way to do it... That's what I'm looking for, you know? Trying to learn more about the language here, sorry if it seemed rude or something ;D – PuckXY Dec 18 '18 at 21:14
  • 1
    Those are the two most common ways of defining class methods. Beyond some extremely esoteric metaprogramming tricks (and I can't think of any off the top of my head), that's it. Doing anything else makes your code more complex and harder to understand. Besides, the [style guide](https://github.com/rubocop-hq/ruby-style-guide#def-self-class-methods) makes it clear that the preferred syntax is `self.method`, and on top of that you should [use modules and not classes](https://github.com/rubocop-hq/ruby-style-guide#modules-vs-classes) in your given example. – anothermh Dec 18 '18 at 21:19
  • 2
    I would always go with the `def self.bar` version. IMHO – especially when you have many class methods – is it much easier to read and understand. Btw. when your class won't be instantiated at all you might want to consider a module instead. – spickermann Dec 18 '18 at 21:19
  • @spickermann you would go with `self.method_name` when you have many class methods? I have always taken the approach of abstracting class methods out into `class ClassName; module ClassMethods; #...; end; end;` when I have many of them because I find the encapsulation to be nicer and more readable and a simple `extend ClassMethods` takes care of the implementation. Also I find it lends to organization and discovery when I have `class_name.rb` and `class_name/class_methods.rb` – engineersmnky Dec 18 '18 at 21:27
  • Personally, I go with either the `class << self` or `extend ClassMethods` (as @engineersmnky says) approach. In my personal aesthetic, `self.method` is yucky. But, I'm an odd one. – jvillian Dec 18 '18 at 21:57
  • @engineersmnky I would also use ‘self.method’. The other way you have one more tab, and you can’t tell if it’s static or not only from reading the function, which I learned that it makes it harder to maintain (at least for me). As been said, it’s good for meta programming,. One pitfall for this though, is that you can’t define private methods. I’m going with hybrid solution, using mostly ‘class << self’ only for private methods. I find it’s better to read the code, which takes most of my time anyway – oren Dec 18 '18 at 22:02
  • @oren nothing is static :) – engineersmnky Dec 18 '18 at 22:04
  • @engineersmnky oops meant class level methods :) Actually now I’m wondering if it’s wrong to use ‘class << self’ for private methods while other method are ‘self.method’ – oren Dec 18 '18 at 22:26
  • 1
    You could also write `singleton_class.define_method(:bar) { puts "me, bar" }`. That's normally only used to create methods dynamically, but it does read well. Perhaps Ruby could benefit from keywords `class_methods` and `instance_methods`, used in conjunction with `public`, `protected` and `private`. @engineersmnky, what about plumbing technology? Where I live it hasn't changed much in decades. – Cary Swoveland Dec 18 '18 at 22:30
  • 1
    @CarySwoveland ain't broke don't fix it :P – engineersmnky Dec 19 '18 at 00:39

2 Answers2

1

Here's an example of an esoteric metaprogramming technique that I mentioned in the comments:

module Foo
  instance_eval do
    def bar
      'baz'
    end
  end
end

Now call the new method:

Foo.bar
=> 'baz'

instance_eval's use is unintuitive because it's meant for defining class methods, whereas class_eval is for instance methods.

There's a similar approach for the technique Cary mentioned (although he used define_method which is for instance methods versus define_singleton_method for class methods):

module Foo
  define_singleton_method(:bar) do
    'baz'
  end
end

Calling the method works the same:

Foo.bar
=> 'baz'

But again, these are advanced techniques that you'll never see used in this way, because there are much simpler standard ways of accomplishing the same task (and you gave two examples in your question). If you tried defining all your class methods this way it would be ugly and repetitive and confusing to anyone reading it.

The techniques shown are typically used by code outside the defined module/class so that you can externally manipulate it without having to directly modify the source code for it, sometimes referred to as monkey patching.

For example, if the module is defined with no methods:

module Foo
end

You could in some other code anywhere else say Foo.instance_eval {} or Foo.define_singleton_method(:bar) {} and it would apply to Foo thereafter. This is useful when you want to frustrate the author of Foo's attempt at encapsulation.

And in case I'm not being clear, the examples I've shown here are what you should not do. They're used when dynamic changes to classes/modules are needed. That's not what you described.

anothermh
  • 9,815
  • 3
  • 33
  • 52
1

(this was already mentioned by engineersmnky in the comments)

You could extend your class with a module that holds your class methods (that module can be nested right within your class):

class Foo
  module ClassMethods
    def bar
      # Do something
    end
  end

  extend ClassMethods
end

You can also move it outside your class, even in its own file (this can be useful structure-wise or if there are many class methods):

# foo/class_methods.rb
class Foo
  module ClassMethods
    def bar
      # Do something
    end
  end
end

# foo.rb
class Foo
  extend ClassMethods
end

Methods defined via extend can even be overridden in the base class:

class Foo
  module ClassMethods
    def bar
      123
    end
  end
end

class Foo
  extend ClassMethods

  def self.bar
    super * 2
  end
end

Foo.bar #=> 246

This is especially useful if you want to add custom functionality when extending multiple classes with the same module (think of shared class methods).

Stefan
  • 109,145
  • 14
  • 143
  • 218