Sinatra Helper in External File gives LoadError - ruby

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.

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

Load Error in require statement when using classes in multiple files

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!

Sinatra Heroku app: `start_server': undefined method `run' for HTTP:Module (NoMethodError) [duplicate]

when i try to start sinatra, i'm getting following error
/var/lib/gems/1.9.1/gems/sinatra-1.4.4/lib/sinatra/base.rb:1488:in start_server': undefined methodrun' for HTTP:Module (NoMethodError)
require 'sinatra/base'
require_relative "twt.rb"
class SinatraApp < Sinatra::Base
set :static, true
set :public_folder, File.dirname(__FILE__) + '/static'
get '/getuserinfo' do
#user = twit.getuserinfo
erb :userInfo
end
end
SinatraApp.run!
in "twt.rb" i require twitter (5.7.1)
require 'twitter'
class Twit
attr_accessor :client
def initialize(consumer_key,consumer_secret,access_token,access_token_secret)
#client = Twitter::REST::Client.new do |config|
config.consumer_key = consumer_key
config.consumer_secret = consumer_secret
config.access_token = access_token
config.access_token_secret = access_token_secret
end
end
def getUserInfo
return user = {
"name"=> client.current_user.name,
"id" => client.current_user.id
}
end
def showAllFriends
client.friends.each { |item| puts item.name }
end
def showFollowers
client.followers.each { |item| puts item.screen_name }
end
def showAllTweets
client.user_timeline.each {|item| puts item.text}
end
def showAllUserTweets(userScreenName)
client.user_timeline(userScreenName).each {|item| puts item.text}
end
def sendTweet(content)
client.update(content)
end
end
if i remove require_relative "twt.rb" line sinatra works fine.
When you run a Sinatra app using the built-in web server (as you do with SinatraApp.run!), Sinatra tries to determine which server to use by checking a list of servers in turn to see which is available. The actual list depends on the version of Ruby you are using, but one server that it always checks is net-http-server, which is simply named HTTP.
The way Sinatra checks for the availability of a server is by using a rack method that calls const_get to try and find the constant Rack::Handler::<server-name>. However, due to the way const_get works, if that constant is not available, but a top level constant with the same name as server-name is, then that will be returned, whatever class it is. (This is arguably a bug in Rack).
The Twitter gem depends on the http gem, and that in turn defines a HTTP module. (Naming a top-level module with something as generic as HTTP is arguably not a good idea).
So what is happening in this case is Sinatra is checking to see if the HTTP server is available, but Rack is returning the HTTP module from the http gem, which isn’t a server. Not being a Rack server it doesn’t have a run method, so when Sinatra tries to use it as one you get the error start_server': undefined method `run' for HTTP:Module.
One workaround is not to use the built-in server, such as the way you have discovered using a config.ru file and starting the app with rackup.
Another solution is to explicitly specify the server to use in your Sinatra app. For example you could install Thin, and then use:
set :server, 'thin'
In fact simply installing Thin would be sufficient as Thin is searched for before HTTP, but you are probably better explicitly setting the server to use. If you cannot install any other server for any reason you could use Webrick instead:
set :server, 'webrick'
i found the solution.
i launch sinatra with config.ru and it works now.
rack config.ru

Sinatra does not start with twitter gem

when i try to start sinatra, i'm getting following error
/var/lib/gems/1.9.1/gems/sinatra-1.4.4/lib/sinatra/base.rb:1488:in start_server': undefined methodrun' for HTTP:Module (NoMethodError)
require 'sinatra/base'
require_relative "twt.rb"
class SinatraApp < Sinatra::Base
set :static, true
set :public_folder, File.dirname(__FILE__) + '/static'
get '/getuserinfo' do
#user = twit.getuserinfo
erb :userInfo
end
end
SinatraApp.run!
in "twt.rb" i require twitter (5.7.1)
require 'twitter'
class Twit
attr_accessor :client
def initialize(consumer_key,consumer_secret,access_token,access_token_secret)
#client = Twitter::REST::Client.new do |config|
config.consumer_key = consumer_key
config.consumer_secret = consumer_secret
config.access_token = access_token
config.access_token_secret = access_token_secret
end
end
def getUserInfo
return user = {
"name"=> client.current_user.name,
"id" => client.current_user.id
}
end
def showAllFriends
client.friends.each { |item| puts item.name }
end
def showFollowers
client.followers.each { |item| puts item.screen_name }
end
def showAllTweets
client.user_timeline.each {|item| puts item.text}
end
def showAllUserTweets(userScreenName)
client.user_timeline(userScreenName).each {|item| puts item.text}
end
def sendTweet(content)
client.update(content)
end
end
if i remove require_relative "twt.rb" line sinatra works fine.
When you run a Sinatra app using the built-in web server (as you do with SinatraApp.run!), Sinatra tries to determine which server to use by checking a list of servers in turn to see which is available. The actual list depends on the version of Ruby you are using, but one server that it always checks is net-http-server, which is simply named HTTP.
The way Sinatra checks for the availability of a server is by using a rack method that calls const_get to try and find the constant Rack::Handler::<server-name>. However, due to the way const_get works, if that constant is not available, but a top level constant with the same name as server-name is, then that will be returned, whatever class it is. (This is arguably a bug in Rack).
The Twitter gem depends on the http gem, and that in turn defines a HTTP module. (Naming a top-level module with something as generic as HTTP is arguably not a good idea).
So what is happening in this case is Sinatra is checking to see if the HTTP server is available, but Rack is returning the HTTP module from the http gem, which isn’t a server. Not being a Rack server it doesn’t have a run method, so when Sinatra tries to use it as one you get the error start_server': undefined method `run' for HTTP:Module.
One workaround is not to use the built-in server, such as the way you have discovered using a config.ru file and starting the app with rackup.
Another solution is to explicitly specify the server to use in your Sinatra app. For example you could install Thin, and then use:
set :server, 'thin'
In fact simply installing Thin would be sufficient as Thin is searched for before HTTP, but you are probably better explicitly setting the server to use. If you cannot install any other server for any reason you could use Webrick instead:
set :server, 'webrick'
i found the solution.
i launch sinatra with config.ru and it works now.
rack config.ru

How to define a method to be called from the configure block of a modular sinatra application?

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. ;)

Resources