Overriding object attribute access - ruby

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.

Related

How can I mock super in ruby using rspec?

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.

How do I bypass a call to super method in test

I have a basic structure like this
class Automobile
def some_method
# this code sets up structure for child classes... I want to test this
end
end
class Car < Automobile
def some_method
super
# code specific to Car... it's tested elsewhere so I don't want to test this now
end
end
class CompactCar < Car
def some_method
super
# code specific to CompactCar... I want to test this
end
end
What is the recommended way to test CompactCar and Automobile without running the code from Car? Automobile#some_method provides the structure that is required by child classes, so I want to always test that, but Car's functionality is tested elsewhere and I don't want to duplicate efforts.
One solution is to use class_eval to overwrite Car#some_method, but this isn't ideal because the overwritten method stays in place for the duration of my testing (unless I re-load the original library file with setup/teardown methods... kind of an ugly solution). Also, simply stubbing the call to Car#some_method does not seem to work.
Is there a cleaner/more generally accepted way of doing this?
Just put the specific code into a separate method. You don't appear to be using anything from super. Unless you are?
class CompactCar < Car
def some_method
super
compact_car_specific_code
end
# Test this method in isolation.
def compact_car_specific_code
# code specific to CompactCar... I want to test this
end
end

When to use `method_missing`

In the code below, method roar is not defined in class Lion, but still can be called using method_missing.
class Lion
def method_missing(name, *args)
puts "Lion will #{name}: #{args[0]}"
end
end
lion = Lion.new
lion.roar("ROAR!!!") # => Lion will roar: ROAR!!!
In which situations and how should I use this method_missing? And is it safe to use?
It's entirely safe to use provided you use it in expected ways and don't get carried away. Not everything you can do is worth doing, after all.
The advantage of method_missing is you can respond to all kinds of things in unique ways.
The disadvantage is you don't advertise your capabilities. Other objects that expect you to respond_to? something will not get confirmation and might treat your custom object in ways you don't intend.
For building Domain Specific Languages and providing very loose glue between components, this sort of thing is invaluable.
A great example of where this is a good fit is the Ruby OpenStruct class.
Summary: When to use? When it will make your life easier and not complicate others' lives.
Here's one example that pops to mind. It's from redis_failover gem.
# Dispatches redis operations to master/slaves.
def method_missing(method, *args, &block)
if redis_operation?(method)
dispatch(method, *args, &block)
else
super
end
end
Here we check if the method called is actually a command of redis connection. If so, we delegate it to underlying connection(s). If not, relay to super.
Another famous example of method_missing application is ActiveRecord finders.
User.find_by_email_and_age('me#example.com', 20)
There's not, of course, a method find_by_email_and_age. Instead, the method_missing breaks the name, analyzes the parts and invokes find with proper parameters.
Here's a favorite of mine
class Hash
def method_missing(sym,*args)
fetch(sym){fetch(sym.to_s){super}}
end
end
Which lets me access values of a hash as if they were attributes. This is particular handy when working with JSON data.
So for example, rather than having to write tweets.collect{|each|each['text']} I can just write tweets.collect(&:text) which is much shorter. Or also, rather than tweets.first['author'] I can just write tweets.first.author which feels much more natural. Actually, it gives you Javascript-style access to values of a hash.
NB, I'm expecting the monkey patching police at my door any minuteā€¦
First and foremost, stick to Sergio Tulentsev's summary.
Apart from that, I think looking at examples is the best way to get a feeling for right and wrong situations for method_missing; so here is another simple example:
I recently made use of method_missing in a Null Object.
The Null Object was a replacement for a Order model.
The Order stores different prices for different currencies.
Without method_missing it looks like this:
class NullOrder
def price_euro
0.0
end
def price_usd
0.0
end
# ...
# repeat for all other currencies
end
With method_missing, I can shorten it to:
class NullOrder
def method_missing(m, *args, &block)
m.to_s =~ /price_/ ? 0.0 : super
end
end
With the added benefit of not having to (remember to) update the NullOrder when I add new price_xxx attributes to Order.
I also found a blog post from (Paolo Perrotta) where it demonstrated when to use method_missing:
class InformationDesk
def emergency
# Call emergency...
"emergency() called"
end
def flights
# Provide flight information...
"flights() called"
end
# ...even more methods
end
Check if a service has been asked during lunch time.
class DoNotDisturb
def initialize
#desk = InformationDesk.new
end
def method_missing(name, *args)
unless name.to_s == "emergency"
hour = Time.now.hour
raise "Out for lunch" if hour >= 12 && hour < 14
end
#desk.send(name, *args)
end
end
# At 12:30...
DoNotDisturb.new.emergency # => "emergency() called"
DoNotDisturb.new.flights # ~> -:37:in `method_missing': Out for lunch (RuntimeError)

Could an object reinitialize itself before a method is executed?

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

Override same Class method in Ruby with Multiple Modules, with need to call super. Do I use Method Alias, or some other clever trick?

Here's the situation:
I have a User model, and two modules for authentication: Oauth and Openid. Both of them override ActiveRecord#save, and have a fair share of implementation logic.
Given that I can tell when the user is trying to login via Oauth vs. Openid, but that both of them have overridden save, how do "finally" override save such that I can conditionally call one of the modules' implementations of it?
Here is the base structure of what I'm describing:
module UsesOauth
def self.included(base)
base.class_eval do
def save
puts "Saving with Oauth!"
end
def save_with_oauth
save
end
end
end
end
module UsesOpenid
def self.included(base)
base.class_eval do
def save
puts "Saving with OpenID!"
end
def save_with_openid
save
end
end
end
end
module Sequencer
def save
if using_oauth?
save_with_oauth
elsif using_openid?
save_with_openid
else
super
end
end
end
class User < ActiveRecord::Base
include UsesOauth
include UsesOpenid
include Sequencer
end
I was thinking about using alias_method like so, but that got too complicated, because I might have 1 or 2 more similar modules. I also tried using those save_with_oauth methods (shown above), which almost works. The only thing that's missing is that I also need to call ActiveRecord::Base#save (the super method), so something like this:
def save_with_oauth
# do this and that
super.save
# the rest
end
But I'm not allowed to do that in ruby.
Any ideas for a clever solution to this?
Is that what alias_method_chain would do? I've avoided that because people seemed to say it was a bad idea.
(Finding things as I go):
Alias Method Chain the Ruby Way
Yes alias method chain would help you in this situation.
But consider using delegate pattern. Original save method would trigger a callback on special delegate object (which can be as well nil) and it would do whatever needs to be done when saving user.
Also there is simliar pattern supported directly by actve record called Observer, try to read somethng about it maybe that's a good solution too.
I'm not saying this chaining methods is wrong, but there are cleaner ways to achieve what you want.

Resources