From 3f8409193716669b9fa61ac74ae1c92cfde00785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 29 Jan 2010 16:16:01 +0100 Subject: ActionMailer should depend just on AbstractController. --- actionpack/lib/abstract_controller.rb | 2 +- actionpack/lib/abstract_controller/url_for.rb | 156 ------------- actionpack/lib/action_controller.rb | 10 +- actionpack/lib/action_controller/base.rb | 8 + actionpack/lib/action_controller/deprecated.rb | 2 +- actionpack/lib/action_controller/metal/url_for.rb | 149 +++++++++++- actionpack/lib/action_dispatch.rb | 1 + actionpack/test/abstract/url_for_test.rb | 272 ---------------------- actionpack/test/controller/url_for_test.rb | 272 ++++++++++++++++++++++ 9 files changed, 432 insertions(+), 440 deletions(-) delete mode 100644 actionpack/lib/abstract_controller/url_for.rb delete mode 100644 actionpack/test/abstract/url_for_test.rb create mode 100644 actionpack/test/controller/url_for_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 2c2ef16622..74ca18b8b4 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -2,6 +2,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require 'active_support/ruby/shim' +require 'active_support/dependencies/autoload' require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/delegation' @@ -17,5 +18,4 @@ module AbstractController autoload :Logger autoload :Rendering autoload :Translation - autoload :UrlFor end diff --git a/actionpack/lib/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb deleted file mode 100644 index 6b7d2b1f34..0000000000 --- a/actionpack/lib/abstract_controller/url_for.rb +++ /dev/null @@ -1,156 +0,0 @@ -module AbstractController - # In routes.rb one defines URL-to-controller mappings, but the reverse - # is also possible: an URL can be generated from one of your routing definitions. - # URL generation functionality is centralized in this module. - # - # See AbstractController::Routing and AbstractController::Resources for general - # information about routing and routes.rb. - # - # Tip: If you need to generate URLs from your models or some other place, - # then AbstractController::UrlFor is what you're looking for. Read on for - # an introduction. - # - # == URL generation from parameters - # - # As you may know, some functions - such as AbstractController::Base#url_for - # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set - # of parameters. For example, you've probably had the chance to write code - # like this in one of your views: - # - # <%= link_to('Click here', :controller => 'users', - # :action => 'new', :message => 'Welcome!') %> - # - # #=> Generates a link to: /users/new?message=Welcome%21 - # - # link_to, and all other functions that require URL generation functionality, - # actually use AbstractController::UrlFor under the hood. And in particular, - # they use the AbstractController::UrlFor#url_for method. One can generate - # the same path as the above example by using the following code: - # - # include UrlFor - # url_for(:controller => 'users', - # :action => 'new', - # :message => 'Welcome!', - # :only_path => true) - # # => "/users/new?message=Welcome%21" - # - # Notice the :only_path => true part. This is because UrlFor has no - # information about the website hostname that your Rails app is serving. So if you - # want to include the hostname as well, then you must also pass the :host - # argument: - # - # include UrlFor - # url_for(:controller => 'users', - # :action => 'new', - # :message => 'Welcome!', - # :host => 'www.example.com') # Changed this. - # # => "http://www.example.com/users/new?message=Welcome%21" - # - # By default, all controllers and views have access to a special version of url_for, - # that already knows what the current hostname is. So if you use url_for in your - # controllers or your views, then you don't need to explicitly pass the :host - # argument. - # - # For convenience reasons, mailers provide a shortcut for AbstractController::UrlFor#url_for. - # So within mailers, you only have to type 'url_for' instead of 'AbstractController::UrlFor#url_for' - # in full. However, mailers don't have hostname information, and what's why you'll still - # have to specify the :host argument when generating URLs in mailers. - # - # - # == URL generation for named routes - # - # UrlFor also allows one to access methods that have been auto-generated from - # named routes. For example, suppose that you have a 'users' resource in your - # routes.rb: - # - # map.resources :users - # - # This generates, among other things, the method users_path. By default, - # this method is accessible from your controllers, views and mailers. If you need - # to access this auto-generated method from other places (such as a model), then - # you can do that by including AbstractController::UrlFor in your class: - # - # class User < ActiveRecord::Base - # include AbstractController::UrlFor - # - # def base_uri - # user_path(self) - # end - # end - # - # User.find(1).base_uri # => "/users/1" - # - module UrlFor - extend ActiveSupport::Concern - - included do - ActionController::Routing::Routes.install_helpers(self) - extlib_inheritable_accessor :default_url_options, - :instance_writer => false, :instance_reader => false - self.default_url_options ||= {} - end - - # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in - # the form of a hash, just like the one you would use for url_for directly. Example: - # - # def default_url_options(options) - # { :project => @project.active? ? @project.url_name : "unknown" } - # end - # - # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the - # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set - # by this method. - def default_url_options(options = nil) - self.class.default_url_options - end - - def rewrite_options(options) #:nodoc: - if options.delete(:use_defaults) != false && (defaults = default_url_options(options)) - defaults.merge(options) - else - options - end - end - - # Generate a url based on the options provided, default_url_options and the - # routes defined in routes.rb. The following options are supported: - # - # * :only_path - If true, the relative url is returned. Defaults to +false+. - # * :protocol - The protocol to connect to. Defaults to 'http'. - # * :host - Specifies the host the link should be targeted at. - # If :only_path is false, this option must be - # provided either explicitly, or via +default_url_options+. - # * :port - Optionally specify the port to connect to. - # * :anchor - An anchor name to be appended to the path. - # * :skip_relative_url_root - If true, the url is not constructed using the - # +relative_url_root+ set in AbstractController::Base.relative_url_root. - # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2009/" - # - # Any other key (:controller, :action, etc.) given to - # +url_for+ is forwarded to the Routes module. - # - # Examples: - # - # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing' - # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok' - # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/' - # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33' - def url_for(options = {}) - options ||= {} - case options - when String - options - when Hash - _url_rewriter.rewrite(rewrite_options(options)) - else - polymorphic_url(options) - end - end - - protected - - def _url_rewriter - ActionController::UrlRewriter - end - end -end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 33e107d216..759e52b135 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,6 +1,5 @@ -activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) -$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) -require 'active_support/ruby/shim' +require 'abstract_controller' +require 'action_dispatch' module ActionController extend ActiveSupport::Autoload @@ -41,6 +40,7 @@ module ActionController autoload :Integration, 'action_controller/deprecated/integration_test' autoload :IntegrationTest, 'action_controller/deprecated/integration_test' autoload :PerformanceTest, 'action_controller/deprecated/performance_test' + autoload :UrlWriter, 'action_controller/deprecated' autoload :Routing, 'action_controller/deprecated' autoload :TestCase, 'action_controller/test_case' @@ -66,13 +66,11 @@ module ActionController end # All of these simply register additional autoloads -require 'abstract_controller' -require 'action_dispatch' require 'action_view' require 'action_controller/vendor/html-scanner' # Common ActiveSupport usage in ActionController -require "active_support/concern" +require 'active_support/concern' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/load_error' require 'active_support/core_ext/module/attr_internal' diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 215b70734c..10244f8216 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -81,5 +81,13 @@ module ActionController filter << block if block filter end + + protected + + # Overwrite url rewriter to use request. + def _url_rewriter + return ActionController::UrlRewriter unless request + @_url_rewriter ||= ActionController::UrlRewriter.new(request, params) + end end end diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb index edc0e5b3fe..a4eef07841 100644 --- a/actionpack/lib/action_controller/deprecated.rb +++ b/actionpack/lib/action_controller/deprecated.rb @@ -2,4 +2,4 @@ ActionController::AbstractRequest = ActionController::Request = ActionDispatch:: ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response ActionController::Routing = ActionDispatch::Routing ActionController::Routing::Routes = ActionDispatch::Routing::RouteSet.new -ActionController::UrlWriter = AbstractController::UrlFor +ActionController::UrlWriter = ActionController::UrlFor diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 73feacb872..387e6a554b 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -1,15 +1,156 @@ module ActionController + # In routes.rb one defines URL-to-controller mappings, but the reverse + # is also possible: an URL can be generated from one of your routing definitions. + # URL generation functionality is centralized in this module. + # + # See ActionController::Routing and ActionController::Resources for general + # information about routing and routes.rb. + # + # Tip: If you need to generate URLs from your models or some other place, + # then ActionController::UrlFor is what you're looking for. Read on for + # an introduction. + # + # == URL generation from parameters + # + # As you may know, some functions - such as ActionController::Base#url_for + # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set + # of parameters. For example, you've probably had the chance to write code + # like this in one of your views: + # + # <%= link_to('Click here', :controller => 'users', + # :action => 'new', :message => 'Welcome!') %> + # + # #=> Generates a link to: /users/new?message=Welcome%21 + # + # link_to, and all other functions that require URL generation functionality, + # actually use ActionController::UrlFor under the hood. And in particular, + # they use the ActionController::UrlFor#url_for method. One can generate + # the same path as the above example by using the following code: + # + # include UrlFor + # url_for(:controller => 'users', + # :action => 'new', + # :message => 'Welcome!', + # :only_path => true) + # # => "/users/new?message=Welcome%21" + # + # Notice the :only_path => true part. This is because UrlFor has no + # information about the website hostname that your Rails app is serving. So if you + # want to include the hostname as well, then you must also pass the :host + # argument: + # + # include UrlFor + # url_for(:controller => 'users', + # :action => 'new', + # :message => 'Welcome!', + # :host => 'www.example.com') # Changed this. + # # => "http://www.example.com/users/new?message=Welcome%21" + # + # By default, all controllers and views have access to a special version of url_for, + # that already knows what the current hostname is. So if you use url_for in your + # controllers or your views, then you don't need to explicitly pass the :host + # argument. + # + # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for. + # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for' + # in full. However, mailers don't have hostname information, and what's why you'll still + # have to specify the :host argument when generating URLs in mailers. + # + # + # == URL generation for named routes + # + # UrlFor also allows one to access methods that have been auto-generated from + # named routes. For example, suppose that you have a 'users' resource in your + # routes.rb: + # + # map.resources :users + # + # This generates, among other things, the method users_path. By default, + # this method is accessible from your controllers, views and mailers. If you need + # to access this auto-generated method from other places (such as a model), then + # you can do that by including ActionController::UrlFor in your class: + # + # class User < ActiveRecord::Base + # include ActionController::UrlFor + # + # def base_uri + # user_path(self) + # end + # end + # + # User.find(1).base_uri # => "/users/1" + # module UrlFor extend ActiveSupport::Concern - include AbstractController::UrlFor - include ActionController::RackDelegation + included do + ActionController::Routing::Routes.install_helpers(self) + extlib_inheritable_accessor :default_url_options, + :instance_writer => false, :instance_reader => false + self.default_url_options ||= {} + end + + # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in + # the form of a hash, just like the one you would use for url_for directly. Example: + # + # def default_url_options(options) + # { :project => @project.active? ? @project.url_name : "unknown" } + # end + # + # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the + # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set + # by this method. + def default_url_options(options = nil) + self.class.default_url_options + end + + def rewrite_options(options) #:nodoc: + if options.delete(:use_defaults) != false && (defaults = default_url_options(options)) + defaults.merge(options) + else + options + end + end + + # Generate a url based on the options provided, default_url_options and the + # routes defined in routes.rb. The following options are supported: + # + # * :only_path - If true, the relative url is returned. Defaults to +false+. + # * :protocol - The protocol to connect to. Defaults to 'http'. + # * :host - Specifies the host the link should be targeted at. + # If :only_path is false, this option must be + # provided either explicitly, or via +default_url_options+. + # * :port - Optionally specify the port to connect to. + # * :anchor - An anchor name to be appended to the path. + # * :skip_relative_url_root - If true, the url is not constructed using the + # +relative_url_root+ set in ActionController::Base.relative_url_root. + # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2009/" + # + # Any other key (:controller, :action, etc.) given to + # +url_for+ is forwarded to the Routes module. + # + # Examples: + # + # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing' + # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok' + # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/' + # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33' + def url_for(options = {}) + options ||= {} + case options + when String + options + when Hash + _url_rewriter.rewrite(rewrite_options(options)) + else + polymorphic_url(options) + end + end protected def _url_rewriter - return ActionController::UrlRewriter unless request - @_url_rewriter ||= ActionController::UrlRewriter.new(request, params) + ActionController::UrlRewriter end end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index f0490a5619..c6eb097ee5 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -23,6 +23,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + require 'active_support' require 'active_support/dependencies/autoload' diff --git a/actionpack/test/abstract/url_for_test.rb b/actionpack/test/abstract/url_for_test.rb deleted file mode 100644 index e5570349b8..0000000000 --- a/actionpack/test/abstract/url_for_test.rb +++ /dev/null @@ -1,272 +0,0 @@ -require 'abstract_unit' - -module AbstractController - module Testing - - class UrlForTests < ActionController::TestCase - class W - include AbstractController::UrlFor - end - - def teardown - W.default_url_options.clear - end - - def add_host! - W.default_url_options[:host] = 'www.basecamphq.com' - end - - def test_exception_is_thrown_without_host - assert_raise RuntimeError do - W.new.url_for :controller => 'c', :action => 'a', :id => 'i' - end - end - - def test_anchor - assert_equal('/c/a#anchor', - W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor') - ) - end - - def test_anchor_should_call_to_param - assert_equal('/c/a#anchor', - W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor')) - ) - end - - def test_anchor_should_be_cgi_escaped - assert_equal('/c/a#anc%2Fhor', - W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor')) - ) - end - - def test_default_host - add_host! - assert_equal('http://www.basecamphq.com/c/a/i', - W.new.url_for(:controller => 'c', :action => 'a', :id => 'i') - ) - end - - def test_host_may_be_overridden - add_host! - assert_equal('http://37signals.basecamphq.com/c/a/i', - W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i') - ) - end - - def test_port - add_host! - assert_equal('http://www.basecamphq.com:3000/c/a/i', - W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000) - ) - end - - def test_protocol - add_host! - assert_equal('https://www.basecamphq.com/c/a/i', - W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') - ) - end - - def test_protocol_with_and_without_separator - add_host! - assert_equal('https://www.basecamphq.com/c/a/i', - W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') - ) - assert_equal('https://www.basecamphq.com/c/a/i', - W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://') - ) - end - - def test_trailing_slash - add_host! - options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'} - assert_equal('http://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) ) - end - - def test_trailing_slash_with_protocol - add_host! - options = { :trailing_slash => true,:protocol => 'https', :controller => 'foo', :action => 'bar', :id => '33'} - assert_equal('https://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) ) - assert_equal 'https://www.basecamphq.com/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string'})) - end - - def test_trailing_slash_with_only_path - options = {:controller => 'foo', :trailing_slash => true} - assert_equal '/foo/', W.new.url_for(options.merge({:only_path => true})) - options.update({:action => 'bar', :id => '33'}) - assert_equal '/foo/bar/33/', W.new.url_for(options.merge({:only_path => true})) - assert_equal '/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string',:only_path => true})) - end - - def test_trailing_slash_with_anchor - options = {:trailing_slash => true, :controller => 'foo', :action => 'bar', :id => '33', :only_path => true, :anchor=> 'chapter7'} - assert_equal '/foo/bar/33/#chapter7', W.new.url_for(options) - assert_equal '/foo/bar/33/?query=string#chapter7', W.new.url_for(options.merge({:query => 'string'})) - end - - def test_trailing_slash_with_params - url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link') - params = extract_params(url) - assert_equal params[0], { :p1 => 'cafe' }.to_query - assert_equal params[1], { :p2 => 'link' }.to_query - end - - def test_relative_url_root_is_respected - orig_relative_url_root = ActionController::Base.relative_url_root - ActionController::Base.relative_url_root = '/subdir' - - add_host! - assert_equal('https://www.basecamphq.com/subdir/c/a/i', - W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') - ) - ensure - ActionController::Base.relative_url_root = orig_relative_url_root - end - - def test_named_routes - with_routing do |set| - set.draw do |map| - match 'this/is/verbose', :to => 'home#index', :as => :no_args - match 'home/sweet/home/:user', :to => 'home#index', :as => :home - end - - # We need to create a new class in order to install the new named route. - kls = Class.new { include AbstractController::UrlFor } - controller = kls.new - assert controller.respond_to?(:home_url) - assert_equal 'http://www.basecamphq.com/home/sweet/home/again', - controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again') - - assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused')) - assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com')) - assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com')) - end - end - - def test_relative_url_root_is_respected_for_named_routes - orig_relative_url_root = ActionController::Base.relative_url_root - ActionController::Base.relative_url_root = '/subdir' - - with_routing do |set| - set.draw do |map| - match '/home/sweet/home/:user', :to => 'home#index', :as => :home - end - - kls = Class.new { include AbstractController::UrlFor } - controller = kls.new - - assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again', - controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again') - end - ensure - ActionController::Base.relative_url_root = orig_relative_url_root - end - - def test_only_path - with_routing do |set| - set.draw do |map| - match 'home/sweet/home/:user', :to => 'home#index', :as => :home - match ':controller/:action/:id' - end - - # We need to create a new class in order to install the new named route. - kls = Class.new { include AbstractController::UrlFor } - controller = kls.new - assert controller.respond_to?(:home_url) - assert_equal '/brave/new/world', - controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true) - - assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true)) - assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama')) - end - end - - def test_one_parameter - assert_equal('/c/a?param=val', - W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :param => 'val') - ) - end - - def test_two_parameters - url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2') - params = extract_params(url) - assert_equal params[0], { :p1 => 'X1' }.to_query - assert_equal params[1], { :p2 => 'Y2' }.to_query - end - - def test_hash_parameter - url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'}) - params = extract_params(url) - assert_equal params[0], { 'query[category]' => 'prof' }.to_query - assert_equal params[1], { 'query[name]' => 'Bob' }.to_query - end - - def test_array_parameter - url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof']) - params = extract_params(url) - assert_equal params[0], { 'query[]' => 'Bob' }.to_query - assert_equal params[1], { 'query[]' => 'prof' }.to_query - end - - def test_hash_recursive_parameters - url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'}) - params = extract_params(url) - assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query - assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query - assert_equal params[2], { 'query[person][position]' => 'prof' }.to_query - end - - def test_hash_recursive_and_array_parameters - url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'}) - assert_match %r(^/c/a/101), url - params = extract_params(url) - assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query - assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query - assert_equal params[2], { 'query[person][position][]' => 'art director' }.to_query - assert_equal params[3], { 'query[person][position][]' => 'prof' }.to_query - end - - def test_path_generation_for_symbol_parameter_keys - assert_generates("/image", :controller=> :image) - end - - def test_named_routes_with_nil_keys - with_routing do |set| - set.draw do |map| - match 'posts.:format', :to => 'posts#index', :as => :posts - match '/', :to => 'posts#index', :as => :main - end - - # We need to create a new class in order to install the new named route. - kls = Class.new { include AbstractController::UrlFor } - kls.default_url_options[:host] = 'www.basecamphq.com' - - controller = kls.new - params = {:action => :index, :controller => :posts, :format => :xml} - assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params)) - params[:format] = nil - assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params)) - end - end - - def test_multiple_includes_maintain_distinct_options - first_class = Class.new { include AbstractController::UrlFor } - second_class = Class.new { include AbstractController::UrlFor } - - first_host, second_host = 'firsthost.com', 'secondhost.com' - - first_class.default_url_options[:host] = first_host - second_class.default_url_options[:host] = second_host - - assert_equal first_class.default_url_options[:host], first_host - assert_equal second_class.default_url_options[:host], second_host - end - - private - def extract_params(url) - url.split('?', 2).last.split('&').sort - end - end - end -end \ No newline at end of file diff --git a/actionpack/test/controller/url_for_test.rb b/actionpack/test/controller/url_for_test.rb new file mode 100644 index 0000000000..749fa5861f --- /dev/null +++ b/actionpack/test/controller/url_for_test.rb @@ -0,0 +1,272 @@ +require 'abstract_unit' + +module AbstractController + module Testing + + class UrlForTests < ActionController::TestCase + class W + include ActionController::UrlFor + end + + def teardown + W.default_url_options.clear + end + + def add_host! + W.default_url_options[:host] = 'www.basecamphq.com' + end + + def test_exception_is_thrown_without_host + assert_raise RuntimeError do + W.new.url_for :controller => 'c', :action => 'a', :id => 'i' + end + end + + def test_anchor + assert_equal('/c/a#anchor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor') + ) + end + + def test_anchor_should_call_to_param + assert_equal('/c/a#anchor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anchor')) + ) + end + + def test_anchor_should_be_cgi_escaped + assert_equal('/c/a#anc%2Fhor', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => Struct.new(:to_param).new('anc/hor')) + ) + end + + def test_default_host + add_host! + assert_equal('http://www.basecamphq.com/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i') + ) + end + + def test_host_may_be_overridden + add_host! + assert_equal('http://37signals.basecamphq.com/c/a/i', + W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i') + ) + end + + def test_port + add_host! + assert_equal('http://www.basecamphq.com:3000/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000) + ) + end + + def test_protocol + add_host! + assert_equal('https://www.basecamphq.com/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') + ) + end + + def test_protocol_with_and_without_separator + add_host! + assert_equal('https://www.basecamphq.com/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') + ) + assert_equal('https://www.basecamphq.com/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https://') + ) + end + + def test_trailing_slash + add_host! + options = {:controller => 'foo', :trailing_slash => true, :action => 'bar', :id => '33'} + assert_equal('http://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) ) + end + + def test_trailing_slash_with_protocol + add_host! + options = { :trailing_slash => true,:protocol => 'https', :controller => 'foo', :action => 'bar', :id => '33'} + assert_equal('https://www.basecamphq.com/foo/bar/33/', W.new.url_for(options) ) + assert_equal 'https://www.basecamphq.com/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string'})) + end + + def test_trailing_slash_with_only_path + options = {:controller => 'foo', :trailing_slash => true} + assert_equal '/foo/', W.new.url_for(options.merge({:only_path => true})) + options.update({:action => 'bar', :id => '33'}) + assert_equal '/foo/bar/33/', W.new.url_for(options.merge({:only_path => true})) + assert_equal '/foo/bar/33/?query=string', W.new.url_for(options.merge({:query => 'string',:only_path => true})) + end + + def test_trailing_slash_with_anchor + options = {:trailing_slash => true, :controller => 'foo', :action => 'bar', :id => '33', :only_path => true, :anchor=> 'chapter7'} + assert_equal '/foo/bar/33/#chapter7', W.new.url_for(options) + assert_equal '/foo/bar/33/?query=string#chapter7', W.new.url_for(options.merge({:query => 'string'})) + end + + def test_trailing_slash_with_params + url = W.new.url_for(:trailing_slash => true, :only_path => true, :controller => 'cont', :action => 'act', :p1 => 'cafe', :p2 => 'link') + params = extract_params(url) + assert_equal params[0], { :p1 => 'cafe' }.to_query + assert_equal params[1], { :p2 => 'link' }.to_query + end + + def test_relative_url_root_is_respected + orig_relative_url_root = ActionController::Base.relative_url_root + ActionController::Base.relative_url_root = '/subdir' + + add_host! + assert_equal('https://www.basecamphq.com/subdir/c/a/i', + W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') + ) + ensure + ActionController::Base.relative_url_root = orig_relative_url_root + end + + def test_named_routes + with_routing do |set| + set.draw do |map| + match 'this/is/verbose', :to => 'home#index', :as => :no_args + match 'home/sweet/home/:user', :to => 'home#index', :as => :home + end + + # We need to create a new class in order to install the new named route. + kls = Class.new { include ActionController::UrlFor } + controller = kls.new + assert controller.respond_to?(:home_url) + assert_equal 'http://www.basecamphq.com/home/sweet/home/again', + controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again') + + assert_equal("/home/sweet/home/alabama", controller.send(:home_path, :user => 'alabama', :host => 'unused')) + assert_equal("http://www.basecamphq.com/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'www.basecamphq.com')) + assert_equal("http://www.basecamphq.com/this/is/verbose", controller.send(:no_args_url, :host=>'www.basecamphq.com')) + end + end + + def test_relative_url_root_is_respected_for_named_routes + orig_relative_url_root = ActionController::Base.relative_url_root + ActionController::Base.relative_url_root = '/subdir' + + with_routing do |set| + set.draw do |map| + match '/home/sweet/home/:user', :to => 'home#index', :as => :home + end + + kls = Class.new { include ActionController::UrlFor } + controller = kls.new + + assert_equal 'http://www.basecamphq.com/subdir/home/sweet/home/again', + controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again') + end + ensure + ActionController::Base.relative_url_root = orig_relative_url_root + end + + def test_only_path + with_routing do |set| + set.draw do |map| + match 'home/sweet/home/:user', :to => 'home#index', :as => :home + match ':controller/:action/:id' + end + + # We need to create a new class in order to install the new named route. + kls = Class.new { include ActionController::UrlFor } + controller = kls.new + assert controller.respond_to?(:home_url) + assert_equal '/brave/new/world', + controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true) + + assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true)) + assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama')) + end + end + + def test_one_parameter + assert_equal('/c/a?param=val', + W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :param => 'val') + ) + end + + def test_two_parameters + url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :p1 => 'X1', :p2 => 'Y2') + params = extract_params(url) + assert_equal params[0], { :p1 => 'X1' }.to_query + assert_equal params[1], { :p2 => 'Y2' }.to_query + end + + def test_hash_parameter + url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:name => 'Bob', :category => 'prof'}) + params = extract_params(url) + assert_equal params[0], { 'query[category]' => 'prof' }.to_query + assert_equal params[1], { 'query[name]' => 'Bob' }.to_query + end + + def test_array_parameter + url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => ['Bob', 'prof']) + params = extract_params(url) + assert_equal params[0], { 'query[]' => 'Bob' }.to_query + assert_equal params[1], { 'query[]' => 'prof' }.to_query + end + + def test_hash_recursive_parameters + url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :query => {:person => {:name => 'Bob', :position => 'prof'}, :hobby => 'piercing'}) + params = extract_params(url) + assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query + assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query + assert_equal params[2], { 'query[person][position]' => 'prof' }.to_query + end + + def test_hash_recursive_and_array_parameters + url = W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :id => 101, :query => {:person => {:name => 'Bob', :position => ['prof', 'art director']}, :hobby => 'piercing'}) + assert_match %r(^/c/a/101), url + params = extract_params(url) + assert_equal params[0], { 'query[hobby]' => 'piercing' }.to_query + assert_equal params[1], { 'query[person][name]' => 'Bob' }.to_query + assert_equal params[2], { 'query[person][position][]' => 'art director' }.to_query + assert_equal params[3], { 'query[person][position][]' => 'prof' }.to_query + end + + def test_path_generation_for_symbol_parameter_keys + assert_generates("/image", :controller=> :image) + end + + def test_named_routes_with_nil_keys + with_routing do |set| + set.draw do |map| + match 'posts.:format', :to => 'posts#index', :as => :posts + match '/', :to => 'posts#index', :as => :main + end + + # We need to create a new class in order to install the new named route. + kls = Class.new { include ActionController::UrlFor } + kls.default_url_options[:host] = 'www.basecamphq.com' + + controller = kls.new + params = {:action => :index, :controller => :posts, :format => :xml} + assert_equal("http://www.basecamphq.com/posts.xml", controller.send(:url_for, params)) + params[:format] = nil + assert_equal("http://www.basecamphq.com/", controller.send(:url_for, params)) + end + end + + def test_multiple_includes_maintain_distinct_options + first_class = Class.new { include ActionController::UrlFor } + second_class = Class.new { include ActionController::UrlFor } + + first_host, second_host = 'firsthost.com', 'secondhost.com' + + first_class.default_url_options[:host] = first_host + second_class.default_url_options[:host] = second_host + + assert_equal first_class.default_url_options[:host], first_host + assert_equal second_class.default_url_options[:host], second_host + end + + private + def extract_params(url) + url.split('?', 2).last.split('&').sort + end + end + end +end \ No newline at end of file -- cgit v1.2.3 From ba82eb2efa24c9124fc4b3dac65c7b7494022f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 29 Jan 2010 16:26:36 +0100 Subject: Get rid of AM warnings in AP test suite. --- actionpack/test/controller/assert_select_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb index 2e77d2f8ad..612827dd41 100644 --- a/actionpack/test/controller/assert_select_test.rb +++ b/actionpack/test/controller/assert_select_test.rb @@ -18,7 +18,7 @@ unless defined?(ActionMailer) end end -ActionMailer::Base.template_root = FIXTURE_LOAD_PATH +ActionMailer::Base.view_paths = FIXTURE_LOAD_PATH class AssertSelectTest < ActionController::TestCase Assertion = ActiveSupport::TestCase::Assertion @@ -716,7 +716,7 @@ EOF def test_assert_select_email assert_raise(Assertion) { assert_select_email {} } - AssertSelectMailer.deliver_test "

foo

bar

" + AssertSelectMailer.test("

foo

bar

").deliver assert_select_email do assert_select "div:root" do assert_select "p:first-child", "foo" -- cgit v1.2.3 From 986a4e616be715e5c5a6ebbd25d339fa9bba4072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 29 Jan 2010 16:49:26 +0100 Subject: Fix rendering of layouts. --- actionpack/lib/abstract_controller/rendering.rb | 3 ++- actionpack/lib/action_controller/metal/rendering.rb | 5 ----- actionpack/test/abstract/render_test.rb | 6 +++--- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 1dec3f2c3e..40cac40ba7 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -157,7 +157,8 @@ module AbstractController options[:_template_name] = options[:file] end - name = (options[:_template_name] || action_name).to_s + name = (options[:_template_name] || options[:action] || action_name).to_s + options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty? options[:_template] ||= with_template_cache(name) do find_template(name, { :formats => formats }, options) diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 8f03035b2b..0aae9f8579 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -58,11 +58,6 @@ module ActionController options.merge! :partial => action end - if (options.keys & [:partial, :file, :template, :text, :inline]).empty? - options[:_template_name] ||= options[:action] - options[:_prefix] = _prefix - end - if options[:status] options[:status] = Rack::Utils.status_code(options[:status]) end diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb index ffd430fa86..9a0a140bee 100644 --- a/actionpack/test/abstract/render_test.rb +++ b/actionpack/test/abstract/render_test.rb @@ -11,13 +11,13 @@ module AbstractController end self.view_paths = [ActionView::FixtureResolver.new( - "default.erb" => "With Default", "template.erb" => "With Template", + "renderer/default.erb" => "With Default", "renderer/string.erb" => "With String", "renderer/symbol.erb" => "With Symbol", + "renderer/template_name.erb" => "With Template Name", "string/with_path.erb" => "With String With Path", - "some/file.erb" => "With File", - "template_name.erb" => "With Template Name" + "some/file.erb" => "With File" )] def template -- cgit v1.2.3 From cc2642b220958894c5a384530f32f622f76cd097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 29 Jan 2010 17:21:31 +0100 Subject: Added :format and :locale options to render. --- actionpack/lib/abstract_controller/rendering.rb | 11 ++++++- actionpack/test/abstract/render_test.rb | 41 ++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 40cac40ba7..ac407bda5e 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -160,11 +160,20 @@ module AbstractController name = (options[:_template_name] || options[:action] || action_name).to_s options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty? + details = _normalize_details(options) + options[:_template] ||= with_template_cache(name) do - find_template(name, { :formats => formats }, options) + find_template(name, details, options) end end + def _normalize_details(options) + details = { :formats => formats } + details[:formats] = Array(options[:format]) if options[:format] + details[:locale] = Array(options[:locale]) if options[:locale] + details + end + def find_template(name, details, options) view_paths.find(name, details, options[:_prefix], options[:_partial]) end diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb index 9a0a140bee..db924633ca 100644 --- a/actionpack/test/abstract/render_test.rb +++ b/actionpack/test/abstract/render_test.rb @@ -17,7 +17,11 @@ module AbstractController "renderer/symbol.erb" => "With Symbol", "renderer/template_name.erb" => "With Template Name", "string/with_path.erb" => "With String With Path", - "some/file.erb" => "With File" + "some/file.erb" => "With File", + "with_format.html.erb" => "With html format", + "with_format.xml.erb" => "With xml format", + "with_locale.en.erb" => "With en locale", + "with_locale.pl.erb" => "With pl locale" )] def template @@ -59,6 +63,22 @@ module AbstractController def object render :_template => ActionView::Template::Text.new("With Object") end + + def with_html_format + render :template => "with_format", :format => :html + end + + def with_xml_format + render :template => "with_format", :format => :xml + end + + def with_en_locale + render :template => "with_locale" + end + + def with_pl_locale + render :template => "with_locale", :locale => :pl + end end class TestRenderer < ActiveSupport::TestCase @@ -117,6 +137,25 @@ module AbstractController assert_equal "With Object", @controller.response_body end + def test_render_with_html_format + @controller.process(:with_html_format) + assert_equal "With html format", @controller.response_body + end + + def test_render_with_xml_format + @controller.process(:with_xml_format) + assert_equal "With xml format", @controller.response_body + end + + def test_render_with_en_locale + @controller.process(:with_en_locale) + assert_equal "With en locale", @controller.response_body + end + + def test_render_with_pl_locale + @controller.process(:with_pl_locale) + assert_equal "With pl locale", @controller.response_body + end end end end -- cgit v1.2.3 From 8a46e1182e3fce36aee5e6cb65737c4e755bbced Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 29 Jan 2010 14:03:38 -0600 Subject: Add rails.js driver to default source list --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 83357dd76f..4df99f8293 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -140,7 +140,7 @@ module ActionView :stylesheets_dir => "#{assets_dir}/stylesheets", } - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) + JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or -- cgit v1.2.3 From 7f181e475080924164b3fa0071ee4898bd66cbb8 Mon Sep 17 00:00:00 2001 From: "Erik St. Martin" Date: Fri, 29 Jan 2010 17:31:22 -0500 Subject: fixed tests to also look for rails.js when using javascript_include_tag :defaults --- actionpack/test/template/asset_tag_helper_test.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 57802ebf42..5fe2c4df24 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -86,11 +86,11 @@ class AssetTagHelperTest < ActionView::TestCase %(javascript_include_tag("bank.js")) => %(), %(javascript_include_tag("bank", :lang => "vbscript")) => %(), %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(\n), - %(javascript_include_tag(:defaults)) => %(\n\n\n\n), + %(javascript_include_tag(:defaults)) => %(\n\n\n\n\n), %(javascript_include_tag(:all)) => %(\n\n\n\n\n\n\n), %(javascript_include_tag(:all, :recursive => true)) => %(\n\n\n\n\n\n\n\n), - %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n), - %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n), + %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n\n), + %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n\n), %(javascript_include_tag("http://example.com/all")) => %(), %(javascript_include_tag("http://example.com/all.js")) => %(), @@ -235,7 +235,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_javascript_include_tag_with_given_asset_id ENV["RAILS_ASSET_ID"] = "1" - assert_dom_equal(%(\n\n\n\n), javascript_include_tag(:defaults)) + assert_dom_equal(%(\n\n\n\n\n), javascript_include_tag(:defaults)) end def test_javascript_include_tag_is_html_safe @@ -246,14 +246,14 @@ class AssetTagHelperTest < ActionView::TestCase def test_register_javascript_include_default ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' - assert_dom_equal %(\n\n\n\n\n), javascript_include_tag(:defaults) + assert_dom_equal %(\n\n\n\n\n\n), javascript_include_tag(:defaults) end def test_register_javascript_include_default_mixed_defaults ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'robber', '/elsewhere/cools.js' - assert_dom_equal %(\n\n\n\n\n\n\n), javascript_include_tag(:defaults) + assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag(:defaults) end def test_custom_javascript_expansions @@ -265,7 +265,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"] - assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') + assert_dom_equal %(\n\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') end def test_custom_javascript_expansions_with_undefined_symbol -- cgit v1.2.3 From bddd1bb6268e8ac2142c8d8c738929d2f2e8447a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 29 Jan 2010 20:00:55 -0600 Subject: Deprecate link_to :popup --- .../lib/action_view/helpers/javascript_helper.rb | 19 +++++--------- actionpack/lib/action_view/helpers/url_helper.rb | 18 ++----------- actionpack/test/template/url_helper_test.rb | 30 ---------------------- 3 files changed, 8 insertions(+), 59 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index ee6481b86d..02d0a88189 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -189,24 +189,21 @@ module ActionView protected def convert_options_to_javascript!(html_options, url = '') - confirm, popup = html_options.delete("confirm"), html_options.delete("popup") + confirm = html_options.delete("confirm") + + if html_options.key?("popup") + ActiveSupport::Deprecation.warn(":popup has been deprecated", caller) + end method, href = html_options.delete("method"), html_options['href'] - if popup && method - raise ActionView::ActionViewError, "You can't use :popup and :method in the same link" - elsif confirm && popup - add_confirm_to_attributes!(html_options, confirm) - html_options["data-popup"] = popup_attributes(popup) - elsif confirm && method + if confirm && method add_confirm_to_attributes!(html_options, confirm) add_method_to_attributes!(html_options, method, url) elsif confirm add_confirm_to_attributes!(html_options, confirm) elsif method add_method_to_attributes!(html_options, method, url) - elsif popup - html_options["data-popup"] = popup_attributes(popup) end end @@ -226,10 +223,6 @@ module ActionView html_options["data-disable-with"] = disable_with if disable_with end - def popup_attributes(popup) - popup.is_a?(Array) ? "{title: '#{popup.first}', options: '#{popup.last}'}" : "true" - end - def options_for_javascript(options) if options.empty? '{}' diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 4424dbba42..837dca6149 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -120,10 +120,6 @@ module ActionView # * :confirm => 'question?' - This will add a JavaScript confirm # prompt with the question specified. If the user accepts, the link is # processed normally, otherwise no action is taken. - # * :popup => true || array of window options - This will force the - # link to open in a popup window. By passing true, a default browser window - # will be opened with the URL. You can also specify an array of options - # that are passed to the window.open JavaScript call. # * :method => symbol of HTTP verb - This modifier will dynamically # create an HTML form and immediately submit the form for processing using # the HTTP verb specified. Useful for having links perform a POST operation @@ -136,10 +132,6 @@ module ActionView # the request object's methods for post?, delete? or put?. # * The +html_options+ will accept a hash of html attributes for the link tag. # - # You can mix and match the +html_options+ with the exception of - # :popup and :method which will raise an - # ActionView::ActionViewError exception. - # # ==== Examples # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base @@ -203,17 +195,11 @@ module ActionView # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux") # # => Nonsense search # - # The three options specific to +link_to+ (:confirm, :popup, and :method) are used as follows: + # The three options specific to +link_to+ (:confirm and :method) are used as follows: # # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?" # # => Visit Other Site # - # link_to "Help", { :action => "help" }, :popup => true - # # => Help - # - # link_to "View Image", @image, :popup => ['new_window_name', 'height=300,width=600'] - # # => View Image - # # link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete # # => "submit", "value" => name) diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index c0e6826ec5..984240d46f 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -183,32 +183,6 @@ class UrlHelperTest < ActionView::TestCase ) end - def test_link_tag_with_popup - assert_dom_equal( - "Hello", - link_to("Hello", "http://www.example.com", :popup => true) - ) - assert_dom_equal( - "Hello", - link_to("Hello", "http://www.example.com", :popup => 'true') - ) - assert_dom_equal( - "Hello", - link_to("Hello", "http://www.example.com", :popup => ['window_name', 'width=300,height=300']) - ) - end - - def test_link_tag_with_popup_and_javascript_confirm - assert_dom_equal( - "Hello", - link_to("Hello", "http://www.example.com", { :popup => true, :confirm => "Fo' sho'?" }) - ) - assert_dom_equal( - "Hello", - link_to("Hello", "http://www.example.com", { :popup => ['window_name', 'width=300,height=300'], :confirm => "Are you serious?" }) - ) - end - def test_link_tag_using_post_javascript assert_dom_equal( "Hello", @@ -245,10 +219,6 @@ class UrlHelperTest < ActionView::TestCase ) end - def test_link_tag_using_post_javascript_and_popup - assert_raise(ActionView::ActionViewError) { link_to("Hello", "http://www.example.com", :popup => true, :method => :post, :confirm => "Are you serious?") } - end - def test_link_tag_using_block_in_erb __in_erb_template = '' -- cgit v1.2.3 From 0e063f435ce31a091d1097156172d551bd9d9d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 30 Jan 2010 16:35:22 +0100 Subject: Fix some backward incompatible behavior on AM. --- actionpack/lib/abstract_controller.rb | 1 + actionpack/lib/abstract_controller/compatibility.rb | 18 ++++++++++++++++++ actionpack/lib/abstract_controller/helpers.rb | 12 +++++++++++- .../lib/action_controller/metal/compatibility.rb | 15 ++------------- actionpack/lib/action_controller/metal/helpers.rb | 15 --------------- 5 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 actionpack/lib/abstract_controller/compatibility.rb (limited to 'actionpack') diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 74ca18b8b4..1e15ab090c 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -12,6 +12,7 @@ module AbstractController autoload :Base autoload :Callbacks autoload :Collector + autoload :Compatibility autoload :Helpers autoload :Layouts autoload :LocalizedCache diff --git a/actionpack/lib/abstract_controller/compatibility.rb b/actionpack/lib/abstract_controller/compatibility.rb new file mode 100644 index 0000000000..7fb93a0eb5 --- /dev/null +++ b/actionpack/lib/abstract_controller/compatibility.rb @@ -0,0 +1,18 @@ +module AbstractController + module Compatibility + extend ActiveSupport::Concern + + def _find_layout(name, details) + details[:prefix] = nil if name =~ /\blayouts/ + super + end + + # Move this into a "don't run in production" module + def _default_layout(details, require_layout = false) + super + rescue ActionView::MissingTemplate + _find_layout(_layout({}), {}) + nil + end + end +end diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb index 1d898d1a4c..eb621c0865 100644 --- a/actionpack/lib/abstract_controller/helpers.rb +++ b/actionpack/lib/abstract_controller/helpers.rb @@ -25,7 +25,7 @@ module AbstractController def inherited(klass) helpers = _helpers klass._helpers = Module.new { include helpers } - + klass.class_eval { default_helper_module! unless name.blank? } super end @@ -146,6 +146,16 @@ module AbstractController end end end + + def default_helper_module! + module_name = name.sub(/Controller$/, '') + module_path = module_name.underscore + helper module_path + rescue MissingSourceFile => e + raise e unless e.is_missing? "helpers/#{module_path}_helper" + rescue NameError => e + raise e unless e.missing_name? "#{module_name}Helper" + end end end end diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index 0e869e4e87..2be0fa097e 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -2,6 +2,8 @@ module ActionController module Compatibility extend ActiveSupport::Concern + include AbstractController::Compatibility + class ::ActionController::ActionControllerError < StandardError #:nodoc: end @@ -103,19 +105,6 @@ module ActionController super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _find_layout(name, details) - details[:prefix] = nil if name =~ /\blayouts/ - super - end - - # Move this into a "don't run in production" module - def _default_layout(details, require_layout = false) - super - rescue ActionView::MissingTemplate - _find_layout(_layout({}), {}) - nil - end - def performed? response_body end diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index 03ba4b3f83..ab2e0c020e 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -63,11 +63,6 @@ module ActionController self.helpers_path = Array(value) end - def inherited(klass) - klass.class_eval { default_helper_module! unless name.blank? } - super - 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: @@ -101,16 +96,6 @@ module ActionController super(args) end - def default_helper_module! - module_name = name.sub(/Controller$/, '') - module_path = module_name.underscore - helper module_path - rescue MissingSourceFile => e - raise e unless e.is_missing? "helpers/#{module_path}_helper" - rescue NameError => e - raise e unless e.missing_name? "#{module_name}Helper" - end - # Extract helper names from files in app/helpers/**/*_helper.rb def all_application_helpers helpers = [] -- cgit v1.2.3 From c164ca1efbb3b097d5a3f3db56c4988858607011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 30 Jan 2010 16:49:58 +0100 Subject: Bring helpers_dir deprecation back. --- actionpack/lib/action_controller/metal/helpers.rb | 2 ++ actionpack/test/controller/helper_test.rb | 21 ++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index ab2e0c020e..05843a061b 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -56,10 +56,12 @@ module ActionController module ClassMethods def helpers_dir + ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead" self.helpers_path end def helpers_dir=(value) + ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead" self.helpers_path = Array(value) end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index e53e62d1ff..75a96d6497 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -135,17 +135,16 @@ class HelperTest < ActiveSupport::TestCase assert methods.include?('foobar') end - # TODO Add this deprecation back before Rails 3.0 final release - # def test_deprecation - # assert_deprecated do - # ActionController::Base.helpers_dir = "some/foo/bar" - # end - # assert_deprecated do - # assert_equal ["some/foo/bar"], ActionController::Base.helpers_dir - # end - # ensure - # ActionController::Base.helpers_path = [File.dirname(__FILE__) + '/../fixtures/helpers'] - # end + def test_deprecation + assert_deprecated do + ActionController::Base.helpers_dir = "some/foo/bar" + end + assert_deprecated do + assert_equal ["some/foo/bar"], ActionController::Base.helpers_dir + end + ensure + ActionController::Base.helpers_path = [File.dirname(__FILE__) + '/../fixtures/helpers'] + end private def expected_helper_methods -- cgit v1.2.3 From 1bd8a50a995a047c8546e8901582b9d1e16af2df Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 12:26:26 -0600 Subject: Deprecation notice for TestResponse#redirected_to --- actionpack/lib/action_dispatch/testing/test_response.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/testing/test_response.rb b/actionpack/lib/action_dispatch/testing/test_response.rb index 6d019023ce..9a51a32899 100644 --- a/actionpack/lib/action_dispatch/testing/test_response.rb +++ b/actionpack/lib/action_dispatch/testing/test_response.rb @@ -36,6 +36,11 @@ module ActionDispatch @template.layout end + def redirected_to + ::ActiveSupport::Deprecation.warn("response.redirected_to is deprecated. Use response.redirect_url instead", caller) + redirect_url + end + def redirect_url_match?(pattern) ::ActiveSupport::Deprecation.warn("response.redirect_url_match? is deprecated. Use assert_match(/foo/, response.redirect_url) instead", caller) return false if redirect_url.nil? -- cgit v1.2.3 From ffc10731c366aa68a0cdd15eb4e9ac9a3c0820f0 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 14:22:30 -0600 Subject: Revert "fixed tests to also look for rails.js when using javascript_include_tag :defaults" This reverts commit 7f181e475080924164b3fa0071ee4898bd66cbb8. --- actionpack/test/template/asset_tag_helper_test.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'actionpack') diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 5fe2c4df24..57802ebf42 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -86,11 +86,11 @@ class AssetTagHelperTest < ActionView::TestCase %(javascript_include_tag("bank.js")) => %(), %(javascript_include_tag("bank", :lang => "vbscript")) => %(), %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(\n), - %(javascript_include_tag(:defaults)) => %(\n\n\n\n\n), + %(javascript_include_tag(:defaults)) => %(\n\n\n\n), %(javascript_include_tag(:all)) => %(\n\n\n\n\n\n\n), %(javascript_include_tag(:all, :recursive => true)) => %(\n\n\n\n\n\n\n\n), - %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n\n), - %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n\n), + %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n), + %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n), %(javascript_include_tag("http://example.com/all")) => %(), %(javascript_include_tag("http://example.com/all.js")) => %(), @@ -235,7 +235,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_javascript_include_tag_with_given_asset_id ENV["RAILS_ASSET_ID"] = "1" - assert_dom_equal(%(\n\n\n\n\n), javascript_include_tag(:defaults)) + assert_dom_equal(%(\n\n\n\n), javascript_include_tag(:defaults)) end def test_javascript_include_tag_is_html_safe @@ -246,14 +246,14 @@ class AssetTagHelperTest < ActionView::TestCase def test_register_javascript_include_default ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' - assert_dom_equal %(\n\n\n\n\n\n), javascript_include_tag(:defaults) + assert_dom_equal %(\n\n\n\n\n), javascript_include_tag(:defaults) end def test_register_javascript_include_default_mixed_defaults ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'robber', '/elsewhere/cools.js' - assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag(:defaults) + assert_dom_equal %(\n\n\n\n\n\n\n), javascript_include_tag(:defaults) end def test_custom_javascript_expansions @@ -265,7 +265,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"] - assert_dom_equal %(\n\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') + assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') end def test_custom_javascript_expansions_with_undefined_symbol -- cgit v1.2.3 From ac20f2cea15844b5cc06bdfefbb410fc55ba5caf Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 14:22:39 -0600 Subject: Revert "Add rails.js driver to default source list" This reverts commit 8a46e1182e3fce36aee5e6cb65737c4e755bbced. --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 4df99f8293..83357dd76f 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -140,7 +140,7 @@ module ActionView :stylesheets_dir => "#{assets_dir}/stylesheets", } - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) + JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or -- cgit v1.2.3 From 4f7a85d2c66f100f83e2fe264e1f03bf079f13dc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 14:39:41 -0600 Subject: Revert "Merge branch 'rails/master' into ujs" This reverts commit 3aa1ea1ae4baa4a03d03644e798eeb98a4745785, reversing changes made to 2c12a71378d2146c822acb389b00b866f6420ff5. Conflicts: actionpack/lib/action_view/helpers/javascript_helper.rb actionpack/lib/action_view/helpers/url_helper.rb actionpack/test/template/url_helper_test.rb --- actionpack/lib/action_view/helpers.rb | 8 +- actionpack/lib/action_view/helpers/ajax_helper.rb | 735 ++------------------- actionpack/lib/action_view/helpers/form_helper.rb | 36 +- .../lib/action_view/helpers/form_tag_helper.rb | 20 +- .../lib/action_view/helpers/javascript_helper.rb | 63 +- .../lib/action_view/helpers/prototype_helper.rb | 449 ++++++++++++- actionpack/lib/action_view/helpers/url_helper.rb | 45 ++ actionpack/test/template/ajax_helper_test.rb | 452 ------------- actionpack/test/template/ajax_test.rb | 114 ++++ actionpack/test/template/form_helper_test.rb | 8 +- actionpack/test/template/form_tag_helper_test.rb | 12 +- actionpack/test/template/prototype_helper_test.rb | 193 ++++++ actionpack/test/template/url_helper_test.rb | 18 +- 13 files changed, 912 insertions(+), 1241 deletions(-) delete mode 100644 actionpack/test/template/ajax_helper_test.rb create mode 100644 actionpack/test/template/ajax_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 080eb87445..3d088678fc 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -3,7 +3,7 @@ require 'active_support/benchmarkable' module ActionView #:nodoc: module Helpers #:nodoc: autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper' - autoload :AjaxHelperCompat, 'action_view/helpers/ajax_helper' + autoload :AjaxHelper, 'action_view/helpers/ajax_helper' autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper' autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper' autoload :CacheHelper, 'action_view/helpers/cache_helper' @@ -15,11 +15,12 @@ module ActionView #:nodoc: autoload :FormTagHelper, 'action_view/helpers/form_tag_helper' autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper' autoload :NumberHelper, 'action_view/helpers/number_helper' - autoload :AjaxHelper, 'action_view/helpers/ajax_helper' + autoload :PrototypeHelper, 'action_view/helpers/prototype_helper' autoload :RawOutputHelper, 'action_view/helpers/raw_output_helper' autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper' autoload :RecordTagHelper, 'action_view/helpers/record_tag_helper' autoload :SanitizeHelper, 'action_view/helpers/sanitize_helper' + autoload :ScriptaculousHelper, 'action_view/helpers/scriptaculous_helper' autoload :TagHelper, 'action_view/helpers/tag_helper' autoload :TextHelper, 'action_view/helpers/text_helper' autoload :TranslationHelper, 'action_view/helpers/translation_helper' @@ -47,11 +48,12 @@ module ActionView #:nodoc: include FormTagHelper include JavaScriptHelper include NumberHelper - include AjaxHelperCompat + include PrototypeHelper include RawOutputHelper include RecordIdentificationHelper include RecordTagHelper include SanitizeHelper + include ScriptaculousHelper include TagHelper include TextHelper include TranslationHelper diff --git a/actionpack/lib/action_view/helpers/ajax_helper.rb b/actionpack/lib/action_view/helpers/ajax_helper.rb index 169a803848..9cc2acc239 100644 --- a/actionpack/lib/action_view/helpers/ajax_helper.rb +++ b/actionpack/lib/action_view/helpers/ajax_helper.rb @@ -1,713 +1,68 @@ module ActionView module Helpers module AjaxHelper - # Included for backwards compatibility / RJS functionality - # Rails classes should not be aware of individual JS frameworks - include PrototypeHelper - - # Returns a form that will allow the unobtrusive JavaScript drivers to submit the - # form dynamically. The default driver behaviour is an XMLHttpRequest in the background - # instead of the regular POST arrangement. Even though it's using JavaScript to serialize - # the form elements, the form submission will work just like a regular submission as - # viewed by the receiving side (all elements available in params). The options - # for specifying the target with :url anddefining callbacks is the same as +link_to_remote+. - # - # === Resource - # - # Example: - # - # # Generates: - # #
...
- # # - # <% remote_form_for(@record, {:html => { :id => 'create-author' }}) do |f| %> - # ... - # <% end %> - # - # This will expand to be the same as: - # - # <% remote_form_for :post, @post, :url => post_path(@post), - # :html => { :method => :put, - # :class => "edit_post", - # :id => "edit_post_45" } do |f| %> - # ... - # <% end %> - # - # === Nested Resource - # - # Example: - # # Generates: - # #
- # # - # <% remote_form_for([@author, @article]) do |f| %> - # ... - # <% end %> - # - # This will expand to be the same as: - # - # <% remote_form_for :article, @article, :url => author_article_path(@author, @article), - # :html => { :method => :put, - # :class => "new_article", - # :id => "new_comment" } do |f| %> - # ... - # <% end %> - # - # If you don't need to attach a form to a resource, then check out form_remote_tag. - # - # See FormHelper#form_for for additional semantics. - def remote_form_for(record_or_name_or_array, *args, &proc) - options = args.extract_options! - - if confirm = options.delete(:confirm) - add_confirm_to_attributes!(options, confirm) - end - - object_name = extract_object_name_for_form!(args, options, record_or_name_or_array) - - concat(form_remote_tag(options)) - fields_for(object_name, *(args << options), &proc) - concat(''.html_safe!) - end - alias_method :form_remote_for, :remote_form_for - - # Returns a form tag that will allow the unobtrusive JavaScript drivers to submit the - # form dynamically. The default JavaScript driver behaviour is an XMLHttpRequest - # in the background instead of the regular POST arrangement. Even though it's using - # JavaScript to serialize the form elements, the form submission will work just like - # a regular submission as viewed by the receiving side (all elements available in - # params). The options for specifying the target with :url and - # defining callbacks is the same as +link_to_remote+. - # - # A "fall-through" target for browsers that doesn't do JavaScript can be - # specified with the :action/:method options on :html. - # - # Example: - # - # # Generates: - # #
- # # - # form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) {} - # - # The Hash passed to the :html key is equivalent to the options (2nd) - # argument in the FormTagHelper.form_tag method. - # - # By default the fall-through action is the same as the one specified in - # the :url (and the default method is :post). - # - # form_remote_tag also takes a block, like form_tag: - # # Generates: - # #
- # # - # #
- # # - # <% form_remote_tag :url => '/posts' do -%> - # <%= submit_tag 'Save' %> - # <% end -%> - # - # # Generates: - # #
Hello world!
- # # - # <% form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) do -%> - # "Hello world!" - # <% end -%> - # - def form_remote_tag(options = {}, &block) - html_options = options.delete(:callbacks) - - attributes = {} - attributes.merge!(extract_remote_attributes!(options)) - attributes.merge!(html_options) if html_options - attributes.merge!(options) - attributes.delete(:builder) - - form_tag(attributes.delete(:action) || attributes.delete("data-url"), attributes, &block) - end - - # Returns a link that will allow unobtrusive JavaScript to dynamical adjust its - # behaviour. The default behaviour is an XMLHttpRequest in the background instead - # of the regular GET arrangement. The result of that request can then be inserted - # into a DOM object whose id can be specified with options[:update]. Usually, - # the result would be a partial prepared by the controller with render :partial. - # - # Examples: - # - # # Generates: - # # Remove Author - # # - # link_to_remote("Remove Author", { :url => { :action => "whatnot" }, - # :method => "delete"}) - # - # - # You can override the generated HTML options by specifying a hash in - # options[:html]. - # - # # Generates: - # # Remove Author - # # - # link_to_remote("Remove Author", { :url => { :action => "whatnot" }, - # :method => "delete", - # :html => { :class => "fine" }}) - # - # - # You can also specify a hash for options[:update] to allow for - # easy redirection of output to an other DOM element if a server-side - # error occurs: - # - # Example: - # # Generates: - # # - # # Delete this Post' - # # - # link_to_remote "Delete this post", - # :url => { :action => "destroy"}, - # :update => { :success => "posts", :failure => "error" } - # - # Optionally, you can use the options[:position] parameter to - # influence how the target DOM element is updated. It must be one of - # :before, :top, :bottom, or :after. - # - # Example: - # # Generates: - # # Remove Author - # # - # link_to_remote("Remove Author", :url => { :action => "whatnot" }, :position => :bottom) - # - # - # The method used is by default POST. You can also specify GET or you - # can simulate PUT or DELETE over POST. All specified with options[:method] - # - # Example: - # # Generates: - # # Destroy - # # - # link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete - # - # By default, these remote requests are processed asynchronous during - # which various JavaScript callbacks can be triggered (for progress - # indicators and the likes). All callbacks get access to the - # request object, which holds the underlying XMLHttpRequest. - # - # To access the server response, use request.responseText, to - # find out the HTTP status, use request.status. - # - # Example: - # # Generates: - # # - # # undo - # # - # link_to_remote "undo", - # :url => { :controller => "words", :action => "undo", :n => word_counter }, - # :complete => "undoRequestCompleted(request)" - # - # The callbacks that may be specified are (in order): - # - # :loading:: Called when the remote document is being - # loaded with data by the browser. - # :loaded:: Called when the browser has finished loading - # the remote document. - # :interactive:: Called when the user can interact with the - # remote document, even though it has not - # finished loading. - # :success:: Called when the XMLHttpRequest is completed, - # and the HTTP status code is in the 2XX range. - # :failure:: Called when the XMLHttpRequest is completed, - # and the HTTP status code is not in the 2XX - # range. - # :complete:: Called when the XMLHttpRequest is complete - # (fires after success/failure if they are - # present). - # - # You can further refine :success and :failure by - # adding additional callbacks for specific status codes. - # - # Example: - # - # # Generates: - # # Hello - # # - # link_to_remote word, - # :url => { :action => "action" }, - # 404 => "alert('Not found...? Wrong URL...?')", - # :failure => "alert('HTTP Error ' + request.status + '!')" - # - # A status code callback overrides the success/failure handlers if - # present. - # - # If you for some reason or another need synchronous processing (that'll - # block the browser while the request is happening), you can specify - # options[:type] = :synchronous. - # - # You can customize further browser side call logic by passing in - # JavaScript code snippets via some optional parameters. In their order - # of use these are: - # - # :confirm:: Adds confirmation dialog. - # :condition:: Perform remote request conditionally - # by this expression. Use this to - # describe browser-side conditions when - # request should not be initiated. - # :before:: Called before request is initiated. - # :after:: Called immediately after request was - # initiated and before :loading. - # :submit:: Specifies the DOM element ID that's used - # as the parent of the form elements. By - # default this is the current form, but - # it could just as well be the ID of a - # table row or any other DOM element. - # :with:: A JavaScript expression specifying - # the parameters for the XMLHttpRequest. - # Any expressions should return a valid - # URL query string. - # - # Example: - # - # :with => "'name=' + $('name').value" - # - # You can generate a link that uses the UJS drivers in the general case, while - # degrading gracefully to plain link behavior in the absence of - # JavaScript by setting html_options[:href] to an alternate URL. - # Note the extra curly braces around the options hash separate - # it as the second parameter from html_options, the third. - # - # Example: - # - # # Generates: - # # Delete this post - # # - # link_to_remote "Delete this post", - # { :update => "posts", :url => { :action => "destroy", :id => post.id } } - # - def link_to_remote(name, options, html_options = {}) - attributes = {} - - attributes.merge!(:rel => "nofollow") if options[:method] && options[:method].to_s.downcase == "delete" - attributes.merge!(extract_remote_attributes!(options)) + include UrlHelper + + def link_to_remote(name, url, options = {}) + html = options.delete(:html) || {} - if confirm = options.delete(:confirm) - add_confirm_to_attributes!(attributes, confirm) - end - - attributes.merge!(html_options) - href = html_options[:href].nil? ? "#" : html_options[:href] - attributes.merge!(:href => href) - - content_tag(:a, name, attributes) - end - - # Returns an input of type button, which allows the unobtrusive JavaScript driver - # to dynamically adjust its behaviour. The default driver behaviour is to call a - # remote action via XMLHttpRequest in the background. - # The options for specifying the target with :url and defining callbacks is the same - # as link_to_remote. - # - # Example: - # - # # Generates: - # # - # # - # button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" }) - # - def button_to_remote(name, options = {}, html_options = {}) - attributes = html_options.merge!(:type => "button", :value => name) - - if confirm = options.delete(:confirm) - add_confirm_to_attributes!(attributes, confirm) - end - - if disable_with = options.delete(:disable_with) - add_disable_with_to_attributes!(attributes, disable_with) - end - - attributes.merge!(extract_remote_attributes!(options)) - - tag(:input, attributes) - end - - # Returns an input tag of type button, with the element name of +name+ and a value (i.e., display text) - # of +value+ which will allow the unobtrusive JavaScript driver to dynamically adjust its behaviour - # The default behaviour is to call a remote action via XMLHttpRequest in the background. - # - # request that reloads the page. - # - # # Create a button that submits to the create action - # # - # # Generates: - # # - # # - # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %> - # - # # Submit to the remote action update and update the DIV succeed or fail based - # # on the success or failure of the request - # # - # # Generates: - # # - # # - # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, - # :update => { :success => "succeed", :failure => "fail" } - # - # options argument is the same as in form_remote_tag. - def submit_to_remote(name, value, options = {}) - html_options = options.delete(:html) || {} - html_options.merge!(:name => name, :value => value, :type => "button") - - attributes = extract_remote_attributes!(options) - attributes.merge!(html_options) - attributes["data-remote-submit"] = true - attributes.delete("data-remote") - - tag(:input, attributes) - end - - # Periodically provides the UJS driver with the information to call the specified - # url (options[:url]) every options[:frequency] seconds (default is 10). Usually used to - # update a specified div (options[:update]) with the results - # of the remote call. The options for specifying the target with :url - # and defining callbacks is the same as link_to_remote. - # Examples: - # # Call get_averages and put its results in 'avg' every 10 seconds - # # Generates: - # # - # # - # periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg') - # - # # Call invoice every 10 seconds with the id of the customer - # # If it succeeds, update the invoice DIV; if it fails, update the error DIV - # # Generates: - # # " - # # - # periodically_call_remote(:url => { :action => 'invoice', :id => 1 }, - # :update => { :success => "invoice", :failure => "error" } - # - # # Call update every 20 seconds and update the new_block DIV - # # Generates: - # # - # # - # periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block') - # - def periodically_call_remote(options = {}) - attributes = extract_observer_attributes!(options) - attributes["data-periodical"] = true - attributes["data-frequency"] ||= 10 - - # periodically_call_remote does not need data-observe=true - attributes.delete('data-observe') - - script_decorator(attributes).html_safe! - end - - # Observes the field with the DOM ID specified by +field_id+ and calls a - # callback when its contents have changed. The default callback is an - # Ajax call. By default the value of the observed field is sent as a - # parameter with the Ajax call. - # - # Example: - # # Generates: - # # "" - # # - # <%= observe_field :suggest, :url => { :action => :find_suggestion }, - # :frequency => 0.25, - # :update => :suggest, - # :with => 'q' - # %> - # - # Required +options+ are either of: - # :url:: +url_for+-style options for the action to call - # when the field has changed. - # :function:: Instead of making a remote call to a URL, you - # can specify javascript code to be called instead. - # Note that the value of this option is used as the - # *body* of the javascript function, a function definition - # with parameters named element and value will be generated for you - # for example: - # observe_field("glass", :frequency => 1, :function => "alert('Element changed')") - # will generate: - # new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')}) - # The element parameter is the DOM element being observed, and the value is its value at the - # time the observer is triggered. - # - # Additional options are: - # :frequency:: The frequency (in seconds) at which changes to - # this field will be detected. Not setting this - # option at all or to a value equal to or less than - # zero will use event based observation instead of - # time based observation. - # :update:: Specifies the DOM ID of the element whose - # innerHTML should be updated with the - # XMLHttpRequest response text. - # :with:: A JavaScript expression specifying the parameters - # for the XMLHttpRequest. The default is to send the - # key and value of the observed field. Any custom - # expressions should return a valid URL query string. - # The value of the field is stored in the JavaScript - # variable +value+. - # - # Examples - # - # :with => "'my_custom_key=' + value" - # :with => "'person[name]=' + prompt('New name')" - # :with => "Form.Element.serialize('other-field')" - # - # Finally - # :with => 'name' - # is shorthand for - # :with => "'name=' + value" - # This essentially just changes the key of the parameter. - # - # Additionally, you may specify any of the options documented in the - # Common options section at the top of this document. - # - # Example: - # - # # Sends params: {:title => 'Title of the book'} when the book_title input - # # field is changed. - # observe_field 'book_title', - # :url => 'http://example.com/books/edit/1', - # :with => 'title' - # - # - def observe_field(name, options = {}) - html_options = options.delete(:callbacks) - - options[:observed] = name - attributes = extract_observer_attributes!(options) - attributes.merge!(html_options) if html_options - - script_decorator(attributes).html_safe! - end - - # Observes the form with the DOM ID specified by +form_id+ and calls a - # callback when its contents have changed. The default callback is an - # Ajax call. By default all fields of the observed field are sent as - # parameters with the Ajax call. - # - # The +options+ for +observe_form+ are the same as the options for - # +observe_field+. The JavaScript variable +value+ available to the - # :with option is set to the serialized form by default. - def observe_form(name, options = {}) - html_options = options.delete(:callbacks) - - options[:observed] = name - attributes = extract_observer_attributes!(options) - attributes.merge!(html_options) if html_options - - script_decorator(attributes).html_safe! - end - - def script_decorator(options) - attributes = %w(type="application/json") - attributes += options.map{|k, v| k + '="' + v.to_s + '"'} - "" - end - - private - - def extract_remote_attributes!(options) - attributes = options.delete(:html) || {} - - attributes.merge!(extract_update_attributes!(options)) - attributes.merge!(extract_request_attributes!(options)) - attributes["data-remote"] = true - - if submit = options.delete(:submit) - attributes["data-submit"] = submit - end - - attributes - end - - def extract_request_attributes!(options) - attributes = {} - if method = options.delete(:method) - attributes["data-method"] = method.to_s - end - - if type = options.delete(:type) - attributes["data-remote-type"] = type.to_s - end - - url_options = options.delete(:url) - url_options = url_options.merge(:escape => false) if url_options.is_a?(Hash) - attributes["data-url"] = escape_javascript(url_for(url_options)) if url_options - - purge_unused_attributes!(attributes) + update = options.delete(:update) + if update.is_a?(Hash) + html["data-update-success"] = update[:success] + html["data-update-failure"] = update[:failure] + else + html["data-update-success"] = update end - def extract_update_attributes!(options) - attributes = {} - update = options.delete(:update) - if update.is_a?(Hash) - attributes["data-update-success"] = update[:success] - attributes["data-update-failure"] = update[:failure] - else - attributes["data-update-success"] = update - end - - if position = options.delete(:position) - attributes["data-update-position"] = position.to_s - end - - purge_unused_attributes!(attributes) - end - - def extract_observer_attributes!(options) - callback = options.delete(:function) - frequency = options.delete(:frequency) || 10 - - - attributes = extract_remote_attributes!(options) - attributes["data-observe"] = true - attributes["data-observed"] = options.delete(:observed) - attributes["data-onobserve"] = callback if callback - attributes["data-frequency"] = frequency if frequency && frequency.to_f != 0 - attributes.delete("data-remote") - - purge_unused_attributes!(attributes) - end - - def purge_unused_attributes!(attributes) - attributes.delete_if {|key, value| value.nil? } - attributes - end - end - - # TODO: All evaled goes here per wycat - module AjaxHelperCompat - include AjaxHelper - - def link_to_remote(name, options, html_options = {}) - set_callbacks(options, html_options) - set_with_and_condition_attributes(options, html_options) - super + html["data-update-position"] = options.delete(:position) + html["data-method"] = options.delete(:method) + html["data-remote"] = "true" + + html.merge!(options) + + url = url_for(url) if url.is_a?(Hash) + link_to(name, url, html) end def button_to_remote(name, options = {}, html_options = {}) - set_callbacks(options, html_options) - set_with_and_condition_attributes(options, html_options) - super - end - - def form_remote_tag(options, &block) - html = {} - set_callbacks(options, html) - set_with_and_condition_attributes(options, html) - options.merge!(:callbacks => html) - super - end - - def observe_field(name, options = {}) - html = {} - set_with_and_condition_attributes(options, html) - options.merge!(:callbacks => html) - super + url = options.delete(:url) + url = url_for(url) if url.is_a?(Hash) + + html_options.merge!(:type => "button", :value => name, + :"data-url" => url) + + tag(:input, html_options) end - def observe_form(name, options = {}) - html = {} - set_with_and_condition_attributes(options, html) - options.merge!(:callbacks => html) - super - end - - private + module Rails2Compatibility def set_callbacks(options, html) - [:before, :after, :uninitialized, :complete, :failure, :success, :interactive, :loaded, :loading].each do |type| - html["data-on#{type}"] = options.delete(type.to_sym) + [:complete, :failure, :success, :interactive, :loaded, :loading].each do |type| + html["data-#{type}-code"] = options.delete(type.to_sym) end options.each do |option, value| if option.is_a?(Integer) - html["data-on#{option}"] = options.delete(option) + html["data-#{option}-code"] = options.delete(option) end end end - - def set_with_and_condition_attributes(options, html) - if with = options.delete(:with) - html["data-with"] = with - end - - if condition = options.delete(:condition) - html["data-condition"] = condition + + def link_to_remote(name, url, options = nil) + if !options && url.is_a?(Hash) && url.key?(:url) + url, options = url.delete(:url), url end + + set_callbacks(options, options[:html] ||= {}) + + super + end + + def button_to_remote(name, options = {}, html_options = {}) + set_callbacks(options, html_options) + super end + end + end end -end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 1615f135b4..20e9916d62 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -262,8 +262,23 @@ module ActionView # FormTagHelper#form_tag. def form_for(record_or_name_or_array, *args, &proc) raise ArgumentError, "Missing block" unless block_given? + options = args.extract_options! - object_name = extract_object_name_for_form!(args, options, record_or_name_or_array) + + case record_or_name_or_array + when String, Symbol + object_name = record_or_name_or_array + when Array + object = record_or_name_or_array.last + object_name = ActionController::RecordIdentifier.singular_class_name(object) + apply_form_for_options!(record_or_name_or_array, options) + args.unshift object + else + object = record_or_name_or_array + object_name = ActionController::RecordIdentifier.singular_class_name(object) + apply_form_for_options!([object], options) + args.unshift object + end concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {})) fields_for(object_name, *(args << options), &proc) @@ -727,25 +742,6 @@ module ActionView def radio_button(object_name, method, tag_value, options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options) end - - private - def extract_object_name_for_form!(args, options, record_or_name_or_array) - case record_or_name_or_array - when String, Symbol - object_name = record_or_name_or_array - when Array - object = record_or_name_or_array.last - object_name = ActionController::RecordIdentifier.singular_class_name(object) - apply_form_for_options!(record_or_name_or_array, options) - args.unshift object - else - object = record_or_name_or_array - object_name = ActionController::RecordIdentifier.singular_class_name(object) - apply_form_for_options!([object], options) - args.unshift object - end - object_name - end end module InstanceTagMethods #:nodoc: diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index ebce5c1513..048bedc7ba 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -352,24 +352,33 @@ module ActionView # # => # # submit_tag "Complete sale", :disable_with => "Please wait..." - # # => # # submit_tag nil, :class => "form_submit" # # => # # submit_tag "Edit", :disable_with => "Editing...", :class => "edit-button" - # # => def submit_tag(value = "Save changes", options = {}) options.stringify_keys! if disable_with = options.delete("disable_with") - add_disable_with_to_attributes!(options, disable_with) + disable_with = "this.value='#{disable_with}'" + disable_with << ";#{options.delete('onclick')}" if options['onclick'] + + options["onclick"] = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }" + options["onclick"] << "else { hiddenCommit = document.createElement('input');hiddenCommit.type = 'hidden';" + options["onclick"] << "hiddenCommit.value = this.value;hiddenCommit.name = this.name;this.form.appendChild(hiddenCommit); }" + options["onclick"] << "this.setAttribute('originalValue', this.value);this.disabled = true;#{disable_with};" + options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());" + options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;" end if confirm = options.delete("confirm") - add_confirm_to_attributes!(options, confirm) + options["onclick"] ||= 'return true;' + options["onclick"] = "if (!#{confirm_javascript_function(confirm)}) return false; #{options['onclick']}" end tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys) @@ -402,7 +411,8 @@ module ActionView options.stringify_keys! if confirm = options.delete("confirm") - add_confirm_to_attributes!(options, confirm) + options["onclick"] ||= '' + options["onclick"] += "return #{confirm_javascript_function(confirm)};" end tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys) diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 02d0a88189..06a9d3405a 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -39,7 +39,7 @@ module ActionView JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts') end - include AjaxHelperCompat + include PrototypeHelper # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the # onclick handler and return false after the fact. @@ -187,57 +187,22 @@ module ActionView "\n//#{cdata_section("\n#{content}\n//")}\n" end - protected - def convert_options_to_javascript!(html_options, url = '') - confirm = html_options.delete("confirm") - - if html_options.key?("popup") - ActiveSupport::Deprecation.warn(":popup has been deprecated", caller) - end - - method, href = html_options.delete("method"), html_options['href'] - - if confirm && method - add_confirm_to_attributes!(html_options, confirm) - add_method_to_attributes!(html_options, method, url) - elsif confirm - add_confirm_to_attributes!(html_options, confirm) - elsif method - add_method_to_attributes!(html_options, method, url) - end - end - - def add_confirm_to_attributes!(html_options, confirm) - html_options["data-confirm"] = confirm if confirm - end - - def add_method_to_attributes!(html_options, method, url = nil) - html_options["rel"] = "nofollow" if method.to_s.downcase == "delete" - html_options["data-method"] = method - if url.size > 0 - html_options["data-url"] = url - end - end - - def add_disable_with_to_attributes!(html_options, disable_with) - html_options["data-disable-with"] = disable_with if disable_with - end - - def options_for_javascript(options) - if options.empty? - '{}' - else - "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" + protected + def options_for_javascript(options) + if options.empty? + '{}' + else + "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" + end end - end - def array_or_string_for_javascript(option) - if option.kind_of?(Array) - "['#{option.join('\',\'')}']" - elsif !option.nil? - "'#{option}'" + def array_or_string_for_javascript(option) + if option.kind_of?(Array) + "['#{option.join('\',\'')}']" + elsif !option.nil? + "'#{option}'" + end end - end end end end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index d861810f19..bef93dd0f8 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -1,7 +1,6 @@ require 'set' require 'active_support/json' require 'active_support/core_ext/object/returning' -require 'action_view/helpers/scriptaculous_helper' module ActionView module Helpers @@ -28,6 +27,40 @@ module ActionView # ActionView::Helpers::JavaScriptHelper for more information on including # this and other JavaScript files in your Rails templates.) # + # Now you're ready to call a remote action either through a link... + # + # link_to_remote "Add to cart", + # :url => { :action => "add", :id => product.id }, + # :update => { :success => "cart", :failure => "error" } + # + # ...through a form... + # + # <% form_remote_tag :url => '/shipping' do -%> + #
<%= submit_tag 'Recalculate Shipping' %>
+ # <% end -%> + # + # ...periodically... + # + # periodically_call_remote(:url => 'update', :frequency => '5', :update => 'ticker') + # + # ...or through an observer (i.e., a form or field that is observed and calls a remote + # action when changed). + # + # <%= observe_field(:searchbox, + # :url => { :action => :live_search }), + # :frequency => 0.5, + # :update => :hits, + # :with => 'query' + # %> + # + # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than + # are listed here); check out the documentation for each method to find out more about its usage and options. + # + # === Common Options + # See link_to_remote for documentation of options common to all Ajax + # helpers; any of the options specified by link_to_remote can be used + # by the other helpers. + # # == Designing your Rails actions for Ajax # When building your action handlers (that is, the Rails actions that receive your background requests), it's # important to remember a few things. First, whatever your action would normally return to the browser, it will @@ -74,8 +107,6 @@ module ActionView # See JavaScriptGenerator for information on updating multiple elements # on the page in an Ajax response. module PrototypeHelper - include ScriptaculousHelper - unless const_defined? :CALLBACKS CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded, :interactive, :complete, :failure, :success ] + @@ -85,6 +116,325 @@ module ActionView :form, :with, :update, :script, :type ]).merge(CALLBACKS) end + # Returns a link to a remote action defined by options[:url] + # (using the url_for format) that's called in the background using + # XMLHttpRequest. The result of that request can then be inserted into a + # DOM object whose id can be specified with options[:update]. + # Usually, the result would be a partial prepared by the controller with + # render :partial. + # + # Examples: + # # Generates: Delete this post + # link_to_remote "Delete this post", :update => "posts", + # :url => { :action => "destroy", :id => post.id } + # + # # Generates: Refresh + # link_to_remote(image_tag("refresh"), :update => "emails", + # :url => { :action => "list_emails" }) + # + # You can override the generated HTML options by specifying a hash in + # options[:html]. + # + # link_to_remote "Delete this post", :update => "posts", + # :url => post_url(@post), :method => :delete, + # :html => { :class => "destructive" } + # + # You can also specify a hash for options[:update] to allow for + # easy redirection of output to an other DOM element if a server-side + # error occurs: + # + # Example: + # # Generates: Delete this post + # link_to_remote "Delete this post", + # :url => { :action => "destroy", :id => post.id }, + # :update => { :success => "posts", :failure => "error" } + # + # Optionally, you can use the options[:position] parameter to + # influence how the target DOM element is updated. It must be one of + # :before, :top, :bottom, or :after. + # + # The method used is by default POST. You can also specify GET or you + # can simulate PUT or DELETE over POST. All specified with options[:method] + # + # Example: + # # Generates: Destroy + # link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete + # + # By default, these remote requests are processed asynchronous during + # which various JavaScript callbacks can be triggered (for progress + # indicators and the likes). All callbacks get access to the + # request object, which holds the underlying XMLHttpRequest. + # + # To access the server response, use request.responseText, to + # find out the HTTP status, use request.status. + # + # Example: + # # Generates: hello + # word = 'hello' + # link_to_remote word, + # :url => { :action => "undo", :n => word_counter }, + # :complete => "undoRequestCompleted(request)" + # + # The callbacks that may be specified are (in order): + # + # :loading:: Called when the remote document is being + # loaded with data by the browser. + # :loaded:: Called when the browser has finished loading + # the remote document. + # :interactive:: Called when the user can interact with the + # remote document, even though it has not + # finished loading. + # :success:: Called when the XMLHttpRequest is completed, + # and the HTTP status code is in the 2XX range. + # :failure:: Called when the XMLHttpRequest is completed, + # and the HTTP status code is not in the 2XX + # range. + # :complete:: Called when the XMLHttpRequest is complete + # (fires after success/failure if they are + # present). + # + # You can further refine :success and :failure by + # adding additional callbacks for specific status codes. + # + # Example: + # # Generates: hello + # link_to_remote word, + # :url => { :action => "action" }, + # 404 => "alert('Not found...? Wrong URL...?')", + # :failure => "alert('HTTP Error ' + request.status + '!')" + # + # A status code callback overrides the success/failure handlers if + # present. + # + # If you for some reason or another need synchronous processing (that'll + # block the browser while the request is happening), you can specify + # options[:type] = :synchronous. + # + # You can customize further browser side call logic by passing in + # JavaScript code snippets via some optional parameters. In their order + # of use these are: + # + # :confirm:: Adds confirmation dialog. + # :condition:: Perform remote request conditionally + # by this expression. Use this to + # describe browser-side conditions when + # request should not be initiated. + # :before:: Called before request is initiated. + # :after:: Called immediately after request was + # initiated and before :loading. + # :submit:: Specifies the DOM element ID that's used + # as the parent of the form elements. By + # default this is the current form, but + # it could just as well be the ID of a + # table row or any other DOM element. + # :with:: A JavaScript expression specifying + # the parameters for the XMLHttpRequest. + # Any expressions should return a valid + # URL query string. + # + # Example: + # + # :with => "'name=' + $('name').value" + # + # You can generate a link that uses AJAX in the general case, while + # degrading gracefully to plain link behavior in the absence of + # JavaScript by setting html_options[:href] to an alternate URL. + # Note the extra curly braces around the options hash separate + # it as the second parameter from html_options, the third. + # + # Example: + # link_to_remote "Delete this post", + # { :update => "posts", :url => { :action => "destroy", :id => post.id } }, + # :href => url_for(:action => "destroy", :id => post.id) + def link_to_remote(name, options = {}, html_options = nil) + link_to_function(name, remote_function(options), html_options || options.delete(:html)) + end + + # Creates a button with an onclick event which calls a remote action + # via XMLHttpRequest + # The options for specifying the target with :url + # and defining callbacks is the same as link_to_remote. + def button_to_remote(name, options = {}, html_options = {}) + button_to_function(name, remote_function(options), html_options) + end + + # Periodically calls the specified url (options[:url]) every + # options[:frequency] seconds (default is 10). Usually used to + # update a specified div (options[:update]) with the results + # of the remote call. The options for specifying the target with :url + # and defining callbacks is the same as link_to_remote. + # Examples: + # # Call get_averages and put its results in 'avg' every 10 seconds + # # Generates: + # # new PeriodicalExecuter(function() {new Ajax.Updater('avg', '/grades/get_averages', + # # {asynchronous:true, evalScripts:true})}, 10) + # periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg') + # + # # Call invoice every 10 seconds with the id of the customer + # # If it succeeds, update the invoice DIV; if it fails, update the error DIV + # # Generates: + # # new PeriodicalExecuter(function() {new Ajax.Updater({success:'invoice',failure:'error'}, + # # '/testing/invoice/16', {asynchronous:true, evalScripts:true})}, 10) + # periodically_call_remote(:url => { :action => 'invoice', :id => customer.id }, + # :update => { :success => "invoice", :failure => "error" } + # + # # Call update every 20 seconds and update the new_block DIV + # # Generates: + # # new PeriodicalExecuter(function() {new Ajax.Updater('news_block', 'update', {asynchronous:true, evalScripts:true})}, 20) + # periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block') + # + def periodically_call_remote(options = {}) + frequency = options[:frequency] || 10 # every ten seconds by default + code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})" + javascript_tag(code) + end + + # Returns a form tag that will submit using XMLHttpRequest in the + # background instead of the regular reloading POST arrangement. Even + # though it's using JavaScript to serialize the form elements, the form + # submission will work just like a regular submission as viewed by the + # receiving side (all elements available in params). The options for + # specifying the target with :url and defining callbacks is the same as + # +link_to_remote+. + # + # A "fall-through" target for browsers that doesn't do JavaScript can be + # specified with the :action/:method options on :html. + # + # Example: + # # Generates: + # #
+ # form_remote_tag :html => { :action => + # url_for(:controller => "some", :action => "place") } + # + # The Hash passed to the :html key is equivalent to the options (2nd) + # argument in the FormTagHelper.form_tag method. + # + # By default the fall-through action is the same as the one specified in + # the :url (and the default method is :post). + # + # form_remote_tag also takes a block, like form_tag: + # # Generates: + # #
+ # #
+ # <% form_remote_tag :url => '/posts' do -%> + #
<%= submit_tag 'Save' %>
+ # <% end -%> + def form_remote_tag(options = {}, &block) + options[:form] = true + + options[:html] ||= {} + options[:html][:onsubmit] = + (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") + + "#{remote_function(options)}; return false;" + + form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block) + end + + # Creates a form that will submit using XMLHttpRequest in the background + # instead of the regular reloading POST arrangement and a scope around a + # specific resource that is used as a base for questioning about + # values for the fields. + # + # === Resource + # + # Example: + # <% remote_form_for(@post) do |f| %> + # ... + # <% end %> + # + # This will expand to be the same as: + # + # <% remote_form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %> + # ... + # <% end %> + # + # === Nested Resource + # + # Example: + # <% remote_form_for([@post, @comment]) do |f| %> + # ... + # <% end %> + # + # This will expand to be the same as: + # + # <% remote_form_for :comment, @comment, :url => post_comment_path(@post, @comment), :html => { :method => :put, :class => "edit_comment", :id => "edit_comment_45" } do |f| %> + # ... + # <% end %> + # + # If you don't need to attach a form to a resource, then check out form_remote_tag. + # + # See FormHelper#form_for for additional semantics. + def remote_form_for(record_or_name_or_array, *args, &proc) + options = args.extract_options! + + case record_or_name_or_array + when String, Symbol + object_name = record_or_name_or_array + when Array + object = record_or_name_or_array.last + object_name = ActionController::RecordIdentifier.singular_class_name(object) + apply_form_for_options!(record_or_name_or_array, options) + args.unshift object + else + object = record_or_name_or_array + object_name = ActionController::RecordIdentifier.singular_class_name(record_or_name_or_array) + apply_form_for_options!(object, options) + args.unshift object + end + + concat(form_remote_tag(options)) + fields_for(object_name, *(args << options), &proc) + concat(''.html_safe!) + end + alias_method :form_remote_for, :remote_form_for + + # Returns a button input tag with the element name of +name+ and a value (i.e., display text) of +value+ + # that will submit form using XMLHttpRequest in the background instead of a regular POST request that + # reloads the page. + # + # # Create a button that submits to the create action + # # + # # Generates: + # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %> + # + # # Submit to the remote action update and update the DIV succeed or fail based + # # on the success or failure of the request + # # + # # Generates: + # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, + # :update => { :success => "succeed", :failure => "fail" } + # + # options argument is the same as in form_remote_tag. + def submit_to_remote(name, value, options = {}) + options[:with] ||= 'Form.serialize(this.form)' + + html_options = options.delete(:html) || {} + html_options[:name] = name + + button_to_remote(value, options, html_options) + end + + # Returns 'eval(request.responseText)' which is the JavaScript function + # that +form_remote_tag+ can call in :complete to evaluate a multiple + # update return document using +update_element_function+ calls. + def evaluate_remote_response + "eval(request.responseText)" + end + # Returns the JavaScript needed for a remote function. # Takes the same arguments as link_to_remote. # @@ -126,6 +476,99 @@ module ActionView return function end + # Observes the field with the DOM ID specified by +field_id+ and calls a + # callback when its contents have changed. The default callback is an + # Ajax call. By default the value of the observed field is sent as a + # parameter with the Ajax call. + # + # Example: + # # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest', + # # '/testing/find_suggestion', {asynchronous:true, evalScripts:true, parameters:'q=' + value})}) + # <%= observe_field :suggest, :url => { :action => :find_suggestion }, + # :frequency => 0.25, + # :update => :suggest, + # :with => 'q' + # %> + # + # Required +options+ are either of: + # :url:: +url_for+-style options for the action to call + # when the field has changed. + # :function:: Instead of making a remote call to a URL, you + # can specify javascript code to be called instead. + # Note that the value of this option is used as the + # *body* of the javascript function, a function definition + # with parameters named element and value will be generated for you + # for example: + # observe_field("glass", :frequency => 1, :function => "alert('Element changed')") + # will generate: + # new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')}) + # The element parameter is the DOM element being observed, and the value is its value at the + # time the observer is triggered. + # + # Additional options are: + # :frequency:: The frequency (in seconds) at which changes to + # this field will be detected. Not setting this + # option at all or to a value equal to or less than + # zero will use event based observation instead of + # time based observation. + # :update:: Specifies the DOM ID of the element whose + # innerHTML should be updated with the + # XMLHttpRequest response text. + # :with:: A JavaScript expression specifying the parameters + # for the XMLHttpRequest. The default is to send the + # key and value of the observed field. Any custom + # expressions should return a valid URL query string. + # The value of the field is stored in the JavaScript + # variable +value+. + # + # Examples + # + # :with => "'my_custom_key=' + value" + # :with => "'person[name]=' + prompt('New name')" + # :with => "Form.Element.serialize('other-field')" + # + # Finally + # :with => 'name' + # is shorthand for + # :with => "'name=' + value" + # This essentially just changes the key of the parameter. + # + # Additionally, you may specify any of the options documented in the + # Common options section at the top of this document. + # + # Example: + # + # # Sends params: {:title => 'Title of the book'} when the book_title input + # # field is changed. + # observe_field 'book_title', + # :url => 'http://example.com/books/edit/1', + # :with => 'title' + # + # + def observe_field(field_id, options = {}) + if options[:frequency] && options[:frequency] > 0 + build_observer('Form.Element.Observer', field_id, options) + else + build_observer('Form.Element.EventObserver', field_id, options) + end + end + + # Observes the form with the DOM ID specified by +form_id+ and calls a + # callback when its contents have changed. The default callback is an + # Ajax call. By default all fields of the observed field are sent as + # parameters with the Ajax call. + # + # The +options+ for +observe_form+ are the same as the options for + # +observe_field+. The JavaScript variable +value+ available to the + # :with option is set to the serialized form by default. + def observe_form(form_id, options = {}) + if options[:frequency] + build_observer('Form.Observer', form_id, options) + else + build_observer('Form.EventObserver', form_id, options) + end + end + # All the methods were moved to GeneratorMethods so that # #include_helpers_from_context has nothing to overwrite. class JavaScriptGenerator #:nodoc: diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 837dca6149..d6bfc6d4c9 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -291,6 +291,9 @@ module ActionView request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end + if confirm = html_options.delete("confirm") + html_options["onclick"] = "return #{confirm_javascript_function(confirm)};" + end url = options.is_a?(String) ? options : self.url_for(options) name ||= url @@ -548,6 +551,48 @@ module ActionView end private + def convert_options_to_javascript!(html_options, url = '') + confirm = html_options.delete("confirm") + method, href = html_options.delete("method"), html_options['href'] + + if html_options.key?("popup") + ActiveSupport::Deprecation.warn(":popup has been deprecated", caller) + end + + html_options["onclick"] = case + when confirm && method + "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method, url, href)} };return false;" + when confirm + "return #{confirm_javascript_function(confirm)};" + when method + "#{method_javascript_function(method, url, href)}return false;" + else + html_options["onclick"] + end + end + + def confirm_javascript_function(confirm) + "confirm('#{escape_javascript(confirm)}')" + end + + def method_javascript_function(method, url = '', href = nil) + action = (href && url.size > 0) ? "'#{url}'" : 'this.href' + submit_function = + "var f = document.createElement('form'); f.style.display = 'none'; " + + "this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};" + + unless method == :post + submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); " + submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);" + end + + if protect_against_forgery? + submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); " + submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);" + end + submit_function << "f.submit();" + end + # Processes the +html_options+ hash, converting the boolean # attributes from true/false form into the form required by # HTML/XHTML. (An attribute is considered to be boolean if diff --git a/actionpack/test/template/ajax_helper_test.rb b/actionpack/test/template/ajax_helper_test.rb deleted file mode 100644 index c925dbb8f6..0000000000 --- a/actionpack/test/template/ajax_helper_test.rb +++ /dev/null @@ -1,452 +0,0 @@ -require 'abstract_unit' -require 'active_model' - -class Author - extend ActiveModel::Naming - include ActiveModel::Conversion - - attr_reader :id - def save - @id = 1 - end - - def new_record? - @id.nil? - end - - def name - @id.nil? ? 'new author' : "author ##{@id}" - end -end - -class Article - extend ActiveModel::Naming - include ActiveModel::Conversion - attr_reader :id - attr_reader :author_id - - def save - @id = 1 - @author_id = 1 - end - - def new_record? - @id.nil? - end - - def name - @id.nil? ? 'new article' : "article ##{@id}" - end -end - -class AjaxHelperBaseTest < ActionView::TestCase - attr_accessor :formats, :output_buffer - - def reset_formats(format) - @format = format - end - - def setup - super - @template = self - @controller = Class.new do - - def url_for(options) - return optons unless options.is_a?(Hash) - - url = options.delete(:only_path) ? '/' : 'http://www.example.com' - - if controller = options.delete(:controller) - url << '/' << controller.to_s - end - if action = options.delete(:action) - url << '/' << action.to_s - end - - if id = options.delete(:id) - url << '/' << id.to_s - end - - url << hash_to_param(options) if options.any? - - url.gsub!(/\/\/+/,'/') - - url - end - - private - def hash_to_param(hash) - hash.map { |k,v| "#{k}=#{v}" }.join('&').insert(0,'?') - end - end.new - end - - protected - def request_forgery_protection_token - nil - end - - def protect_against_forgery? - false - end -end - -class AjaxHelperTest < AjaxHelperBaseTest - def _evaluate_assigns_and_ivars() end - - def setup - @record = @author = Author.new - @article = Article.new - super - end - - test "link_to_remote" do - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", { :url => { :action => "whatnot" }}, { :class => "fine" }) - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", :complete => "alert(request.responseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", :success => "alert(request.responseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", :failure => "alert(request.responseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", :url => { :action => "whatnot" }, :type => :synchronous) - assert_dom_equal %(Remove Author), - link_to_remote("Remove Author", :url => { :action => "whatnot" }, :position => :bottom) - end - - test "link_to_remote with url and oncomplete" do - actual = link_to_remote "undo", :url => { :controller => "words", :action => "undo", :n => 5 }, :complete => "undoRequestCompleted(request)" - expected = 'undo' - assert_dom_equal expected, actual - end - - test "link_to_remote with delete" do - actual = link_to_remote("Remove Author", { :url => { :action => "whatnot" }, :method => 'delete'}, { :class => "fine" }) - expected = 'Remove Author' - assert_dom_equal expected, actual - end - - test "link_to_remote using both url and href" do - expected = 'Delete this Post' - assert_dom_equal expected, link_to_remote( "Delete this Post", - { :update => "posts", - :url => { :action => "destroy" } }, - :href => url_for(:action => "destroy")) - end - - test "link_to_remote with update-success and url" do - expected = 'Delete this Post' - assert_dom_equal expected, link_to_remote( "Delete this Post", :url => { :action => "destroy"}, - :update => { :success => "posts", :failure => "error" }) - end - - test "link_to_remote with before/after callbacks" do - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :before => "before();", :after => "after();") - end - - test "link_to_remote using :with expression" do - expected = %(Remote outauthor) - assert_dom_equal expected, link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :with => "id=123") - end - - test "link_to_remote using :condition expression" do - expected = %(Remote outauthor) - assert_dom_equal expected, link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :condition => '$(\'foo\').val() == true') - end - - test "link_to_remote using explicit :href" do - expected = %(Remote outauthor) - assert_dom_equal expected, link_to_remote("Remote outauthor", {:url => { :action => "whatnot" }, :condition => '$(\'foo\').val() == true'}, :href => 'http://www.example.com/testhref') - end - - test "link_to_remote using :submit" do - expected = %(Remote outauthor) - assert_dom_equal expected, link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :submit => 'myForm') - end - - test "link_to_remote with method delete" do - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :method => "delete"}, { :class => "fine" }) - end - - test "link_to_remote with method delete as symbol" do - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :method => :delete}, { :class => "fine" }) - end - - test "link_to_remote html options" do - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } }) - end - - test "link_to_remote url quote escaping" do - assert_dom_equal %(Remote), - link_to_remote("Remote", { :url => { :action => "whatnot's" } }) - end - - test "link_to_remote with confirm" do - assert_dom_equal %(Remote confirm), - link_to_remote("Remote confirm", { :url => { :action => "whatnot" }, :method => "delete", :confirm => "Are you sure?"}, { :class => "fine" }) - end - - test "button_to_remote" do - assert_dom_equal %(), - button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) - end - - test "button_to_remote with confirm" do - assert_dom_equal %(), - button_to_remote("Remote outpost", { :url => { :action => "whatnot" }, :confirm => "Are you sure?"}, { :class => "fine" }) - end - - test "button_to_remote with :submit" do - assert_dom_equal %(), - button_to_remote("Remote outpost", { :url => { :action => "whatnot" }, :submit => "myForm"}, { :class => "fine" }) - end - - test "periodically_call_remote" do - expected = "" - actual = periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" }) - assert_dom_equal expected, actual - end - - test "periodically_call_remote_with_frequency" do - expected = "" - actual = periodically_call_remote(:frequency => 2) - assert_dom_equal expected, actual - end - - test "periodically_call_remote_with_function" do - expected = "" - actual = periodically_call_remote(:frequency => 2, :function => "alert('test')") - assert_dom_equal expected, actual - end - - test "periodically_call_remote_with_update" do - actual = periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg') - expected = "" - assert_dom_equal expected, actual - end - - test "periodically_call_remote with update success and failure" do - actual = periodically_call_remote(:url => { :action => 'invoice', :id => 1 },:update => { :success => "invoice", :failure => "error" }) - expected = "" - assert_dom_equal expected, actual - end - - test "periodically_call_remote with frequency and update" do - actual = periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block') - expected = "" - assert_dom_equal expected, actual - end - - test "form_remote_tag" do - assert_dom_equal %(
), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast } ) - assert_dom_equal %(), - form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }) - assert_dom_equal %(), - form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast }) - assert_dom_equal %(), - form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast }) - end - - test "form_remote_tag with method" do - assert_dom_equal %(
), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put }) - end - - test "form_remote_tag with url" do - form_remote_tag(:url => '/posts' ){} - expected = "
" - assert_dom_equal expected, output_buffer - end - - test "form_remote_tag with block in erb" do - __in_erb_template = '' - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" } - assert_dom_equal %(
Hello world!
), output_buffer - end - - test "remote_form_for with record identification with new record" do - remote_form_for(@record, {:html => { :id => 'create-author' }}) {} - expected = %(
) - assert_dom_equal expected, output_buffer - end - - test "remote_form_for with url" do - remote_form_for(@record, {:html => { :id => 'create-author' }}) {} - expected = "
" - assert_dom_equal expected, output_buffer - end - - test "remote_form_for with record identification without html options" do - remote_form_for(@record) {} - expected = %(
) - assert_dom_equal expected, output_buffer - end - - test "remote_form_for with record identification with existing record" do - @record.save - remote_form_for(@record) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - test "remote_form_for with new nested object and an excisting parent" do - @author.save - remote_form_for([@author, @article]) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - test "remote_form_for with existing object in list" do - @author.save - @article.save - - remote_form_for([@author, @article]) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - test "on callbacks" do - callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure] - callbacks.each do |callback| - assert_dom_equal %(
), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - assert_dom_equal %(), - form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();") - assert_dom_equal %(), - form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();") - assert_dom_equal %(), - form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();") - end - - #HTTP status codes 200 up to 599 have callbacks - #these should work - 100.upto(599) do |callback| - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - end - - #test 200 and 404 - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();") - - #these shouldn't - 1.upto(99) do |callback| - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - end - 600.upto(999) do |callback| - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - end - - #test ultimate combo - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();") - - end - - test "submit_to_remote" do - assert_dom_equal %(), - submit_to_remote("More beer!", 1_000_000, :url => { :action => 'empty_bottle' }, :update => "empty_bottle") - end - - test "submit_to_remote simple" do - expected = "" - actual = submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } - assert_dom_equal expected, actual - end - - test "submit_to_remote with success and failure" do - expected = "" - actual = submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, :update => { :success => "succeed", :failure => "fail" } - assert_dom_equal expected, actual - end - - test "observe_field" do - assert_dom_equal %(), - observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" }) - end - - test "observe_field with url, frequency, update and with" do - actual = observe_field :suggest, :url => { :action => :find_suggestion }, :frequency => 0.25, :update => :suggest, :with => 'q' - expected = "" - assert_dom_equal actual, expected - end - - test "observe_field default frequency" do - actual = observe_field :suggest - expected = "" - assert_dom_equal actual, expected - end - - test "observe_field using with option" do - expected = %() - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id=123') - end - - test "observe_field using condition option" do - expected = %() - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :condition => '$(\'foo\').val() == true') - end - - test "observe_field using json in with option" do - expected = %() - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}") - end - - test "observe_field using function for callback" do - assert_dom_equal %(), - observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')") - end - - test "observe_form" do - assert_dom_equal %(), - observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" }) - end - - test "observe_form using function for callback" do - assert_dom_equal %(), - observe_form("cart", :frequency => 2, :function => "alert('Form changed')") - end - - test "observe_field without frequency" do - assert_dom_equal %(), - observe_field("glass") - end - - protected - def author_path(record) - "/authors/#{record.id}" - end - - def authors_path - "/authors" - end - - def author_articles_path(author) - "/authors/#{author.id}/articles" - end - - def author_article_path(author, article) - "/authors/#{author.id}/articles/#{article.id}" - end -end diff --git a/actionpack/test/template/ajax_test.rb b/actionpack/test/template/ajax_test.rb new file mode 100644 index 0000000000..aeb7c09b09 --- /dev/null +++ b/actionpack/test/template/ajax_test.rb @@ -0,0 +1,114 @@ +require "abstract_unit" + +class AjaxTestCase < ActiveSupport::TestCase + include ActionView::Helpers::AjaxHelper + include ActionView::Helpers::TagHelper + + def assert_html(html, matches) + matches.each do |match| + assert_match(Regexp.new(Regexp.escape(match)), html) + end + end + + def self.assert_callbacks_work(&blk) + define_method(:assert_callbacks_work, &blk) + + [:complete, :failure, :success, :interactive, :loaded, :loading, 404].each do |callback| + test "#{callback} callback" do + markup = assert_callbacks_work(callback) + assert_html markup, %W(data-#{callback}-code="undoRequestCompleted\(request\)") + end + end + end +end + +class LinkToRemoteTest < AjaxTestCase + def url_for(hash) + "/blog/destroy/4" + end + + def link(options = {}) + link_to_remote("Delete this post", "/blog/destroy/3", options) + end + + test "with no update" do + assert_html link, %w(href="/blog/destroy/4" Delete\ this\ post data-remote="true") + end + + test "basic" do + assert_html link(:update => "#posts"), + %w(data-update-success="#posts") + end + + test "using a url hash" do + link = link_to_remote("Delete this post", {:controller => :blog}, :update => "#posts") + assert_html link, %w(href="/blog/destroy/4" data-update-success="#posts") + end + + test "with :html options" do + expected = %{Delete this post} + assert_equal expected, link(:update => "#posts", :html => {"data-custom" => "me"}) + end + + test "with a hash for :update" do + link = link(:update => {:success => "#posts", :failure => "#error"}) + assert_match(/data-update-success="#posts"/, link) + assert_match(/data-update-failure="#error"/, link) + end + + test "with positional parameters" do + link = link(:position => :top, :update => "#posts") + assert_match(/data\-update\-position="top"/, link) + end + + test "with an optional method" do + link = link(:method => "delete") + assert_match(/data-method="delete"/, link) + end + + class LegacyLinkToRemoteTest < AjaxTestCase + include ActionView::Helpers::AjaxHelper::Rails2Compatibility + + def link(options) + link_to_remote("Delete this post", "/blog/destroy/3", options) + end + + test "basic link_to_remote with :url =>" do + expected = %{Delete this post} + assert_equal expected, + link_to_remote("Delete this post", :url => "/blog/destroy/3", :update => "#posts") + end + + assert_callbacks_work do |callback| + link(callback => "undoRequestCompleted(request)") + end + end +end + +class ButtonToRemoteTest < AjaxTestCase + def button(options, html = {}) + button_to_remote("Remote outpost", options, html) + end + + def url_for(*) + "/whatnot" + end + + class StandardTest < ButtonToRemoteTest + test "basic" do + button = button({:url => {:action => "whatnot"}}, {:class => "fine"}) + [/input/, /class="fine"/, /type="button"/, /value="Remote outpost"/, + /data-url="\/whatnot"/].each do |match| + assert_match(match, button) + end + end + end + + class LegacyButtonToRemoteTest < ButtonToRemoteTest + include ActionView::Helpers::AjaxHelper::Rails2Compatibility + + assert_callbacks_work do |callback| + button(callback => "undoRequestCompleted(request)") + end + end +end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 147d3dc05d..0c5c5d17ee 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1232,7 +1232,7 @@ class FormHelperTest < ActionView::TestCase # Perhaps this test should be moved to prototype helper tests. def test_remote_form_for_with_labelled_builder - self.extend ActionView::Helpers::AjaxHelper + self.extend ActionView::Helpers::PrototypeHelper remote_form_for(:post, @post, :builder => LabelledFormBuilder) do |f| concat f.text_field(:title) @@ -1241,7 +1241,7 @@ class FormHelperTest < ActionView::TestCase end expected = - %() + + %() + "
" + "
" + "
" + @@ -1397,10 +1397,10 @@ class FormHelperTest < ActionView::TestCase end def test_remote_form_for_with_html_options_adds_options_to_form_tag - self.extend ActionView::Helpers::AjaxHelper + self.extend ActionView::Helpers::PrototypeHelper remote_form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end - expected = "
" + expected = "
" assert_dom_equal expected, output_buffer end diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index c8d929cee8..47462b1237 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -285,35 +285,35 @@ class FormTagHelperTest < ActionView::TestCase def test_submit_tag assert_dom_equal( - %(), - submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!');") + %(), + submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')") ) end def test_submit_tag_with_no_onclick_options assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...") ) end def test_submit_tag_with_confirmation assert_dom_equal( - %(), + %(), submit_tag("Save", :confirm => "Are you sure?") ) end def test_submit_tag_with_confirmation_and_with_disable_with assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?") ) end def test_image_submit_tag_with_confirmation assert_dom_equal( - %(), + %(), image_submit_tag("save.gif", :confirm => "Are you sure?") ) end diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 86f9c231c0..9225153798 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -82,6 +82,199 @@ class PrototypeHelperTest < PrototypeHelperBaseTest super end + def test_link_to_remote + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }}, { :class => "fine" }) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot" }) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :success => "alert(request.responseText)", :url => { :action => "whatnot" }) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" }) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous) + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :position => :bottom) + end + + def test_link_to_remote_html_options + assert_dom_equal %(Remote outauthor), + link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } }) + end + + def test_link_to_remote_url_quote_escaping + assert_dom_equal %(Remote), + link_to_remote("Remote", { :url => { :action => "whatnot's" } }) + end + + def test_button_to_remote + assert_dom_equal %(), + button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" }) + assert_dom_equal %(), + button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" }) + assert_dom_equal %(), + button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot" }) + assert_dom_equal %(), + button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" }) + assert_dom_equal %(), + button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) + end + + def test_periodically_call_remote + assert_dom_equal %(), + periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" }) + end + + def test_periodically_call_remote_with_frequency + assert_dom_equal( + "", + periodically_call_remote(:frequency => 2) + ) + end + + def test_form_remote_tag + assert_dom_equal %(
), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) + assert_dom_equal %(), + form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }) + assert_dom_equal %(), + form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast }) + assert_dom_equal %(), + form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast }) + end + + def test_form_remote_tag_with_method + assert_dom_equal %(
), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put }) + end + + def test_form_remote_tag_with_block_in_erb + __in_erb_template = '' + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" } + assert_dom_equal %(Hello world!
), output_buffer + end + + def test_remote_form_for_with_record_identification_with_new_record + remote_form_for(@record, {:html => { :id => 'create-author' }}) {} + + expected = %(
) + assert_dom_equal expected, output_buffer + end + + def test_remote_form_for_with_record_identification_without_html_options + remote_form_for(@record) {} + + expected = %(
) + assert_dom_equal expected, output_buffer + end + + def test_remote_form_for_with_record_identification_with_existing_record + @record.save + remote_form_for(@record) {} + + expected = %(
) + assert_dom_equal expected, output_buffer + end + + def test_remote_form_for_with_new_object_in_list + remote_form_for([@author, @article]) {} + + expected = %(
) + assert_dom_equal expected, output_buffer + end + + def test_remote_form_for_with_existing_object_in_list + @author.save + @article.save + remote_form_for([@author, @article]) {} + + expected = %(
) + assert_dom_equal expected, output_buffer + end + + def test_on_callbacks + callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure] + callbacks.each do |callback| + assert_dom_equal %(
), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") + assert_dom_equal %(), + form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();") + assert_dom_equal %(), + form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();") + assert_dom_equal %(), + form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();") + end + + #HTTP status codes 200 up to 599 have callbacks + #these should work + 100.upto(599) do |callback| + assert_dom_equal %(), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") + end + + #test 200 and 404 + assert_dom_equal %(), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();") + + #these shouldn't + 1.upto(99) do |callback| + assert_dom_equal %(), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") + end + 600.upto(999) do |callback| + assert_dom_equal %(), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") + end + + #test ultimate combo + assert_dom_equal %(), + form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();") + + end + + def test_submit_to_remote + assert_dom_equal %(), + submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle") + end + + def test_observe_field + assert_dom_equal %(), + observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" }) + end + + def test_observe_field_using_with_option + expected = %() + assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id') + assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)") + end + + def test_observe_field_using_json_in_with_option + expected = %() + assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}") + end + + def test_observe_field_using_function_for_callback + assert_dom_equal %(), + observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')") + end + + def test_observe_form + assert_dom_equal %(), + observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" }) + end + + def test_observe_form_using_function_for_callback + assert_dom_equal %(), + observe_form("cart", :frequency => 2, :function => "alert('Form changed')") + end + + def test_observe_field_without_frequency + assert_dom_equal %(), + observe_field("glass") + end + def test_update_page old_output_buffer = output_buffer diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 984240d46f..dd28aec786 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -76,7 +76,7 @@ class UrlHelperTest < ActionView::TestCase def test_button_to_with_javascript_confirm assert_dom_equal( - "
", + "
", button_to("Hello", "http://www.example.com", :confirm => "Are you sure?") ) end @@ -170,50 +170,50 @@ class UrlHelperTest < ActionView::TestCase def test_link_tag_with_javascript_confirm assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :confirm => "Are you sure?") ) assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?") ) assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?") ) end def test_link_tag_using_post_javascript assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :method => :post) ) end def test_link_tag_using_delete_javascript assert_dom_equal( - "Destroy", + "Destroy", link_to("Destroy", "http://www.example.com", :method => :delete) ) end def test_link_tag_using_delete_javascript_and_href assert_dom_equal( - "Destroy", + "Destroy", link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#') ) end def test_link_tag_using_post_javascript_and_confirm assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?") ) end def test_link_tag_using_delete_javascript_and_href_and_confirm assert_dom_equal( - "Destroy", + "Destroy", link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"), "When specifying url, form should be generated with it, but not this.href" ) -- cgit v1.2.3 From 61c9b1648703c29133c3ea2ec083e275d95edbec Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 14:53:43 -0600 Subject: Move observe_field and observe_form to prototype_legacy_helper plugin --- .../lib/action_view/helpers/prototype_helper.rb | 189 ++++----------------- actionpack/test/template/prototype_helper_test.rb | 37 ---- 2 files changed, 35 insertions(+), 191 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index bef93dd0f8..92f4ca838f 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -43,16 +43,6 @@ module ActionView # # periodically_call_remote(:url => 'update', :frequency => '5', :update => 'ticker') # - # ...or through an observer (i.e., a form or field that is observed and calls a remote - # action when changed). - # - # <%= observe_field(:searchbox, - # :url => { :action => :live_search }), - # :frequency => 0.5, - # :update => :hits, - # :with => 'query' - # %> - # # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than # are listed here); check out the documentation for each method to find out more about its usage and options. # @@ -476,99 +466,6 @@ module ActionView return function end - # Observes the field with the DOM ID specified by +field_id+ and calls a - # callback when its contents have changed. The default callback is an - # Ajax call. By default the value of the observed field is sent as a - # parameter with the Ajax call. - # - # Example: - # # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest', - # # '/testing/find_suggestion', {asynchronous:true, evalScripts:true, parameters:'q=' + value})}) - # <%= observe_field :suggest, :url => { :action => :find_suggestion }, - # :frequency => 0.25, - # :update => :suggest, - # :with => 'q' - # %> - # - # Required +options+ are either of: - # :url:: +url_for+-style options for the action to call - # when the field has changed. - # :function:: Instead of making a remote call to a URL, you - # can specify javascript code to be called instead. - # Note that the value of this option is used as the - # *body* of the javascript function, a function definition - # with parameters named element and value will be generated for you - # for example: - # observe_field("glass", :frequency => 1, :function => "alert('Element changed')") - # will generate: - # new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')}) - # The element parameter is the DOM element being observed, and the value is its value at the - # time the observer is triggered. - # - # Additional options are: - # :frequency:: The frequency (in seconds) at which changes to - # this field will be detected. Not setting this - # option at all or to a value equal to or less than - # zero will use event based observation instead of - # time based observation. - # :update:: Specifies the DOM ID of the element whose - # innerHTML should be updated with the - # XMLHttpRequest response text. - # :with:: A JavaScript expression specifying the parameters - # for the XMLHttpRequest. The default is to send the - # key and value of the observed field. Any custom - # expressions should return a valid URL query string. - # The value of the field is stored in the JavaScript - # variable +value+. - # - # Examples - # - # :with => "'my_custom_key=' + value" - # :with => "'person[name]=' + prompt('New name')" - # :with => "Form.Element.serialize('other-field')" - # - # Finally - # :with => 'name' - # is shorthand for - # :with => "'name=' + value" - # This essentially just changes the key of the parameter. - # - # Additionally, you may specify any of the options documented in the - # Common options section at the top of this document. - # - # Example: - # - # # Sends params: {:title => 'Title of the book'} when the book_title input - # # field is changed. - # observe_field 'book_title', - # :url => 'http://example.com/books/edit/1', - # :with => 'title' - # - # - def observe_field(field_id, options = {}) - if options[:frequency] && options[:frequency] > 0 - build_observer('Form.Element.Observer', field_id, options) - else - build_observer('Form.Element.EventObserver', field_id, options) - end - end - - # Observes the form with the DOM ID specified by +form_id+ and calls a - # callback when its contents have changed. The default callback is an - # Ajax call. By default all fields of the observed field are sent as - # parameters with the Ajax call. - # - # The +options+ for +observe_form+ are the same as the options for - # +observe_field+. The JavaScript variable +value+ available to the - # :with option is set to the serialized form by default. - def observe_form(form_id, options = {}) - if options[:frequency] - build_observer('Form.Observer', form_id, options) - else - build_observer('Form.EventObserver', form_id, options) - end - end - # All the methods were moved to GeneratorMethods so that # #include_helpers_from_context has nothing to overwrite. class JavaScriptGenerator #:nodoc: @@ -1042,65 +939,49 @@ module ActionView javascript_tag update_page(&block), html_options end - protected - def options_for_ajax(options) - js_options = build_callbacks(options) - - js_options['asynchronous'] = options[:type] != :synchronous - js_options['method'] = method_option_to_s(options[:method]) if options[:method] - js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position] - js_options['evalScripts'] = options[:script].nil? || options[:script] - - if options[:form] - js_options['parameters'] = 'Form.serialize(this)' - elsif options[:submit] - js_options['parameters'] = "Form.serialize('#{options[:submit]}')" - elsif options[:with] - js_options['parameters'] = options[:with] - end - - if protect_against_forgery? && !options[:form] - if js_options['parameters'] - js_options['parameters'] << " + '&" - else - js_options['parameters'] = "'" + protected + def options_for_ajax(options) + js_options = build_callbacks(options) + + js_options['asynchronous'] = options[:type] != :synchronous + js_options['method'] = method_option_to_s(options[:method]) if options[:method] + js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position] + js_options['evalScripts'] = options[:script].nil? || options[:script] + + if options[:form] + js_options['parameters'] = 'Form.serialize(this)' + elsif options[:submit] + js_options['parameters'] = "Form.serialize('#{options[:submit]}')" + elsif options[:with] + js_options['parameters'] = options[:with] end - js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')" - end - options_for_javascript(js_options) - end - - def method_option_to_s(method) - (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'" - end + if protect_against_forgery? && !options[:form] + if js_options['parameters'] + js_options['parameters'] << " + '&" + else + js_options['parameters'] = "'" + end + js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')" + end - def build_observer(klass, name, options = {}) - if options[:with] && (options[:with] !~ /[\{=(.]/) - options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)" - else - options[:with] ||= 'value' unless options[:function] + options_for_javascript(js_options) end - callback = options[:function] || remote_function(options) - javascript = "new #{klass}('#{name}', " - javascript << "#{options[:frequency]}, " if options[:frequency] - javascript << "function(element, value) {" - javascript << "#{callback}}" - javascript << ")" - javascript_tag(javascript) - end + def method_option_to_s(method) + (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'" + end - def build_callbacks(options) - callbacks = {} - options.each do |callback, code| - if CALLBACKS.include?(callback) - name = 'on' + callback.to_s.capitalize - callbacks[name] = "function(request){#{code}}" + def build_callbacks(options) + callbacks = {} + options.each do |callback, code| + if CALLBACKS.include?(callback) + name = 'on' + callback.to_s.capitalize + callbacks[name] = "function(request){#{code}}" + end end + callbacks end - callbacks - end end # Converts chained method calls on DOM proxy elements into JavaScript chains diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 9225153798..b37f71dd61 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -239,42 +239,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle") end - def test_observe_field - assert_dom_equal %(), - observe_field("glass", :frequency => 5.minutes, :url => { :action => "reorder_if_empty" }) - end - - def test_observe_field_using_with_option - expected = %() - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id') - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)") - end - - def test_observe_field_using_json_in_with_option - expected = %() - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "{'id':value}") - end - - def test_observe_field_using_function_for_callback - assert_dom_equal %(), - observe_field("glass", :frequency => 5.minutes, :function => "alert('Element changed')") - end - - def test_observe_form - assert_dom_equal %(), - observe_form("cart", :frequency => 2, :url => { :action => "cart_changed" }) - end - - def test_observe_form_using_function_for_callback - assert_dom_equal %(), - observe_form("cart", :frequency => 2, :function => "alert('Form changed')") - end - - def test_observe_field_without_frequency - assert_dom_equal %(), - observe_field("glass") - end - def test_update_page old_output_buffer = output_buffer @@ -294,7 +258,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block) end - protected def author_path(record) "/authors/#{record.id}" -- cgit v1.2.3 From 95f317b0204d63810b837eae48667d9ed311f7dd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 15:01:02 -0600 Subject: Move periodically_call_remote into prototype_legacy_helper plugin --- .../lib/action_view/helpers/prototype_helper.rb | 35 ---------------------- actionpack/test/template/prototype_helper_test.rb | 12 -------- 2 files changed, 47 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 92f4ca838f..0ec7b48c25 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -39,10 +39,6 @@ module ActionView #
<%= submit_tag 'Recalculate Shipping' %>
# <% end -%> # - # ...periodically... - # - # periodically_call_remote(:url => 'update', :frequency => '5', :update => 'ticker') - # # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than # are listed here); check out the documentation for each method to find out more about its usage and options. # @@ -255,37 +251,6 @@ module ActionView button_to_function(name, remote_function(options), html_options) end - # Periodically calls the specified url (options[:url]) every - # options[:frequency] seconds (default is 10). Usually used to - # update a specified div (options[:update]) with the results - # of the remote call. The options for specifying the target with :url - # and defining callbacks is the same as link_to_remote. - # Examples: - # # Call get_averages and put its results in 'avg' every 10 seconds - # # Generates: - # # new PeriodicalExecuter(function() {new Ajax.Updater('avg', '/grades/get_averages', - # # {asynchronous:true, evalScripts:true})}, 10) - # periodically_call_remote(:url => { :action => 'get_averages' }, :update => 'avg') - # - # # Call invoice every 10 seconds with the id of the customer - # # If it succeeds, update the invoice DIV; if it fails, update the error DIV - # # Generates: - # # new PeriodicalExecuter(function() {new Ajax.Updater({success:'invoice',failure:'error'}, - # # '/testing/invoice/16', {asynchronous:true, evalScripts:true})}, 10) - # periodically_call_remote(:url => { :action => 'invoice', :id => customer.id }, - # :update => { :success => "invoice", :failure => "error" } - # - # # Call update every 20 seconds and update the new_block DIV - # # Generates: - # # new PeriodicalExecuter(function() {new Ajax.Updater('news_block', 'update', {asynchronous:true, evalScripts:true})}, 20) - # periodically_call_remote(:url => 'update', :frequency => '20', :update => 'news_block') - # - def periodically_call_remote(options = {}) - frequency = options[:frequency] || 10 # every ten seconds by default - code = "new PeriodicalExecuter(function() {#{remote_function(options)}}, #{frequency})" - javascript_tag(code) - end - # Returns a form tag that will submit using XMLHttpRequest in the # background instead of the regular reloading POST arrangement. Even # though it's using JavaScript to serialize the form elements, the form diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index b37f71dd61..4e37de2aef 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -122,18 +122,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) end - def test_periodically_call_remote - assert_dom_equal %(), - periodically_call_remote(:update => "schremser_bier", :url => { :action => "mehr_bier" }) - end - - def test_periodically_call_remote_with_frequency - assert_dom_equal( - "", - periodically_call_remote(:frequency => 2) - ) - end - def test_form_remote_tag assert_dom_equal %(
), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) -- cgit v1.2.3 From 2de311a09363f0e7bb4e21cef8e77df7ee92d33f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 15:08:29 -0600 Subject: Drop AjaxHelper --- actionpack/lib/action_view/helpers.rb | 1 - actionpack/lib/action_view/helpers/ajax_helper.rb | 68 ------------- actionpack/test/template/ajax_test.rb | 114 ---------------------- 3 files changed, 183 deletions(-) delete mode 100644 actionpack/lib/action_view/helpers/ajax_helper.rb delete mode 100644 actionpack/test/template/ajax_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 3d088678fc..ceb0e18d80 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -3,7 +3,6 @@ require 'active_support/benchmarkable' module ActionView #:nodoc: module Helpers #:nodoc: autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper' - autoload :AjaxHelper, 'action_view/helpers/ajax_helper' autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper' autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper' autoload :CacheHelper, 'action_view/helpers/cache_helper' diff --git a/actionpack/lib/action_view/helpers/ajax_helper.rb b/actionpack/lib/action_view/helpers/ajax_helper.rb deleted file mode 100644 index 9cc2acc239..0000000000 --- a/actionpack/lib/action_view/helpers/ajax_helper.rb +++ /dev/null @@ -1,68 +0,0 @@ -module ActionView - module Helpers - module AjaxHelper - include UrlHelper - - def link_to_remote(name, url, options = {}) - html = options.delete(:html) || {} - - update = options.delete(:update) - if update.is_a?(Hash) - html["data-update-success"] = update[:success] - html["data-update-failure"] = update[:failure] - else - html["data-update-success"] = update - end - - html["data-update-position"] = options.delete(:position) - html["data-method"] = options.delete(:method) - html["data-remote"] = "true" - - html.merge!(options) - - url = url_for(url) if url.is_a?(Hash) - link_to(name, url, html) - end - - def button_to_remote(name, options = {}, html_options = {}) - url = options.delete(:url) - url = url_for(url) if url.is_a?(Hash) - - html_options.merge!(:type => "button", :value => name, - :"data-url" => url) - - tag(:input, html_options) - end - - module Rails2Compatibility - def set_callbacks(options, html) - [:complete, :failure, :success, :interactive, :loaded, :loading].each do |type| - html["data-#{type}-code"] = options.delete(type.to_sym) - end - - options.each do |option, value| - if option.is_a?(Integer) - html["data-#{option}-code"] = options.delete(option) - end - end - end - - def link_to_remote(name, url, options = nil) - if !options && url.is_a?(Hash) && url.key?(:url) - url, options = url.delete(:url), url - end - - set_callbacks(options, options[:html] ||= {}) - - super - end - - def button_to_remote(name, options = {}, html_options = {}) - set_callbacks(options, html_options) - super - end - end - - end - end -end \ No newline at end of file diff --git a/actionpack/test/template/ajax_test.rb b/actionpack/test/template/ajax_test.rb deleted file mode 100644 index aeb7c09b09..0000000000 --- a/actionpack/test/template/ajax_test.rb +++ /dev/null @@ -1,114 +0,0 @@ -require "abstract_unit" - -class AjaxTestCase < ActiveSupport::TestCase - include ActionView::Helpers::AjaxHelper - include ActionView::Helpers::TagHelper - - def assert_html(html, matches) - matches.each do |match| - assert_match(Regexp.new(Regexp.escape(match)), html) - end - end - - def self.assert_callbacks_work(&blk) - define_method(:assert_callbacks_work, &blk) - - [:complete, :failure, :success, :interactive, :loaded, :loading, 404].each do |callback| - test "#{callback} callback" do - markup = assert_callbacks_work(callback) - assert_html markup, %W(data-#{callback}-code="undoRequestCompleted\(request\)") - end - end - end -end - -class LinkToRemoteTest < AjaxTestCase - def url_for(hash) - "/blog/destroy/4" - end - - def link(options = {}) - link_to_remote("Delete this post", "/blog/destroy/3", options) - end - - test "with no update" do - assert_html link, %w(href="/blog/destroy/4" Delete\ this\ post data-remote="true") - end - - test "basic" do - assert_html link(:update => "#posts"), - %w(data-update-success="#posts") - end - - test "using a url hash" do - link = link_to_remote("Delete this post", {:controller => :blog}, :update => "#posts") - assert_html link, %w(href="/blog/destroy/4" data-update-success="#posts") - end - - test "with :html options" do - expected = %{Delete this post} - assert_equal expected, link(:update => "#posts", :html => {"data-custom" => "me"}) - end - - test "with a hash for :update" do - link = link(:update => {:success => "#posts", :failure => "#error"}) - assert_match(/data-update-success="#posts"/, link) - assert_match(/data-update-failure="#error"/, link) - end - - test "with positional parameters" do - link = link(:position => :top, :update => "#posts") - assert_match(/data\-update\-position="top"/, link) - end - - test "with an optional method" do - link = link(:method => "delete") - assert_match(/data-method="delete"/, link) - end - - class LegacyLinkToRemoteTest < AjaxTestCase - include ActionView::Helpers::AjaxHelper::Rails2Compatibility - - def link(options) - link_to_remote("Delete this post", "/blog/destroy/3", options) - end - - test "basic link_to_remote with :url =>" do - expected = %{Delete this post} - assert_equal expected, - link_to_remote("Delete this post", :url => "/blog/destroy/3", :update => "#posts") - end - - assert_callbacks_work do |callback| - link(callback => "undoRequestCompleted(request)") - end - end -end - -class ButtonToRemoteTest < AjaxTestCase - def button(options, html = {}) - button_to_remote("Remote outpost", options, html) - end - - def url_for(*) - "/whatnot" - end - - class StandardTest < ButtonToRemoteTest - test "basic" do - button = button({:url => {:action => "whatnot"}}, {:class => "fine"}) - [/input/, /class="fine"/, /type="button"/, /value="Remote outpost"/, - /data-url="\/whatnot"/].each do |match| - assert_match(match, button) - end - end - end - - class LegacyButtonToRemoteTest < ButtonToRemoteTest - include ActionView::Helpers::AjaxHelper::Rails2Compatibility - - assert_callbacks_work do |callback| - button(callback => "undoRequestCompleted(request)") - end - end -end -- cgit v1.2.3 From b3787643ec4b24b4628e29ebccc5fb517b01b824 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 15:20:00 -0600 Subject: Move link_to_function and link_to_remote into prototype_legacy_helper plugin --- actionpack/README | 77 +++++------ .../lib/action_view/helpers/javascript_helper.rb | 54 -------- .../lib/action_view/helpers/prototype_helper.rb | 141 --------------------- .../action_view/helpers/scriptaculous_helper.rb | 101 +++++++-------- actionpack/test/template/javascript_helper_test.rb | 29 ----- actionpack/test/template/prototype_helper_test.rb | 28 ---- 6 files changed, 82 insertions(+), 348 deletions(-) (limited to 'actionpack') diff --git a/actionpack/README b/actionpack/README index e4ce4aa044..8bdcb9120a 100644 --- a/actionpack/README +++ b/actionpack/README @@ -37,15 +37,15 @@ A short rundown of the major features: def show @customer = find_customer end - + def update @customer = find_customer @customer.attributes = params[:customer] - @customer.save ? + @customer.save ? redirect_to(:action => "show") : render(:action => "edit") end - + private def find_customer() Customer.find(params[:id]) end end @@ -64,7 +64,7 @@ A short rundown of the major features: <% unless @person.is_client? %> Not for clients to see... <% end %> - + {Learn more}[link:classes/ActionView.html] @@ -99,24 +99,24 @@ A short rundown of the major features: before_filter :authenticate, :cache, :audit after_filter { |c| c.response.body = Gzip::compress(c.response.body) } after_filter LocalizeFilter - + def index # Before this action is run, the user will be authenticated, the cache # will be examined to see if a valid copy of the results already # exists, and the action will be logged for auditing. - - # After this action has run, the output will first be localized then + + # After this action has run, the output will first be localized then # compressed to minimize bandwidth usage end - + private def authenticate # Implement the filter with full access to both request and response end end - + {Learn more}[link:classes/ActionController/Filters/ClassMethods.html] - + * Helpers for forms, dates, action links, and text @@ -124,26 +124,26 @@ A short rundown of the major features: <%= html_date_select(Date.today) %> <%= link_to "New post", :controller => "post", :action => "new" %> <%= truncate(post.title, :length => 25) %> - + {Learn more}[link:classes/ActionView/Helpers.html] -* Layout sharing for template reuse (think simple version of Struts +* Layout sharing for template reuse (think simple version of Struts Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html]) class WeblogController < ActionController::Base layout "weblog_layout" - + def hello_world end end Layout file (called weblog_layout): <%= yield %> - + Template for hello_world action:

Hello world

- + Result of running hello_world action:

Hello world

@@ -156,9 +156,9 @@ A short rundown of the major features: Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params] - + From that URL, you can rewrite the redirect in a number of ways: - + redirect_to(:action => "edit") => /clients/37signals/basecamp/project/dash @@ -168,15 +168,6 @@ A short rundown of the major features: {Learn more}[link:classes/ActionController/Base.html] -* Javascript and Ajax integration - - link_to_function "Greeting", "alert('Hello world!')" - link_to_remote "Delete this post", :update => "posts", - :url => { :action => "destroy", :id => post.id } - - {Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html] - - * Easy testing of both controller and rendered template through ActionController::TestCase class LoginControllerTest < ActionController::TestCase @@ -218,18 +209,18 @@ A short rundown of the major features: class WeblogController < ActionController::Base caches_page :show caches_action :account - + def show - # the output of the method will be cached as + # the output of the method will be cached as # ActionController::Base.page_cache_directory + "/weblog/show/n.html" # and the web server will pick it up without even hitting Rails end - + def account # the output of the method will be cached in the fragment store # but Rails is hit to retrieve it, so filters are run end - + def update List.update(params[:list][:id], params[:list]) expire_page :action => "show", :id => params[:list][:id] @@ -256,26 +247,26 @@ A short rundown of the major features: class AccountController < ActionController::Base scaffold :account end - + The AccountController now has the full CRUD range of actions and default templates: list, show, destroy, new, create, edit, update - + {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html] * Form building for Active Record model objects - The post object has a title (varchar), content (text), and + The post object has a title (varchar), content (text), and written_on (date) <%= form "post" %> - + ...will generate something like (the selects will have more options, of course): - +

- Title:
+ Title:

@@ -293,7 +284,7 @@ A short rundown of the major features:

This form generates a params[:post] array that can be used directly in a save action: - + class WeblogController < ActionController::Base def create post = Post.create(params[:post]) @@ -318,19 +309,19 @@ methods: class WeblogController < ActionController::Base layout "weblog/layout" - + def index @posts = Post.find(:all) end - + def show @post = Post.find(params[:id]) end - + def new @post = Post.new end - + def create @post = Post.create(params[:post]) redirect_to :action => "show", :id => @post.id @@ -364,7 +355,7 @@ And the templates look like this: weblog/new.html.erb: <%= form "post" %> - + This simple setup will list all the posts in the system on the index page, which is called by accessing /weblog/. It uses the form builder for the Active Record model to make the new screen, which in turn hands everything over to @@ -379,7 +370,7 @@ The latest version of Action Pack can be found at * http://rubyforge.org/project/showfiles.php?group_id=249 -Documentation can be found at +Documentation can be found at * http://api.rubyonrails.com diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 06a9d3405a..f9249e667d 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -41,60 +41,6 @@ module ActionView include PrototypeHelper - # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the - # onclick handler and return false after the fact. - # - # The first argument +name+ is used as the link text. - # - # The next arguments are optional and may include the javascript function definition and a hash of html_options. - # - # The +function+ argument can be omitted in favor of an +update_page+ - # block, which evaluates to a string when the template is rendered - # (instead of making an Ajax request first). - # - # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button" - # - # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil - # - # - # Examples: - # link_to_function "Greeting", "alert('Hello world!')" - # Produces: - # Greeting - # - # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()") - # Produces: - # - # Delete - # - # - # link_to_function("Show me more", nil, :id => "more_link") do |page| - # page[:details].visual_effect :toggle_blind - # page[:more_link].replace_html "Show me less" - # end - # Produces: - # Show me more - # - def link_to_function(name, *args, &block) - html_options = args.extract_options!.symbolize_keys - - function = block_given? ? update_page(&block) : args[0] || '' - onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;" - href = html_options[:href] || '#' - - content_tag(:a, name, html_options.merge(:href => href, :onclick => onclick)) - end - # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the # onclick handler. # diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 0ec7b48c25..56a7198792 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -102,147 +102,6 @@ module ActionView :form, :with, :update, :script, :type ]).merge(CALLBACKS) end - # Returns a link to a remote action defined by options[:url] - # (using the url_for format) that's called in the background using - # XMLHttpRequest. The result of that request can then be inserted into a - # DOM object whose id can be specified with options[:update]. - # Usually, the result would be a partial prepared by the controller with - # render :partial. - # - # Examples: - # # Generates: Delete this post - # link_to_remote "Delete this post", :update => "posts", - # :url => { :action => "destroy", :id => post.id } - # - # # Generates: Refresh - # link_to_remote(image_tag("refresh"), :update => "emails", - # :url => { :action => "list_emails" }) - # - # You can override the generated HTML options by specifying a hash in - # options[:html]. - # - # link_to_remote "Delete this post", :update => "posts", - # :url => post_url(@post), :method => :delete, - # :html => { :class => "destructive" } - # - # You can also specify a hash for options[:update] to allow for - # easy redirection of output to an other DOM element if a server-side - # error occurs: - # - # Example: - # # Generates: Delete this post - # link_to_remote "Delete this post", - # :url => { :action => "destroy", :id => post.id }, - # :update => { :success => "posts", :failure => "error" } - # - # Optionally, you can use the options[:position] parameter to - # influence how the target DOM element is updated. It must be one of - # :before, :top, :bottom, or :after. - # - # The method used is by default POST. You can also specify GET or you - # can simulate PUT or DELETE over POST. All specified with options[:method] - # - # Example: - # # Generates: Destroy - # link_to_remote "Destroy", :url => person_url(:id => person), :method => :delete - # - # By default, these remote requests are processed asynchronous during - # which various JavaScript callbacks can be triggered (for progress - # indicators and the likes). All callbacks get access to the - # request object, which holds the underlying XMLHttpRequest. - # - # To access the server response, use request.responseText, to - # find out the HTTP status, use request.status. - # - # Example: - # # Generates: hello - # word = 'hello' - # link_to_remote word, - # :url => { :action => "undo", :n => word_counter }, - # :complete => "undoRequestCompleted(request)" - # - # The callbacks that may be specified are (in order): - # - # :loading:: Called when the remote document is being - # loaded with data by the browser. - # :loaded:: Called when the browser has finished loading - # the remote document. - # :interactive:: Called when the user can interact with the - # remote document, even though it has not - # finished loading. - # :success:: Called when the XMLHttpRequest is completed, - # and the HTTP status code is in the 2XX range. - # :failure:: Called when the XMLHttpRequest is completed, - # and the HTTP status code is not in the 2XX - # range. - # :complete:: Called when the XMLHttpRequest is complete - # (fires after success/failure if they are - # present). - # - # You can further refine :success and :failure by - # adding additional callbacks for specific status codes. - # - # Example: - # # Generates: hello - # link_to_remote word, - # :url => { :action => "action" }, - # 404 => "alert('Not found...? Wrong URL...?')", - # :failure => "alert('HTTP Error ' + request.status + '!')" - # - # A status code callback overrides the success/failure handlers if - # present. - # - # If you for some reason or another need synchronous processing (that'll - # block the browser while the request is happening), you can specify - # options[:type] = :synchronous. - # - # You can customize further browser side call logic by passing in - # JavaScript code snippets via some optional parameters. In their order - # of use these are: - # - # :confirm:: Adds confirmation dialog. - # :condition:: Perform remote request conditionally - # by this expression. Use this to - # describe browser-side conditions when - # request should not be initiated. - # :before:: Called before request is initiated. - # :after:: Called immediately after request was - # initiated and before :loading. - # :submit:: Specifies the DOM element ID that's used - # as the parent of the form elements. By - # default this is the current form, but - # it could just as well be the ID of a - # table row or any other DOM element. - # :with:: A JavaScript expression specifying - # the parameters for the XMLHttpRequest. - # Any expressions should return a valid - # URL query string. - # - # Example: - # - # :with => "'name=' + $('name').value" - # - # You can generate a link that uses AJAX in the general case, while - # degrading gracefully to plain link behavior in the absence of - # JavaScript by setting html_options[:href] to an alternate URL. - # Note the extra curly braces around the options hash separate - # it as the second parameter from html_options, the third. - # - # Example: - # link_to_remote "Delete this post", - # { :update => "posts", :url => { :action => "destroy", :id => post.id } }, - # :href => url_for(:action => "destroy", :id => post.id) - def link_to_remote(name, options = {}, html_options = nil) - link_to_function(name, remote_function(options), html_options || options.delete(:html)) - end - # Creates a button with an onclick event which calls a remote action # via XMLHttpRequest # The options for specifying the target with :url diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index 04af2781d7..d30164e402 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -3,11 +3,11 @@ require 'active_support/json' module ActionView module Helpers - # Provides a set of helpers for calling Scriptaculous JavaScript + # Provides a set of helpers for calling Scriptaculous JavaScript # functions, including those which create Ajax controls and visual effects. # - # To be able to use these helpers, you must include the Prototype - # JavaScript framework and the Scriptaculous JavaScript library in your + # To be able to use these helpers, you must include the Prototype + # JavaScript framework and the Scriptaculous JavaScript library in your # pages. See the documentation for ActionView::Helpers::JavaScriptHelper # for more information on including the necessary JavaScript. # @@ -18,22 +18,17 @@ module ActionView unless const_defined? :TOGGLE_EFFECTS TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind] end - + # Returns a JavaScript snippet to be used on the Ajax callbacks for # starting visual effects. # - # Example: - # <%= link_to_remote "Reload", :update => "posts", - # :url => { :action => "reload" }, - # :complete => visual_effect(:highlight, "posts", :duration => 0.5) - # # If no +element_id+ is given, it assumes "element" which should be a local - # variable in the generated JavaScript execution context. This can be + # variable in the generated JavaScript execution context. This can be # used for example with +drop_receiving_element+: # # <%= drop_receiving_element (...), :loading => visual_effect(:fade) %> # - # This would fade the element that was dropped on the drop receiving + # This would fade the element that was dropped on the drop receiving # element. # # For toggling visual effects, you can use :toggle_appear, :toggle_slide, and @@ -44,13 +39,13 @@ module ActionView # http://script.aculo.us for more documentation. def visual_effect(name, element_id = false, js_options = {}) element = element_id ? ActiveSupport::JSON.encode(element_id) : "element" - + js_options[:queue] = if js_options[:queue].is_a?(Hash) '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}' elsif js_options[:queue] "'#{js_options[:queue]}'" end if js_options[:queue] - + [:endcolor, :direction, :startcolor, :scaleMode, :restorecolor].each do |option| js_options[option] = "'#{js_options[option]}'" if js_options[option] end @@ -61,7 +56,7 @@ module ActionView "new Effect.#{name.to_s.camelize}(#{element},#{options_for_javascript(js_options)});" end end - + # Makes the element with the DOM ID specified by +element_id+ sortable # by drag-and-drop and make an Ajax call whenever the sort order has # changed. By default, the action called gets the serialized sortable @@ -71,84 +66,84 @@ module ActionView # # <%= sortable_element("my_list", :url => { :action => "order" }) %> # - # In the example, the action gets a "my_list" array parameter - # containing the values of the ids of elements the sortable consists + # In the example, the action gets a "my_list" array parameter + # containing the values of the ids of elements the sortable consists # of, in the current order. # # Important: For this to work, the sortable elements must have id # attributes in the form "string_identifier". For example, "item_1". Only # the identifier part of the id attribute will be serialized. - # + # # Additional +options+ are: # # * :format - A regular expression to determine what to send as the # serialized id to the server (the default is /^[^_]*_(.*)$/). - # + # # * :constraint - Whether to constrain the dragging to either # :horizontal or :vertical (or false to make it unconstrained). - # + # # * :overlap - Calculate the item overlap in the :horizontal # or :vertical direction. - # + # # * :tag - Which children of the container element to treat as # sortable (default is li). - # + # # * :containment - Takes an element or array of elements to treat as # potential drop targets (defaults to the original target element). - # + # # * :only - A CSS class name or array of class names used to filter # out child elements as candidates. - # + # # * :scroll - Determines whether to scroll the list during drag # operations if the list runs past the visual border. - # + # # * :tree - Determines whether to treat nested lists as part of the # main sortable list. This means that you can create multi-layer lists, # and not only sort items at the same level, but drag and sort items # between levels. - # + # # * :hoverclass - If set, the Droppable will have this additional CSS class - # when an accepted Draggable is hovered over it. - # + # when an accepted Draggable is hovered over it. + # # * :handle - Sets whether the element should only be draggable by an # embedded handle. The value may be a string referencing a CSS class value # (as of script.aculo.us V1.5). The first child/grandchild/etc. element # found within the element that has this CSS class value will be used as # the handle. - # + # # * :ghosting - Clones the element and drags the clone, leaving # the original in place until the clone is dropped (default is false). - # + # # * :dropOnEmpty - If true the Sortable container will be made into # a Droppable, that can receive a Draggable (as according to the containment # rules) as a child element when there are no more elements inside (default # is false). - # + # # * :onChange - Called whenever the sort order changes while dragging. When # dragging from one Sortable to another, the callback is called once on each # Sortable. Gets the affected element as its parameter. - # + # # * :onUpdate - Called when the drag ends and the Sortable's order is # changed in any way. When dragging from one Sortable to another, the callback # is called once on each Sortable. Gets the container as its parameter. - # + # # See http://script.aculo.us for more documentation. def sortable_element(element_id, options = {}) javascript_tag(sortable_element_js(element_id, options).chop!) end - + def sortable_element_js(element_id, options = {}) #:nodoc: options[:with] ||= "Sortable.serialize(#{ActiveSupport::JSON.encode(element_id)})" options[:onUpdate] ||= "function(){" + remote_function(options) + "}" options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) } - + [:tag, :overlap, :constraint, :handle].each do |option| options[option] = "'#{options[option]}'" if options[option] end - + options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment] options[:only] = array_or_string_for_javascript(options[:only]) if options[:only] - + %(Sortable.create(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end @@ -156,24 +151,24 @@ module ActionView # # Example: # <%= draggable_element("my_image", :revert => true) - # + # # You can change the behaviour with various options, see # http://script.aculo.us for more documentation. def draggable_element(element_id, options = {}) javascript_tag(draggable_element_js(element_id, options).chop!) end - + def draggable_element_js(element_id, options = {}) #:nodoc: %(new Draggable(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end # Makes the element with the DOM ID specified by +element_id+ receive # dropped draggable elements (created by +draggable_element+). - # and make an AJAX call. By default, the action called gets the DOM ID + # and make an AJAX call. By default, the action called gets the DOM ID # of the element as parameter. # # Example: - # <%= drop_receiving_element("my_cart", :url => + # <%= drop_receiving_element("my_cart", :url => # { :controller => "cart", :action => "add" }) %> # # You can change the behaviour with various options, see @@ -181,41 +176,41 @@ module ActionView # # Some of these +options+ include: # * :accept - Set this to a string or an array of strings describing the - # allowable CSS classes that the +draggable_element+ must have in order + # allowable CSS classes that the +draggable_element+ must have in order # to be accepted by this +drop_receiving_element+. - # + # # * :confirm - Adds a confirmation dialog. Example: - # + # # :confirm => "Are you sure you want to do this?" - # + # # * :hoverclass - If set, the +drop_receiving_element+ will have # this additional CSS class when an accepted +draggable_element+ is - # hovered over it. - # + # hovered over it. + # # * :onDrop - Called when a +draggable_element+ is dropped onto - # this element. Override this callback with a JavaScript expression to + # this element. Override this callback with a JavaScript expression to # change the default drop behaviour. Example: - # + # # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }" - # + # # This callback gets three parameters: The Draggable element, the Droppable # element and the Event object. You can extract additional information about # the drop - like if the Ctrl or Shift keys were pressed - from the Event object. - # + # # * :with - A JavaScript expression specifying the parameters for # the XMLHttpRequest. Any expressions should return a valid URL query string. def drop_receiving_element(element_id, options = {}) javascript_tag(drop_receiving_element_js(element_id, options).chop!) end - + def drop_receiving_element_js(element_id, options = {}) #:nodoc: options[:with] ||= "'id=' + encodeURIComponent(element.id)" options[:onDrop] ||= "function(element){" + remote_function(options) + "}" options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) } - options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept] + options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept] options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass] - + # Confirmation happens during the onDrop callback, so it can be removed from the options options.delete(:confirm) if options[:confirm] diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 03caad3d46..b3e7abc387 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -30,35 +30,6 @@ class JavaScriptHelperTest < ActionView::TestCase assert_equal %(dont <\\/close> tags), escape_javascript(%(dont tags)) end - def test_link_to_function - assert_dom_equal %(Greeting), - link_to_function("Greeting", "alert('Hello world!')") - end - - def test_link_to_function_with_existing_onclick - assert_dom_equal %(Greeting), - link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')") - end - - def test_link_to_function_with_rjs_block - html = link_to_function( "Greet me!" ) do |page| - page.replace_html 'header', "

Greetings

" - end - assert_dom_equal %(Greet me!), html - end - - def test_link_to_function_with_rjs_block_and_options - html = link_to_function( "Greet me!", :class => "updater" ) do |page| - page.replace_html 'header', "

Greetings

" - end - assert_dom_equal %(Greet me!), html - end - - def test_link_to_function_with_href - assert_dom_equal %(Greeting), - link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/') - end - def test_button_to_function assert_dom_equal %(), button_to_function("Greeting", "alert('Hello world!')") diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 4e37de2aef..fd868efecd 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -82,33 +82,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest super end - def test_link_to_remote - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }}, { :class => "fine" }) - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :complete => "alert(request.responseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :success => "alert(request.responseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :failure => "alert(request.responseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :type => :synchronous) - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", :url => { :action => "whatnot" }, :position => :bottom) - end - - def test_link_to_remote_html_options - assert_dom_equal %(Remote outauthor), - link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }, :html => { :class => "fine" } }) - end - - def test_link_to_remote_url_quote_escaping - assert_dom_equal %(Remote), - link_to_remote("Remote", { :url => { :action => "whatnot's" } }) - end - def test_button_to_remote assert_dom_equal %(), button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" }) @@ -610,4 +583,3 @@ return value.reverse(); assert_equal "MyObject.myMethod(function() { $(\"one\").show();\n$(\"two\").hide(); });", @generator.to_s end end - -- cgit v1.2.3 From 779094a6024c762b3dfb60db7efb1d5d7f0c4ddc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 15:28:31 -0600 Subject: Move button_to_remote and submit_to_remote into prototype_legacy_helper plugin --- .../lib/action_view/helpers/prototype_helper.rb | 38 ---------------------- actionpack/test/template/prototype_helper_test.rb | 18 ---------- 2 files changed, 56 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 56a7198792..818412c345 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -102,14 +102,6 @@ module ActionView :form, :with, :update, :script, :type ]).merge(CALLBACKS) end - # Creates a button with an onclick event which calls a remote action - # via XMLHttpRequest - # The options for specifying the target with :url - # and defining callbacks is the same as link_to_remote. - def button_to_remote(name, options = {}, html_options = {}) - button_to_function(name, remote_function(options), html_options) - end - # Returns a form tag that will submit using XMLHttpRequest in the # background instead of the regular reloading POST arrangement. Even # though it's using JavaScript to serialize the form elements, the form @@ -212,36 +204,6 @@ module ActionView end alias_method :form_remote_for, :remote_form_for - # Returns a button input tag with the element name of +name+ and a value (i.e., display text) of +value+ - # that will submit form using XMLHttpRequest in the background instead of a regular POST request that - # reloads the page. - # - # # Create a button that submits to the create action - # # - # # Generates: - # <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %> - # - # # Submit to the remote action update and update the DIV succeed or fail based - # # on the success or failure of the request - # # - # # Generates: - # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, - # :update => { :success => "succeed", :failure => "fail" } - # - # options argument is the same as in form_remote_tag. - def submit_to_remote(name, value, options = {}) - options[:with] ||= 'Form.serialize(this.form)' - - html_options = options.delete(:html) || {} - html_options[:name] = name - - button_to_remote(value, options, html_options) - end - # Returns 'eval(request.responseText)' which is the JavaScript function # that +form_remote_tag+ can call in :complete to evaluate a multiple # update return document using +update_element_function+ calls. diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index fd868efecd..51a03d25a4 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -82,19 +82,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest super end - def test_button_to_remote - assert_dom_equal %(), - button_to_remote("Remote outpost", { :url => { :action => "whatnot" }}, { :class => "fine" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :success => "alert(request.reponseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" }) - assert_dom_equal %(), - button_to_remote("Remote outpost", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) - end - def test_form_remote_tag assert_dom_equal %(
), form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) @@ -195,11 +182,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest end - def test_submit_to_remote - assert_dom_equal %(), - submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle") - end - def test_update_page old_output_buffer = output_buffer -- cgit v1.2.3 From 9c2c307ee48b91177c3e1cb8831afe4f972d19eb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 15:42:30 -0600 Subject: Move form_remote_tag and remote_form_for into prototype_legacy_helper --- .../lib/action_view/helpers/prototype_helper.rb | 109 --------------------- .../controller/request_forgery_protection_test.rb | 45 ++++----- actionpack/test/template/form_helper_test.rb | 29 ------ actionpack/test/template/prototype_helper_test.rb | 100 ------------------- 4 files changed, 18 insertions(+), 265 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 818412c345..1fa2fed460 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -102,115 +102,6 @@ module ActionView :form, :with, :update, :script, :type ]).merge(CALLBACKS) end - # Returns a form tag that will submit using XMLHttpRequest in the - # background instead of the regular reloading POST arrangement. Even - # though it's using JavaScript to serialize the form elements, the form - # submission will work just like a regular submission as viewed by the - # receiving side (all elements available in params). The options for - # specifying the target with :url and defining callbacks is the same as - # +link_to_remote+. - # - # A "fall-through" target for browsers that doesn't do JavaScript can be - # specified with the :action/:method options on :html. - # - # Example: - # # Generates: - # # - # form_remote_tag :html => { :action => - # url_for(:controller => "some", :action => "place") } - # - # The Hash passed to the :html key is equivalent to the options (2nd) - # argument in the FormTagHelper.form_tag method. - # - # By default the fall-through action is the same as the one specified in - # the :url (and the default method is :post). - # - # form_remote_tag also takes a block, like form_tag: - # # Generates: - # #
- # # - # <% form_remote_tag :url => '/posts' do -%> - #
<%= submit_tag 'Save' %>
- # <% end -%> - def form_remote_tag(options = {}, &block) - options[:form] = true - - options[:html] ||= {} - options[:html][:onsubmit] = - (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") + - "#{remote_function(options)}; return false;" - - form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block) - end - - # Creates a form that will submit using XMLHttpRequest in the background - # instead of the regular reloading POST arrangement and a scope around a - # specific resource that is used as a base for questioning about - # values for the fields. - # - # === Resource - # - # Example: - # <% remote_form_for(@post) do |f| %> - # ... - # <% end %> - # - # This will expand to be the same as: - # - # <% remote_form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %> - # ... - # <% end %> - # - # === Nested Resource - # - # Example: - # <% remote_form_for([@post, @comment]) do |f| %> - # ... - # <% end %> - # - # This will expand to be the same as: - # - # <% remote_form_for :comment, @comment, :url => post_comment_path(@post, @comment), :html => { :method => :put, :class => "edit_comment", :id => "edit_comment_45" } do |f| %> - # ... - # <% end %> - # - # If you don't need to attach a form to a resource, then check out form_remote_tag. - # - # See FormHelper#form_for for additional semantics. - def remote_form_for(record_or_name_or_array, *args, &proc) - options = args.extract_options! - - case record_or_name_or_array - when String, Symbol - object_name = record_or_name_or_array - when Array - object = record_or_name_or_array.last - object_name = ActionController::RecordIdentifier.singular_class_name(object) - apply_form_for_options!(record_or_name_or_array, options) - args.unshift object - else - object = record_or_name_or_array - object_name = ActionController::RecordIdentifier.singular_class_name(record_or_name_or_array) - apply_form_for_options!(object, options) - args.unshift object - end - - concat(form_remote_tag(options)) - fields_for(object_name, *(args << options), &proc) - concat(''.html_safe!) - end - alias_method :form_remote_for, :remote_form_for - - # Returns 'eval(request.responseText)' which is the JavaScript function - # that +form_remote_tag+ can call in :complete to evaluate a multiple - # update return document using +update_element_function+ calls. - def evaluate_remote_response - "eval(request.responseText)" - end - # Returns the JavaScript needed for a remote function. # Takes the same arguments as link_to_remote. # diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 09003adf73..b2a0e2e2a3 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -6,14 +6,10 @@ module RequestForgeryProtectionActions def index render :inline => "<%= form_tag('/') {} %>" end - + def show_button render :inline => "<%= button_to('New', '/') {} %>" end - - def remote_form - render :inline => "<% form_remote_tag(:url => '/') {} %>" - end def unsafe render :text => 'pwn' @@ -30,11 +26,11 @@ end class FreeCookieController < RequestForgeryProtectionController self.allow_forgery_protection = false - + def index render :inline => "<%= form_tag('/') {} %>" end - + def show_button render :inline => "<%= button_to('New', '/') {} %>" end @@ -65,11 +61,6 @@ module RequestForgeryProtectionTests assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token end - def test_should_render_remote_form_with_only_one_token_parameter - get :remote_form - assert_equal 1, @response.body.scan(@token).size - end - def test_should_allow_get get :index assert_response :success @@ -84,12 +75,12 @@ module RequestForgeryProtectionTests @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html } end - + def test_should_not_allow_html_put_without_token @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html } end - + def test_should_not_allow_html_delete_without_token @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html } @@ -154,51 +145,51 @@ module RequestForgeryProtectionTests delete :index, :format => 'xml' end end - + def test_should_allow_xhr_post_without_token assert_nothing_raised { xhr :post, :index } end - + def test_should_allow_xhr_put_without_token assert_nothing_raised { xhr :put, :index } end - + def test_should_allow_xhr_delete_without_token assert_nothing_raised { xhr :delete, :index } end - + def test_should_allow_xhr_post_with_encoded_form_content_type_without_token @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s assert_nothing_raised { xhr :post, :index } end - + def test_should_allow_post_with_token post :index, :authenticity_token => @token assert_response :success end - + def test_should_allow_put_with_token put :index, :authenticity_token => @token assert_response :success end - + def test_should_allow_delete_with_token delete :index, :authenticity_token => @token assert_response :success end - + def test_should_allow_post_with_xml @request.env['CONTENT_TYPE'] = Mime::XML.to_s post :index, :format => 'xml' assert_response :success end - + def test_should_allow_put_with_xml @request.env['CONTENT_TYPE'] = Mime::XML.to_s put :index, :format => 'xml' assert_response :success end - + def test_should_allow_delete_with_xml @request.env['CONTENT_TYPE'] = Mime::XML.to_s delete :index, :format => 'xml' @@ -231,17 +222,17 @@ class FreeCookieControllerTest < ActionController::TestCase ActiveSupport::SecureRandom.stubs(:base64).returns(@token) end - + def test_should_not_render_form_with_token_tag get :index assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false end - + def test_should_not_render_button_to_with_token_tag get :show_button assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false end - + def test_should_allow_all_methods_without_token [:post, :put, :delete].each do |method| assert_nothing_raised { send(method, :index)} diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 0c5c5d17ee..c97343fbe5 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1230,26 +1230,6 @@ class FormHelperTest < ActionView::TestCase end - # Perhaps this test should be moved to prototype helper tests. - def test_remote_form_for_with_labelled_builder - self.extend ActionView::Helpers::PrototypeHelper - - remote_form_for(:post, @post, :builder => LabelledFormBuilder) do |f| - concat f.text_field(:title) - concat f.text_area(:body) - concat f.check_box(:secret) - end - - expected = - %(
) + - "
" + - "
" + - "
" + - "
" - - assert_dom_equal expected, output_buffer - end - def test_fields_for_with_labelled_builder fields_for(:post, @post, :builder => LabelledFormBuilder) do |f| concat f.text_field(:title) @@ -1396,15 +1376,6 @@ class FormHelperTest < ActionView::TestCase assert_equal expected, output_buffer end - def test_remote_form_for_with_html_options_adds_options_to_form_tag - self.extend ActionView::Helpers::PrototypeHelper - - remote_form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end - expected = "
" - - assert_dom_equal expected, output_buffer - end - protected def comments_path(post) "/posts/#{post.id}/comments" diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 51a03d25a4..d95bdc2b90 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -82,106 +82,6 @@ class PrototypeHelperTest < PrototypeHelperBaseTest super end - def test_form_remote_tag - assert_dom_equal %(
), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) - assert_dom_equal %(), - form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }) - assert_dom_equal %(), - form_remote_tag(:update => { :failure => "glass_of_water" }, :url => { :action => :fast }) - assert_dom_equal %(), - form_remote_tag(:update => { :success => 'glass_of_beer', :failure => "glass_of_water" }, :url => { :action => :fast }) - end - - def test_form_remote_tag_with_method - assert_dom_equal %(
), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :html => { :method => :put }) - end - - def test_form_remote_tag_with_block_in_erb - __in_erb_template = '' - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) { concat "Hello world!" } - assert_dom_equal %(Hello world!
), output_buffer - end - - def test_remote_form_for_with_record_identification_with_new_record - remote_form_for(@record, {:html => { :id => 'create-author' }}) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - def test_remote_form_for_with_record_identification_without_html_options - remote_form_for(@record) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - def test_remote_form_for_with_record_identification_with_existing_record - @record.save - remote_form_for(@record) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - def test_remote_form_for_with_new_object_in_list - remote_form_for([@author, @article]) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - def test_remote_form_for_with_existing_object_in_list - @author.save - @article.save - remote_form_for([@author, @article]) {} - - expected = %(
) - assert_dom_equal expected, output_buffer - end - - def test_on_callbacks - callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure] - callbacks.each do |callback| - assert_dom_equal %(
), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - assert_dom_equal %(), - form_remote_tag(:update => { :success => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();") - assert_dom_equal %(), - form_remote_tag(:update => { :failure => "glass_of_beer" }, :url => { :action => :fast }, callback=>"monkeys();") - assert_dom_equal %(), - form_remote_tag(:update => { :success => "glass_of_beer", :failure => "glass_of_water" }, :url => { :action => :fast }, callback=>"monkeys();") - end - - #HTTP status codes 200 up to 599 have callbacks - #these should work - 100.upto(599) do |callback| - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - end - - #test 200 and 404 - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, 200=>"monkeys();", 404=>"bananas();") - - #these shouldn't - 1.upto(99) do |callback| - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - end - 600.upto(999) do |callback| - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, callback=>"monkeys();") - end - - #test ultimate combo - assert_dom_equal %(), - form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }, :loading => "c1()", :success => "s()", :failure => "f();", :complete => "c();", 200=>"monkeys();", 404=>"bananas();") - - end - def test_update_page old_output_buffer = output_buffer -- cgit v1.2.3 From ad1924125d0aedec9d4555e1e13247e84d65fa01 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 16:02:26 -0600 Subject: Move scripty JSG helpers into scriptaculous_helper.rb --- .../lib/action_view/helpers/prototype_helper.rb | 26 ------------------ .../action_view/helpers/scriptaculous_helper.rb | 32 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 26 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 1fa2fed460..a8fba91354 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -519,32 +519,6 @@ module ActionView record "}, #{(seconds * 1000).to_i})" end - # Starts a script.aculo.us visual effect. See - # ActionView::Helpers::ScriptaculousHelper for more information. - def visual_effect(name, id = nil, options = {}) - record @context.send(:visual_effect, name, id, options) - end - - # Creates a script.aculo.us sortable element. Useful - # to recreate sortable elements after items get added - # or deleted. - # See ActionView::Helpers::ScriptaculousHelper for more information. - def sortable(id, options = {}) - record @context.send(:sortable_element_js, id, options) - end - - # Creates a script.aculo.us draggable element. - # See ActionView::Helpers::ScriptaculousHelper for more information. - def draggable(id, options = {}) - record @context.send(:draggable_element_js, id, options) - end - - # Creates a script.aculo.us drop receiving element. - # See ActionView::Helpers::ScriptaculousHelper for more information. - def drop_receiving(id, options = {}) - record @context.send(:drop_receiving_element_js, id, options) - end - private def loop_on_multiple_args(method, ids) record(ids.size>1 ? diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index d30164e402..189c14c97a 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -217,5 +217,37 @@ module ActionView %(Droppables.add(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end end + + module PrototypeHelper + class JavaScriptGenerator + module GeneratorMethods + # Starts a script.aculo.us visual effect. See + # ActionView::Helpers::ScriptaculousHelper for more information. + def visual_effect(name, id = nil, options = {}) + record @context.send(:visual_effect, name, id, options) + end + + # Creates a script.aculo.us sortable element. Useful + # to recreate sortable elements after items get added + # or deleted. + # See ActionView::Helpers::ScriptaculousHelper for more information. + def sortable(id, options = {}) + record @context.send(:sortable_element_js, id, options) + end + + # Creates a script.aculo.us draggable element. + # See ActionView::Helpers::ScriptaculousHelper for more information. + def draggable(id, options = {}) + record @context.send(:draggable_element_js, id, options) + end + + # Creates a script.aculo.us drop receiving element. + # See ActionView::Helpers::ScriptaculousHelper for more information. + def drop_receiving(id, options = {}) + record @context.send(:drop_receiving_element_js, id, options) + end + end + end + end end end -- cgit v1.2.3 From ff3f779629ef0d987642222ff2c74271b029e996 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 16:04:58 -0600 Subject: Unused JAVASCRIPT_PATH const --- actionpack/lib/action_view/helpers/javascript_helper.rb | 4 ---- 1 file changed, 4 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index f9249e667d..c84bd2ce96 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -35,10 +35,6 @@ module ActionView # For documentation on +javascript_include_tag+ see # ActionView::Helpers::AssetTagHelper. module JavaScriptHelper - unless const_defined? :JAVASCRIPT_PATH - JAVASCRIPT_PATH = File.join(File.dirname(__FILE__), 'javascripts') - end - include PrototypeHelper # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the -- cgit v1.2.3 From 570e02c96a12ad06888b4ba8d6d8bd3262705dcf Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 16:08:56 -0600 Subject: Move button_to_function to prototype helper --- .../lib/action_view/helpers/javascript_helper.rb | 33 ---------------------- .../lib/action_view/helpers/prototype_helper.rb | 33 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 33 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index c84bd2ce96..19243f092a 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -37,39 +37,6 @@ module ActionView module JavaScriptHelper include PrototypeHelper - # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the - # onclick handler. - # - # The first argument +name+ is used as the button's value or display text. - # - # The next arguments are optional and may include the javascript function definition and a hash of html_options. - # - # The +function+ argument can be omitted in favor of an +update_page+ - # block, which evaluates to a string when the template is rendered - # (instead of making an Ajax request first). - # - # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button" - # - # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil - # - # Examples: - # button_to_function "Greeting", "alert('Hello world!')" - # button_to_function "Delete", "if (confirm('Really?')) do_delete()" - # button_to_function "Details" do |page| - # page[:details].visual_effect :toggle_slide - # end - # button_to_function "Details", :class => "details_button" do |page| - # page[:details].visual_effect :toggle_slide - # end - def button_to_function(name, *args, &block) - html_options = args.extract_options!.symbolize_keys - - function = block_given? ? update_page(&block) : args[0] || '' - onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" - - tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) - end - JS_ESCAPE_MAP = { '\\' => '\\\\', ' '<\/', diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index a8fba91354..4a32875e00 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -102,6 +102,39 @@ module ActionView :form, :with, :update, :script, :type ]).merge(CALLBACKS) end + # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the + # onclick handler. + # + # The first argument +name+ is used as the button's value or display text. + # + # The next arguments are optional and may include the javascript function definition and a hash of html_options. + # + # The +function+ argument can be omitted in favor of an +update_page+ + # block, which evaluates to a string when the template is rendered + # (instead of making an Ajax request first). + # + # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button" + # + # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil + # + # Examples: + # button_to_function "Greeting", "alert('Hello world!')" + # button_to_function "Delete", "if (confirm('Really?')) do_delete()" + # button_to_function "Details" do |page| + # page[:details].visual_effect :toggle_slide + # end + # button_to_function "Details", :class => "details_button" do |page| + # page[:details].visual_effect :toggle_slide + # end + def button_to_function(name, *args, &block) + html_options = args.extract_options!.symbolize_keys + + function = block_given? ? update_page(&block) : args[0] || '' + onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};" + + tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick)) + end + # Returns the JavaScript needed for a remote function. # Takes the same arguments as link_to_remote. # -- cgit v1.2.3 From 657d85580e914caf368a8a12ff5642e4d979ab7e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 16:13:49 -0600 Subject: Reorg options_for_javascript and array_or_string_for_javascript --- actionpack/lib/action_view/helpers/javascript_helper.rb | 17 ----------------- actionpack/lib/action_view/helpers/prototype_helper.rb | 8 ++++++++ .../lib/action_view/helpers/scriptaculous_helper.rb | 9 +++++++++ 3 files changed, 17 insertions(+), 17 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 19243f092a..7dca9849c0 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -95,23 +95,6 @@ module ActionView def javascript_cdata_section(content) #:nodoc: "\n//#{cdata_section("\n#{content}\n//")}\n" end - - protected - def options_for_javascript(options) - if options.empty? - '{}' - else - "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" - end - end - - def array_or_string_for_javascript(option) - if option.kind_of?(Array) - "['#{option.join('\',\'')}']" - elsif !option.nil? - "'#{option}'" - end - end end end end diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 4a32875e00..d335d89274 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -624,6 +624,14 @@ module ActionView end protected + def options_for_javascript(options) + if options.empty? + '{}' + else + "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" + end + end + def options_for_ajax(options) js_options = build_callbacks(options) diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index 189c14c97a..37319cca1b 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -216,6 +216,15 @@ module ActionView %(Droppables.add(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});) end + + protected + def array_or_string_for_javascript(option) + if option.kind_of?(Array) + "['#{option.join('\',\'')}']" + elsif !option.nil? + "'#{option}'" + end + end end module PrototypeHelper -- cgit v1.2.3 From 7d9ed8eec1c899dac10d582ced07149fe5c58c4f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 19:20:49 -0600 Subject: Include prototype ujs adapter with new apps --- actionpack/lib/action_view/helpers/asset_tag_helper.rb | 2 +- actionpack/test/template/asset_tag_helper_test.rb | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 83357dd76f..4df99f8293 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -140,7 +140,7 @@ module ActionView :stylesheets_dir => "#{assets_dir}/stylesheets", } - JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) + JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES) # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 57802ebf42..586de66714 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -86,11 +86,11 @@ class AssetTagHelperTest < ActionView::TestCase %(javascript_include_tag("bank.js")) => %(), %(javascript_include_tag("bank", :lang => "vbscript")) => %(), %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(\n), - %(javascript_include_tag(:defaults)) => %(\n\n\n\n), + %(javascript_include_tag(:defaults)) => %(\n\n\n\n\n), %(javascript_include_tag(:all)) => %(\n\n\n\n\n\n\n), %(javascript_include_tag(:all, :recursive => true)) => %(\n\n\n\n\n\n\n\n), - %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n), - %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n), + %(javascript_include_tag(:defaults, "bank")) => %(\n\n\n\n\n\n), + %(javascript_include_tag("bank", :defaults)) => %(\n\n\n\n\n\n), %(javascript_include_tag("http://example.com/all")) => %(), %(javascript_include_tag("http://example.com/all.js")) => %(), @@ -235,7 +235,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_javascript_include_tag_with_given_asset_id ENV["RAILS_ASSET_ID"] = "1" - assert_dom_equal(%(\n\n\n\n), javascript_include_tag(:defaults)) + assert_dom_equal(%(\n\n\n\n\n), javascript_include_tag(:defaults)) end def test_javascript_include_tag_is_html_safe @@ -246,14 +246,14 @@ class AssetTagHelperTest < ActionView::TestCase def test_register_javascript_include_default ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' - assert_dom_equal %(\n\n\n\n\n), javascript_include_tag(:defaults) + assert_dom_equal %(\n\n\n\n\n\n), javascript_include_tag(:defaults) end def test_register_javascript_include_default_mixed_defaults ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'bank' ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'robber', '/elsewhere/cools.js' - assert_dom_equal %(\n\n\n\n\n\n\n), javascript_include_tag(:defaults) + assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag(:defaults) end def test_custom_javascript_expansions @@ -265,7 +265,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end ENV["RAILS_ASSET_ID"] = "" ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"] - assert_dom_equal %(\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') + assert_dom_equal %(\n\n\n\n\n\n\n\n\n), javascript_include_tag('controls',:defaults, :robbery, 'effects') end def test_custom_javascript_expansions_with_undefined_symbol @@ -965,6 +965,5 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase def test_assert_css_and_js_of_the_same_name_return_correct_extension assert_dom_equal(%(/collaboration/hieraki/javascripts/foo.js), javascript_path("foo")) assert_dom_equal(%(/collaboration/hieraki/stylesheets/foo.css), stylesheet_path("foo")) - end end -- cgit v1.2.3 From 392817cf11e2e840eb564dd4f8713092cff167f8 Mon Sep 17 00:00:00 2001 From: "Erik St. Martin" Date: Sat, 30 Jan 2010 19:44:35 -0600 Subject: updating link_to and button_to to support :remote => true and other options such as :confirm in a unobtrusive manor Signed-off-by: Joshua Peek --- .../lib/action_view/helpers/form_tag_helper.rb | 12 ++-- actionpack/lib/action_view/helpers/url_helper.rb | 77 +++++++++++++--------- actionpack/test/template/form_tag_helper_test.rb | 6 +- actionpack/test/template/url_helper_test.rb | 25 ++++--- 4 files changed, 70 insertions(+), 50 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 048bedc7ba..0e034d781a 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -57,7 +57,7 @@ module ActionView # ==== Examples # select_tag "people", options_from_collection_for_select(@people, "name", "id") # # - # + # # select_tag "people", "" # # => # @@ -128,7 +128,7 @@ module ActionView # Creates a label field # - # ==== Options + # ==== Options # * Creates standard HTML attributes for the tag. # # ==== Examples @@ -367,7 +367,7 @@ module ActionView if disable_with = options.delete("disable_with") disable_with = "this.value='#{disable_with}'" disable_with << ";#{options.delete('onclick')}" if options['onclick'] - + options["onclick"] = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }" options["onclick"] << "else { hiddenCommit = document.createElement('input');hiddenCommit.type = 'hidden';" options["onclick"] << "hiddenCommit.value = this.value;hiddenCommit.name = this.name;this.form.appendChild(hiddenCommit); }" @@ -377,8 +377,7 @@ module ActionView end if confirm = options.delete("confirm") - options["onclick"] ||= 'return true;' - options["onclick"] = "if (!#{confirm_javascript_function(confirm)}) return false; #{options['onclick']}" + add_confirm_to_attributes!(options, confirm) end tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys) @@ -411,8 +410,7 @@ module ActionView options.stringify_keys! if confirm = options.delete("confirm") - options["onclick"] ||= '' - options["onclick"] += "return #{confirm_javascript_function(confirm)};" + add_confirm_to_attributes!(options, confirm) end tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index d6bfc6d4c9..9e52275537 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -218,11 +218,11 @@ module ActionView html_options = args[2] url = url_for(options) + html_options = convert_options_to_data_attributes(options, html_options) if html_options html_options = html_options.stringify_keys href = html_options['href'] - convert_options_to_javascript!(html_options, url) tag_options = tag_options(html_options) else tag_options = nil @@ -291,14 +291,10 @@ module ActionView request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end - if confirm = html_options.delete("confirm") - html_options["onclick"] = "return #{confirm_javascript_function(confirm)};" - end - url = options.is_a?(String) ? options : self.url_for(options) name ||= url - convert_options_to_javascript!(html_options, url) + html_options = convert_options_to_data_attributes(options, html_options) html_options.merge!("type" => "submit", "value" => name) @@ -551,46 +547,65 @@ module ActionView end private - def convert_options_to_javascript!(html_options, url = '') + def convert_options_to_data_attributes(options, html_options) + html_options = {} if html_options.nil? + html_options = html_options.stringify_keys + + if (options.is_a?(Hash) && options.key?('remote')) || (html_options.is_a?(Hash) && html_options.key?('remote')) + html_options['data-remote'] = 'true' + options.delete('remote') if options.is_a?(Hash) + html_options.delete('remote') if html_options.is_a?(Hash) + end + confirm = html_options.delete("confirm") - method, href = html_options.delete("method"), html_options['href'] if html_options.key?("popup") ActiveSupport::Deprecation.warn(":popup has been deprecated", caller) end - html_options["onclick"] = case - when confirm && method - "if (#{confirm_javascript_function(confirm)}) { #{method_javascript_function(method, url, href)} };return false;" - when confirm - "return #{confirm_javascript_function(confirm)};" - when method - "#{method_javascript_function(method, url, href)}return false;" - else - html_options["onclick"] + method, href = html_options.delete("method"), html_options['href'] + + if confirm && method + add_confirm_to_attributes!(html_options, confirm) + add_method_to_attributes!(html_options, method) + elsif confirm + add_confirm_to_attributes!(html_options, confirm) + elsif method + add_method_to_attributes!(html_options, method) end + + html_options["data-url"] = options[:url] if options.is_a?(Hash) && options[:url] + + html_options end - def confirm_javascript_function(confirm) - "confirm('#{escape_javascript(confirm)}')" + def add_confirm_to_attributes!(html_options, confirm) + html_options["data-confirm"] = confirm if confirm end - def method_javascript_function(method, url = '', href = nil) - action = (href && url.size > 0) ? "'#{url}'" : 'this.href' - submit_function = - "var f = document.createElement('form'); f.style.display = 'none'; " + - "this.parentNode.appendChild(f); f.method = 'POST'; f.action = #{action};" + def add_method_to_attributes!(html_options, method) + html_options["rel"] = "nofollow" if method && method.to_s.downcase == "delete" + html_options["data-method"] = method if method + end - unless method == :post - submit_function << "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); " - submit_function << "m.setAttribute('name', '_method'); m.setAttribute('value', '#{method}'); f.appendChild(m);" + def add_disable_with_to_attributes!(html_options, disable_with) + html_options["data-disable-with"] = disable_with if disable_with + end + + def options_for_javascript(options) + if options.empty? + '{}' + else + "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}" end + end - if protect_against_forgery? - submit_function << "var s = document.createElement('input'); s.setAttribute('type', 'hidden'); " - submit_function << "s.setAttribute('name', '#{request_forgery_protection_token}'); s.setAttribute('value', '#{escape_javascript form_authenticity_token}'); f.appendChild(s);" + def array_or_string_for_javascript(option) + if option.kind_of?(Array) + "['#{option.join('\',\'')}']" + elsif !option.nil? + "'#{option}'" end - submit_function << "f.submit();" end # Processes the +html_options+ hash, converting the boolean diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 47462b1237..41ab5c1621 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -299,21 +299,21 @@ class FormTagHelperTest < ActionView::TestCase def test_submit_tag_with_confirmation assert_dom_equal( - %(), + %(), submit_tag("Save", :confirm => "Are you sure?") ) end def test_submit_tag_with_confirmation_and_with_disable_with assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?") ) end def test_image_submit_tag_with_confirmation assert_dom_equal( - %(), + %(), image_submit_tag("save.gif", :confirm => "Are you sure?") ) end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index dd28aec786..6d9384d40b 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -76,7 +76,7 @@ class UrlHelperTest < ActionView::TestCase def test_button_to_with_javascript_confirm assert_dom_equal( - "
", + "
", button_to("Hello", "http://www.example.com", :confirm => "Are you sure?") ) end @@ -170,50 +170,57 @@ class UrlHelperTest < ActionView::TestCase def test_link_tag_with_javascript_confirm assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :confirm => "Are you sure?") ) assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure, can you?") ) assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :confirm => "You can't possibly be sure,\n can you?") ) end + def test_link_to_with_remote + assert_dom_equal( + "Hello", + link_to("Hello", "http://www.example.com", :remote => true) + ) + end + def test_link_tag_using_post_javascript assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :method => :post) ) end def test_link_tag_using_delete_javascript assert_dom_equal( - "Destroy", + "Destroy", link_to("Destroy", "http://www.example.com", :method => :delete) ) end def test_link_tag_using_delete_javascript_and_href assert_dom_equal( - "Destroy", + "Destroy", link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#') ) end def test_link_tag_using_post_javascript_and_confirm assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?") ) end def test_link_tag_using_delete_javascript_and_href_and_confirm assert_dom_equal( - "Destroy", + "Destroy", link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"), "When specifying url, form should be generated with it, but not this.href" ) -- cgit v1.2.3 From 31820b9dd60dc94744028b41f818f1fdab36e271 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 20:22:15 -0600 Subject: Generate UJS code for :disable_with --- actionpack/lib/action_view/helpers/form_tag_helper.rb | 11 +---------- actionpack/lib/action_view/helpers/url_helper.rb | 4 ---- actionpack/test/template/form_tag_helper_test.rb | 6 +++--- 3 files changed, 4 insertions(+), 17 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 0e034d781a..fb32f78e5b 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -365,15 +365,7 @@ module ActionView options.stringify_keys! if disable_with = options.delete("disable_with") - disable_with = "this.value='#{disable_with}'" - disable_with << ";#{options.delete('onclick')}" if options['onclick'] - - options["onclick"] = "if (window.hiddenCommit) { window.hiddenCommit.setAttribute('value', this.value); }" - options["onclick"] << "else { hiddenCommit = document.createElement('input');hiddenCommit.type = 'hidden';" - options["onclick"] << "hiddenCommit.value = this.value;hiddenCommit.name = this.name;this.form.appendChild(hiddenCommit); }" - options["onclick"] << "this.setAttribute('originalValue', this.value);this.disabled = true;#{disable_with};" - options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());" - options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;" + options["data-disable-with"] = disable_with if disable_with end if confirm = options.delete("confirm") @@ -490,7 +482,6 @@ module ActionView def sanitize_to_id(name) name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_") end - end end end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 9e52275537..7d42a2ef69 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -588,10 +588,6 @@ module ActionView html_options["data-method"] = method if method end - def add_disable_with_to_attributes!(html_options, disable_with) - html_options["data-disable-with"] = disable_with if disable_with - end - def options_for_javascript(options) if options.empty? '{}' diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb index 41ab5c1621..01bde8ea04 100644 --- a/actionpack/test/template/form_tag_helper_test.rb +++ b/actionpack/test/template/form_tag_helper_test.rb @@ -285,14 +285,14 @@ class FormTagHelperTest < ActionView::TestCase def test_submit_tag assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...", :onclick => "alert('hello!')") ) end def test_submit_tag_with_no_onclick_options assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...") ) end @@ -306,7 +306,7 @@ class FormTagHelperTest < ActionView::TestCase def test_submit_tag_with_confirmation_and_with_disable_with assert_dom_equal( - %(), + %(), submit_tag("Save", :disable_with => "Saving...", :confirm => "Are you sure?") ) end -- cgit v1.2.3 From 48459c82783c53e1ac671259d65c806384f60f2b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 30 Jan 2010 20:26:30 -0600 Subject: Add rel=nofollow on non get remote links --- actionpack/lib/action_view/helpers/url_helper.rb | 2 +- actionpack/test/template/url_helper_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 7d42a2ef69..bd179ef0b3 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -584,7 +584,7 @@ module ActionView end def add_method_to_attributes!(html_options, method) - html_options["rel"] = "nofollow" if method && method.to_s.downcase == "delete" + html_options["rel"] = "nofollow" if method && method.to_s.downcase != "get" html_options["data-method"] = method if method end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 6d9384d40b..b498ec8429 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -192,7 +192,7 @@ class UrlHelperTest < ActionView::TestCase def test_link_tag_using_post_javascript assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :method => :post) ) end @@ -213,7 +213,7 @@ class UrlHelperTest < ActionView::TestCase def test_link_tag_using_post_javascript_and_confirm assert_dom_equal( - "Hello", + "Hello", link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?") ) end -- cgit v1.2.3 From b3a028259f373fd58fea2171a1e9e8b2fe3e253a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 31 Jan 2010 10:24:38 +0100 Subject: Improve missing template error messages a little bit. --- actionpack/lib/action_view/base.rb | 17 +++++++++++------ actionpack/lib/action_view/paths.rb | 2 +- actionpack/test/controller/mime_responds_test.rb | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index af13f2cd3e..07ef3f2140 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -6,15 +6,20 @@ module ActionView #:nodoc: end class MissingTemplate < ActionViewError #:nodoc: - attr_reader :path, :action_name + attr_reader :path - def initialize(paths, path, template_format = nil) + def initialize(paths, path, details, partial) @path = path - @action_name = path.split("/").last.split(".")[0...-1].join(".") - full_template_path = path.include?('.') ? path : "#{path}.erb" display_paths = paths.compact.join(":") - template_type = (path =~ /layouts/i) ? 'layout' : 'template' - super("Missing #{template_type} #{full_template_path} in view path #{display_paths}") + template_type = if partial + "partial" + elsif path =~ /layouts/i + 'layout' + else + 'template' + end + + super("Missing #{template_type} #{path} with #{details.inspect} in view path #{display_paths}") end end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 0059b79e5f..6e55d69d00 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -45,7 +45,7 @@ module ActionView #:nodoc: end end - raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path} - #{details.inspect} - partial: #{!!partial}") + raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) end def exists?(path, extension = nil, prefix = nil, partial = false) diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index ba2347e4e2..3bd3369242 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -499,7 +499,7 @@ class RespondWithController < ActionController::Base def using_resource_with_action respond_with(resource, :action => :foo) do |format| - format.html { raise ActionView::MissingTemplate.new([], "method") } + format.html { raise ActionView::MissingTemplate.new([], "foo/bar", {}, false) } end end -- cgit v1.2.3