Can I have an object reinitialize itself before a method is executed? I'm using Ruby and selenium to test a web app and I am trying to improve my page objects. for example
class Foo
def initialize
#stuff happens here
end
def NewMethod
self.initialize
#What happens here is what I really want to happen
end
end
Is this a good or bad idea? Or is there a better way to do this?
it's always possible to change the data contained in your object. you could ie put all the init-logic into an additional method and then call that from your custom methods.
in general, what you are trying to do does not sound like a good idea...
A remark in advance: methods are written in lower case. Please replace NewMethod with newmethod.
If you try Foo.newmethod you get an error.
What do you want to do? Do you want to define different possibilities to create an object?
What you could do:
class Foo
def initialize
end
def self.newmethod
me = self.new
#Some actions ...
me #Return the (modified?) object
end
end
p Foo.newmethod #->#<Foo:0xb77d58>
Time is using something like this. There is Time.new, Time.gm, Time.local...
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.
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.
I was wondering if it's possible to make it so that if I had something like
class Test
attr_reader :access_times
def initialize
#access_times = 0
end
def get_two
2
end
...
end
t = Test.new
That any access to t would run a particular piece of code before actually running the method?
For example, if I suddenly decided to say t.get_two, the fact that I used the . syntax would increment #access_times by 1. Or perhaps I made a check t.is_a?(Test), it would also increment #access_times by 1. Accessing any methods or attributes inherited by Test would also increment the variable by 1.
Basically I want to add some stuff to the . syntax if possible.
I am not asking whether this is good or bad code, just whether it's possible and how it would be done. I wouldn't normally use it since I could just add the increment logic to every method manually and replace all direct instance variable accessing with methods (even things like is_a? and other things inherited from Object)
a pretty hardcore-version would be to use set_trace_func: http://apidock.com/ruby/Kernel/set_trace_func
this allows you to subscribe to all the ruby events fired throughout your program, which can be a ton of calls...
i don't think that there is a build-in hook for registering to arbitrary method-calls. you could implement something with method-missing, method-chaining or delegation, but that would depend on your requirments.
If you don't need everything to be standalone, a suggestion would just be to extend ActiveModel::Callbacks. Simply extend the class and you'll have all of the functionality of a before_filter without requiring all of the other Rails stuff.
Here is a workaround according to your description. Basically it will incremental #access_times for each of the instance method, and the method also does what it does before.
class Test
attr_accessor :access_times
def initialize
#access_times = 0
end
def get_two
2
end
end
class Test
##im = instance_methods
##im.each do |m|
class_eval <<-END
alias temporary #{m}
END
define_method(m) do |*args, &block|
#access_times += 1
temporary(*args, &block)
end
end
undef :temporary
end
Test.new.get_two # => #access_times += 1 and original get_two is called: 2
While this piece of code doesn't work as expected, I'll have a look at it later. Thanks.
Let's say that I have a class called MyClass, basically I want to do something like this:
class MyClass
def initialize(a)
do_stuff a, 4, 11
end
def do_stuff(a,b,c)
# ...
end
end
# rspec below
MyClass.any_instance.should_receive(:do_stuff).with_the_values(10,
anything, anything)
MyClass.new 10
Basically, I want to check that initialize will call on, and pass the correct value to do_stuff.
I think you should test if your class behaves correctly or not instead of watching who passes what to whom.
I guess, the do_stuff method is supposed to produce a side-effect of some sort. If so, then check if this side-effect is the one you expect. This way you're free to change actual implementation of your class without needing to rewrite your specs every time.
Is it OK to create a class array and save every object of that class in it? I'm not sure if there is something similar to this natively in Ruby or a design pattern that accomplish this, so this is my solution for accessing every object of a class:
class Foo
##all = []
def self.all
##all
end
def initialize
##all << self
end
end
Foo.all.each do |foo|
# do something
end
You can do it, natively:
ObjectSpace.each_object(Foo) do |foo|
# do something with foo
end
It's problematic in that it will make all instances of the class immortal — the array will keep them alive as long as they are in it. It's better to use ObjectSpace.each_object(Foo) (mentioned by LBg) or an array of WeakRefs that you periodically cull (this is less space-efficient).
If this is for a short-lived script that won't be using huge data-sets, or you actually want to make all the objects immortal and you'll be careful not to blow the heap, then there is no problem.
You may end up putting too much logic in the Foo class itself. Instead, you may want to create a FooCollection object.
This'll prove especially useful if you turn out to need multiple collections of foo objects. This has happened to me!