No method error when calling a helper method from its controller - ruby-on-rails

Should this work?
module SessionsHelper
def foobar
"hello"
end
end
class SessionsController < ApplicationController
def new
foobar
end
end
When I visit the URI that goes to session#new I get an undefined method or variable 'foobar' error. I thought helper modules were automatically included in their respective controllers?

Well, including the module from the controller did it:
module SessionsHelper
def foobar
"hello"
end
end
class SessionsController < ApplicationController
include SessionsHelper
def new
foobar
end
end
I thought this behaviour was default in rails though? That the relevant helper modules are included in their controllers?

Related

require_dependency is seen but methods not activated

The following universal_methods.rb file is placed in the controllers directory to contain methods to share across controllers.
class UniversalMethods
def initialise_municipal(user_params)
user_params = params[:user]
municipal_via_id = user_params[:municipal_id]
[...]
end
end
The UserController method is instantiated and contains a method calling the above:
require_dependency 'universal_methods'
class UsersController < ApplicationController
def admin_create
set_user_login_name
user_params = params[:user]
initialise_municipal(user_params)
[...]
end
I have tested that the application accesses the file (by using just require it complains of not finding if). However, the method is definitely not accessed:
NoMethodError: undefined method initialise_municipal' for #UsersController:...`
What is wrong in this syntax / setup ?
To strictly fix your code you need UniversalMethods.new.initialise_municipal(user_params) instead of just initialise_municipal(user_params) and there's no need to require the file because everything in /controllers is autoloaded.
Another approach for this is with Concerns. Create a folder app/controllers/concerns and put universal_methods.rb there.
For Rails 6
# app/controllers/concerns/universal_methods.rb
module UniversalMethods
extend ActiveSupport::Concern
included do
def initialise_municipal(user_params)
user_params = params[:user]
municipal_via_id = user_params[:municipal_id]
end
end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
include UniversalMethods
def admin_create
set_user_login_name
user_params = params[:user]
initialise_municipal(user_params)
[...]
end
end

access application_helper in before_action

I have the following in my application_helper.rb file:
module ApplicationHelper
def require_employer_profile_for_employers(page)
if current_user.type == 'Employer'
if current_user.employer_profile
else
flash[:error] = "You must create a profile before accessing #{page}."
redirect_to new_employer_profile_path
end
end
end
end
I try calling it in my projects controller like this:
before_action "require_employer_profile_for_employers('Projects')"
but my server responds with this error:
NoMethodError (undefined method `require_employer_profile_for_employers' for #<ProjectsController:0x007fb741f82e38>):
How do I access the helper in the before_action in the projects controller?
include ApplicationHelper in your ProjectsController:
class ProjectsController < ApplicationController
include ApplicationHelper
# ...
end
Helpers are not directly accessible within a controller as opposed to view layer where they are freely accessible.

How can I extend ApplicationController in a gem?

I thought I'd come up with a slick way to extend ApplicationController in a Rails 3.x gem.
In my gem's lib/my_namespace/my_controller.rb, I had:
class MyNamespace::MyController < ApplicationController
before_filter :some_method
after_filter :another_method
def initialize
# getting classname of the subclass to use for lookup of the associated model, etc.
# and storing the model_class in an instance variable
# ...
end
# define :some_method, :another_method, etc.
# ...
private
attr_accessor :subclass_defined_during_initialize # etc.
# etc.
end
but when the Gem is loaded, app/controllers/application_controller.rb is not yet loaded, so it fails:
/path/to/rvm/gemset/gems/activesupport-3.2.6/lib/active_support/dependencies.rb:251:
in `require': cannot load such file -- my_gem_name/application_controller (LoadError)
As a workaround, I had defined ApplicationController in my gem's lib/gem_namespace/application_controller.rb as:
class ApplicationController < ActionController::Base
end
I assumed that even though I had defined it there, it would be redefined in my Rails 3 application's app/controllers/application_controller.rb, such that both controllers in the application that extended ApplicationController and controllers that extended MyNamespace::MyController would directly or indirectly extend the ApplicationController defined in app/controllers/application_controller.rb.
However, we noticed that after loading the gem, controllers that extend ApplicationController were unable to access methods defined in app/controllers/application_controller.rb. Also, the ApplicationHelper (app/helpers/application_helper.rb) module was no longer being loaded by other helper modules.
How can I extend ApplicationController within the controller in my gem for the purpose of defining a before_filter and after_filter to and use initialize to access the class's name to determine the associated model's class that it could then store and use within its methods?
Update 2012/10/22:
Here's what I came up with:
In lib/your_gem_name/railtie.rb:
module YourGemsModuleName
class Railtie < Rails::Railtie
initializer "your_gem_name.action_controller" do
ActiveSupport.on_load(:action_controller) do
puts "Extending #{self} with YourGemsModuleName::Controller"
# ActionController::Base gets a method that allows controllers to include the new behavior
include YourGemsModuleName::Controller # ActiveSupport::Concern
end
end
end
and in lib/your_gem_name/controller.rb:
module YourGemsModuleName
module Controller
extend ActiveSupport::Concern
# note: don't specify included or ClassMethods if unused
included do
# anything you would want to do in every controller, for example: add a class attribute
class_attribute :class_attribute_available_on_every_controller, instance_writer: false
end
module ClassMethods
# notice: no self.method_name here, because this is being extended because ActiveSupport::Concern was extended
def make_this_controller_fantastic
before_filter :some_instance_method_available_on_every_controller # to be available on every controller
after_filter :another_instance_method_available_on_every_controller # to be available on every controller
include FantasticStuff
end
end
# instance methods to go on every controller go here
def some_instance_method_available_on_every_controller
puts "a method available on every controller!"
end
def another_instance_method_available_on_every_controller
puts "another method available on every controller!"
end
module FantasticStuff
extend ActiveSupport::Concern
# note: don't specify included or ClassMethods if unused
included do
class_attribute :class_attribute_only_available_on_fantastic_controllers, instance_writer: false
end
module ClassMethods
# class methods available only if make_this_controller_fantastic is specified in the controller
def some_fanastic_class_method
put "a fantastic class method!"
end
end
# instance methods available only if make_this_controller_fantastic is specified in the controller
def some_fantastic_instance_method
puts "a fantastic instance method!"
end
def another_fantastic_instance_method
puts "another fantastic instance method!"
end
end
end
end
For this specific kind of functionality I would recommend creating a module in your gem and include that module in your Application Controller
class ApplicationController < ActionController::Base
include MyCoolModule
end
To add before filters, etc (add this to your module)
def self.included(base)
base.send(:before_filter, my_method)
end
Update: you may be able to just do base.before_filter :my_method which is cleaner.
Here is a Gist
that shows how to access the class of the subclass and store it in an instance variable and access it in the before and after filters. It uses the include method.
Truth is much much simpler and flexible.
Add to lib/engine.rb this: class Engine < Rails::Engine; end
And then simply use:
ActionController::Base.class_eval do
include SomethingFromMineGemModule
# or:
def hello_from_gem
'Hey people!'
end
end
I was able to reference ApplicationController with an initializer callback.
gem code that subclasses/references ApplicationController:
class GemApplicationController < ApplicationController
before_filter :method_to_call
def method_to_call
#your code here
end
end
gem code callback to create subclassed controller:
module GemName
def self.load_gem_application_controller
require "path/to/gem_application_controller"
end
end
rails_app/config/initializers/gem_name.rb
GemName.load_gem_application_controller
Then have controllers that use this functionality subclass GemApplicationController
class SpecialCaseController < GemApplicationController
# this will inherit from the gem's controller,
# which inherits from the rails_app ApplicationController
end

How to use a custom helper via the "helper" method in Rails 3?

I'm trying to create a custom helper like this:
# app/controllers/my_controller.rb
class MyController < ApplicationController
helper :my
def index
puts foo
end
end
# app/helpers/my_helper.rb
module MyHelper
def foo
"Hello"
end
end
But, I got the following error:
undefined local variable or method `foo' for #<MyController:0x20e01d0>
What am I missing ?
Generally, I do the opposite: I use controller methods as helpers.
class MyController < ApplicationController
helper_method :my_helper
private
def my_helper
"text"
end
end
Helpers are accessed from the views, not the controllers. so if you try to put the following inside your index template it should work:
#my/index.html.erb
<%= foo %>
If you do want to access something from the controller, then you should use the include syntax instead of helper, but do not name it like a helper module in that case.
How about just including the helper as a mixin in the controller...
class MyController < ApplicationController
include MyHelper
def index
puts foo
end
end

How do you test a Rails controller method exposed as a helper_method?

They don't seem to be accessible from ActionView::TestCase
That's right, helper methods are not exposed in the view tests - but they can be tested in your functional tests. And since they are defined in the controller, this is the right place to test them. Your helper method is probably defined as private, so you'll have to use Ruby metaprogramming to call the method.
app/controllers/posts_controller.rb:
class PostsController < ApplicationController
private
def format_something
"abc"
end
helper_method :format_something
end
test/functional/posts_controller_test.rb:
require 'test_helper'
class PostsControllerTest < ActionController::TestCase
test "the format_something helper returns 'abc'" do
assert_equal 'abc', #controller.send(:format_something)
end
end
This feels awkward, because you're getting around encapsulation by using send on a private method.
A better approach is to put the helper method in a module in the /controller/concerns directory, and create tests specifically just for this module.
e.g. in app controller/posts_controller.rb
class PostsController < ApplicationController
include Formattable
end
in app/controller/concerns/formattable.rb
module Concerns
module Formattable
extend ActiveSupport::Concern # adds the new hot concerns stuff, optional
def format_something
"abc"
end
end
end
And in the test/functional/concerns/formattable_test.rb
require 'test_helper'
# setup a fake controller to test against
class FormattableTestController
include Concerns::Formattable
end
class FormattableTest < ActiveSupport::TestCase
test "the format_something helper returns 'abc'" do
controller = FormattableTestController.new
assert_equal 'abc', controller.format_something
end
end
You could test #controller.view_context from your functional/controller tests. This method is available in Rails 3, 4, and 5, as far as I can tell.
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
# ...
end
test/controllers/application_controller_test.rb
require 'test_helper'
class ApplicationControllerTest < ActionController::TestCase
test 'current_user helper exists in view context' do
assert_respond_to #controller.view_context, :current_user
end
end
If you didn't want to test one of your controller subclasses, you could also create a test controller to verify that the method in the view_context is the same one from the controller and not from one of your view helpers.
class ApplicationControllerHelperTest < ActionController::TestCase
class TestController < ApplicationController
private
def current_user
User.new
end
end
tests TestController
test 'current_user helper exists in view context' do
assert_respond_to #controller.view_context, :current_user
end
test 'current_user returns value from controller' do
assert_instance_of User, #controller.view_context.current_user
end
end
Or, more likely, you'd want to be able to test the helper in the presence of a request.
class ApplicationControllerHelperTest < ActionController::TestCase
class TestController < ApplicationController
def index
render plain: 'Hello, World!'
end
end
tests TestController
def with_routing
# http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-with_routing
# http://guides.rubyonrails.org/routing.html#connecting-urls-to-code
super do |set|
set.draw do
get 'application_controller_test/test', to: 'application_controller_test/test#index'
end
yield
end
end
test 'current_user helper exists in view context' do
assert_respond_to #controller.view_context, :current_user
end
test 'current_user returns value from controller' do
with_routing do
# set up your session, perhaps
user = User.create! username: 'testuser'
session[:user_id] = user.id
get :index
assert_equal user.id, #controller.view_context.current_user.id
end
end
end
I've struggled with this for a bit, because the accepted answer didn't actually test whether the method was exposed as a helper method.
That said, we can use the #helpers method to get a proxy for testing.
For example:
class FooController < ApplicationController
private
def bar
'bar'
end
helper_method :bar
end
Can be tested with:
require 'test_helper'
class FooControllerTest < ActionController::TestCase
test 'bar is a helper method' do
assert_equal 'bar', #controller.helpers.bar
end
end
Indeed they're not. The view tests are specifically for the views. They don't load the controllers.
You should mock this method and make it return whatever is appropriate depending of your context.

Resources