mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
541a51ecf8
It is a common pattern in the Rails community that when people want to :xa use any kind of helper that is defined inside app/helpers they includes the helper module inside the controller like: module UserHelper def my_user_helper # ... end end class UsersController < ApplicationController include UserHelper def index render inline: my_user_helper end end This has problem because the helper can't access anything that is defined in the view level context class. Also all public methods of the helper become available in the controller what can lead to undesirable methods being routed and behaving as actions. Also if you helper depends on other helpers or even Action View helpers you need to include each one of these dependencies in your controller otherwise your helper is not going to work. We already have a helpers proxy at controller class level but that proxy doesn't have access to the instance variables defined in the controller. With this new instance level helper proxy users can reuse helpers in the controller without having to include the modules and with access to instance variables defined in the controller. class UsersController < ApplicationController def index render inline: helpers.my_user_helper end end
294 lines
7.1 KiB
Ruby
294 lines
7.1 KiB
Ruby
require 'abstract_unit'
|
|
|
|
ActionController::Base.helpers_path = File.expand_path('../../fixtures/helpers', __FILE__)
|
|
|
|
module Fun
|
|
class GamesController < ActionController::Base
|
|
def render_hello_world
|
|
render :inline => "hello: <%= stratego %>"
|
|
end
|
|
end
|
|
|
|
class PdfController < ActionController::Base
|
|
def test
|
|
render :inline => "test: <%= foobar %>"
|
|
end
|
|
end
|
|
end
|
|
|
|
class AllHelpersController < ActionController::Base
|
|
helper :all
|
|
end
|
|
|
|
module ImpressiveLibrary
|
|
extend ActiveSupport::Concern
|
|
included do
|
|
helper_method :useful_function
|
|
end
|
|
|
|
def useful_function() end
|
|
end
|
|
|
|
ActionController::Base.include(ImpressiveLibrary)
|
|
|
|
class JustMeController < ActionController::Base
|
|
clear_helpers
|
|
|
|
def flash
|
|
render :inline => "<h1><%= notice %></h1>"
|
|
end
|
|
|
|
def lib
|
|
render :inline => '<%= useful_function %>'
|
|
end
|
|
end
|
|
|
|
class MeTooController < JustMeController
|
|
end
|
|
|
|
class HelpersPathsController < ActionController::Base
|
|
paths = ["helpers2_pack", "helpers1_pack"].map do |path|
|
|
File.join(File.expand_path('../../fixtures', __FILE__), path)
|
|
end
|
|
$:.unshift(*paths)
|
|
|
|
self.helpers_path = paths
|
|
helper :all
|
|
|
|
def index
|
|
render :inline => "<%= conflicting_helper %>"
|
|
end
|
|
end
|
|
|
|
class HelpersTypoController < ActionController::Base
|
|
path = File.expand_path('../../fixtures/helpers_typo', __FILE__)
|
|
$:.unshift(path)
|
|
self.helpers_path = path
|
|
end
|
|
|
|
module LocalAbcHelper
|
|
def a() end
|
|
def b() end
|
|
def c() end
|
|
end
|
|
|
|
class HelperPathsTest < ActiveSupport::TestCase
|
|
def test_helpers_paths_priority
|
|
responses = HelpersPathsController.action(:index).call(ActionController::TestRequest::DEFAULT_ENV.dup)
|
|
|
|
# helpers1_pack was given as a second path, so pack1_helper should be
|
|
# included as the second one
|
|
assert_equal "pack1", responses.last.body
|
|
end
|
|
end
|
|
|
|
class HelpersTypoControllerTest < ActiveSupport::TestCase
|
|
def setup
|
|
@autoload_paths = ActiveSupport::Dependencies.autoload_paths
|
|
ActiveSupport::Dependencies.autoload_paths = Array(HelpersTypoController.helpers_path)
|
|
end
|
|
|
|
def test_helper_typo_error_message
|
|
e = assert_raise(NameError) { HelpersTypoController.helper 'admin/users' }
|
|
assert_equal "Couldn't find Admin::UsersHelper, expected it to be defined in helpers/admin/users_helper.rb", e.message
|
|
end
|
|
|
|
def teardown
|
|
ActiveSupport::Dependencies.autoload_paths = @autoload_paths
|
|
end
|
|
end
|
|
|
|
class HelperTest < ActiveSupport::TestCase
|
|
class TestController < ActionController::Base
|
|
attr_accessor :delegate_attr
|
|
def delegate_method() end
|
|
end
|
|
|
|
def setup
|
|
# Increment symbol counter.
|
|
@symbol = (@@counter ||= 'A0').succ!.dup
|
|
|
|
# Generate new controller class.
|
|
controller_class_name = "Helper#{@symbol}Controller"
|
|
eval("class #{controller_class_name} < TestController; end")
|
|
@controller_class = self.class.const_get(controller_class_name)
|
|
|
|
# Set default test helper.
|
|
self.test_helper = LocalAbcHelper
|
|
end
|
|
|
|
def test_helper
|
|
assert_equal expected_helper_methods, missing_methods
|
|
assert_nothing_raised { @controller_class.helper TestHelper }
|
|
assert_equal [], missing_methods
|
|
end
|
|
|
|
def test_helper_method
|
|
assert_nothing_raised { @controller_class.helper_method :delegate_method }
|
|
assert master_helper_methods.include?(:delegate_method)
|
|
end
|
|
|
|
def test_helper_attr
|
|
assert_nothing_raised { @controller_class.helper_attr :delegate_attr }
|
|
assert master_helper_methods.include?(:delegate_attr)
|
|
assert master_helper_methods.include?(:delegate_attr=)
|
|
end
|
|
|
|
def call_controller(klass, action)
|
|
klass.action(action).call(ActionController::TestRequest::DEFAULT_ENV.dup)
|
|
end
|
|
|
|
def test_helper_for_nested_controller
|
|
assert_equal 'hello: Iz guuut!',
|
|
call_controller(Fun::GamesController, "render_hello_world").last.body
|
|
end
|
|
|
|
def test_helper_for_acronym_controller
|
|
assert_equal "test: baz", call_controller(Fun::PdfController, "test").last.body
|
|
end
|
|
|
|
def test_default_helpers_only
|
|
assert_equal [JustMeHelper], JustMeController._helpers.ancestors.reject(&:anonymous?)
|
|
assert_equal [MeTooHelper, JustMeHelper], MeTooController._helpers.ancestors.reject(&:anonymous?)
|
|
end
|
|
|
|
def test_base_helper_methods_after_clear_helpers
|
|
assert_nothing_raised do
|
|
call_controller(JustMeController, "flash")
|
|
end
|
|
end
|
|
|
|
def test_lib_helper_methods_after_clear_helpers
|
|
assert_nothing_raised do
|
|
call_controller(JustMeController, "lib")
|
|
end
|
|
end
|
|
|
|
def test_all_helpers
|
|
methods = AllHelpersController._helpers.instance_methods
|
|
|
|
# abc_helper.rb
|
|
assert methods.include?(:bare_a)
|
|
|
|
# fun/games_helper.rb
|
|
assert methods.include?(:stratego)
|
|
|
|
# fun/pdf_helper.rb
|
|
assert methods.include?(:foobar)
|
|
end
|
|
|
|
def test_all_helpers_with_alternate_helper_dir
|
|
@controller_class.helpers_path = File.expand_path('../../fixtures/alternate_helpers', __FILE__)
|
|
|
|
# Reload helpers
|
|
@controller_class._helpers = Module.new
|
|
@controller_class.helper :all
|
|
|
|
# helpers/abc_helper.rb should not be included
|
|
assert !master_helper_methods.include?(:bare_a)
|
|
|
|
# alternate_helpers/foo_helper.rb
|
|
assert master_helper_methods.include?(:baz)
|
|
end
|
|
|
|
def test_helper_proxy
|
|
methods = AllHelpersController.helpers.methods
|
|
|
|
# Action View
|
|
assert methods.include?(:pluralize)
|
|
|
|
# abc_helper.rb
|
|
assert methods.include?(:bare_a)
|
|
|
|
# fun/games_helper.rb
|
|
assert methods.include?(:stratego)
|
|
|
|
# fun/pdf_helper.rb
|
|
assert methods.include?(:foobar)
|
|
end
|
|
|
|
def test_helper_proxy_in_instance
|
|
methods = AllHelpersController.new.helpers.methods
|
|
|
|
# Action View
|
|
assert_includes methods, :pluralize
|
|
|
|
# abc_helper.rb
|
|
assert_includes methods, :bare_a
|
|
|
|
# fun/games_helper.rb
|
|
assert_includes methods, :stratego
|
|
|
|
# fun/pdf_helper.rb
|
|
assert_includes methods, :foobar
|
|
end
|
|
|
|
def test_helper_proxy_config
|
|
AllHelpersController.config.my_var = 'smth'
|
|
|
|
assert_equal 'smth', AllHelpersController.helpers.config.my_var
|
|
end
|
|
|
|
private
|
|
def expected_helper_methods
|
|
TestHelper.instance_methods
|
|
end
|
|
|
|
def master_helper_methods
|
|
@controller_class._helpers.instance_methods
|
|
end
|
|
|
|
def missing_methods
|
|
expected_helper_methods - master_helper_methods
|
|
end
|
|
|
|
def test_helper=(helper_module)
|
|
silence_warnings { self.class.const_set('TestHelper', helper_module) }
|
|
end
|
|
end
|
|
|
|
|
|
class IsolatedHelpersTest < ActionController::TestCase
|
|
class A < ActionController::Base
|
|
def index
|
|
render :inline => '<%= shout %>'
|
|
end
|
|
end
|
|
|
|
class B < A
|
|
helper { def shout; 'B' end }
|
|
|
|
def index
|
|
render :inline => '<%= shout %>'
|
|
end
|
|
end
|
|
|
|
class C < A
|
|
helper { def shout; 'C' end }
|
|
|
|
def index
|
|
render :inline => '<%= shout %>'
|
|
end
|
|
end
|
|
|
|
def call_controller(klass, action)
|
|
klass.action(action).call(@request.env)
|
|
end
|
|
|
|
def setup
|
|
super
|
|
@request.action = 'index'
|
|
end
|
|
|
|
def test_helper_in_a
|
|
assert_raise(ActionView::Template::Error) { call_controller(A, "index") }
|
|
end
|
|
|
|
def test_helper_in_b
|
|
assert_equal 'B', call_controller(B, "index").last.body
|
|
end
|
|
|
|
def test_helper_in_c
|
|
assert_equal 'C', call_controller(C, "index").last.body
|
|
end
|
|
end
|