How can I use my Sinatra helpers in included modules? - ruby

I have my routes and helpers defined in external files and included by Sinatra, however I'm new to Ruby and I can't now figure out how I can use my helper methods in my routes. When I run the code in RubyMine and access a profile URL I get the error "NoMethodError - undefined method `protected!'"
## Main class
require 'sinatra/base'
class MyApp < Sinatra::Base
register Sinatra::MyHelpers
register ProfileRoutes
...
end
## Helpers include
require 'sinatra/base'
module Sinatra
module LocutusHelpers
def self.registered( app )
app.before do
...
end
def protected!
...
end
end
end
end
## Routes include
require 'sinatra/base'
module ProfileRoutes
def self.registered( app )
app.get '/profile/:userid' do
protected!
...
end
end
end
I've tried def self.protected! for the helper but then it cant access the request object.
I've also tried Sinatra::MyHelpers.protected!, Sinatra.protected! and app.protected!, errors are thrown for all of these too
Do you know how I can access the helpers from my routes? Or have I set something up incorrectly?

It looks like you are mixing up adding helpers from extensions and configuring your app from extensions.
You need to move protected! into a module, then when registering your extension add that module as a helpers module.
module LocutusHelpers
# new module, move protected! into here
module HelperMethods
def protected!
...
end
end
def self.registered( app )
# add new hlpers module
app.helpers HelperMethods
# other extension setup as before...
app.before do
...
end
end
end

Related

How to map routes to modules without the use of multiple Sinatra apps?

I have this structure:
module Analytics
def self.registered(app)
module DepartmentLevel
departmentParticipation = lambda do
end
departmentStatistics = lambda do
end
app.get '/participation', &departmentParticipation
end
module CourseLevel
courseParticipation = lambda do
end
end
end
And at the end of the module Analytics I would like to route each piece of the request to his specific subModule. If it is requested
'analytics/department'
it should redirect to the module DepartmentLevel which has its own routes as
app.get 'participation', &departmentParticipation
I first thought on using map. But how to use it without having to run a new or inherit Sinatra::Base object?
Not sure if this is what you need, but here's how I build my modular Sinatra apps: By using use
First, I have my ApplicationController. It's the base class for all other Controllers. It lives in controllers/application_controller.rb
class ApplicationController < Sinatra::Base
# some settings that are valid for all controllers
set :views, File.expand_path('../../views', __FILE__)
set :public_folder, File.expand_path('../../public', __FILE__)
enable :sessions
# Helpers
helpers BootstrapHelpers
helpers ApplicationHelpers
helpers DatabaseHelpers
configure :production do
enable :logging
end
end
Now, all other Controllers/Modules inherit from ApplicationController. Example controllers/website_controller.rb:
require 'controllers/application_controller'
class WebsiteController < ApplicationController
helpers WebsiteHelpers
get('/') { slim :home }
get('/updates') { slim :website_updates }
get('/test') { binding.pry; 'foo' } if settings.development?
end
At last, in app.rb is where it all comes together:
# Require some stuff
require 'yaml'
require 'bundler'
Bundler.require
require 'logger'
# Require own stuff
APP_ROOT = File.expand_path('..', __FILE__)
$LOAD_PATH.unshift APP_ROOT
require 'lib/core_ext/string'
require 'controllers/application_controller.rb'
# Some Run-Time configuration...
ApplicationController.configure do
# DB Connections, Logging and Stuff like that
end
# Require ALL models, controllers and helpers
Dir.glob("#{APP_ROOT}/{helpers,models,controllers}/*.rb").each { |file| require file }
# here I glue everything together
class MyApp < Sinatra::Base
use WebsiteController
use OtherController
use ThingController
not_found do
slim :'404'
end
end
With this Setup, all I need to do in config.ru is
require './app.rb'
run MyApp
Hope this helps!

Creating Sinatra Extension That Depends On Another Sinatra Extension

I'm extracting functionality from a sinatra application into an extension. My extension module (ExtensionBar) depends on the existence of class-level dsl extensions created by another extension module (ExtensionFoo). Because of this, my extension dies when required by my main app.
my_app.rb:
require "extension_foo"
require "extension_bar"
class MyApp < Sinatra::Base
register ExtensionFoo
register ExtensionBar
end
extension_foo.rb:
module ExtensionFoo
def with_foo
yield
end
end
extension_bar.rb:
module ExtensionBar
with_foo do
"bar"
end
end
My question: How can I most robustly and simply write an extension that depends on the registry of another extension? I'd like to avoid metaprogramming as much as possible.
In the example you've given, ExtensionBar doesn't do anything to any app. You also need to register the dependent extension in the extension you wish to use in the app. The instructions on writing modules gives a before block as the example for the LinkBlocker DSL, both of which would make your example more like this:
# extension_foo.rb
require 'sinatra/base'
module Sinatra
module ExtensionFoo
def with_foo
warn "Calling with_foo"
s = yield
warn "s = #{s}"
s
end
end
register ExtensionFoo
end
# extension_bar.rb
require 'sinatra/base'
require_relative 'extension_foo.rb'
module Sinatra
module ExtensionBar
before do
warn "Calling with_foo in before"
with_foo do
"bar"
end
end
end
register ExtensionBar
end
# app.rb
require 'sinatra'
require_relative 'extension_bar.rb'
get "/" do
with_foo do
"blah"
end.inspect
end
When I run this I don't get an error and I do see the warnings in my STDOUT, and the output is "blah".
Calling with_foo in before
Calling with_foo
from /Volumes/RubyProjects/Test/extension_dependency/extension_foo.rb:6:in `with_foo'
s = bar
from /Volumes/RubyProjects/Test/extension_dependency/extension_foo.rb:8:in `with_foo'
Calling with_foo
from /Volumes/RubyProjects/Test/extension_dependency/extension_foo.rb:6:in `with_foo'
s = blah
from /Volumes/RubyProjects/Test/extension_dependency/extension_foo.rb:8:in `with_foo'

Access Sinatra settings from a model

I have a modular Sinatra app. I'm setting some custom variables in my configure block and want to access these settings in my model.
The problem is, I get a NoMethodError when I try and access my custom settings from MyModel. Standard settings still seem to work fine though. How can I make this work?
# app.rb
require_relative 'models/document'
class App < Sinatra::Base
configure do
set :resource_path, '/xfiles/i_want_to_believe'
end
get '/' do
#model = MyModel.new
haml :index
end
end
# models/my_model.rb
class MyModel
def initialize
do_it
end
def do_it
...
settings.resource_path # no method error
...
settings.root # works fine
end
end
i think that you should be able to access it via
Sinatra::Application.settings.documents_path
I ended up doing:
#document.rb
class Document
def self.documents_path=(path)
#documents_path = path
end
def self.documents_path
#documents_path
end
...
end
#app.rb
configure do
set :documents_path, settings.root + "/../documents/"
Document.documents_path = settings.documents_path
end
then just using Document.documents_path inside my find method.

Sinatra : how to use helpers in Mustache views

I want to use my Sinatra helpers methods in Mustache views.
I do this:
# in app.rb:
...
helpers do
def helloworld
"helloworld!"
end
end
get '/'
mustache :home
end
...
# in views/home
class App < Sinatra::Base
module Views
class Home < Mustache
def hello
helloworld
end
end
end
end
# in home.mustache
<p>{{hello}}</p>
It does not work, I have the error message :
«undefined local variable or method `helloworld' for App::Views::Home:0x000000023ebd48»
How can I use my method helper in Mustache view ?
Or, how can I use my method helper directly from home.mustache ? like this :
# in home.mustache
<p>{{helloworld}}</p>
Many thanks for your help!
You should be able to do something with a module:
# app_helpers.rb
module AppHelpers
def helloworld
"helloworld!"
end
end
# app.rb
helpers AppHelpers
get '/'
mustache :home
end
# views/home.rb
class App < Sinatra::Base
module Views
class Home < Mustache
include AppHelpers
def hello
helloworld
end
end
end

How mix in routes in Sinatra for a better structure

I found nothing about how I can mix-in routes from another module, like this:
module otherRoutes
get "/route1" do
end
end
class Server < Sinatra::Base
include otherRoutes
get "/" do
#do something
end
end
Is that possible?
You don't do include with Sinatra. You use extensions together with register.
I.e. build your module in a separate file:
require 'sinatra/base'
module Sinatra
module OtherRoutes
def self.registered(app)
app.get "/route1" do
...
end
end
end
register OtherRoutes # for non modular apps, just include this file and it will register
end
And then register:
class Server < Sinatra::Base
register Sinatra::OtherRoutes
...
end
It's not really clear from the docs that this is the way to go for non-basic Sinatra apps. Hope it helps others.
You could do this:
module OtherRoutes
def self.included( app )
app.get "/route1" do
...
end
end
end
class Server < Sinatra::Base
include OtherRoutes
...
end
Unlike Ramaze, Sinatra's routes are not methods, and so cannot use Ruby's method lookup chaining directly. Note that with this you can't later monkey-patch OtherRoutes and have the changes reflected in Server; this is just a one-time convenience for defining the routes.
I prefer the use of sinatra-contrib gem to extend sinatra for cleaner syntax and shared namespace
# Gemfile
gem 'sinatra', '~> 1.4.7'
gem 'sinatra-contrib', '~> 1.4.6', require: 'sinatra/extension'
# other_routes.rb
module Foo
module OtherRoutes
extend Sinatra::Extension
get '/some-other-route' do
'some other route'
end
end
end
# app.rb
module Foo
class BaseRoutes < Sinatra::Base
get '/' do
'base route'
end
register OtherRoutes
end
end
sinata-contrib is maintained alongside the sinatra project
Well you can also use the map method to map routes to your sinatra apps
map "/" do
run Rack::Directory.new("./public")
end
map '/posts' do
run PostsApp.new
end
map '/comments' do
run CommentsApp.new
end
map '/users' do
run UserssApp.new
end
Just my two cents:
my_app.rb:
require 'sinatra/base'
class MyApp < Sinatra::Base
set :root, File.expand_path('../', __FILE__)
set :app_file, __FILE__
disable :run
files_to_require = [
"#{root}/app/helpers/**/*.{rb}",
"#{root}/app/routes/**/*.{rb}"
]
files_to_require.each {|path| Dir.glob(path, &method(:require))}
helpers App::Helpers
end
app/routes/health.rb:
MyApp.configure do |c|
c.before do
content_type "application/json"
end
c.get "/health" do
{ Ruby: "#{RUBY_VERSION}",
Rack: "#{Rack::VERSION}",
Sinatra: "#{Sinatra::VERSION}"
}.to_json
end
end
app/helpers/application.rb:
module App
module Helpers
def t(*args)
::I18n::t(*args)
end
def h(text)
Rack::Utils.escape_html(text)
end
end
end
config.ru:
require './my_app.rb'

Resources