Aliasing ExampleGroup - ruby-on-rails

In RSpec I can give alias to examples. For instance, alias_example_to.
Is there any way of aliasing Example Groups? I can use only describe and context. But I want to use, say, feature, scenario...etc. For example,
describe MyObject do
scenario "doing smth with object" do
...
end
end
I found an article on http://benediktdeicke.com/2013/01/custom-rspec-example-groups/.
Is there any other way to alias Example Groups.

As I interpret github, this feature was requested via https://github.com/rspec/rspec-core/issues/493 and is awaiting integration via https://github.com/rspec/rspec-core/pull/870. It is not yet available.

A possible workaround until the feature is released is this:
# spec/support/example_group_aliases.rb
module ExampleGroupAliases
extend ActiveSupport::Concern
included do
class << self
alias_method :simple, :context
end
end
module ClassMethods
def fancy(description, options = {}, &block)
context(description, options.merge(:fancy => true), &block)
end
end
RSpec.configure do |config|
config.include self
end
end
The code shows two ways of defining aliases for the context method. The first (simple) one is using alias_method. The second one (fancy) is defining a new method that then calls the original context method. The last approach allows you to do additional stuff, like adding some more options.

Related

Can I disallow a Rails model from being access outside of a module?

Is there a way to have a model such that only code within the same module can access it?
Something like:
module SomeModule
class SomeActiveRecordModel
# has attribute `some_attribute`
...
end
end
module SomeModule
class SomeOtherClass
def self.sum_of_attribute
SomeActiveRecordModel.sum(:some_attribute)
end
end
end
class OutsideOfModule
def self.sum_of_attribute
SomeModule::SomeActiveRecordModel.sum(:some_attribute)
end
end
SomeModule::SomeOtherClass.sum_of_attribute # works
OutsideOfModule.sum_of_attribute # raises error
Short answer is no. Here's why
Ideally, you want to implement this in your SomeModule. But when you call SomeModule::SomeOtherClass.sum_of_attribute in other classes, you are in a scope of SomeModule::SomeOtherClass.
SomeModule::SomeActiveRecordModel.sum(:some_attribute)
||
\/
module SomeModule
class SomeActiveRecordModel
def sum(*args)
# Here, self => SomeModule::SomeActiveRecordModel
# That's why you won't be able to do any meta trick to the module
# or classes in the module to identify if it's being invoked outside
end
end
end
So you wouldn't know who the original caller is.
You might be able to dig through the call stack to do that. Here's another SO thread you might find helpful if you want to go down that path.
In short, no. But this is more a question of Ruby's approach and philosophy. There are other ways of thinking about the code that allow you achieve something similar to what you're looking for, in a more Rubyesque way.
This answer covers the different ways of making things private.

RSpec page variable

How is it that rspec feature tests implicitly know to use methods such as find, within, and fill_in from the page object?
I've written a helper class for some of my rspec tests and wanted to use those methods, and realized that I needed to pass the page object into the method, and then use page.find and the like.
RSpec achieves this by including Capybara::DSL in those cases where it wants those methods available. The module is pretty elegant, if you want to take a look at https://github.com/jnicklas/capybara/blob/f83edc2a515a3a4fd80eef090734d14de76580d3/lib/capybara/dsl.rb
suppose you want to include the following module:
module MailerMacros
def last_email
ActionMailer::Base.deliveries.last
end
def reset_email
ActionMailer::Base.deliveries = []
end
end
to include them, just call config.include(MailerMacros), like this:
RSpec.configure do |config|
config.include(MailerMacros)
end
now, you should be able to call reset_email() & last_email instead of MailerMacros::reset_email().

RSpec: how can I mixin a module into RSpec test?

In my app when user share something he's rating grows. When he tries to share something twice – he will get no additional rating for second try. For application, share callback is triggered by client-side with JS, so, it's just a regular GET-request. So, I need to test this functionality. It's easy. But I'v got several sections with this behavior. Every controller from that sections have method named "rating_from_share", so tests are pretty similar. I think it is good idea to extract that test's in a mixing and include them where it should be, but I can't figure out, how can I do this.
So, is it real to include a mixing with RSpec to a RSpec test? Maybe something kind of metaprogramming can solve this problem?
P.S. realization of "rating_from_share" method is not really the same but only the output result, so I can't to aggregate it to a superclass and test them here.
EDIT:
According to Vimsha answer, should I do something like this?
Module Share
def share
it 'should be fun'
expect(#fun.isFun?).toBe == 'yup' # the #fun is declared in ShareTest
end
end
end
describe "Share Test" do
extend Share
before :each do
#fun = Fun.new
end
it 'should do test' do
share # call method from Share module, which has real RSpec code?
end
end
The code is written just here, I'm just trying to get the idea.
A common practice in RSpec is to store such logic under spec/support. For instance:
# spec/support/ratings_macros.rb
module RatingsMacros
...
end
You then need to load it from your spec_helper:
# spec/spec_helper.rb
...
RSpec.configure do |config|
...
config.include RatingsMacros
You can now call in your tests all the methods defined in the RatingsMacros module.
You can use shared examples.
These are typically saved under spec/support and loaded via spec_helper.rb. Be sure to read the docs to understand how to load the shared code--it is not automagically performed for you.
Once they are defined you can include them like so:
# spec/support/decorated_model.rb
shared_examples "decorated_model" do
it "can be decorated" do
subject.should respond_to?(:decorate)
end
end
# my_class_spec.rb
describe MyClass do
it_behaves_like "decorated_model"
end
module Share
def share
end
end
describe "Share Test" do
extend Share
end
You can call the methods of the module directly within the tests
The other answers pollute the test with the module's methods, or involve writing a dummy class. This solution uses the built-in double object as a throwaway object to extend with the module's methods.
RSpec.describe Share do
describe '#share' do
subject { double.extend(described_class) }
end
it 'does something cool' do
expect(subject.share).to eq 'something_cool'
end
end

Ruby: Mixin which adds dynamic instance methods whose names are created using a class method

I have the following:
module Thing
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
attr_reader :things
def has_things(*args)
options = args.extract_options! # Ruby on Rails: pops the last arg if it's a Hash
# Get a list of the things (Symbols only)
#things = args.select { |p| p.is_a?(Symbol) }
include InstanceMethods
end
end
module InstanceMethods
self.class.things.each do |thing_name| # !!! Problem is here, explaination below
define_method "foo_for_thing_#{thing_name}" do
"bar for thing #{thing_name}"
end
end
end
end
In another class which mixes-in the Thing module:
class Group
has_things :one, :two, :option => "something"
end
When calling has_things within a class, I would like to have the dynamic "foo_for_thing_one" and "foo_for_thing_two" instance methods available. For example:
#group = Group.new
#group.foo_for_thing_one # => "bar for thing one"
#group.foo_for_thing_two # => "bar for thing two"
However, I get the following error:
`<module:InstanceMethods>': undefined method `things' for Module:Class (NoMethodError)
I realize that "self" in the problem line pointed out above (first line of the InstanceMethods module) refers to the InstanceMethods module.
How do I reference the "things" class method (which returns [:one, :two] in this example) so I can loop through and create dynamic instance methods for each? Thanks. Or if you have other suggestions for accomplishing this, please let me know.
Quick answer:
Put the contents of InstanceMethods inside the has_things method definition and remove the InstanceMethods module.
Better answer:
Your use of the InstanceMethods-ClassMethods anti-pattern is especially unwarranted here and cargo-culting it has added to your confusion about scope and context. Do the simplest thing that could possibly work. Don't copy someone else's code without critical thinking.
The only module you need is ClassMethods, which should be given a useful name and should not be included but rather used to extend the class that you want to grant the has_things functionality. Here's the simplest thing that could possibly work:
module HasThings
def has_things(*args)
args.each do |thing|
define_method "thing_#{thing}" do
"this is thing #{thing}"
end
end
end
end
class ThingWithThings
extend HasThings
has_things :foo
end
ThingWithThings.new.thing_foo # => "this is thing foo"
Only add complexity (options extraction, input normalization, etc) when you need it. Code just in time, not just in case.

Rails: I can't call a function in a module in /lib - what am I doing wrong?

I have a module saved in /lib as test_functions.rb that looks like this
module TestFunctions
def abc
puts 123
end
end
Going into ruby script/runner, I can see that the module is loading automatically (good ol' convention over configuration and all that...)
>> TestFunctions.instance_methods
=> ["abc"]
so the method is known, let's try calling it
>> TestFunctions.abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):3
Nope. How about this?
>> TestFunctions::abc
NoMethodError: undefined method `abc' for TestFunctions:Module from (irb):4
Test
Nope again.
defined?(TestFunctions::abc) #=> nil, but
TestFunctions.method_defined? :abc #=> true
Like I said at the top, I know I'm being dumb, can anyone de-dumb me?
If you want Module-level functions, define them in any of these ways:
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
end
All of these ways will make the methods available as Foo.method_one or Foo::method_one etc
As other people have mentioned, instance methods in Modules are the methods which are available in places where you've included the Module
I'm going to try to summarise the various answers myself, since each had something valuable to say, but none really got to what I now realise is probably the best response:
I was asking the wrong question because I was doing it wrong.
For reasons I can no longer explain, I wanted a set of completely stand-alone functions in a library, which represented methods I was trying to DRY out of my classes. That can be achieved, using things like
module Foo
def self.method_one
end
def Foo.method_two
end
class << self
def method_three
end
end
def method_four
end
module_function :method_four
end
I could also include my module, either within a class, in which case the methods become part of the class or outside, in which case they are defined on whatever class I'm running inside (Object? Kernel? Irb, if I'm interactive? Probably not a great idea, then)
The thing is, there was no good reason not to have a class in the first place - I'd somehow got on to a train of thought that took me down an seldom-used and frankly slightly weird branch line. Probably a flashback to the days before OO became mainstream (I'm old enough that up to today I've spent a lot more years writing procedural code).
So the functions have moved into a class, where they seem pretty happy, and the class methods thus exposed are being cheerfully used wherever necessary.
You can also use module_function like so:
module TestFunctions
def abc
puts 123
end
module_function :abc
end
TestFunctions.abc # => 123
Now you can include TestFunctions in class and call "abc" from within TestFunctions module.
I messed with this for a while and learned several things. Hopefully this will help someone else out. I am running Rails 3.2.8.
My module (utilities.rb) looks like this and is in the /lib directory of my rails app:
module Utilities
def compute_hello(input_string)
return "Hello #{input_string}"
end
end
My test (my_test.rb) looks like this and is in the /test/unit directory of my rails app:
require "test_helper"
require "utilities"
class MyTest < ActiveSupport::TestCase
include Utilities
def test_compute_hello
x = compute_hello(input_string="Miles")
print x
assert x=="Hello Miles", "Incorrect Response"
end
end
Here are a few things to note: My test extends ActiveSupport::TestCase. This is important because ActiveSupport adds /lib to the $LOAD_PATH. (seehttp://stackoverflow.com/questions/1073076/rails-lib-modules-and)
Secondly, I needed to both "require" my module file, and also "include" the module. Lastly, it is important to note that the stuff that gets included from the module essentially gets placed in the test class. So... be careful that the module that you include doesn't start with "test_". Otherwise, Rails will attempt to run your module method as a test.
You can't call a method in a Module directly. You need to include it in a class. Try this:
>> class MyTest
>> include TestFunctions
>> end
=> MyTest
>> MyTest.new.abc
123
=> nil
You need to include the module
include Testfunctions
Then 'abc' will return something.
You need to prefix your function with the module name because modules are not classes:
Your /lib/test_functions.rb:
module TestFunctions
def TestFunctions.abc
puts 123
end
end
Your code using the module method:
require 'test_functions'
TestFunctions.abc
Today you can do it using module_function notation.
module TestFunctions
def abc
puts 123
end
end
Now TestFunctions.abc prints "123"
A little more about module_function: https://apidock.com/ruby/Module/module_function
Try this code:
service = Class.new { extend TestFunctions }
service.method_four

Resources