I'm new to TDD, and I'm trying to write methods to generate URLs on a per-class basis that inherit from a parent class using Sinatra and the Namespace library in Sinatra-Contrib. I'm not too far along, because I've gotten a failed RSpec test that has kept me from moving further: undefined method 'namespace'. Can anyone help?
Gemfile:
source 'https://rubygems.org'
ruby '1.9.3'
gem 'sinatra'
gem 'sinatra-contrib'
gem 'rack'
gem 'thin'
group :development, :test do
gem 'rspec'
gem 'rack-test'
gem 'ZenTest'
gem 'autotest-growl'
gem 'autotest-fsevent'
end
base_model.rb:
require 'sinatra'
require 'sinatra/namespace'
require 'rack'
def generate_routes_for_model(model_class, rootUrl)
namespace rootUrl do
get '/show' do
"I'm the model's show route"
end
end
end
base_model_spec.rb
require_relative '../base_model.rb'
require 'rack/test'
set :environment, :test
class Foo
end
def app
Sinatra::Application
end
include Rack::Test::Methods
describe 'Create Routes for test Class' do
it "should load foo.show" do
generate_routes_for_model(Foo, '/foo')
get '/foo/show'
last_response.should be_ok
end
end
Here's the result of the test:
Failures:
1) Create Routes for test Class should load foo.show
Failure/Error: generate_routes_for_model(Foo, '/foo')
NoMethodError:
undefined method `namespace' for #<RSpec::Core::ExampleGroup::Nested_2:0x007f8571102e00>
# ./base_model.rb:16:in `generate_routes_for_model'
# ./spec/base_model_spec.rb:24:in `block (2 levels) in <top (required)>'
It appears to be a scope problem, on first look. namespace is a class method of a Sinatra extension (which is why you call it within the class definition and not inside an instance method).
When you run RSpec, is namespace ever within a class that inherits from Sinatra::Base? I don't think so. You need to provide a Sinatra class that namespace can run within. e.g.
class Foo; end
app = Sinatra.new do
register Sinatra::Namespace
end
def generate_routes_for_model(app_class, model_class, rootUrl)
['show'].each do |route|
app_class.namespace rootUrl do
get route do
"I'm the model's show route"
end
end
end
end
generate_routes_for_model app, Foo, "/"
app.run!
Then going to http://localhost:4567/show will respond with "I'm the model's show route".
Or perhaps use class_eval to define the code, there's a few ways you could tackle this, main thing to keep in mind is make sure the scope is right or the app or RSpec etc won't be able to find the method.
Edit:
Also, I'd move the generation into a before block, as it's set up for an integration test (you're essentially saying "If the route exists then the generation worked, I don't need to test the method here", so it's a black box test of the public API - an integration test by any other name:) If you wanted to test the method itself that would be a unit test, and you'd need to look into the app to see the routes had been generated.
describe 'Create Routes for test Class' do
before do
generate_routes_for_model(Foo, '/foo')
get '/foo/show' # this, too, is not a test but a pre-requisite for the test
end
it "should load foo.show" do
last_response.should be_ok
end
end
Related
I am trying to access a variable defined in Sinatra settings from my test helper with no luck. This is my code:
Main app:
require 'sinatra'
set :foo, 'bar'
# use settings.foo in the routes
tests/test_helper.rb
ENV['RACK_ENV'] = 'test'
require 'minitest/autorun'
require 'rack/test'
module Minitest
class Spec
include Rack::Test::Methods
def app
Sinatra::Application
end
before do
# do something with settings.foo
end
end
end
I have tried Sinatra::Application.settings.foo and also app.settings.foo but none work.
I have also tried adding a helper method like the one below:
lib/helpers/settings_helper.rb
module SettingsHelper
def foo
settings.foo
end
end
helpers SettingsHelper
This works inside app, but again doesn't work inside test_helper. I tried requiring settings_helper.rb in test_helper. Also added an include. None of this worked.
Does anybody know what am I doing wrong?
Happy holidays
Not sure if this will work with a classic style app. But with a modular app you can do MyApp.set :foo, 'bar'. So you could try Sinatra::Application.set :foo, 'bar'
Hope this helps.
I followed this tutorial to create a simple command-line gem and now I want to extend functionality with raad. Currently I have made the following changes after following the tutorial:
bin/zerp:
#!/usr/bin/env ruby
require 'zerp'
class Zerp
def start
Raad::Logger.debug 'zerp started'
EventMachine.run do
EventMachine.add_periodic_timer(1) do
Raad::Logger.info 'zerp is running'
end
end
end
def stop
EventMachine.stop
Raad::Logger.debug 'zerp stopped'
end
end
lib/zerp.rb:
require 'zerp/version'
require 'eventmachine'
require 'raad'
module Zerp
end
When I run the client from commandline I get the following error.
user#zenbook:~/git/zerp :) zerp
/home/user/.local/lib/ry/rubies/1.9.3-p194/lib/ruby/gems/1.9.1/gems/zerp-0.0.1/bin/zerp:5:in `<top (required)>': Zerp is not a class (TypeError)
from /home/user/.local/lib/ry/current/bin/zerp:23:in `load'
from /home/user/.local/lib/ry/current/bin/zerp:23:in `<main>'
user#zenbook:~/git/zerp :(
What is the proper approach to get this working?
This is a use-case I did not really look into when making Raad and only minor modifications will make it better integrate within a "executable" gem context. In the meantime you can use this workaround:
bin/zerp
#!/usr/bin/env ruby
begin
require 'zerp'
rescue LoadError
$:.unshift 'zerp/lib'
require 'zerp'
end
# raad must be required after the Zerp class definition
require 'raad'
zerp/lib/zerp.rb
require "zerp/version"
require "zerp/service"
zerp/lib/service.rb
require 'eventmachine'
class Zerp
def start
Raad::Logger.debug 'zerp started'
EventMachine.run do
EventMachine.add_periodic_timer(1) do
Raad::Logger.info 'zerp is running'
end
end
end
def stop
EventMachine.stop
Raad::Logger.debug 'zerp stopped'
end
end
zerp/lib/version
module ZerpModule
VERSION = "0.0.1"
end
The way Raad works right now is that since the file requiring the raad gem is "zerp" then Raad tries to bootstrap using the "Zerp" class. To avoid conflicts you should use another module name than "Zerp" - I just picked "ZerpModule".
I'll try to make a fix shortly to have Raad integrate better in a gem context.
In zerp.rb, you declare Zerp as a module, but in bin/zerp, you declare it as a class. Since it's already a module, you cannot do what you are trying to do.
I would just remove the module Zerp stuff. I would also put class Zerp etc. into zerp.rb.
Finally, in bin/zerp you need to actually call some methods. Something like this:
zerp = Zerp.new
Process.signal('SIGINT') do
zerp.stop
end
zerp.start
Not knowing what you are trying to do, that's just a guess…
I am writing a gem which includes a Sinatra application that a developer can extend. For example:
# gem code:
require 'sinatra'
module Mygem
class Application < Sinatra::Base
get 'auth/login' {}
get 'auth/logout {}
end
end
# developer code:
require 'mygem'
class DeveloperApp < Mygem::Application
# ..
end
I am also getting started using RSpec. How should I configure RSpec for testing this functionality?
The references above are all informative and useful but mostly rails specific. I found it quite hard to find a simple recipe for a basic test of a modular Sinatra app, so I am hoping this will answer the question for others. Here is a completely bare-bones, small as possible test. This is probably not the only way to do it, but it works well for a modular app:
require 'sinatra'
class Foo < Sinatra::Base
get '/' do
"Hello"
end
end
require 'rack/test'
describe Foo do
include Rack::Test::Methods
def app
Foo.new
end
it "should be testable" do
get '/'
last_response.should be_ok
end
end
Note that there is no need to have the server running when you launch the test (some tutorials I saw implied that you do) - it's not an integration test.
It's actually pretty simple -- just add rspec to your gemfile (then bundle install), and make a directory in your gem called spec/. Once you've done that, add a file spec/spec_helper.rb that contains some configuration for rspec (mostly requiring various files from your library) as well as defining some helper methods for your specs. Then, for each model and controller, make a file called my_model_name_spec.rb or my_controller_name_spec.rb, and do the test there.
Here are some useful resources for getting started with rspec:
Railscasts:
http://railscasts.com/episodes/275-how-i-test
http://railscasts.com/episodes/71-testing-controllers-with-rspec
http://railscasts.com/episodes/157-rspec-matchers-macros/
And for some more advanced (but well-explained) stuff:
http://benscheirman.com/2011/05/dry-up-your-rspec-files-with-subject-let-blocks
Be sure to include the rack-test gem.
You spec helper should have:
require 'rack/test'
require 'foo' # or where ever your app is
# This can go in a helper somewhere
module AppHelper
def app
Foo.new
end
end
RSpec.configure do |config|
config.include Rack::Test::Methods
config.include AppHelper
end
Then, your spec can be as follows:
require 'spec_helper'
# Example app. Delete this example.
class Foo < Sinatra::Base
get '/' do
'Jesse Pinkman'
end
end
describe Foo do
it 'is testable' do
get '/' do
expect(last_response).to be_ok
end
end
end
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
Ok so. I'm wanting to do request specs with RSpec for my Sinatra app.
I have a config.ru
# config.ru
require File.dirname(__FILE__) + '/config/boot.rb'
map 'this_route' do
run ThisApp
end
map 'that_route' do
run ThatApp
end
The boot.rb just uses Bundler and does additional requires for the rest of the app:
ThisApp looks like:
# lib/this_app.rb
class ThisApp < Sinatra::Base
get '/hello' do
'hello'
end
end
So I'm using RSpec and I want to write request specs like:
# spec/requests/this_spec.rb
require_relative '../spec_helper'
describe "This" do
describe "GET /this_route/hello" do
it "should reach a page" do
get "/hello"
last_response.status.should be(200)
end
end
it "should reach a page that says hello" do
get "/hello"
last_response.body.should have_content('hello')
end
end
end
end
This works fine because my spec_helper.rb is setup as follows:
# spec/spec_helper.rb
ENV['RACK_ENV'] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/boot")
require 'capybara/rspec'
RSpec.configure do |config|
config.include Rack::Test::Methods
end
def app
ThisApp
end
But my problem is that I want to test "ThatApp" from my rackup file along with any amount more apps I might add later along with "ThisApp". For example if I had a second request spec file:
# spec/requests/that_spec.rb
require_relative '../spec_helper'
describe "That" do
describe "GET /that_route/hello" do
it "should reach a page" do
get "/hello"
last_response.status.should be(200)
end
end
it "should reach a page that says hello" do
get "/hello"
last_response.body.should have_content('hello')
end
end
end
end
RackTest requires the rack app I'm testing be defined in the spec_helper file with that 'app' method, and I think eventually I'm going to have to feed Capybara.app the same thing as well when doing further request specs with it.
I feel like I'm missing something and maybe there's an easy way to setup 'app' for RackTest and Capybara at runtime depending on what route and consequent rack app I'm testing in my request specs. Like a before filter in RSpec.configure maybe, but I can't think of or find how I'd access what rack app is currently loaded and try and set it there before the test suite runs.
Anyone get what I'm trying to do and can think of something? Thanks for any help.
Define a different helper module for each sinatra app you want to test, each of which should define its own app method that returns the corresponding app. Then you can simply include MySinatraAppHelper in the appropriate example groups where you want to test the the given app.
You can also use rspec metadata to have the module included in example groups automatically.
Have a look at my sinatra-rspec-bundler-template. Especially at the spec files. I think that's what you are trying to achieve.
It combines two separate Sinatra apps each with it's own specs.