I am trying to understand this Ruby code as a C# student who never programmed in Ruby. I have a good idea of what most of this does other than the line with just self on it. I would be grateful for any help in understanding. My best guess is that the self is used to call the classes initialize function but I'm not 100 % certain.
def link(cell, bidi=true)
#links[cell] = true
cell.link(self,false) if bidi
self
end
Every method returns something. Either through an explicit return line, or if there's no explicit return, then the last executed line is returned.
In your example, self causes the method to return the object which is the receiver for the method... typically an instance of the class where the method was defined, although it could be inherited by other classes, or it could be from an included module (a mixin).
Related
So, I'm currently learning about metaprogramming in Ruby and I want to fully understand what is happening behind the scenes.
I followed a tutorial where I included some of the methods in my own small project, an importer for CSV files and I have difficulties to wrap my hand around one of the methods used.
I know that the define_method method in Ruby exists to create methods "on the fly", which is great. Now, in the tutorial the method initialize to instantiate an object from a class is defined with this method, so basically it looks like this:
class Foo
def self.define_initialize(attributes)
define_method(:initialize) do |*args|
attributes.zip(args) do |attribute, value|
instance_variable_set("##{attribute}", value)
end
end
end
end
Next, in an initializer of the other class first this method is called with Foo.define_initialize(attributes), where attributes are the header row from the CSV file like ["attr_1", "attr_2", ...], so the *args are not provided yet.
Then in the next step a loop loops over the the data:
#foos = data[1..-1].map do |d|
Foo.new(*d)
end
So here the *d get passed as the *args to the initialize method respectively to the block.
So, is it right that when Foo.define_initialize gets called, the method is just "built" for later calls to the class?
So I theoretically get a class which now has this method like:
def initialize(*args)
... do stuff
end
Because otherwise, it had to throw an exception like "missing arguments" or something - so, in other words, it just defines the method like the name implies.
I hope that I made my question clear enough, cause as a Rails developer coming from the "Rails magic" I would really like to understand what is happening behind the scenes in some cases :).
Thanks for any helpful reply!
Short answer, yes, long answer:
First, let's start explaining in a really (REALLY) simple way, how metaprogramming works on Ruby. In Ruby, the definition of anything is never close, that means that you can add, update, or delete the behavior of anything (really, almost anything) at any moment. So, if you want to add a method to Object class, you are allowed, same for delete or update.
In your example, you are doing nothing more than update or create the initialize method of a given class. Note that initialize is not mandatory, because ruby builds a default "blank" one for you if you didn't create one. You may think, "what happens if the initialize method already exist?" and the answer is "nothing". I mean, ruby is going to rewrite the initialize method again, and new Foo.new calls are going to call the new initialize.
Some open source code I'm integrating in my application has some classes that include code to that effect:
class SomeClass < SomeParentClass
def self.new(options = {})
super().tap { |o|
# do something with `o` according to `options`
}
end
def initialize(options = {})
# initialize some data according to `options`
end
end
As far as I understand, both self.new and initialize do the same thing - the latter one "during construction" and the former one "after construction", and it looks to me like a horrible pattern to use - why split up the object initialization into two parts where one is obviously "The Wrong Think(tm)"?
Ideally, I'd like to see what is inside the super().tap { |o| block, because although this looks like bad practice, just maybe there is some interaction required before or after initialize is called.
Without context, it is possible that you are just looking at something that works but is not considered good practice in Ruby.
However, maybe the approach of separate self.new and initialize methods allows the framework designer to implement a subclass-able part of the framework and still ensure setup required for the framework is completed without slightly awkward documentation that requires a specific use of super(). It would be a slightly easier to document and cleaner-looking API if the end user gets functionality they expect with just the subclass class MyClass < FrameworkClass and without some additional note like:
When you implement the subclass initialize, remember to put super at the start, otherwise the magic won't work
. . . personally I'd find that design questionable, but I think there would at least be a clear motivation.
There might be deeper Ruby language reasons to have code run in a custom self.new block - for instance it may allow constructor to switch or alter the specific object (even returning an object of a different class) before returning it. However, I have very rarely seen such things done in practice, there is nearly always some other way of achieving the goals of such code without customising new.
Examples of custom/different Class.new methods raised in the comments:
Struct.new which can optionally take a class name and return objects of that dynamically created class.
In-table inheritance for ActiveRecord, which allows end user to load an object of unknown class from a table and receive the right object.
The latter one could possibly be avoided with a different ORM design for inheritance (although all such schemes have pros/cons).
The first one (Structs) is core to the language, so has to work like that now (although the designers could have chosen a different method name).
It's impossible to tell why that code is there without seeing the rest of the code.
However, there is something in your question I want to address:
As far as I understand, both self.new and initialize do the same thing - the latter one "during construction" and the former one "after construction"
They do not do the same thing.
Object construction in Ruby is performed in two steps: Class#allocate allocates a new empty object from the object space and sets its internal class pointer to self. Then, you initialize the empty object with some default values. Customarily, this initialization is performed by a method called initialize, but that is just a convention; the method can be called anything you like.
There is an additional helper method called Class#new which does nothing but perform the two steps in sequence, for the programmer's convenience:
class Class
def new(*args, &block)
obj = allocate
obj.send(:initialize, *args, &block)
obj
end
def allocate
obj = __MagicVM__.__allocate_an_empty_object_from_the_object_space__
obj.__set_internal_class_pointer__(self)
obj
end
end
class BasicObject
private def initialize(*) end
end
The constructor new has to be a class method since you start from where there is no instance; you can't be calling that method on a particular instance. On the other hand, an initialization routine initialize is better defined as an instance method because you want to do something specifically with a certain instance. Hence, Ruby is designed to internally call the instance method initialize on a new instance right after its creation by the class method new.
So I'm pretty new to Rspec and I'm trying to figure out how to write tests for a class that takes an object as a constructor parameter and sets that object to an instance variable. Then it calls that instance variable's object methods in other methods.
Example:
class ClassA
def initialize(string_object, gem_object)
#instance_variable1 = gem_object
#string = string_object
end
def check_validity?(some_arg)
unless #instance_variable1.gemObjectMethod1.gemObjectMethod2(some_arg).empty?
return true
end
false
end
..
..
end
I feel very lost in how to write specifications for this. For one I don't really understand what specifying a constructor actually entails. What I realize is that I'd have to find some way of mocking or stubbing the gem_object I'm getting as argument, but I'm not sure how.
For the next method, what I've tried to this point is:
describe '#check_validity?' do
context 'gets empty list' do
let (:actual) { subject.check_validity?("sample") }
before do
allow(subject).to receive(#instance_variable1.gemObjectMethod1.gemObjectMethod2).with("sample").and_return([])
end
it 'returns false' do
expect(actual).to be false
end
end
end
But this gives me error relating to my constructor saying that it expected 2 arguments but was given 0.
Any help would be much appreciated! Also, I couldn't really find anything on line about specifying constructors with their arguments mocked. Maybe I'm looking in the wrong place or maybe missing something obvious as this is my first experience with BDD.
In RSpec, 'receive' is a method that accepts a symbol that represents the name of a method. (It allows you to chain a 'with' method that accepts the expected list of parameters.) To fix the before-block you could do this:
before do
allow(subject.instance_variable_get(:#instance_variable1).gemObjectMethod1).to receive(:gemObjectMethod2).with("sample").and_return([])
end
The sheer ugliness of that, though, suggests that something is wrong. And it is. The code is violating the law of demeter pretty badly and the test is being drawn into it.
As a first attempt to clean it up, you might consider a method that "caches" the results of calling #instance_variable1.gemObjectMethod1. Let's say that that first method returns an enumerable group of widgets. You could change your class to include something like this:
def check_validity(a_string)
widgets.gemObjectMethod2(a_string).empty?
end
private
def widgets
#widgets ||= #instance_variable1.gemObjectMethod1
end
Your class still knows a bit too much about the gem object, but now you have broken it down in such a way that you could refactor how you find widgets -- perhaps a different gem or your own implementation of it. For the purposes of your testing, you can isolate that decision from the test by mocking widgets.
let(:gem_widgets) do
instance_double(GemObjectMethod1ResultClass, gemObjectMethod2: true)
end
before do
allow(subject).to receive(:widgets).and_return(gem_widgets)
allow(gem_widgets).to receive(:gemObjectMethod2).with("sample").
and_return([])
end
it 'should pass with "sample"' do
expect(actual).to eql true
end
I have the following code:
class Bike
attr_reader :chain
def initialize
#chain = default_chain
end
def default_chain
raise 'SomeError'
end
end
class MountainBike < Bike
def initialize
super
end
def default_chain
4
end
end
mb = MountainBike.new
p mb.chain
When in the initialization we call super, I would expect the default_chain of the super class to be called, and therefore the Exception to be launched. However, it seems that the Bike class actually goes and calls the default_chain method of the original caller. Why is that?
As said #BorisStitnicky you need to use singleton_class. For more understanding, in this post you may find info: Understanding the singleton class when aliasing a instance method
The point of putting a same-named method in a sub-class is to overload that method.
ie to replace it with your new version.
The code will look at the calling sub-class's version of any method - before it starts looking up the ancestry chain for a method, because the sub-class is the context of the current code.
So even though the method is referred-to in the superclass... the ruby interpreter will still first look at your sub-class's set of method as the main context.
If you also want to call the parent's one - you must use super in your default_chain method too.
Alternatively rename the one in the parent class to something else and call it from your superclass only.
In object oriented programming, which method is called is solely determined by the receiver and the method name.
Method name initialize is called on the receiver: MountainBike instance, for which MountainBike#initialize matches, and is executed.
Within the execution, super is called, for which Bike#initialize matches, and is executed.
Within the execution, method name default_chain is called on the implicit receiver self, which is the MountainBike instance. MountainBike#default_chain matches, and is executed.
If Bike#default_chain were to be called in the last step on the receiver (MountainBike instance), it would mean that method calling would depend not only on the receiver and the method name, but also on the context. That would make it too complicated, and would defeat the purpose of object oriented programming. Again, the idea of object oriented programming is that method call is solely determined by the receiver and the method name, not the context.
If I have a class where the constructor calls another function, how do I check that it was called and the right number of times?
class MyClass
def initialize(count)
count.times{self.do_something}
end
def do_something
# whatever
end
end
I want to say something like
n = 4
MyClass.new(n).should_receive(:do_something).exactly(n).times
n = 2
MyClass.new(n).should_receive(:do_something).exactly(n).times
but that fails because the call to do_something happens before should_receive gets attached to it (at least I think that's why).
expected: 4 times with any arguments
received: 0 times with any arguments
Or is it just wrong to call stuff like this from the constructor and I should refactor?
Also, this question is very similar to this one:
rspec: How to stub an instance method called by constructor?
but I'm hoping in the last 5 years the answer has gotten better than setting up manual implementations of a stubbed new call.
The new syntax for doing this is as follows:
allow_any_instance_of(ExampleClass).to receive(:example_method).and_return("Example")
expect_any_instance_of(ExampleClass).to receive(:example_method).and_return("Example")
See the docs
Expectations are meant to be placed before the code that will be expected to meet them.
MyClass.any_instance.should_receive(:do_something).exactly(1).time
MyClass.new
That should work.
Another answer is to use rspec spies. The issue here is that you have to create a mock for the methods you want to make requirements on after the fact. Turns out it's not hard, but you still have to enumerate them. The and_call_original is the magic that makes the implementation not change.
MyClass.any_instance.stub(:method_name).and_call_original
Then you can just say:
MyClass.new(4).should have_received(:method_name).exactly(4).times
I'd still love to find a way that doesn't require you to enumerate the methods, though.
Here's the best I came up with, but I don't like it:
Create a new method in Class that runs a block on the allocated, but not yet initialize'd object. I put this in the before block in my spec file, but there's probably a better place for it:
class Class
def rspec_new(*params, &block)
o = allocate
yield(o) if block
o.__send__(:initialize, *params)
return o
end
end
Then call it like this:
MyClass.rspec_new(n){|obj| obj.should_receive(:do_something).exactly(n).times}
it seems to work, but you can't pass a block to your constructor. At least not the way you normally do. That's why I didn't override the regular new method.