Sinatra: how to use helpers in dynamically generated routes? - ruby

I create a route for each product from database with following code:
Products.all.each do |product|
get "/#{product.title.latinize}"
end
end
class String
def latinize
self
end
end #or with helpers
which raises NoMethodError: undefined method `latinize' for "hello":String.
How to use helpers (or class's extensions as seen here) from dynamically generated routes in Sinatra?

It's probably because you are defining the latinize method after you are generating the routes. Move to above the Product.all section.

Related

Access Ruby Instance Variable from Included Module

I have a Sinatra API file that has following code-
require 'json'
require_relative 'api_logger'
include ApiLogger
get /myapi/:id
request_params = request.env
write_log('log message')
end
Then I have a module containing the methods 'write_log'-
module ApiLogger
def write_log(message)
file.write(request['user']+message)
end
But request['user'] is coming out blank.
So the question is how to access the request variable from Sinatra API file in ApiLogger module? Also, I'm creating service class objects from API class and pass them request object at initialization. Can the module 'ApiLogger' access that 'request' instance variable from service class if the service classes just include 'ApiLogger'?
You could pass it as an additional argument.
Something like:
require 'json'
require_relative '../../lib/helpers/api_logger'
include ApiLogger
get /myapi/:id
request_params = request.env
write_json_log('log message', request)
end
and
def write_json_log(message, request)
file.write(request['auth_subject']+message)
end
I did not want to pass request object to each method. So I made 'request_params' a global variable in all classes that need to log and added this line in 'ApiLogger' to fetch the value of request object-
request = instance_variable_get '#request_params'
You were almost there, all you needed was include your module in the helpers in order to have a direct access to the request object. Here's a slightly modified version of your code that runs as a standalone program:
require 'sinatra'
module ApiLogger
def write_log(message)
$stdout.write(request.env['sinatra.route'] + message)
end
end
helpers do
include ApiLogger
end
get '/test' do
write_log('log message')
'ok'
end

Padrino cannot use output helpers in custom form builder

I am trying to create a custom form builder that generates a span with an error message. I keep getting the message
NoMethodError at /class/create
undefined method `content_tag' for #<Padrino::Helpers::FormBuilder::StandardFormBuilder:0x00000005aa24b8>
Here is my extension:
module Padrino
module Helpers
module FormBuilder
class CustomFormBuilder < AbstractFormBuilder
def errors_for(field)
if object.errors[field.to_sym]
error = object.errors[field.to_sym].first
content_tag(:span, error, class: 'error')
end
end
end
end
end
end
end
I have placed this extension in the lib folder.
You should include helper modules you need in your builder class.
module Padrino
module Helpers
module FormBuilder
class CustomFormBuilder < AbstractFormBuilder
include TagHelpers
include FormHelpers
include AssetTagHelpers
include OutputHelpers
...your methods here...
end
end
end
end

undefined method error when accessing class page object from module

I'm trying to access class method which is defined in Module, I can call function but function has page object element which performs some operation like click, I'm getting following error:
undefined method "label_year" for Datefunctions:Class (NoMethodError)
Here's my files structure:
./lib/calender_util.rb:
module CalenderUtil
def set_date
Datefunctions.get_calender_year
end
end
class Datefunctions
include PageObject
span(:label_year, :class=> 'ui-datepicker-year')
span(:label_month, :class=> 'ui-datepicker-month')
def self.get_calender_year
return label_year
end
end
./home_page.rb:
require 'calender_helper.rb'
include CalenderUtil
def setTravelDate date
CalenderUtil.set_date
end
parts of env.rb:
require 'page-object'
require 'page-object/page_factory'
$: << File.dirname(__FILE__)+'/../../lib'
require 'calender_helper.rb'
include CalenderHelper
World PageObject::PageFactory
World CalenderHelper
In addition; I've defined include/require multiple times I'll take off once this solved.
The reason is, the methods auto-generated by PageObject, are all instance methods. You can't use it in a class method because there is no instance.
Look at the doc's example:
class LoginPage
include PageObject
text_field(:username, :id => 'username')
text_field(:password, :id => 'password')
button(:login, :id => 'login')
end
login_page.username = 'cheezy'
login_page.password = 'secret'
login_page.login
The methods are for instances.
To fix, you need to create an instance.
module CalenderUtil
def set_date
page = Datefunctions.new(args_foo)
page.label_year
end
end
The problem is that label_year is an instance method while get_calender_year is a class method. You cannot call the instance method since you have not created an instance of the class.
As Billy Chan pointed out, for your code to work, you need to create an instance of the Datefunctions class within your module. This seems a bit awkward since you would need to pass the browser instance to each method called in the CalenderUtil. To me CalenderUtil is a layer of abstraction that is not adding any value.
I think that you should:
Use modules to encapsulate controls that are used across multiple pages.
Include these modules within the page object classes that have the controls.
Call the methods from the page objects.
For your example, I would create a Datefunctions module that defines the datepicker controls.
module DateFunctions
include PageObject
span(:label_year, :class=> 'ui-datepicker-year')
span(:label_month, :class=> 'ui-datepicker-month')
end
Then for each page class that uses the datepicker control, include the module:
class MyPage
include PageObject
include DateFunctions
end
In your tests, I assume it is Cucumber but the same is true for whatever framework, use the method from the page object.
page = MyPage.new(browser)
page.label_year.should == '1/1/2013'

Monkey patch class methods

I'm trying to do some monkey patching in ActiveShipping UPS class .
I need to add a class level method (starting with .self), so here it's what I'm trying to do:
module ActiveMerchant
module Shipping
class UPS < Carrier
def self.process_request(receiver, sender, packages, options = {})
# some code
end
def regular_method
"foobar"
end
end
end
end
Unfortunately when I'm trying to use it:
ActiveMerchant::Shipping::UPS.process_request(receiver etc)
I get an error:
NoMethodError: undefined method `process_request' for ActiveMerchant::Shipping::UPS:Class
from (irb):6
from C:/Ruby19/bin/irb.bat:19:in `<main>'
There is no class method named process_request in original class.
In original UPS class provided in gem there is one static method defined self.retry_safe = true
and I can use it without errors.
I can also use regular_method after creating instance of UPS class.
More details provided:
I'm working with Rails 2.3 ( :-( ) and Ruby 1.9.2. I have no influce on environment.
Monkey patched code is under plugins/my_plugin/lib/active_shipping/ext/carriers/ups.rb
In /active_shipping I have file named extensions.rb in which i have:
require 'active_shipping'
require_relative 'ext/carriers'
require_relative 'ext/carriers/ups'
It deals with loading everything properly (I suppose basing on regular_method beheaviour from first chunk of code in my question).
I try to invoke process_request in one of my Controllers. This part is little tricky, beacuse i'm using sth like this:
MyModel.courier_service.process_request(parameters)
where courier_service, in this case holds the ActiveMerchant::Shipping::UPS class.
I'm still a newbie in Ruby and don't know what sort of details i should provide.
Maybe you want to do it in another way
File patch_classes.rb:
module ActiveMerchantExpand
module Shipping
module ClassMethods
def self.process_request(receiver, sender, packages, options = {})
# some code
end
end
module InstanceMethods
def regular_method
"foobar"
end
end
def self.included(receiver)
receiver.extend ClassMethods
receiver.send :include, InstanceMethods
end
end
end
Then you have to load your class "ActiveMerchant::Shipping::UPS"
and after that you can attach your methods to your class via
Rails.configuration.to_prepare do
require_dependency [[file for ActiveMerchant::Shipping::UPS]]
require 'patch_classes' )
ActiveMerchant::Shipping::UPS.send(:include, ::ActiveMerchantExpand::Shipping)
end
This is from rails plugin writing, i hope this helps.
regards tingel2k
Do you explicitly require file with your monkey patch? If you just put it under your app or lib path without requiring, it wouldn't load because constant ActiveMerchant::Shipping::UPS is defined in gem and it doesn't trigger dependency resolution mechanism.

Call Sinatra erb from another class

I need to render a Sinatra erb template inside a class in my controller. I'm having issues calling this though. I've looked in the Sinatra rdocs and have come up with this:
Sinatra::Templates.erb :template_to_render
When I do this, I get the following error:
undefined method `erb' for Sinatra::Templates:Module
Is there a way to call this from another class?
To imitate rendering behavior of Sinatra controller in some other class (not controller) you can create module like this:
module ErbRender
include Sinatra::Templates
include Sinatra::Helpers
include Sinatra::ContentFor
def settings
#settings ||= begin
settings = Sinatra::Application.settings
settings.root = "#{ROOT}/app"
settings
end
end
def template_cache
#template_cache ||= Tilt::Cache.new
end
end
Here you may need to tune settings.root
Usage example:
class ArticleIndexingPostBody
include ErbRender
def get_body
erb :'amp/articles/show', layout: :'amp/layout'
end
end
This will properly render templates with layouts including content_for
why you don't require 'erb' and after use only erb
## You'll need to require erb in your app
require 'erb'
get '/' do
erb :index
end
You could have your class return the template name and render it in the main app.
Of course that's not exactly an answer (I don't have enough rep to add a comment with this account) and you're probably doing just that by now anyway...

Resources