Duncan Malashock
Duncan Malashock

Reputation: 786

Sinatra-Contrib: undefined method `namespace'

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)>'

Upvotes: 1

Views: 1219

Answers (1)

ian
ian

Reputation: 12251

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

Upvotes: 1

Related Questions