Spying on Classes of the Same Namespace - ruby-on-rails

I'm creating spies for two classes belonging to the same namespace with the goal of expecting each to receive specific arguments:
allow(SibApiV3Sdk::SendSmtpEmail).to receive(:new).and_return(seb_send_email)
allow(SibApiV3Sdk::SMTPApi).to receive(:new).and_return(seb_smtp_api)
def seb_send_email
#seb_smtp_api ||= SibApiV3Sdk::SendSmtpEmail.new(email_params)
end
def seb_smtp_api
#seb_smtp_api ||= SibApiV3Sdk::SMTPApi.new
end
When I do, the second spy fails to work properly and returns the first spied object instead. I suspect this has something to do with it being a namespaced class. Is this the expected behavior and is there an alternative approach for handling namespaced class spies?

You assign both to #seb_smtp_api variable and that's the source of your problems.
You probably call the seb_send_email method first, then it's memoized as #seb_smtp_api and when you call seb_smtp_api it just returns the memoized value.
You can check that by replacing allow with expect and see that SibApiV3Sdk::SMTPApi's new method is never called.

Related

RSpec Stubbing Service Object Method

I'm having an issue with stubbing out a call to a service object (QuickbooksService) from an AR object. As far as I can tell the method should be stubbed properly and return the value I'm specifying but when I run the spec I can see the method being called and failing.
Here's the class definition of the object I'm testing.
class Order < ActiveRecord::Base
def create_invoice
QuickbooksService.new(:estimate).create_invoice(arg1, arg2, arg3)
end
end
And from order_spec
describe("#create_invoice") do
expect(QuickbooksService.new(:estimate)).to receive(:create_invoice).and_return(1)
end
I've also tried
allow(QuickbooksService.new(:estimate)).to receive(:form_invoice_create).with(anything()).and_return(1)
So instead of returning 1 the create_invoice method is being executed inside of QuickbooksService. Any insight would be appreciated!
The problem you are having is that you are stubbing a seperate instance. i.e When you define the expectation, you telling it to expect that a particular instance receives a call to the method, but when the code is executed, it is creating a different instance. What you need to do is allow any instance to receive the method call. Something like allow_any_instance_of(QuickbooksService).to receive(:invoice_create) will work, but it is much better practice to create a double, something like:
let(:quickbooks_service) { instance_double(QuickbooksService) }
describe("#create_invoice") do
before { allow(quickbooks_service).to receive(:create_invoice).and_return(1) }
it "Creates quickbook invoice" do
order.create_invoice
expect(quickbooks_service).to have_received(:create_invoice)
end
end
See: https://relishapp.com/rspec/rspec-mocks/docs
The problem is that you are instantiating the class while stubbing it allow(QuickbooksService.new(:estimate)).to receive(:form_invoice_create).with(anything()).and_return(1)
Try this:
allow_any_instance_of(QuickbooksService).to receive(:form_invoice_create).with(anything()).and_return(1)

Rails uppercase/lowercase object

This is a fairly basic Ruby/Rails question but I'm not sure why it makes a difference if you call a class of an object in some circumstances vs calling an instance of that object in different places of the framework.
Say you have a model e.g. Product and you call Product.new you have a new instance of the class. But if you have certain methods that are defined in the model I only seem to be able to access these if I call the Class rather than an instance e.g. Product.where(param, param). But I cannot call product.where(param, param) - why is this?
There are two types of methods: Class methods, and instance methods. You must call the appropriate method on the right object.
class Product
def self.foo
# class method, only callable on Product
end
def name
# instance method, callable on an instance of Product.
end
end
If you attempt to call an instance method on a class, or vice versa, you'll see an undefined method error.
To use someone else's analogy, imagine a house and a blue print; the class is a blue print for an object, while a house would represent the instance. An instance of that class will have its own set of attributes (wall colour, window type, etc...).
What would this mean?
p = Product.find(1)
p.where('something == 2')
That doesn't make any sense, you have an instance, what are you querying for? Good API design results in methods defined where they make sense.

What's the difference between sending :include to class and directly defining method in second class definition?

Recently I had to add a method to Redmine's core class. I was unable to use inheritance, so I've done something like this:
require_dependency 'time_entry_query'
class TimeEntryQuery < Query
def my_new_method(foo, bar)
end
end
and it works perfectly - my method is added to all new objects. However, I've seen someone declaring the new method in their own module instead and then sending :include to class, so it become a mixin. Here's an example:
module Patches
module SomeClassPatch
def my_new_method
end
end
and somewhere in app's initialization:
SomeClass.send(:include, Patches::SomeClassPatch) unless SomeClass.include? (Patches::SomeClassPatch)
What's difference between these two methods and which one should I use?
There are two differences:
When you use a mixin, there is a clear place where your "patch" methods can live. If I wonder "Hmm, where's this my_new_method" coming from, and I look at, say, TimeEntryQuery.ancestors or TimeEntryQuery.instance_method(:my_new_method).owner, that will return Patches::SomeClassPatch. So I know I have to look for a file named lib/patches/some_class_patch.rb somewhere to find where it is probably defined. (I could try source_location as well, but that is not always reliable.)
Mixing in a module into a class makes the module the superclass of the class it is being mixed into. So, if there already is a my_new_method defined in TimeEntryQuery, your first option will overwrite it, whereas in your second option, your method will become the super method of that method. IOW: with your second option, your new method won't be called unless the already existing method calls super.

Sample Ruby code, How is this abstracting things out?

Watching this video by Yehuda, and he gave this snippet about how Ruby helps you build better abstractions.
class FOWA
def self.is_fun
def fun?
true
end
end
is_fun
end
He was talking about, in ruby, how if you are repeating code in your class over and over again, you can abstract it out without having to think about things in terms of methods etc. And he said this was using a metaprogramming technique.
Can someone explain what this is?
It is a class method on FOWA (so its like a static method, you don't need an instance to call it), and this class method is really just wrapping another method that returns true.
And this is_fun class method is now being executed or what? not sure what the last line "is_fun" is doing?
http://vimeo.com/11679138
The is_fun call at the end of the class calls the static method. The static method then defines the fun? method inside of the FOWA class. Then, you can do this:
f = FOWA.new
f.fun?
If you take out the is_fun call at the end of the class, the fun? method doesn't get defined.
He mentioned that you wouldn't use it in this way, but the point is how easy it is to dynamically add a method to a class. You might use it like this if you wanted the method to be available in subclasses and you wouldn't call is_fun in FOWA, but you might in a subclass. It gets a little more interesting if you have a parameter for is_fun and the definition of fun? changes depending on that parameter.
This also leads right into modules because you can define a module with the same is_fun method and then just have your class extend the module and the methods in the module are available in the class. You would use this technique if you want your method to be available to more than just subclasses of FOWA.

Why does Sunspot change the `self` in the search DSL block?

I noticed (and verified in the sunspot code) the following behavior
class Foo < ActiveRecord::Base
def bar
search_str = "foo"
Boo.search do
keywords(search_str)
p self.id
p self
end
end
end
In the code above, the DSL block can access the variables defined in
the context. But the self inside block, points to an instance of
Sunspot::DSL::Search class (instead of an instance of Foo class.)
When I try to access self.id, instead of getting the id of a Foo
object; I get the id of a Sunspot::DSL::Search object.
I think Sunpot is doing some binding swapping/delegation magic in Util.instance_eval_or_call method.
I am curious why Sunspot does this and why there is no warning about
this behavior in documentation.
Edit:
The Sunspot search method can be found at this link
The code below will illustrate my point. In the method foo I have a block that behaves as expected. In the method bar, the block doesn't behave.
class Order < ActiveRecord::Base
def foo
p self.class.name # prints Order
# The `self` inside the block passed to the each method
# points to an object of type Order (as expected)
# This is the normal block behavior.
[1,2,3].each do |val|
p self.class.name # prints Order
end
end
def bar
p self.class.name # prints Order
# the `self` inside the block passed to the search method
# points to an object of type Sunspot::DSL::Search.
# This is NOT the normal block behavior.
Order.search do
keywords("hello")
p self.class.name # prints Sunspot::DSL::Search
end
end
Note2
I have located the code in Sunspot source tree that modifies the normal block behavior. My question is about the reason for rigging the binding like this.
Note3
Specifically, I found an issue while invoking the id method in side the block. The search method delegates the method invocation inside the block to the DSL object and if it doesn't find the method then call is re-delegated to the calling context. Search method strips all but the essential methods from the DSL object before registering delegation code. The id method is not stripped out. This is causing the problem. For all the other methods delegation works fine.
This behavior is not documented in the Sunspot method documentation.
Ok, I know how it works:
The magic is found in ContextBoundDelegate in util.rb.
It creates a blank slate delegator object.
The delegator forwards all method calls to the 'receiver'. In your example the 'receiver' is probably the object which contains the methods keywords and with and any_of and so on.
If a given method is not found in 'receiver' then it forwards the method call onto the 'context' object
The context object is the object that holds the bindings for the block.
You find the context object for a given block by doing this: eval('self', block.binding)
Rationale:
So the effect of all this is that the block not only gets access to the methods in the search object (a la instance_eval) but it also gets access to local methods in the calling scope of the block.
The block also, of course, gets access to local variables in the calling scope of the block, but this is just normal closure behaviour.
The block does not, however, get access to instance variables in the calling scope of the block.
The following code might be useful as it follows roughly the same idea but is much simpler and less sophisticated: Using methods from two different scopes?
Isn't it just an instance_eval? Unless you're talking about accessing instance variables from the calling context, this is normal closure behaviour.
I'm assuming the instance_eval (the change in self) is used to provide keywords and other related methods to the block.

Resources