Replace instance method with rspec-mock - ruby

The RSpec documentation shows how to mock a class method. How can I replace an instance method instead. Here is a code example using rspec-mocks to make this more concrete:
require 'rspec'
class Foo
def foo
"foo"
end
def foobar
foo + "bar"
end
end
RSpec.describe "Foo" do
it "mocks foo" do
allow(Foo).to receive(:foo).and_return("bar")
foo = Foo.new
expect(foo.foobar).to eq("barbar")
end
end
This gives "Foo does not implement: foo". Granted, this is contrived for simplification. In the actual class I'm testing, foo makes a HTTP request, so I want to mock it while writing a spec for foobar. I'd like to do this with rspec-mocks, but if another gem can help here, I'm open to that as well.

Just stub the method on the instance instead of the class:
RSpec.describe "Foo" do
it "mocks foo" do
foo = Foo.new
allow(foo).to receive(:foo).and_return("bar")
expect(foo.foobar).to eq("barbar")
end
end

Related

Dynamically adding statements to a method

I have a class Foo:
class Foo
def a
"something" if true
end
end
I want an add_statement method to add new statements to the method, keeping the old implementation. Is it possible?
I want to do something like this:
foo = Foo.new
foo.extend_method(:a, &block)
so now the source of my a method should be something like this:
def a
"something" if true
&block
end
where &block is the code I passed as argument in extend_method.
You want to use alias_method_chain, or Module#prepend.
There are many tutorials / documentation about them on the net, for example "AVOIDING ALIAS_METHOD_CHAIN IN RUBY" or "Module.prepend: a super story".
For your specific example (when you want to extend the function outside of the class), you have to use alias_method_chain, added in a new module, and included in your original class using Foo.send.
You can find more details and examples in "When to use alias_method_chain".
Here is an aspect oriented example:
require 'aspector'
class Foo
def a
puts "in Foo.a"
end
end
aspect = Aspector do
before :a do
puts 'this should print first'
end
after :a do |result_of_a|
puts 'this should print last'
end
end
puts "\n\nplain instance"
foo = Foo.new
foo.a
puts "\n\napply to an instance"
aspect.apply(foo)
foo.a
puts "\n\napply to all instances of Foo"
aspect.apply(Foo)
Foo.new.a
You will need to 'gem install aspector'.

Ruby - accessing instance methods / variables from anonymous class

I have following Ruby code:
class Baz
def foo()
qux = Class.new() {
def call()
bar()
end
}.new()
qux.call()
end
def bar()
puts "bar"
end
end
b = Baz.new()
b.foo()
How can I access method bar from the anonymous class, that means from qux.call? Is it possible?
I'm keeping getting this message:
classes-test.rb:5:in `call': undefined method `bar' for #<#<Class:0x00000002d9c248>:0x00000002d9c1a8> (NoMethodError)
I'm new to Ruby, so any advice or even deeper explanation of the problem will be appreciated. Thanks in advance.
Since .bar is an instance method of Baz, you need to have an instance of Baz available in your context to call .bar. You can do that by passing the instance object to the class on initialization, so you can call its .bar method on it.
This works:
class Baz
def foo
qux = Class.new do
def initialize(a)
#a = a
end
def call
#a.bar
end
end.new(self)
qux.call
end
def bar
puts "bar"
end
end
b = Baz.new
b.foo
=> 'bar'
If you need to pass a class to Class.new() as you mention in the comments, you can override the initializer method like this (please note that you may have to consider the arguments that your Closure class initialize needs for super:
qux = Class.new(Fiddle::Closure) do
def initialize(baz)
#baz = baz
super
end
def call
#baz.bar
end
end.new(self)
On a side note, you don't need all those (), it's Ruby style to omit them if not needed.

How do I hide a method from a superclass?

I have a method in a superclass from a third-party gem which I want to hide. I would prefer if it is impossible to call this method at all, so not just override it and leave the body empty.
I believe this may be what you're looking for:
undef_method :foo
This will prevent any calls to the method foo.
In contrast, this will not achieve the same effect:
remove_method :foo
That will remove the method from the child, but will still pass through up the inheritance chain.
Docs: undef_method and remove_method
Use the undef keyword.
class A
def foo
5
end
end
class B < A
undef foo
end
A.new.foo #=> 5
B.new.foo #=> NameError: undefined local variable or method `foo'
That is wrong OOP that you're trying to do there. I suggest you use composition instead of inheritance.
require 'forwardable'
class SomeBaseClass
def foo
puts 'foo'
end
def bar
puts 'bar'
end
def quux
puts 'quux'
end
end
class MyClass
def initialize
#base = SomeBaseClass.new
end
extend Forwardable
def_delegators :#base, :foo, :bar # but not quux
end
mc = MyClass.new
mc.foo
mc.bar
mc.quux
# >> foo
# >> bar
# ~> -:32:in `<main>': undefined method `quux' for #<MyClass:0x007febcc155210> (NoMethodError)

Ruby Singleton avoid using instance member

I like Ruby's singleton but I would like to make usage of it better so here is example
require 'singleton'
class Foo
include Singleton
def initialize
# code to setup singleton here
end
def self.get_bar
Foo.instance.get_bar
end
def get_bar
end
def get_nar
end
end
Usage
Foo.instance.get_bar (default) or Foo.get_bar (due to static self.get_bar method I made)
Is there elegant way to make all methods accessible without me having to write static wrapper for each method? Just seems redundant to have to write for each method .instance
UPDATE
Ruby 1.8.7
You could mix this module:
module DelegateToSingleton
def respond_to_missing?(method)
super || instance.respond_to?(method)
end
def method_missing(method, *args)
instance.send(method, *args)
end
end
into your singleton:
class Foo
extend DelegateToSingleton
include Singleton
def foo
'foo'
end
def bar
'bar'
end
end
with these results:
p Foo.foo # => "foo"
p Foo.bar # => "bar"
DelegateToSingleton::method_missing is what makes it work: Whenever Foo receives a method it doesn't know about, it just forwards it to its instance.
DelegateToSingleton::respond_to_missing? is not strictly needed, but having it is good manners whenever playing tricks with method_missing.
For Ruby earlier than 1.9.2: Override respond_to? instead of respond_to_missing?
Just separate the class from the instance:
class Foo
def initialize
end
def get_bar
end
def get_nar
end
end
MyFoo = Foo.new
MyFoo.get_bar

Including methods into blocks

Anyone know how to get this to work if it's possible?
class Foo
def self.go(&block)
class << block
include Bar
end
puts "Within Foo#go: #{block.methods.include? 'baz'}"
block.call
end
end
module Bar
def baz
puts "I'm happily in my place!"
end
end
Foo.go {
puts "Within actual block: #{methods.include? 'baz'}"
baz
}
This gets the output:
Within Foo#go: true
Within actual block: false
NameError: undefined local variable or method ‘baz’ for main:Object
EDIT: when I print out the block's class in Foo#go, it's Proc, but when I print it out within the Proc, it's Object. Could this be related?
You can't do this. The reason for what you're seeing is that there are two different contexts here. One is the context of the block, which closes over the context where it's defined. The other is the context of the Proc object wrapper, which is just the same as any other object context and completely unrelated to the context of the block itself.
I think the closest you'll get is to instance_eval the block using a context object that has the methods you want, but then the block won't have access to the self that existed where it was defined. It's up to you whether that makes sense for the method you want to write.
The other option is to pass the block an actual receiver for the baz method.
You could use eval with Proc#binding:
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
eval('include Bar', block.binding)
block[]
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> "hi from baz!"
But unless you use a mixin/mixout framework (like mixico or mixology), you'll be placing the methods from the included module into the lexical scope, so they'll still be accessible once the block returns.
require 'rubygems'
require 'mixico'
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
Module.mix_eval(Bar, &block)
end
baz #=> NameError
go { baz } #=> "hi from baz!"
baz #=> NameError
Here's a good article on different ways to use a DSL from within a block.
Another alternative, following on from rampion, is to dup the context of the block before mixing into it, this way you're not messing up the context after you've finished.
module Bar
def baz
puts "hi from baz!"
end
end
def go(&block)
dup_context = eval('self', block.binding).dup
dup_context.send(:include, Bar)
dup_context.instance_eval &block
end
Note this will only be useful to you if you're not running any mutator methods in the block

Resources