From e976c489e6416cdc4714721df78dd43dd6d13d99 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 23 May 2009 00:17:05 +0200 Subject: Add all the existing helpers related features to the new base --- actionpack/Rakefile | 2 +- .../lib/action_controller/abstract/helpers.rb | 27 ++++- actionpack/lib/action_controller/base/helpers.rb | 22 ++-- actionpack/lib/action_controller/new_base.rb | 1 + actionpack/lib/action_controller/new_base/base.rb | 4 +- .../lib/action_controller/new_base/helpers.rb | 133 +++++++++++++++++++++ actionpack/test/controller/helper_test.rb | 6 +- actionpack/test/new_base/abstract_unit.rb | 3 + actionpack/test/new_base/render_text_test.rb | 3 - 9 files changed, 175 insertions(+), 26 deletions(-) create mode 100644 actionpack/lib/action_controller/new_base/helpers.rb diff --git a/actionpack/Rakefile b/actionpack/Rakefile index c9ed9ac532..0ce6f7823b 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -66,7 +66,7 @@ Rake::TestTask.new(:test_new_base_on_old_tests) do |t| render render_json render_xml send_file request_forgery_protection rescue url_rewriter verification webservice http_basic_authentication http_digest_authentication - action_pack_assertions assert_select filter_params + action_pack_assertions assert_select filter_params helper ).map { |name| "test/controller/#{name}_test.rb" } end diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb index 968d3080c1..41decfd0c7 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/action_controller/abstract/helpers.rb @@ -24,11 +24,30 @@ module AbstractController super end - + + # Makes all the (instance) methods in the helper module available to templates rendered through this controller. + # See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules + # available to the templates. def add_template_helper(mod) master_helper_module.module_eval { include mod } end - + + # Declare a controller method as a helper. For example, the following + # makes the +current_user+ controller method available to the view: + # class ApplicationController < ActionController::Base + # helper_method :current_user, :logged_in? + # + # def current_user + # @current_user ||= User.find_by_id(session[:user]) + # end + # + # def logged_in? + # current_user != nil + # end + # end + # + # In a view: + # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> def helper_method(*meths) meths.flatten.each do |meth| master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 @@ -39,14 +58,14 @@ module AbstractController end end - def helper(*args, &blk) + def helper(*args, &block) args.flatten.each do |arg| case arg when Module add_template_helper(arg) end end - master_helper_module.module_eval(&blk) if block_given? + master_helper_module.module_eval(&block) if block_given? end end diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb index ba65032f6a..96fa7896a9 100644 --- a/actionpack/lib/action_controller/base/helpers.rb +++ b/actionpack/lib/action_controller/base/helpers.rb @@ -3,23 +3,19 @@ require 'active_support/dependencies' # FIXME: helper { ... } is broken on Ruby 1.9 module ActionController #:nodoc: module Helpers #:nodoc: - def self.included(base) + extend ActiveSupport::DependencyModule + + included do # Initialize the base module to aggregate its helpers. - base.class_inheritable_accessor :master_helper_module - base.master_helper_module = Module.new + class_inheritable_accessor :master_helper_module + self.master_helper_module = Module.new # Set the default directory for helpers - base.class_inheritable_accessor :helpers_dir - base.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") - - # Extend base with class methods to declare helpers. - base.extend(ClassMethods) + class_inheritable_accessor :helpers_dir + self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") - base.class_eval do - # Wrap inherited to create a new master helper module for subclasses. - class << self - alias_method_chain :inherited, :helper - end + class << self + alias_method_chain :inherited, :helper end end diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb index 41b2a859bc..d6107d3653 100644 --- a/actionpack/lib/action_controller/new_base.rb +++ b/actionpack/lib/action_controller/new_base.rb @@ -13,6 +13,7 @@ module ActionController autoload :Testing, "action_controller/new_base/testing" autoload :UrlFor, "action_controller/new_base/url_for" autoload :Session, "action_controller/new_base/session" + autoload :Helpers, "action_controller/new_base/helpers" # Ported modules # require 'action_controller/routing' diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb index 0c85896a00..6340b7fde9 100644 --- a/actionpack/lib/action_controller/new_base/base.rb +++ b/actionpack/lib/action_controller/new_base/base.rb @@ -4,9 +4,9 @@ module ActionController include AbstractController::Benchmarker include AbstractController::Callbacks - include AbstractController::Helpers include AbstractController::Logger - + + include ActionController::Helpers include ActionController::HideActions include ActionController::UrlFor include ActionController::Redirector diff --git a/actionpack/lib/action_controller/new_base/helpers.rb b/actionpack/lib/action_controller/new_base/helpers.rb new file mode 100644 index 0000000000..401741c249 --- /dev/null +++ b/actionpack/lib/action_controller/new_base/helpers.rb @@ -0,0 +1,133 @@ +require 'active_support/core_ext/load_error' +require 'active_support/core_ext/name_error' +require 'active_support/dependencies' + +module ActionController + module Helpers + extend ActiveSupport::DependencyModule + + depends_on AbstractController::Helpers + + included do + # Set the default directory for helpers + class_inheritable_accessor :helpers_dir + self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers") + end + + module ClassMethods + def inherited(klass) + # klass.master_helper_module = Module.new + # klass.master_helper_module.__send__ :include, master_helper_module + klass.__send__ :default_helper_module! + super + end + + # The +helper+ class method can take a series of helper module names, a block, or both. + # + # * *args: One or more modules, strings or symbols, or the special symbol :all. + # * &block: A block defining helper methods. + # + # ==== Examples + # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file + # and include the module in the template class. The second form illustrates how to include custom helpers + # when working with namespaced controllers, or other cases where the file containing the helper definition is not + # in one of Rails' standard load paths: + # helper :foo # => requires 'foo_helper' and includes FooHelper + # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper + # + # When the argument is a module it will be included directly in the template class. + # helper FooHelper # => includes FooHelper + # + # When the argument is the symbol :all, the controller will include all helpers beneath + # ActionController::Base.helpers_dir (defaults to app/helpers/**/*.rb under RAILS_ROOT). + # helper :all + # + # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available + # to the template. + # # One line + # helper { def hello() "Hello, world!" end } + # # Multi-line + # helper do + # def foo(bar) + # "#{bar} is the very best" + # end + # end + # + # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of + # +symbols+, +strings+, +modules+ and blocks. + # helper(:three, BlindHelper) { def mice() 'mice' end } + # + def helper(*args, &block) + args.flatten.each do |arg| + case arg + when :all + helper all_application_helpers + when String, Symbol + file_name = arg.to_s.underscore + '_helper' + class_name = file_name.camelize + + begin + require_dependency(file_name) + rescue LoadError => load_error + requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1] + if requiree == file_name + msg = "Missing helper file helpers/#{file_name}.rb" + raise LoadError.new(msg).copy_blame!(load_error) + else + raise + end + end + + add_template_helper(class_name.constantize) + else + # Explcit 'return' here so that the supplied block ( if any ) doesn't get included twice + return super + end + end + + # Evaluate block in template class if given. + master_helper_module.module_eval(&block) if block_given? + end + + # Declares helper accessors for controller attributes. For example, the + # following adds new +name+ and name= instance methods to a + # controller and makes them available to the view: + # helper_attr :name + # attr_accessor :name + def helper_attr(*attrs) + attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") } + end + + # Provides a proxy to access helpers methods from outside the view. + def helpers + unless @helper_proxy + @helper_proxy = ActionView::Base.new + @helper_proxy.extend master_helper_module + else + @helper_proxy + end + end + + private + + def default_helper_module! + unless name.blank? + module_name = name.sub(/Controller$|$/, 'Helper') + module_path = module_name.split('::').map { |m| m.underscore }.join('/') + require_dependency module_path + helper module_name.constantize + end + rescue MissingSourceFile => e + raise unless e.is_missing? module_path + rescue NameError => e + raise unless e.missing_name? module_name + end + + # Extract helper names from files in app/helpers/**/*.rb + def all_application_helpers + extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/ + Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' } + end + end # ClassMethods + end +end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 3bbda9eb3a..5b9feb3630 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -27,7 +27,7 @@ module Fun end end -class ApplicationController < ActionController::Base +class AllHelpersController < ActionController::Base helper :all end @@ -127,7 +127,7 @@ class HelperTest < Test::Unit::TestCase end def test_all_helpers - methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s) + methods = AllHelpersController.master_helper_module.instance_methods.map(&:to_s) # abc_helper.rb assert methods.include?('bare_a') @@ -154,7 +154,7 @@ class HelperTest < Test::Unit::TestCase end def test_helper_proxy - methods = ApplicationController.helpers.methods.map(&:to_s) + methods = AllHelpersController.helpers.methods.map(&:to_s) # ActionView assert methods.include?('pluralize') diff --git a/actionpack/test/new_base/abstract_unit.rb b/actionpack/test/new_base/abstract_unit.rb index 5df3467ff5..569e4e764f 100644 --- a/actionpack/test/new_base/abstract_unit.rb +++ b/actionpack/test/new_base/abstract_unit.rb @@ -2,6 +2,9 @@ $:.unshift(File.dirname(__FILE__) + '/../../lib') $:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib') $:.unshift(File.dirname(__FILE__) + '/../lib') +$:.unshift(File.dirname(__FILE__) + '/../fixtures/helpers') +$:.unshift(File.dirname(__FILE__) + '/../fixtures/alternate_helpers') + ENV['new_base'] = "true" $stderr.puts "Running old tests on new_base" diff --git a/actionpack/test/new_base/render_text_test.rb b/actionpack/test/new_base/render_text_test.rb index aed903ee8a..ffc149283b 100644 --- a/actionpack/test/new_base/render_text_test.rb +++ b/actionpack/test/new_base/render_text_test.rb @@ -1,8 +1,5 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") -class ApplicationController < ActionController::Base -end - module RenderText class SimpleController < ActionController::Base self.view_paths = [ActionView::Template::FixturePath.new] -- cgit v1.2.3