I wanted to override the expect in under Syntax module. So, i have placed the below code into the .config/initializers/syntax.rb file
module RSpec
module Expectations
module Syntax
def enable_expect(syntax_host=::RSpec::Matchers)
return if expect_enabled?(syntax_host)
syntax_host.module_exec do
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
::RSpec::Expectations::ExpectationTarget.for(value, block)
end
end
end
end
end
end
And required this inside the env.rb file.
require_relative '../../.config/initializers/syntax'
This is not overriding the existing method. I'm using RSpec gem 3.2.0
What went wrong with the configuration?
I suggest you to put this override codes in spec/support directory and require it in rails or spec helper instead of putting in initializers.
Related
Objective:
I want to run a basic RSpec unit test on an instance method of a mixin (module) named Debug. Below are the file contents of the Debug mixin:
Mixin File: ./mixins/debug.rb
module Debug
public
def class_info?
"#{self.class.name}"
end
end
Validate Debug mixin instance methods accessible from RSpec:
When I run irb and include the Debug mixin with commands require_relative './mixins/debug.rb' and include Debug, and then call Debug.class_info? it successfully returns "Module"
Then if I run rspec with the following RSpec unit test to confirm that the RSpec context can access the instance methods of the mixin, the test successfully passes:
RSpec Unit Test Setup #1: ./spec/mixins/debug_spec.rb
require_relative '../../mixins/debug.rb'
RSpec.describe Debug, "#class_info?" do
include Debug
before(:each) do
#class_info_instance_method = Debug.instance_methods[0].to_s
end
context "with mixins" do
it "has class info instance method" do
expect(#class_info_instance_method).to eq "class_info?"
end
end
end
Problem when calling Debug mixin instance method from RSpec:
Lastly, I change the RSpec unit test to be as follows, so instead it actually calls the class_info? instance method of the Debug mixin:
RSpec Unit Test Setup #2: ./spec/mixins/debug_spec.rb
require_relative '../../mixins/debug.rb'
RSpec.describe Debug, "#class_info?" do
include Debug
before(:each) do
#class_info = Debug.class_info?
end
context "with mixins" do
it "shows class info" do
expect(#class_info).to eq "Module"
end
end
end
But now when I run rspec from the command line, why does it return the following error? (Note: even though in the previous RSpec Unit Test Setup #1 that was entirely similar I checked I could successfully access this Debug mixin instance method)
1) Debug#class_info? with mixins shows class info
Failure/Error: #class_info = Debug.class_info?
NoMethodError:
undefined method `class_info?' for Debug:Module
Note: I have shared the above code in my RubyTest GitHub repo.
Setup and References:
My System:
Ruby: ruby 2.3.0p0 (ruby -v)
RSpec: 3.5.4 (rspec -v)
References:
Applying example from Mixins chapter of Programming Ruby book
When you include a module, the methods become instance methods in the included class. Debug.class_info? doesn't work because there is no class method class_info?. I'm also not sure that the way you've included the module in your test is the best way to do it. Would something like this work?
require_relative '../../mixins/debug.rb'
class TestClass
include Debug
end
RSpec.describe Debug, "#class_info?" do
let(:test_instance) { TestClass.new }
context "with mixins" do
it "shows class info" do
expect(test_instance.class_info?).to eq "TestClass"
end
end
end
Assuming that "clean" Ruby monkey patching isn't an option, and all gem extension methods need to be completely contained within their own namespace, and given a structure like
module ARubyGem
class GemClassOne
def method1
# instance method to be available when calling extended module
end
def self.method2
# singleton method to also be available in extended module
end
end
end
Then which of these is the better way to extend a gem if the original gem code has some singleton methods that begin with "self"?
module ARubyGemExtension
class GemClassOneExtension < ARubyGem::GemClassOne
def method_to_override
# new code here
end
end
end
vs.
module ARubyGemExtension
include ARubyGem
class GemClassOneExtension
def method_to_override
# new code here
end
end
end
Seems like you would need to go with:
module ARubyGemExtension
class GemClassOneExtension < ARubyGem::GemClassOne
def method_to_override
# new code here
end
end
end
(Remember to namespace GemClassOne.)
I don't know anything about the gem you're using, but unless it's specifically set up for providing methods through include, I think you may not get what you're after.
I'm pretty new to Ruby and facing a pretty basic problem i guess. I'm probably missing out on some basic concepts and constructs. So this is what i'm trying to do,
I'm writing a sinatra project, and i have a classes which are written in different files. The structure looks something of this sort,
project_name
- api.rb
- base.rb
- settings.rb
In my api.rb file i have defined a class and some methods, it also calls some methods form base.rb and base.rb calls some methods from settings.rb
In api.rb
require 'sinatra'
require 'json'
require 'uri'
require 'base' --> This is the base.rb which is resulting in error
module XX
class Api
def some_method
base = Base.new
base.setup
# some more code
end
end
end
In base.rb, it has the following code
require 'settings'
module XX
class Base
def setup
# some code
end
def some_method
#some code
end
end
end
When i just run ruby api.rb, i'm getting an error in the require statement, unable to load such file-- base (LoadError).
What is it that i'm missing here? Also, how is it that ruby know whether it a gem or a file required..does it check to see if the require is a file in the project and then goes on to check for a gem ? How is this process done in ruby?
Any help is much appreciated!
I have a Sinatra app that, boiled down, looks basically like this:
class MyApp < Sinatra::Base
configure :production do
myConfigVar = read_config_file()
end
configure :development do
myConfigVar = read_config_file()
end
def read_config_file()
# interpret a config file
end
end
Unfortunately, this doesn't work. I get undefined method read_config_file for MyApp:Class (NoMethodError)
The logic in read_config_file is non-trivial, so I don't want to duplicate in both. How can I define a method that can be called from both my configuration blocks? Or am I just approaching this problem in entirely the wrong way?
It seems the configure block is executed as the file is read. You simply need to move the definition of your method before the configure block, and convert it to a class method:
class MyApp < Sinatra::Base
def self.read_config_file()
# interpret a config file
end
configure :production do
myConfigVar = self.read_config_file()
end
configure :development do
myConfigVar = self.read_config_file()
end
end
Your configure blocks are run when the class definition is evaluated. So, the context is the class itself, not an instance. So, you need a class method, not an instance method.
def self.read_config_file
That should work. Haven't tested though. ;)
I have a portfolio website built in Sinatra. I haven't worked on it for a while, been doing some Rails. I updated my gem list yesterday by running 'gem update'. I don't know if this has anything to do with that, but I started working on the portfolio website again today and I've been getting some deprecation warnings.
DEPRECATION WARNING: The InstanceMethods module inside
ActiveSupport::Concern will be no longer included automatically.
Please define instance methods directly in Work instead. (called from
include at /Users/joris/Desktop/sinatra/portfolio/models/work.rb:2)
I'm not sure how to fix this and when I run the application it doesn't work anymore.. going to my routes just returns the Sinatra 404 page. (Also, isn't ActiveSupport part of Rails? Why is this coming up in my Sinatra app..)
The file it mentions in the error is work.rb:
class Work
include MongoMapper::Document
key :title, String
key :url, String
key :filename, String
key :file, String
key :description, String
timestamps!
end
This is my main file (portfolio.rb):
require "sinatra"
require 'twitter'
require 'RedCloth'
require 'html_truncator'
require 'digest/md5'
class Portfolio < Sinatra::Application
require_relative 'config/init'
require_relative 'helpers/init'
require_relative 'models/init'
require_relative 'routes/init'
The models init file (which calls the work.rb file) has these contents:
require 'mongo_mapper'
MongoMapper.connection = Mongo::Connection.new('lalaland.com', 10070)
MongoMapper.database = 'hello'
MongoMapper.database.authenticate('lalala', 'hello')
require_relative 'post'
require_relative 'work'
EDIT: Just saw I'm also getting it for models/post.rb
DEPRECATION WARNING: The InstanceMethods module inside
ActiveSupport::Concern will be no longer included automatically.
Please define instance methods directly in Post instead. (called from
include at /Users/joris/Desktop/sinatra/portfolio/models/post.rb:2)
Somewhere in your app (or its dependencies) you're doing
module Blah
extend ActiveSupport::Concern
module InstanceMethods
def foo
end
end
...
end
and Active Support is telling you to do
module Blah
extend ActiveSupport::Concern
def foo
end
end
You're right that Active Support is part of Rails, but like Active Record it can also be used without the rest of rails. Mongo mapper uses it for example, and at a cursory glance it uses the deprecated InstanceMethods idiom in a bunch of places
It looks like this was patched earlier this month in the mongo_mapper gem, so I would expect the fix to make it into the next release:
https://github.com/jnunemaker/mongomapper/commit/d2333d944ce6ae59ecab3c45e25bbed261f8180e