John Nunemaker recently blogged about the various ways to define class methods in Ruby, giving these three alternatives:
# Way 1
class Foo
def self.bar
puts 'class method'
end
end
# Way 2
class Foo
class << self
def bar
puts 'class method'
end
end
end
# Way 3
class Foo; end
def Foo.bar
puts 'class method'
end
What's your preferred way to do this?
Do you prefer something other than those above?
If you use more than one way, under what circumstances do you use them?
I consistently use Way 1:
class Foo
def self.bar
puts 'class method'
end
end
It's not verbose, and it keeps the method in the same context of the class.
I generally prefer def self.foo for single methods, and class << self for long stretches of class methods. I feel it makes the distinction between the class method part and the instance method part of the class definition.
I prefer Way 1 as it isn't context sensitive. I dislike jumping into the middle of a file and then having to scroll up or down to see if the indentation means I'm in a class << self block or if it's just a nested module.
Agree with most of the users. I tend to use primarily the
# Way 1
class Foo
def self.bar
puts 'class method'
end
end
There are some small differences, if I recall correctly, that are shown on the Pragmatic Programmers Metaprogramming talks (which I recommend), which relate to how the class code is called and executed.
They were quite small, though and mostly things we won't have to deal with on a normal basis. Will see if I can check them out and post it.
I view << for adding a method as too unusual (though I happily use << with strings and IO).
I avoid Foo.bar because it means repeating yourself.
I use Way #3, but I think Way #1 is great also. It depends on your usage. If you want your code to be "cut/pastable" into other modules and classes, then Way #1 is better. I use Way #3 to actually make it more of pain to cut/paste code, b/c Ruby's mantra is "don't repeat yourself" so you shouldn't cut/paste code very often..
Related
This is essentially a snippet from Ruby Metaprogramming 2. In the section they gloss over this example but there isn't really an explanation.
module MyRefinement
refine MyClass do
def my_method
"refined"
end
end
end
class MyClass
def my_method
"original"
end
def another_method
my_method
end
end
using MyRefinement
obj = MyClass.new
puts obj.my_method #=> "refined"
puts obj.another_method #=> "original"
Why doesn't the refinement apply when you call my_method from another method?
It avoids "leaky" refinements, e.g., the refinement applies specifically to the method you refine.
http://yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice/
Very near the bottom this functionality is explained; nutshell:
[the] refinement should not leak [...]. If it did, it would mean that any call into any method could leak a refinement into that method, which is the opposite of the purpose of the feature.
refine keyword use to Refinements of the class locally. It's mean we can monkey patch any method by refinement of the class.
In your case, the process of refine/redfined/monkey patch only active when the method get call directly. Also Refinements are lexical in scope. When control is transferred outside the scope the refinement is deactivated.
To get better insight, read the scope part of refinements from here: Refinements
I am extending an existing library by creating a child class which extends to the library class.
In the child class, I was able to test most of functionality in initialize method, but was not able to mock super call. The child class looks like something like below.
class Child < SomeLibrary
def initialize(arg)
validate_arg(arg)
do_something
super(arg)
end
def validate_arg(arg)
# do the validation
end
def do_something
#setup = true
end
end
How can I write rspec test (with mocha) such that I can mock super call? Note that I am testing functionality of initialize method in the Child class. Do I have to create separate code path which does not call super when it is provided with extra argument?
You can't mock super, and you shouldn't. When you mock something, you are verifying that a particular message is received, and super is not a message -- it's a keyword.
Instead, figure out what behavior of this class will change if the super call is missing, and write an example that exercises and verifies that behavior.
As #myron suggested you probably want to test the behavior happening in super.
But if you really want to do this, you could do:
expect_any_instance_of(A).to receive(:instance_method).and_call_original
Assuming
class B < A
def instance_method
super
end
end
class A
def instance_method
#
end
end
Disclaimer expect_any_instance_of are a mark of weak test (see):
This feature is sometimes useful when working with legacy code, though
in general we discourage its use for a number of reasons:
The rspec-mocks API is designed for individual object instances, but
this feature operates on entire classes of objects. As a result there
are some semantically confusing edge cases. For example, in
expect_any_instance_of(Widget).to receive(:name).twice it isn't clear
whether a specific instance is expected to receive name twice, or if
two receives total are expected. (It's the former.)
Using this feature is often a design smell. It may be that your test is trying to do too much or that the object under test is too
complex.
It is the most complicated feature of rspec-mocks, and has historically received the most bug reports. (None of the core team
actively use it, which doesn't help.)
A good way to test this is to set an expectation of some action taken by the superclass - example :
class Some::Thing < Some
def instance_method
super
end
end
and the super class:
class Some
def instance_method
another_method
end
def self.another_method # not private!
'does a thing'
end
end
now test :
describe '#instance_method' do
it 'appropriately triggers the super class method' do
sawm = Some::Thing.new
expect(sawm).to receive(:another_method)
sawm.instance_method
end
end
All This Determines Is That Super Was Called On the Superclass
This pattern's usefulness is dependent on how you structure your tests/what expectations you have of the child/derivative class' mutation by way of the super method being applied.
Also - pay close attention to class and instance methods, you will need to adjust allows and expects accordingly
YMMV
A bit late to this party, but what you can also do is forego using the super keyword and instead do
class Parent
def m(*args)
end
end
class Child < Parent
alias super_m m
def m(*args)
super_m(*args)
end
end
That way your super method is accessible like any other method and can e.g. be stubbed like any other method. The main downside is that you have to explicitly pass arguments to the call to the super method.
When it comes to run time introspection and dynamic code generation I don't think ruby has any rivals except possibly for some lisp dialects. The other day I was doing some code exercise to explore ruby's dynamic facilities and I started to wonder about ways of adding methods to existing objects. Here are 3 ways I could think of:
obj = Object.new
# add a method directly
def obj.new_method
...
end
# add a method indirectly with the singleton class
class << obj
def new_method
...
end
end
# add a method by opening up the class
obj.class.class_eval do
def new_method
...
end
end
This is just the tip of the iceberg because I still haven't explored various combinations of instance_eval, module_eval and define_method. Is there an online/offline resource where I can find out more about such dynamic tricks?
Ruby Metaprogramming seems to be a good resource. (And, linked from there, The Book of Ruby.)
If obj has a superclass, you can add methods to obj from the superclass using define_method (API) as you mentioned. If you ever look at the Rails source code, you'll notice that they do this quite a bit.
Also while this isn't exactly what you're asking for, you can easily give the impression of creating an almost infinite number of methods dynamically by using method_missing:
def method_missing(name, *args)
string_name = name.to_s
return super unless string_name =~ /^expected_\w+/
# otherwise do something as if you have a method called expected_name
end
Adding that to your class will allow it to respond to any method call which looks like
#instance.expected_something
I like the book Metaprogramming Ruby which is published by the publishers of the pickaxe book.
I have a large class with lots of methods and it's starting to get a bit unorganized and hard to navigate. I'd like to break it up into modules, where each module is a collection of class and instance methods. Perhaps something like this:
UPDATE: I've now realized that this is a pretty poor example. You probably wouldn't want to move validations or attributes out of the core class.
class Large
include Validations
include Attributes
include BusinessLogic
include Callbacks
end
After reading Yehuda's post about Better Ruby Idioms, I'm curious how others are tackling this problem. Here's the two methods I can think of.
First Method
module Foo
module Validations
module ClassMethods
def bar
"bar"
end
end
module InstanceMethods
def baz
"baz"
end
end
end
class Large
extend Validations::ClassMethods
include Validations::InstanceMethods
end
end
Second Method
module Foo
module Validations
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def bar
"bar"
end
end
def baz
"baz"
end
end
class Base
include Validations
end
end
My questions are:
Is there a better way to do this?
How do you get a one-liner module mixin for a set of class/instance methods with the least amount of magic?
How do you namespace these modules to the base class without namespacing the class itself?
How do you organize these files?
Breaking a class into modules, while tempting (because it's so easy in Ruby), is rarely the right answer. I usually regard the temptation to break out modules as the code's way of telling me it wants to be split into more tightly-focussed classes. A class that's so big you want to break it into multiple files is pretty much guaranteed to be violating the Single Responsibility Principle.
EDIT: To elaborate a bit on why breaking code into modules is a bad idea: it's confusing to the reader/maintainer. A class should represent a single tightly-focussed concept. It's bad enough when you have to scroll hundreds of lines to find the definition of an instance method used at the other end of a long class file. It's even worse when you come across an instance method call and have to go looking in another file for it.
After doing what Avdi said, these are the things I would do before putting anything into a module:
Whether this module can or will be used in any other class?
Would it make sense to extract the functionality of these modules into a different or base class?
If the answer for 1 is no and 2 is yes then IMHO that indicates to better have a class rather a module.
Also, I think putting attributes in a module is conceptually wrong because classes never share their attributes or instance variables or in other words their internal state with any other class. The attributes of a class belongs to that class only.
Business logics do definitely belong to the class itself and if the business logic of class A has some common responsibilities with class C then that needs to be extracted into a base class to make it clear instead of just putting it into a module.
The standard idiom seems to be
foo.rb
foo/base.rb
foo/validations.rb
foo/network.rb
foo/bar.rb
and foo.rb would be something like
class Foo
include Foo::Base
include Foo::Validations
include Foo::Network
include Foo::Bar
end
This is the standard idiom, and it works fairly well for letting you break things up. Don't do class methods vs instance methods. Those are generally pretty arbitrary distinctions, and you're better off putting code that deals with similar subjects together. That will minimize how many files you have to touch for any given change.
BEWARE: Rails can get confused by nesting models like this, at least if everything were classes. I think it'll do better with all the nested files just being modules, but you'll have to see. I'm still suggesting this because it's the normal idiom used by the Ruby community, but you may have to avoid having both a foo.rb and a foo/ directory amongst your Rails models (if that's the kind of class you're talking about).
Although including different modules will work, it is generally more troublesome than simply reopening the class in multiple places.
There is a (very simple) gem that you can use to makes this as pretty as can be: concerned_with
Example (from the readme)
# app/models/user.rb
class User < ActiveRecord::Base
concerned_with :validations,
:authentication
end
# app/models/user/validations.rb
class User < ActiveRecord::Base
validates_presence_of :name
end
#app/models/user/authentication.rb
class User < ActiveRecord::Base
def self.authenticate(name, password)
find_by_name_and_password(name, password)
end
end
I tend to use Ruby's duck typing approach to interfaces, which basically allows you to send any message to any object, which then evaluates what to do with it.
This approach allows me to stick to the same pattern Avdi mentions, keeping classes small and concise- only ever being responsible for one thing.
The great thing about Ruby is that you can delegate responsibilities to other concise classes, without muddling any of the logic together. For example:
class Dog
def initialize(name)
#name = name
end
def bark
"woof"
end
def fetch(object)
"here's that #{object}"
end
def sit
"sitting down"
end
private
attr_accessor :name
end
Here we have my dog class that has loads of dog related methods. They're all specific to dog, so could happily reside here. However, there would be a problem if these methods got a bit complex, calling other methods or perhaps this dog learns a bunch of new tricks!? So I could separate these out into their own classes and then delegate responsibility to those, like so:
class Tricks
def initialize(name)
#name = name
end
def fetch(object)
"here's that #{object}"
end
def sit
"sitting down"
end
def come_when_called(my_name)
"I'm coming" if my_name == name
end
def put_toy_away(object)
"#{fetch(object)}, I'll put it away"
end
private
attr_reader :name
end
class Dog
def initialize(name)
#name = name
end
delegate :sit, :fetch, :come_when_called, :put_away_toy, to: :tricks_klass
def bark
"woof"
end
private
attr_accessor :name
def tricks_klass
#tricks_klass ||= Tricks.new(name)
end
end
So now, that Dog class really starts to behave like an interface to dog-related behaviors, whilst these tricks are no longer coupled to it. This'll make testing easier by being able to instantiate a Tricks object and test it more generically, without the need for a Dog (because they don't always listen).
Now, we could have a Cat class that delegates responsibility to this Tricks class as well- although, that'd be one smart Cat!
You could also now use the Tricks class on its own- that's the power encapsulating single behavior its own class. You could even separate these behaviors even further- but only you as the developer know if that's worth while!
Do you use the alias method in order to add more ways to call methods (like length and size) or is there another use for it?
The alias_method call is also useful for re-implementing something but preserving the original version. There's also alias_method_chain from Rails which makes that kind of thing even easier.
alias_method also comes in handy when you have a number of behaviors that are initially identical but might diverge in the future, where you can at least rough them in to start.
def handle_default_situation
nil
end
%w[ poll push foo ].each do |type|
alias_method :"handle_#{type}_situation", :handle_default_situation
end
Yes.
It is often used to preserve a handle to existing methods before overriding them. (contrived example)
Given a class like this:
class Foo
def do_something
puts "something"
end
end
You could see code that adds new behaviour like so:
class Foo
def do_something_with_logging
puts "started doing something"
do_something_without_logging # call original implementation
puts "stopped doing something"
end
alias_method :do_something_without_logging, :do_something
alias_method :do_something, :do_something_with_logging
end
(this is exactly how alias_method_chain works)
However, for this use case it't often more appropriate to use inheritance and modules to your advantage.
Still, alias_method is a useful tool to have, if you absolutely need to redefine behaviour in an existing class (or if you wanted to implement something like alias_method_chain)