How to handle errors in Sinatra from external file? - ruby

I'm trying to handle errors in a Sinatra App including a Helper Module. I tried doing it like this:
# application_controller.rb
require './application_helper'
class ApplicationController < Sinatra::Base
helpers Sinatra::ApplicationHelper
end
# application_helper.rb
require 'sinatra/base'
module Sinatra
module ApplicationHelper
error StandardError do |e|
handle_error 500, e
end
end
helpers ApplicationHelper
end
But I cannot make it work, it is raising the error:
NoMethodError:
undefined method `error' for Sinatra::ApplicationHelper:Module
How can I use error in an external module?

Rather than using the helpers method, you need to define a registered method on your helper module. In this method the parameter passed is your app, and you can use it to configure errors, for example:
module Sinatra
module ApplicationHelper
def self.registered(app)
app.error StandardError do |e|
handle_error 500, e
end
end
end
register ApplicationHelper
end
Then in your app file you use register instead of helpers:
class ApplicationController < Sinatra::Base
register Sinatra::ApplicationHelper
end
You might want to add some helpers at the same time you register your extension, for example in this case you might want to include the handle_error method in the extension. To do that you can add another helper module inside your extension module, and call app.helpers to include it in registered.
module Sinatra
module ApplicationHelper
module Helpers
def handle_error(code, error)
#... Helper code here.
end
# Other helpers if needed.
end
def self.registered(app)
app.error StandardError do |e|
handle_error 500, e
end
# Include helpers at registration.
app.helpers Helpers
end
end
register ApplicationHelper
end

Related

In a module, using accessors blows up

My module:
# test.rb
module Database
# not used in this example, but illustrates how I intend to include the module in class
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
attr_accessor :db, :dbname
self.dbname = ENV['RACK_ENV'] == 'test' ? 'mydb_test' : 'mydb'
end
end
When I load it, I get this:
test.rb:7:in `<module:ClassMethods>': undefined method `dbname=' for Database::ClassMethods:Module (NoMethodError)
from bin/test:7:in `<module:Database>'
from bin/test:1:in `<main>'
Can't figure out why. If I check instance_methods, it's empty before attr_accessor, and has the appropriate four methods after. Yet when I call them, they don't exist.
attr_accessor defines instance methods on ClassMethods (such as ClassMethods#dbname=), but you are trying to call a class method (ClassMethods.dbname=).
With self.dbname = ENV..., you are calling a method #self.dbname=, which doesn't exist. Maybe this is what you are after?
# test.rb
module Database
# not used in this example, but illustrates how I intend to include the module in class
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
attr_accessor :db, :dbname
def self.dbname ; ENV['RACK_ENV'] == 'test' ? 'mydb_test' : 'mydb' ; end
puts self.dbname
end
end

Ruby modules - included do end block

There is a module MyModule:
module MyModule
extend ActiveSupport::Concern
def first_method
end
def second_method
end
included do
second_class_method
end
module ClassMethods
def first_class_method
end
def second_class_method
end
end
end
When some class includes this module, it will have 2 methods exposed as instance methods (first_method and second_method) and 2 class methods (first_class_method and second_class_method) - it is clear.
It is said, that
included block will be executed within the context of the class that
is including the module.
What does it mean exactly? Meaning, when exactly would this method (second_class_method) be executed?
Here's a practical example.
class MyClass
include MyModule
end
When you will include the module in a class, the included hook will be called. Therefore, second_class_method will be called within the scope of Class.
What happens here is
first_method and second_method are included as instance-methods of MyClass.
instance = MyClass.new
instance.first_method
# => whatever returned value of first_method is
The methods of ClassMethods are automatically mixed as class methods of MyClass. This is a common Ruby pattern, that ActiveSupport::Concern encapsulates. The non-Rails Ruby code is
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def this_is_a_class_method
end
end
end
Which results in
MyClass.this_is_a_class_method
or in your case
MyClass.first_class_method
included is a hook that is effectively to the following code
# non-Rails version
module MyModule
def self.included(base)
base.class_eval do
# somecode
end
end
end
# Rails version with ActiveSupport::Concerns
module MyModule
included do
# somecode
end
end
It's mostly "syntactic sugar" for common patterns. What happens in practice, is that when you mix the module, that code is executed in the context of the mixer class.
included is called when you include module into a class, it is used for defining relations, scopes, validations, ...
It gets called before you even have created object from that class.
example
module M
extend ActiveSupport::Concern
...
included do
validates :attr, presence: true
has_many :groups
end
...
end

Unable to include shoulda tests in a module

I am using Rails with MiniTest and have several classes that are all related through inheritance. I would like to reuse tests by placing them in a module. Something like this:
module MyModule
should 'work' do
assert true
end
end
Then in my tests:
class MyTest < ActiveSupport::TestCase
require MyModule
end
The problem is that I get a NoMethodError: undefined method should
What am I missing?
1) That's not what require is for.
2a) You probably want to extend a module and define a method that you can call instead.
module MyModule
def should_work
should 'work' do
assert true
end
end
end
class MyTest < ActiveSupport::TestCase
extend MyModule
should_work
end
2b) Alternatively, you can just extend the module and have the tests automatically called via Module.extended:
module MyModule
def self.extended(base)
base.should_work
end
def should_work
should 'work' do
assert true
end
end
end
class MyTest < ActiveSupport::TestCase
extend MyModule
end
2c) You can put everything in Module.extended too:
module MyModule
def self.extended(base)
base.should 'work' do
assert true
end
end
end
class MyTest < ActiveSupport::TestCase
extend MyModule
end
You need to require your module file in MyTest class, on the top:
require 'spec_helper'
require_relative '../spec/modules/module_file_name'
Or you can put this relative_path into spec_helper and than just require 'spec_helper' .
Hope this help you

How to make modular helper in Sinatra

i want to make a method inside a module (for grouping reason) that can be called as a module.method, something like this:
helpers do
module UserSession
def logged_in?
not session[:email].nil?
end
def logout!
session[:email] = nil
end
end
end
but when i try to call it using UserSession.logged_in? it said that logged_in is not UserSession's method
undefined method `logged_in?' for UserSession:Module
when i move the method as UserSession's method:
helpers do
module UserSession
def self.logged_in?
not session[:email].nil? # error
end
def self.logout!
session[:email] = nil
end
end
end
it gives an error, that i could not access the session variable
undefined local variable or method `session' for UserSession:Module
what is the best solution for this problem?
You can use a different convention for the helpers method.
module UserSession
def logged_in?
not session[:email].nil?
end
def logout!
session[:email] = nil
end
end
helpers UserSession
get '/foo' do
if logged_in?
'Hello you!'
else
'Do I know you?'
end
end
The module definition can of course be in another (required) file.
Behind the scenes, helpers <Module> is doing an include, but not simply into the Sinatra application sub-class you are using for your app. The include needs to be made compatible with how get, post etc work their magic, and helpers does that for you.
nevermind, i found the answer, i have tried define_method('UserSession.logged_in?') also, but no luck
last thing i've tried is:
# outside helpers
class UserSession
##session = nil
def initialize session
##session ||= session
end
def self.check
throw('must be initialized first') if ##session.nil?
end
def self.logged_in?
self.check
not ##session[:email].nil?
end
def self.logout
self.check
##session.delete :email
end
end
but something must be called first
before // do
UserSession.new session
end
then it can be used as desired:
get '/' do
if UserSession.logged_in?
# do something here
end
end

How to access Sinatra's params in helpers?

How can I access Sinatra's params hash in a custom helper?
E.g.:
# in app/helpers/my_helper.rb
module MyApp
module MyHelper
def self.test ()
params.inspect
end
end
end
# in app.rb
helpers MyHelper
test_result = test # undefined method `params' for MyApp:Module
You don’t need self for helpers:
module MyApp
module MyHelper
def test()
params.inspect
end
end
end
Note that the helper is only available in the request context (i.e. during the processing of a request):
get '/' do
test_result = test
# ...
end
Replace params.inspect with params[:inspect]. This should work.

Resources