Possible Noob Warning: New to RoR
I am trying to use concerns in RoR. Right now I just have a very simple concern writen
#./app/controllers/concerns/foo.rb
module Foo
extend ActiveSupport::Concern
def somethingfoo
puts "Ayyyy! Foo"
end
end
When I try and use this concern in my controller I get a undefined method error
#./app/controllers/foo_controller.rb
class FooController < ApplicationController
include Foo
def show
Foo.somethingfoo # undefined method 'somethingfoo' for Foo:Module
render plain: "Ohh no, It doesnt even show me because of the error above me"
end
end
To my knowledge somethingfoo should be called but it is not. I have also tried defining somethingfoo in a included do ... end block in the concern but this does not work either.
Is there something I am missing? Can concerns not be used like this with controllers?
If you include modules (extended by ActiveSupport::Concern or not), the methods of that module become instance methods of the including class/module.
Your Controller method should hence read
def show
somethingfoo
render plain: "Yeah, I'm shown!"
end
Related
I've got a method defined in ApplicationController as a helper method.
helper_method :can_access_participant_contact_data?
I'm trying to write a test for a helper method that resides in a helper file. This helper method makes a call to helper_method :can_access_participant_contact_data?
# In participants_helper.rb
#
def redacted_contact_data participant, attribute_name
attribute = participant.try(:contact_data).try(attribute_name)
return attribute if can_access_participant_contact_data?(participant)
return nil if attribute.blank?
return attribute.gsub(/\S/i, '*') # Asterisked string
end
All I'm doing so far in my test is making a call to redacted_contact_data
require 'test_helper'
class ParticipantsHelperTest < ActionView::TestCase
test "should return an asterisked string with spaces" do
redacted_contact_data(Participant.first, :name)
end
end
When I run my test, I'm getting this message
undefined method `can_access_participant_contact_data?' for #<ParticipantsHelperTest:0x007fd6c7c6d608>
I've been having a look around but I'm not sure how to get around this issue. Do I need to mock can_access_participant_contact_data? somehow? or can I just include the method into the test?
AFAIK (As far as I know), you cannot fix this without stubbing, or doing some change in your code, as essentially a helper file is just a module of itself that should be treated independent of where it's gonna be included. Who knows you might want to include such helper file inside your model files for example, in which incidentally the model file also has a method named can_access_participant_contact_data? but does differently from that one defined in the ApplicationController, therefore you cannot unit test this without specifying the context / base.
Possible Workarounds:
Stubbing:
Use Mocha or rework testing into RSpec
Or manually (maybe there's a better way) by:
test "should return an asterisked string with spaces" do
ParticipantsHelper.class_eval do
define_method :can_access_participant_contact_data? do |arg|
true
end
end
redacted_contact_data(Participant.first, :name)
end
Or, moving all your ApplicationController helper methods into a separate/existing helper file, say inside your already existing ApplicationHelper. Then afterwards, include that helper inside your other helper file that you are testing that is making use of the method/s. i.e.:
# helpers/application_helper.rb
module ApplicationHelper
def can_access_participant_contact_data?(participant)
# YOUR CODE
end
end
# helpers/participants_helper.rb
module ParticipantHelper
include ApplicationHelper
def redacted_contact_data participant, attribute_name
attribute = participant.try(:contact_data).try(attribute_name)
return attribute if can_access_participant_contact_data?(participant)
return nil if attribute.blank?
return attribute.gsub(/\S/i, '*') # Asterisked string
end
end
If using this approach, then two ways to call the helper method inside the controller:
Use Rails helpers method inside a controller:
class ParticipantsController
def show
helpers.can_access_participant_contact_data?(#participant)
end
end
Or, include the helpers directly (I personally prefer the other approach just above)
class ApplicationController < ActionController::Base
include ApplicationHelper
end
class ParticipantsController < ApplicationController
def show
can_access_participant_contact_data?(#participant)
end
end
For the view files, you won't need to update any code.
Another idea is to do "helper test" in "controller test" as follows:
require 'test_helper'
class ParticipantsControllerTest < ActionController::TestCase
setup do
# do some initialization here. e.g. login, etc.
end
test "should return an asterisked string with spaces" do
participant = ...
get :show, id: participant.id
assert_equal '...', #controller.view_context.redacted_contact_data(...)
end
end
Where, #controller is ParticipantsController object already defined by rails controller testing framework (or you can explicitly define it when controller name is different from *ControllerTest), and view_context is the object for helper methods (see https://api.rubyonrails.org/classes/ActionView/Rendering.html#method-i-view_context for more detail).
Helper method often refer controller object and/or method (like session, request) so that it is sometimes difficult to do unit-test only in test/helpers/*. This is the reason why I test helper in controller in such a case.
Is it possible to make an includable controller action within a Rails Helper through an included block? I'm thinking something like this:
module XablauHelper
included do
def my_shared_action
true
end
end
end
Already tried doing it through class.eval block and through using like a class method i.e. self.my_shared_action but no success, I have already found a solution that is making a parent controller with the desired shared actions and inheriting from it, but for the sake of modular design I would like to make it a more "global" approach, so I could gemify my solution and reuse code, any suggestions that doesn't use inheritance?
Adding controller actions in a helper is probably the wrong choice, as these methods are intended for your views.
Consider using controller concerns instead, and including them where required. For example:
# in app/controllers/concerns/useful_functions_concern.rb
module UsefulFunctionsConcern
extend ActiveSupport::Concern
included do
rescue_from SomeException, with: :handle_access_denied
end
def useful_method
# ...
end
protected
def handle_access_denied
# ...
end
end
# in your controller
class XyzController < ApplicationController
include UsefulFunctionsConcern
def index
useful_method
end
end
Where common controller actions can be shared and the controllers have something in common e.g. they are all API controllers, also consider using inheritance to achieve this. For example:
# parent controller
class ApiController < ApplicationController
def my_shared_action
end
end
class SpecificApiController < ApiController
end
I'm confused by the rails documentation that I'm reading here. In particular, this sentence:
By default, each controller will include all helpers. These helpers
are only accessible on the controller through .helpers
What is this .helpers that it is referring to? I have a helper defined in app/helpers/areas_helper.rb:
module AreasHelper
def my_helper
puts "Test from helper"
end
end
I would like to use this helper in app/controllers/locations_controller.rb:
class LocationsController < ApplicationController
def show
helpers.my_helper
end
end
However, I get a method undefined error. How is this .helpers supposed to be used?
I know there are other ways to get access to helpers in controllers, but I'm specifically asking about this piece of documentation and what it's trying to say.
You're meant to include the helper class in the controller:
#app/controllers/locations_controller.rb
class LocationsController < ApplicationController
include AreasHelper
def show
my_helper
end
end
This feature was introduced in Rails 5 with following PR
https://github.com/rails/rails/pull/24866
So, we can use this feature from Rails 5 and onwards and not in Rails 4.x.
I've created a helper which I'd like to use for text manipulation
module ApplicationHelper
module TextHelper
extend ActionView::Helpers
end
end
However when I run ApplicationHelper::TextHelper.simple_format "foo" in Rails console I get
NoMethodError: undefined method `white_list_sanitizer' for Module:Class
Is there anything else I need included?
I have already looked at https://github.com/rails/rails/issues/13837 but it didn't work.
Using Rails 4, Ruby 1.9.3
If you're in the console, you should be able to just do helper.simple_format('hi'). The helper method is available in console as a way to call some helper methods.
When using a custom helper:
# app/helpers/custom_helper.rb
module CustomHelper
def custom_method(x)
puts "Custom method #{x}"
end
end
# from the console
helper.custom_method('hi')
# from the controller
class SomeController < ApplicationController
def index
view_context.custom_method('hi')
end
end
My goal is to keep in application_helper only the methods that I use in the templates, otherwise my application_helper gets too long.
My problem relies in the suffix Helper:
This works
# app/helpers
module ApplicationHelper
include ApplicationLayout
end
# app/helpers/layouts
module ApplicationLayout
def my_helper
puts 'my_helper!'
end
end
This doesn't
# app/helpers
module ApplicationHelper
include ApplicationLayoutHelper
end
# app/helpers/layouts
module ApplicationLayoutHelper
def my_helper
puts 'my_helper!'
end
end
The error is:
Expected app/helpers/layouts/application_layout_helper.rb to define Layouts::ApplicationLayoutHelper
So I nest it and I get:
Routing Error undefined method `sub' for nil:NilClass
Actually I'd like to implement (give your opinion) app/helpers/layouts/application_helper.rb but it gives the same error, so for simplicity I stated this case.
Any suggestion on how to extract helper methods for layouts? Is there any convention over configuration I could use?
Thank you
Well, I tried what you expect and it's pretty straight:
in helpers/layouts/application_layout_helper.rb
module Layouts
module ApplicationLayoutHelper
def my_helper
'my_helper!'
end
end
end
in helpers/application_helper.rb
module ApplicationHelper
include Layouts::ApplicationLayoutHelper
end
Ad in my view I directly do:
<%= my_helper %>
I still don't really understand what you'll do with these methods.