How do I write methods that insert rspec examples? - ruby

In a bunch of rspec rails unit specifications I do something like:
describe Foo do
[:bar, :baz].each do |a|
it "should have many #{a}" do
Foo.should have_many(a)
end
end
end
For cleaner code I'd rather do something like:
describe Foo do
spec_has_many Foo, :bar, :baz
end
So how do I write a helper method like spec_has_many() for inserting DSL code like rspec's it() method? If it were for an ordinary instance method I'd do something like:
def spec_has_many(model, *args)
args.each do |a|
define_method("it_should_have_many_#{a}") do
model.should have_many(a)
end
end
end
What would be the equivalent for defining rspec examples?

Ok, this took some messing around, but I think I got it working. It's a bit of metaprogramming hackery, and I personally would just use the first thing you described, but it's what you wanted :P
module ExampleMacros
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
# This will be available as a "Class Macro" in the included class
def should_have_many(*args)
args.each do |a|
# Runs the 'it' block in the context of the current instance
instance_eval do
# This is just normal RSpec code at this point
it "should have_many #{a.to_s}" do
subject.should have_many(a)
end
end
end
end
end
end
describe Foo do
# Include the module which will define the should_have_many method
# Can be done automatically in RSpec configuration (see below)
include ExampleMacros
# This may or may not be required, but the should_have_many method expects
# subject to be defined (which it is by default, but this just makes sure
# it's what we expect)
subject { Foo }
# And off we go. Note that you don't need to pass it a model
should_have_many :a, :b
end
My specs fail because Foo doesn't have a has_many? method, but both tests run, so it should work.
You can define (and rename) the ExampleMacros module in your spec_helper.rb file and it will be available for inclusion. You want to call include ExampleMacros in your describe blocks (and not any others).
To make all of your specs include the module automatically, configure RSpec like so:
# RSpec 2.0.0
RSpec.configure do |c|
c.include ExampleMacros
end

Related

Ruby — after include, self.included is never invoked?

I'm writing an acts_as_thingy module, intended to be used as per
class TestThingy
include ActsAsThingy
acts_as_thingy :name
end
ActsAsThingy is defined as
module ActsAsThingy
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def acts_as_thingy *attributes
attributes.each do |attribute|
define_method attribute do
"thingy - #{attribute.to_s}"
end
end
end
end
end
And tested as
describe ActsAsReadOnlyI18nLocalised do
let(:thingy) { TestThingy.new }
it 'has a name method' do
expect(thingy.name).to eq "thingy - name"
end
end
What happens however is that, when I run the rspec, ActsAsThingy's self.included method is never invoked, and rspec complains that there is no such method as acts_as_thingy.
I seem to be missing something entirely obvious, but just can't see it.
Why isn't the self.included method being called when I include ActsAsThingy in the class?
update
Stepping through with pry I can see that after the include ActsAsThingy, if I then look at self.included_modules it shows up as [ActsAsThingy, PP::ObjectMixin, Kernel] So the include is working, it's not a paths issue or anything like that. The core question remains; why isn't self.included being invoked?
So after all that it turned out that I simply needed to add
require 'acts_as_thingy' to the top of the file that contained
class TestThingy
include ActsAsThingy
acts_as_thingy :name
end
I am not sure why Ruby didn't just throw an error when it couldn't find ActsAsThingy but it explains why the self.included method never got called (the include failed, but silently).
Check your scope.
Check your file require path.
Include is used for including methods into other
Modules
Require is what you want to use in your case.
Read up on the differences between require and include:
What is the difference between include and require in Ruby?
Here is my test code for your problem.
class A
require_relative 'test.rb'
end
p('test')
module Test
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def p(str)
print "#{str}"
end
end
end
output:
vagrant [test]> ruby file.rb
"test"

How do I wrap the invocation of a Ruby method by including a module?

I want to be notified when certain things happen in some of my classes. I want to set this up in such a way that the implementation of my methods in those classes doesn't change.
I was thinking I'd have something like the following module:
module Notifications
extend ActiveSupport::Concern
module ClassMethods
def notify_when(method)
puts "the #{method} method was called!"
# additional suitable notification code
# now, run the method indicated by the `method` argument
end
end
end
Then I can mix it into my classes like so:
class Foo
include Notifications
# notify that we're running :bar, then run bar
notify_when :bar
def bar(...) # bar may have any arbitrary signature
# ...
end
end
My key desire is that I don't want to have to modify :bar to get notifications working correctly. Can this be done? If so, how would I write the notify_when implementation?
Also, I'm using Rails 3, so if there are ActiveSupport or other techniques I can use, please feel free to share. (I looked at ActiveSupport::Notifications, but that would require me to modify the bar method.)
It has come to my attention that I might want to use "the Module+super trick". I'm not sure what this is -- perhaps someone can enlighten me?
It has been quite a while since this question here has been active, but there is another possibility to wrap methods by an included (or extended) Module.
Since 2.0 you can prepend a Module, effectively making it a proxy for the prepending class.
In the example below, a method of an extended module module is called, passing the names of the methods you want to be wrapped. For each of the method names, a new Module is created and prepended. This is for code simplicity. You can also append multiple methods to a single proxy.
An important difference to the solutions using alias_method and instance_method which is later bound on self is that you can define the methods to be wrapped before the methods themselves are defined.
module Prepender
def wrap_me(*method_names)
method_names.each do |m|
proxy = Module.new do
define_method(m) do |*args|
puts "the method '#{m}' is about to be called"
super *args
end
end
self.prepend proxy
end
end
end
Use:
class Dogbert
extend Prepender
wrap_me :bark, :deny
def bark
puts 'Bah!'
end
def deny
puts 'You have no proof!'
end
end
Dogbert.new.deny
# => the method 'deny' is about to be called
# => You have no proof!
I imagine you could use an alias method chain.
Something like this:
def notify_when(method)
alias_method "#{method}_without_notification", method
define_method method do |*args|
puts "#{method} called"
send "#{method}_without_notification", args
end
end
You do not have to modify methods yourself with this approach.
I can think of two approaches:
(1) Decorate the Foo methods to include a notification.
(2) Use a proxy object that intercepts method calls to Foo and notifies you when they happen
The first solution is the approach taken by Jakub, though the alias_method solution is not the best way to achieve this, use this instead:
def notify_when(meth)
orig_meth = instance_method(meth)
define_method(meth) do |*args, &block|
puts "#{meth} called"
orig_meth.bind(self).call *args, &block
end
end
The second solution requires you to use method_missing in combination with a proxy:
class Interceptor
def initialize(target)
#target = target
end
def method_missing(name, *args, &block)
if #target.respond_to?(name)
puts "about to run #{name}"
#target.send(name, *args, &block)
else
super
end
end
end
class Hello; def hello; puts "hello!"; end; end
i = Interceptor.new(Hello.new)
i.hello #=> "about to run hello"
#=> "hello!"
The first method requires modifying the methods (something you said you didn't want) and the second method requires using a proxy, maybe something you do not want. There is no easy solution I'm sorry.

How do I write an RSpec test to unit-test this interesting metaprogramming code?

Here's some simple code that, for each argument specified, will add specific get/set methods named after that argument. If you write attr_option :foo, :bar, then you will see #foo/foo= and #bar/bar= instance methods on Config:
module Configurator
class Config
def initialize()
#options = {}
end
def self.attr_option(*args)
args.each do |a|
if not self.method_defined?(a)
define_method "#{a}" do
#options[:"#{a}"] ||= {}
end
define_method "#{a}=" do |v|
#options[:"#{a}"] = v
end
else
throw Exception.new("already have attr_option for #{a}")
end
end
end
end
end
So far, so good. I want to write some RSpec tests to verify this code is actually doing what it's supposed to. But there's a problem! If I invoke attr_option :foo in one of the test methods, that method is now forever defined in Config. So a subsequent test will fail when it shouldn't, because foo is already defined:
it "should support a specified option" do
c = Configurator::Config
c.attr_option :foo
# ...
end
it "should support multiple options" do
c = Configurator::Config
c.attr_option :foo, :bar, :baz # Error! :foo already defined
# by a previous test.
# ...
end
Is there a way I can give each test an anonymous "clone" of the Config class which is independent of the others?
One very simple way to "clone" your Config class is to simply subclass it with an anonymous class:
c = Class.new Configurator::Config
c.attr_option :foo
d = Class.new Configurator::Config
d.attr_option :foo, :bar
This runs for me without error. This works because all instance variables and methods that get set are tied to the anonymous class instead of Configurator::Config.
The syntax Class.new Foo creates an anonymous class with Foo as a superclass.
Also, throwing an Exception in Ruby is incorrect; Exceptions are raised. throw is meant to be used like a goto, such as to break out of multiple nests. Read this Programming Ruby section for a good explanation on the differences.
As another style nitpick, try not to use if not ... in Ruby. That's what unless is for. But unless-else is poor style as well. I'd rewrite the inside of your args.each block as:
raise "already have attr_option for #{a}" if self.method_defined?(a)
define_method "#{a}" do
#options[:"#{a}"] ||= {}
end
define_method "#{a}=" do |v|
#options[:"#{a}"] = v
end

How do I "fake" C# style attributes in Ruby?

EDIT: I slightly changed the spec, to better match what I imagined this to do.
Well, I don't really want to fake C# attributes, I want to one-up-them and support AOP as well.
Given the program:
class Object
def Object.profile
# magic code here
end
end
class Foo
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
Foo.new.bar("test")
Foo.new.barbar("test")
puts Foo.get_comment(:snafu)
Desired output:
Foo.bar was called with param: b = "test"
test
Foo.bar call finished, duration was 1ms
test
This really should be fixed
Is there any way to achieve this?
I have a somewhat different approach:
class Object
def self.profile(method_name)
return_value = nil
time = Benchmark.measure do
return_value = yield
end
puts "#{method_name} finished in #{time.real}"
return_value
end
end
require "benchmark"
module Profiler
def method_added(name)
profile_method(name) if #method_profiled
super
end
def profile_method(method_name)
#method_profiled = nil
alias_method "unprofiled_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
name = "\#{self.class}##{method_name}"
msg = "\#{name} was called with \#{args.inspect}"
msg << " and a block" if block_given?
puts msg
Object.profile(name) { unprofiled_#{method_name}(*args, &blk) }
end
ruby_eval
end
def profile
#method_profiled = true
end
end
module Comment
def method_added(name)
comment_method(name) if #method_commented
super
end
def comment_method(method_name)
comment = #method_commented
#method_commented = nil
alias_method "uncommented_#{method_name}", method_name
class_eval <<-ruby_eval
def #{method_name}(*args, &blk)
puts #{comment.inspect}
uncommented_#{method_name}(*args, &blk)
end
ruby_eval
end
def comment(text)
#method_commented = text
end
end
class Foo
extend Profiler
extend Comment
# This is the fake attribute, it profiles a single method.
profile
def bar(b)
puts b
end
def barbar(b)
puts(b)
end
comment("this really should be fixed")
def snafu(b)
end
end
A few points about this solution:
I provided the additional methods via modules which could be extended into new classes as needed. This avoids polluting the global namespace for all modules.
I avoided using alias_method, since module includes allow AOP-style extensions (in this case, for method_added) without the need for aliasing.
I chose to use class_eval rather than define_method to define the new method in order to be able to support methods that take blocks. This also necessitated the use of alias_method.
Because I chose to support blocks, I also added a bit of text to the output in case the method takes a block.
There are ways to get the actual parameter names, which would be closer to your original output, but they don't really fit in a response here. You can check out merb-action-args, where we wrote some code that required getting the actual parameter names. It works in JRuby, Ruby 1.8.x, Ruby 1.9.1 (with a gem), and Ruby 1.9 trunk (natively).
The basic technique here is to store a class instance variable when profile or comment is called, which is then applied when a method is added. As in the previous solution, the method_added hook is used to track when the new method is added, but instead of removing the hook each time, the hook checks for an instance variable. The instance variable is removed after the AOP is applied, so it only applies once. If this same technique was used multiple time, it could be further abstracted.
In general, I tried to stick as close to your "spec" as possible, which is why I included the Object.profile snippet instead of implementing it inline.
Great question. This is my quick attempt at an implementation (I did not try to optimise the code). I took the liberty of adding the profile method to the
Module class. In this way it will be available in every class and module definition. It would be even better
to extract it into a module and mix it into the class Module whenever you need it.
I also didn't know if the point was to make the profile method behave like Ruby's public/protected/private keywords,
but I implemented it like that anyway. All methods defined after calling profile are profiled, until noprofile is called.
class Module
def profile
require "benchmark"
#profiled_methods ||= []
class << self
# Save any original method_added callback.
alias_method :__unprofiling_method_added, :method_added
# Create new callback.
def method_added(method)
# Possible infinite loop if we do not check if we already replaced this method.
unless #profiled_methods.include?(method)
#profiled_methods << method
unbound_method = instance_method(method)
define_method(method) do |*args|
puts "#{self.class}##{method} was called with params #{args.join(", ")}"
bench = Benchmark.measure do
unbound_method.bind(self).call(*args)
end
puts "#{self.class}##{method} finished in %.5fs" % bench.real
end
# Call the original callback too.
__unprofiling_method_added(method)
end
end
end
end
def noprofile # What's the opposite of profile?
class << self
# Remove profiling callback and restore previous one.
alias_method :method_added, :__unprofiling_method_added
end
end
end
You can now use it as follows:
class Foo
def self.method_added(method) # This still works.
puts "Method '#{method}' has been added to '#{self}'."
end
profile
def foo(arg1, arg2, arg3 = nil)
puts "> body of foo"
sleep 1
end
def bar(arg)
puts "> body of bar"
end
noprofile
def baz(arg)
puts "> body of baz"
end
end
Call the methods as you would normally:
foo = Foo.new
foo.foo(1, 2, 3)
foo.bar(2)
foo.baz(3)
And get benchmarked output (and the result of the original method_added callback just to show that it still works):
Method 'foo' has been added to 'Foo'.
Method 'bar' has been added to 'Foo'.
Method 'baz' has been added to 'Foo'.
Foo#foo was called with params 1, 2, 3
> body of foo
Foo#foo finished in 1.00018s
Foo#bar was called with params 2
> body of bar
Foo#bar finished in 0.00016s
> body of baz
One thing to note is that it is impossible to dynamically get the name of the arguments with Ruby meta-programming.
You'd have to parse the original Ruby file, which is certainly possible but a little more complex. See the parse_tree and ruby_parser
gems for details.
A fun improvement would be to be able to define this kind of behaviour with a class method in the Module class. It would be cool to be able to do something like:
class Module
method_wrapper :profile do |*arguments|
# Do something before calling method.
yield *arguments # Call original method.
# Do something afterwards.
end
end
I'll leave this meta-meta-programming exercise for another time. :-)

Can I invoke an instance method on a Ruby module without including it?

Background:
I have a module which declares a number of instance methods
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
And I want to call some of these methods from within a class. How you normally do this in ruby is like this:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
Problem
include UsefulThings brings in all of the methods from UsefulThings. In this case I only want format_text and explicitly do not want get_file and delete_file.
I can see several possible solutions to this:
Somehow invoke the method directly on the module without including it anywhere
I don't know how/if this can be done. (Hence this question)
Somehow include Usefulthings and only bring in some of it's methods
I also don't know how/if this can be done
Create a proxy class, include UsefulThings in that, then delegate format_text to that proxy instance
This would work, but anonymous proxy classes are a hack. Yuck.
Split up the module into 2 or more smaller modules
This would also work, and is probably the best solution I can think of, but I'd prefer to avoid it as I'd end up with a proliferation of dozens and dozens of modules - managing this would be burdensome
Why are there lots of unrelated functions in a single module? It's ApplicationHelper from a rails app, which our team has de-facto decided on as the dumping ground for anything not specific enough to belong anywhere else. Mostly standalone utility methods that get used everywhere. I could break it up into seperate helpers, but there'd be 30 of them, all with 1 method each... this seems unproductive
I think the shortest way to do just throw-away single call (without altering existing modules or creating new ones) would be as follows:
Class.new.extend(UsefulThings).get_file
If a method on a module is turned into a module function you can simply call it off of Mods as if it had been declared as
module Mods
def self.foo
puts "Mods.foo(self)"
end
end
The module_function approach below will avoid breaking any classes which include all of Mods.
module Mods
def foo
puts "Mods.foo"
end
end
class Includer
include Mods
end
Includer.new.foo
Mods.module_eval do
module_function(:foo)
public :foo
end
Includer.new.foo # this would break without public :foo above
class Thing
def bar
Mods.foo
end
end
Thing.new.bar
However, I'm curious why a set of unrelated functions are all contained within the same module in the first place?
Edited to show that includes still work if public :foo is called after module_function :foo
Another way to do it if you "own" the module is to use module_function.
module UsefulThings
def a
puts "aaay"
end
module_function :a
def b
puts "beee"
end
end
def test
UsefulThings.a
UsefulThings.b # Fails! Not a module method
end
test
If you want to call these methods without including module in another class then you need to define them as module methods:
module UsefulThings
def self.get_file; ...
def self.delete_file; ...
def self.format_text(x); ...
end
and then you can call them with
UsefulThings.format_text("xxx")
or
UsefulThings::format_text("xxx")
But anyway I would recommend that you put just related methods in one module or in one class. If you have problem that you want to include just one method from module then it sounds like a bad code smell and it is not good Ruby style to put unrelated methods together.
To invoke a module instance method without including the module (and without creating intermediary objects):
class UsefulWorker
def do_work
UsefulThings.instance_method(:format_text).bind(self).call("abc")
...
end
end
Not sure if someone still needs it after 10 years but I solved it using eigenclass.
module UsefulThings
def useful_thing_1
"thing_1"
end
class << self
include UsefulThings
end
end
class A
include UsefulThings
end
class B
extend UsefulThings
end
UsefulThings.useful_thing_1 # => "thing_1"
A.new.useful_thing_1 # => "thing_1"
B.useful_thing_1 # => "thing_1"
Firstly, I'd recommend breaking the module up into the useful things you need. But you can always create a class extending that for your invocation:
module UsefulThings
def a
puts "aaay"
end
def b
puts "beee"
end
end
def test
ob = Class.new.send(:include, UsefulThings).new
ob.a
end
test
A. In case you, always want to call them in a "qualified", standalone way (UsefulThings.get_file), then just make them static as others pointed out,
module UsefulThings
def self.get_file; ...
def self.delete_file; ...
def self.format_text(x); ...
# Or.. make all of the "static"
class << self
def write_file; ...
def commit_file; ...
end
end
B. If you still want to keep the mixin approach in same cases, as well the one-off standalone invocation, you can have a one-liner module that extends itself with the mixin:
module UsefulThingsMixin
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
module UsefulThings
extend UsefulThingsMixin
end
So both works then:
UsefulThings.get_file() # one off
class MyUser
include UsefulThingsMixin
def f
format_text # all useful things available directly
end
end
IMHO it's cleaner than module_function for every single method - in case want all of them.
As I understand the question, you want to mix some of a module's instance methods into a class.
Let's begin by considering how Module#include works. Suppose we have a module UsefulThings that contains two instance methods:
module UsefulThings
def add1
self + 1
end
def add3
self + 3
end
end
UsefulThings.instance_methods
#=> [:add1, :add3]
and Fixnum includes that module:
class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
include UsefulThings
end
We see that:
Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
#=> [:add2, :add3, :add1]
1.add1
2
1.add2
cat
1.add3
dog
Were you expecting UsefulThings#add3 to override Fixnum#add3, so that 1.add3 would return 4? Consider this:
Fixnum.ancestors
#=> [Fixnum, UsefulThings, Integer, Numeric, Comparable,
# Object, Kernel, BasicObject]
When the class includes the module, the module becomes the class' superclass. So, because of how inheritance works, sending add3 to an instance of Fixnum will cause Fixnum#add3 to be invoked, returning dog.
Now let's add a method :add2 to UsefulThings:
module UsefulThings
def add1
self + 1
end
def add2
self + 2
end
def add3
self + 3
end
end
We now wish Fixnum to include only the methods add1 and add3. Is so doing, we expect to get the same results as above.
Suppose, as above, we execute:
class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
include UsefulThings
end
What is the result? The unwanted method :add2 is added to Fixnum, :add1 is added and, for reasons I explained above, :add3 is not added. So all we have to do is undef :add2. We can do that with a simple helper method:
module Helpers
def self.include_some(mod, klass, *args)
klass.send(:include, mod)
(mod.instance_methods - args - klass.instance_methods).each do |m|
klass.send(:undef_method, m)
end
end
end
which we invoke like this:
class Fixnum
def add2
puts "cat"
end
def add3
puts "dog"
end
Helpers.include_some(UsefulThings, self, :add1, :add3)
end
Then:
Fixnum.instance_methods.select { |m| m.to_s.start_with? "add" }
#=> [:add2, :add3, :add1]
1.add1
2
1.add2
cat
1.add3
dog
which is the result we want.
After almost 9 years here's a generic solution:
module CreateModuleFunctions
def self.included(base)
base.instance_methods.each do |method|
base.module_eval do
module_function(method)
public(method)
end
end
end
end
RSpec.describe CreateModuleFunctions do
context "when included into a Module" do
it "makes the Module's methods invokable via the Module" do
module ModuleIncluded
def instance_method_1;end
def instance_method_2;end
include CreateModuleFunctions
end
expect { ModuleIncluded.instance_method_1 }.to_not raise_error
end
end
end
The unfortunate trick you need to apply is to include the module after the methods have been defined. Alternatively you may also include it after the context is defined as ModuleIncluded.send(:include, CreateModuleFunctions).
Or you can use it via the reflection_utils gem.
spec.add_dependency "reflection_utils", ">= 0.3.0"
require 'reflection_utils'
include ReflectionUtils::CreateModuleFunctions
This old question comes to me today when I am studing Ruby and found interesting so I want to answer with my new knowlege.
Assume that you have the module
module MyModule
def say
'I say'
end
def cheer
'I cheer'
end
end
then with the class so call Animal I can take cheer method from MyModule as following
class Animal
define_method(:happy, MyModule.method(:cheer))
end
This is so called unbound method, so you can take a callable object and bind it to another place(s).
From this point, you can use the method as usual, such as
my_dog = Animal.new
my_dog.happy # => "I cheer"
Hope this help as I also learned something new today.
To learn further, you can use irb and take a look at Method object.

Resources