Ruby URI module oddness in modular Sinatra app - ruby

I'm having trouble using the Ruby URI module's encode_www_form method in a modular Sinatra app. For some reason, URI is interpreted as being the URI::Parser subclass, and so the method call understandably fails.
I've reduced this to a minimal test case. The Gemfile:
source 'https://rubygems.org'
ruby '1.9.3'
gem 'sinatra'
And app.rb:
require 'sinatra/base'
class Frontend < Sinatra::Base
get '/test/' do
URI.encode_www_form(:a => 1, :b => 2)
end
run! if app_file == $0
end
If I then run ruby app.rb and access /test/ I get:
NoMethodError - undefined method `encode_www_form' for #<URI::Parser:0x007fa9221ca868>:
app.rb:6:in `block in <class:Frontend>'
If I convert it to a classic-style Sinatra app, so that app.rb is like this:
require 'sinatra'
get '/test/' do
URI.encode_www_form(:a => 1, :b => 2)
end
Then call ruby app.rb and access /test/, the page shows "a=1&b=2" as desired.
So what's going wrong in the modular format that means something's up with URI?

The class Sinatra::Base redefines URI on line 856 of https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb, which is why your URI reference gets evaluated as that value.
If you want to avoid this problem, you can change your reference to ::URI.

As of Sinatra 1.4.4, the URI module is no longer overwritten.

I tried to reproduce this in irb. This may sound stupid, but require 'uri' did the trick there.

Related

How to set authentication in Sinatra?

I need simple authentication for blog. For a single person. Just login to the website
Can't configure sinatra_warden. Write the line
require 'rubygems'
require 'sinatra'
require 'pry-byebug'
require "sinatra/activerecord"
require "carrierwave"
require "carrierwave/orm/activerecord"
require 'sinatra_warden'
require 'warden'
register Sinatra::Warden
use Rack::Session::Pool
in app.rb, but I get an error
NoMethodError: undefined method `register' for main:Object
the gem sinatra_warden has installed. as well written require "warden" & require "sinatra_warden"
sinatra_warden 0.3.2
warden 1.2.6
When I add the authorize! method in controller, I get an error
undefined method `authorize!'
Because you didn't use the sinatra/base you should add sinatra/namespace. Add to your app.rb this require require "sinatra/namespace".
Sinatra::Namespace is an extension that adds namespaces to an
application. This namespaces will allow you to share a path prefix for
the routes within the namespace, and define filters, conditions and
error handlers exclusively for them. Besides that, you can also
register helpers and extensions that will be used only within the
namespace.
Or change your application to the modular style:
require "sinatra/base"
class MyApp < Sinatra::Base
register Sinatra::Warden
# The rest of your modular application code goes here...
end

Sinatra app with multiple controllers: how do I tell rspec what the routes are?

I'm working on an existing sinatra app that spans a number of controllers. These are currently routed in the config.ru file like so:
require 'bundler'
Bundler.require(:default)
=begin
# The gem file includes these:
gem 'rack' , '1.5.2', :groups => [:default, :test]
gem 'rack-accept' , '0.4.5'
gem 'rack-mount' , '0.8.3'
gem 'rack-protection' , '1.5.1'
gem 'rack-test' , '0.6.2', :groups => [:test]
=end
use Rack::ContentType
maps = {
'/' => RootController,
'/users' => UsersController,
'/animals' => AnimalsController,
}
maps.each do |path, controller|
map(path){ run controller}
end
This file is launched via config.ru (under Thin). But test files don't run under rackup (or Thin).
How do I load the controllers under rspec?
The problem is that when I put a 'get "/PATH"' or 'post' in my tests, Ruby complains about an
argument mismatch (2 for 0). If the 'get' has no argument, Ruby gives a different argument
mismatch (0 for 1). They're both sort of wrong -- get takes a path, an optional hash, and an optional block.
So something is clearly not wired up correctly.
Some of the code is here:
config.ru: http://pastie.org/8673836
spec_helper.rb at http://pastie.org/8673835
Error message at http://pastie.org/8673793
Simple controller at http://pastie.org/8673785
The names are slightly different in the pasties, but the gist is the same.
How do you wire up the controllers when you don't have the environment
config.ru gives you?
Thanks for any help.
The problem is somewhere in the code base, but rack doesn't make it easy to determine where.
I filed a bug on this at
https://github.com/rack/rack/issues/652

Sinatra commands do not work in modules

I have a Sinatra app which requires a module in a different file. When I use Sinatra commands in that module (e.g. redirect "http://facebook.com"), I get a NoMethodError. To illustrate the problem, I have made a simplified version:
--- mainapp.rb ---
#config
require './redirector.rb'
get '/' do
Redirector::redirect_to_stackoverflow
end
--- redirector.rb ---
module Redirector
require 'sinatra'
def self.redirect_to_stackoverflow
redirect "http://stackoverflow.com"
end
end
--- config.ru ---
require 'rubygems'
require 'sinatra'
require File.dirname(__FILE__) + "/ptt.rb"
run Sinatra::Application
What is wrong? Is there a place where I haven't required something properly?
The call to redirect inside the Redirector module is sent to the Redirector Module object, where the method does not exist. require 'sinatra' inside module Redirector is not necessary, and does not do any kind of method composition.
You probably could compose Sinatra methods into your Redirector module, but that is not normal practice. Usually it's the other way around - you write "helper" modules that are composed in to your Sinatra application in various ways.
This is a similar example application, with a more usual approach to composition:
app.rb
require 'sinatra'
require_relative 'redirect.rb'
class MyApp < Sinatra::Application
include Redirector
get '/' do
redirect_to_stackoverflow
end
end
redirect.rb
module Redirector
def redirect_to_stackoverflow
redirect "http://stackoverflow.com"
end
end
config.ru
require File.dirname(__FILE__) + "/app.rb"
run MyApp
#Neil Slater's explanation is correct, but I'd suggest you also make it an Sinatra extension, e.g.
require 'sinatra/base'
module Sinatra
module Redirector
def redirect_to_stackoverflow
redirect "http://stackoverflow.com"
end
end
helpers Redirector
end
Then (for a classic app) all you need to do is require it.
require 'sinatra/redirector'
get "/" do
redirect_to_stackoverflow
end

Sinatra Helper in External File gives LoadError

I'm trying to add a helper to connect to a mongo db to my modular Sinatra application
When I type foreman start in my console I get:
/home/sunny/Programs/landing_pages/app.rb:17:in `block in <class:LandingPages>': undefined local variable or method `connect' for LandingPages:Class (NameError)
My app.rb file looks like this:
require 'sinatra/base'
require 'sinatra/partial'
require 'sinatra/db_helper'
require 'bundler/setup'
require 'mongo'
class LandingPages < Sinatra::Base
helpers Sinatra::DbHelper
configure do
$collection = connect
end
end
My ./lib/sinatra/db_helper.rb looks like this:
require 'sinatra/base'
module Sinatra
module DbHelper
def connect
conn = Mongo::Connection.new("localhost")
db = conn.db("leads")
db.collection("laws")
end
end
helpers DbHelper
end
My config.ru looks like this:
require './app'
run LandingPages
I thought I was following the instructions correctly on:
http://www.sinatrarb.com/extensions.html
but I'm not totally sure. I'm not making a gem but just a sinatra app so maybe my directory hierarchy isn't correct. I don't have a rake file or a gem spec. Do I need them?
Some googling also found this:
sinatra helper in external file
Dave Sag answers my question perfectly but I can't get it work.
This comes about because of the scope of methods created through the helpers is on the sinatra application instance, since it calls ruby's include under the hood. So this would work:
get '/some/route' do
db = connect
# do something else ...
end
But the configure block has a class scope, so it can be used for configuring the application as a whole. So to make this work, you can define the method as:
module Sinatra
module DbHelper
def self.connect
conn = Mongo::Connection.new("localhost")
db = conn.db("leads")
db.collection("laws")
end
end
end
which could then be called via: $collection = Sinatra::DbHelper.connect or perhaps more favoured, you could call register instead of helpers. register calls extend under the hood, so you end up with class level methods (if you extend a class, anyway). You could then make the configure block as so:
configure do |app|
$collection = app.connect
end
You could also do all of this in an registered method on the DbHelpers module. See the example in the documentation for how this might work.

Sinatra, modular style. What did I do wrong?

I use Sinatra modular style, i don't know what going bad. I serach google but didn't find anything
require 'sinatra/base'
class App < Sinatra::Base
get '/' do
haml '%h1 Test'
end
end
run App
And a see test.rb:12:in <main>': undefined methodrun' for main:Object (NoMethodError)
What going wrong?
did you run it via ruby -rubygems hi.rb (assuming this code is in hi.rb). If so, you don't need run App. Unless you are running it through another framework built on/with Sinatra.
Also might want to include haml...
You have a config.ru:
# config.ru
require 'my_app'
run MyApp
and a my_app.rb:
# my_app.rb
require 'sinatra/base'
require 'haml'
class MyApp < Sinatra::Base
get('/') { haml '%h1 Test' }
# start the server if ruby file executed directly
run! if app_file == $0
end
then in the folder where the my_app.rb is run this to start the app on localhost:4657:
rackup -p 4567
Regarding the comment above where the error below is displayed:
`start_tcp_server': no acceptor (RuntimeError)
This appears when you are trying to bind to an already bound port. Trying a different port number should resolve.

Resources