aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2010-01-31 14:32:26 +0000
committerPratik Naik <pratiknaik@gmail.com>2010-01-31 14:32:26 +0000
commited60021f39f7913537dcad181e4061e423dc6c3d (patch)
tree86532c43aabe9125422f84b48a899f2672900bf5 /actionpack/lib
parentc6af337d2d6c86792cdc8132224ebe9294d35774 (diff)
parentb3a028259f373fd58fea2171a1e9e8b2fe3e253a (diff)
downloadrails-ed60021f39f7913537dcad181e4061e423dc6c3d.tar.gz
rails-ed60021f39f7913537dcad181e4061e423dc6c3d.tar.bz2
rails-ed60021f39f7913537dcad181e4061e423dc6c3d.zip
Merge remote branch 'mainstream/master'
Conflicts: activemodel/lib/active_model/state_machine.rb
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller.rb3
-rw-r--r--actionpack/lib/abstract_controller/compatibility.rb18
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb12
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb14
-rw-r--r--actionpack/lib/abstract_controller/url_for.rb156
-rw-r--r--actionpack/lib/action_controller.rb10
-rw-r--r--actionpack/lib/action_controller/base.rb8
-rw-r--r--actionpack/lib/action_controller/deprecated.rb2
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb15
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb17
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb5
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb149
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/testing/test_response.rb5
-rw-r--r--actionpack/lib/action_view/base.rb17
-rw-r--r--actionpack/lib/action_view/helpers.rb7
-rw-r--r--actionpack/lib/action_view/helpers/ajax_helper.rb713
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb36
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb152
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb170
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb142
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb80
-rw-r--r--actionpack/lib/action_view/paths.rb2
25 files changed, 493 insertions, 1254 deletions
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 2c2ef16622..1e15ab090c 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'
@@ -11,11 +12,11 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
+ autoload :Compatibility
autoload :Helpers
autoload :Layouts
autoload :LocalizedCache
autoload :Logger
autoload :Rendering
autoload :Translation
- autoload :UrlFor
end
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/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 1dec3f2c3e..ac407bda5e 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -157,13 +157,23 @@ 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?
+
+ 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/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 <b>routes.rb</b> 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.
- #
- # <b>Tip:</b> 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 <tt>:only_path => true</tt> 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 <tt>:host</tt>
- # 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 <tt>:host</tt>
- # 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 <tt>:host</tt> 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
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # This generates, among other things, the method <tt>users_path</tt>. 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:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> - Optionally specify the port to connect to.
- # * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in AbstractController::Base.relative_url_root.
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
- #
- # Any other key (<tt>:controller</tt>, <tt>:action</tt>, 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/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..05843a061b 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -56,18 +56,15 @@ 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
- 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 <tt>name=</tt> instance methods to a
# controller and makes them available to the view:
@@ -101,16 +98,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 = []
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/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 <b>routes.rb</b> 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.
+ #
+ # <b>Tip:</b> 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 <tt>:only_path => true</tt> 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 <tt>:host</tt>
+ # 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 <tt>:host</tt>
+ # 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 <tt>:host</tt> 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
+ # <b>routes.rb</b>:
+ #
+ # map.resources :users
+ #
+ # This generates, among other things, the method <tt>users_path</tt>. 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:
+ #
+ # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+ # * <tt>:host</tt> - Specifies the host the link should be targeted at.
+ # If <tt>:only_path</tt> is false, this option must be
+ # provided either explicitly, or via +default_url_options+.
+ # * <tt>:port</tt> - Optionally specify the port to connect to.
+ # * <tt>:anchor</tt> - An anchor name to be appended to the path.
+ # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
+ # +relative_url_root+ set in ActionController::Base.relative_url_root.
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
+ #
+ # Any other key (<tt>:controller</tt>, <tt>:action</tt>, 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/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?
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/helpers.rb b/actionpack/lib/action_view/helpers.rb
index 080eb87445..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 :AjaxHelperCompat, '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 +14,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 +47,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
deleted file mode 100644
index 169a803848..0000000000
--- a/actionpack/lib/action_view/helpers/ajax_helper.rb
+++ /dev/null
@@ -1,713 +0,0 @@
-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 <tt>params</tt>). The options
- # for specifying the target with <tt>:url</tt> anddefining callbacks is the same as +link_to_remote+.
- #
- # === Resource
- #
- # Example:
- #
- # # Generates:
- # # <form action='/authors'
- # # data-remote='true'
- # # class='new_author'
- # # id='create-author'
- # # method='post'> ... </form>
- # #
- # <% 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:
- # # <form action='/authors/1/articles'
- # # data-remote="true"
- # # class='new_article'
- # # method='post'
- # # id='new_article'></form>
- # #
- # <% 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('</form>'.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
- # <tt>params</tt>). The options for specifying the target with <tt>:url</tt> 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 <tt>:action</tt>/<tt>:method</tt> options on <tt>:html</tt>.
- #
- # Example:
- #
- # # Generates:
- # # <form action="http://www.example.com/fast"
- # # method="post"
- # # data-remote="true"
- # # data-update-success="glass_of_beer"></form>
- # #
- # form_remote_tag(:update => "glass_of_beer", :url => { :action => :fast }) {}
- #
- # The Hash passed to the <tt>:html</tt> 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 <tt>:url</tt> (and the default method is <tt>:post</tt>).
- #
- # form_remote_tag also takes a block, like form_tag:
- # # Generates:
- # # <form action='/posts'
- # # method='post'
- # # data-remote='true'>
- # # <input name="commit" type="submit" value="Save" />
- # # </form>
- # #
- # <% form_remote_tag :url => '/posts' do -%>
- # <%= submit_tag 'Save' %>
- # <% end -%>
- #
- # # Generates:
- # # <form action="http://www.example.com/fast"
- # # method="post"
- # # data-remote="true"
- # # data-update-success="glass_of_beer">Hello world!</form>
- # #
- # <% 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 <tt>options[:update]</tt>. Usually,
- # the result would be a partial prepared by the controller with render :partial.
- #
- # Examples:
- #
- # # Generates:
- # # <a href="#"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot"
- # # data-method="delete"
- # # rel="nofollow">Remove Author</a>
- # #
- # link_to_remote("Remove Author", { :url => { :action => "whatnot" },
- # :method => "delete"})
- #
- #
- # You can override the generated HTML options by specifying a hash in
- # <tt>options[:html]</tt>.
- #
- # # Generates:
- # # <a class="fine"
- # # href="#"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot"
- # # data-method="delete"
- # # rel="nofollow">Remove Author</a>
- # #
- # link_to_remote("Remove Author", { :url => { :action => "whatnot" },
- # :method => "delete",
- # :html => { :class => "fine" }})
- #
- #
- # You can also specify a hash for <tt>options[:update]</tt> to allow for
- # easy redirection of output to an other DOM element if a server-side
- # error occurs:
- #
- # Example:
- # # Generates:
- # #
- # # <a href="#"
- # # data-url="http://www.example.com/destroy"
- # # data-update-success="posts"
- # # data-update-failure="error"
- # # data-remote="true">Delete this Post</a>'
- # #
- # link_to_remote "Delete this post",
- # :url => { :action => "destroy"},
- # :update => { :success => "posts", :failure => "error" }
- #
- # Optionally, you can use the <tt>options[:position]</tt> parameter to
- # influence how the target DOM element is updated. It must be one of
- # <tt>:before</tt>, <tt>:top</tt>, <tt>:bottom</tt>, or <tt>:after</tt>.
- #
- # Example:
- # # Generates:
- # # <a href="#"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot"
- # # data-update-position="bottom">Remove Author</a>
- # #
- # 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 <tt>options[:method]</tt>
- #
- # Example:
- # # Generates:
- # # <a href='#'
- # # data-url='/person/4'
- # # rel='nofollow'
- # # data-remote='true'
- # # data-method='delete'>Destroy</a>
- # #
- # 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
- # <tt>request</tt> object, which holds the underlying XMLHttpRequest.
- #
- # To access the server response, use <tt>request.responseText</tt>, to
- # find out the HTTP status, use <tt>request.status</tt>.
- #
- # Example:
- # # Generates:
- # #
- # # <a href='#'
- # # data-url='http://www.example.com/undo?n=5'
- # # data-oncomplete='undoRequestCompleted(request)'
- # # data-remote='true'>undo</a>
- # #
- # link_to_remote "undo",
- # :url => { :controller => "words", :action => "undo", :n => word_counter },
- # :complete => "undoRequestCompleted(request)"
- #
- # The callbacks that may be specified are (in order):
- #
- # <tt>:loading</tt>:: Called when the remote document is being
- # loaded with data by the browser.
- # <tt>:loaded</tt>:: Called when the browser has finished loading
- # the remote document.
- # <tt>:interactive</tt>:: Called when the user can interact with the
- # remote document, even though it has not
- # finished loading.
- # <tt>:success</tt>:: Called when the XMLHttpRequest is completed,
- # and the HTTP status code is in the 2XX range.
- # <tt>:failure</tt>:: Called when the XMLHttpRequest is completed,
- # and the HTTP status code is not in the 2XX
- # range.
- # <tt>:complete</tt>:: Called when the XMLHttpRequest is complete
- # (fires after success/failure if they are
- # present).
- #
- # You can further refine <tt>:success</tt> and <tt>:failure</tt> by
- # adding additional callbacks for specific status codes.
- #
- # Example:
- #
- # # Generates:
- # # <a href='/testing/action'
- # # date-remote='true'
- # # data-failure="function(request){alert('HTTP Error '+ request.status +'+!');return false}"
- # # data-404="function(request){alert('Not found...? Wrong URL...?')}"> Hello</a>
- # #
- # 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
- # <tt>options[:type] = :synchronous</tt>.
- #
- # 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:
- #
- # <tt>:confirm</tt>:: Adds confirmation dialog.
- # <tt>:condition</tt>:: Perform remote request conditionally
- # by this expression. Use this to
- # describe browser-side conditions when
- # request should not be initiated.
- # <tt>:before</tt>:: Called before request is initiated.
- # <tt>:after</tt>:: Called immediately after request was
- # initiated and before <tt>:loading</tt>.
- # <tt>:submit</tt>:: 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.
- # <tt>:with</tt>:: 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 <tt>html_options[:href]</tt> to an alternate URL.
- # Note the extra curly braces around the <tt>options</tt> hash separate
- # it as the second parameter from <tt>html_options</tt>, the third.
- #
- # Example:
- #
- # # Generates:
- # # <a href='/posts/1'
- # # rel='nofollow'
- # # data-remote='true'
- # # data-method='delete'> Delete this post</a>
- # #
- # 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))
-
- 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:
- # # <input class="fine"
- # # type="button"
- # # value="Remote outpost"
- # # data-remote="true"
- # # data-url="http://www.example.com/whatnot" />
- # #
- # 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:
- # # <input name='create_btn'
- # # type='button'
- # # value='Create'
- # # data-remote='true'
- # # data-url='/create' />
- # #
- # <%= 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:
- # # <input name='update_btn'
- # # type='button'
- # # value='Update'
- # # date-remote-submit='true'
- # # data-url='/testing/update'
- # # data-success='succeed'
- # # data-failure='fail' />
- # #
- # <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' },
- # :update => { :success => "succeed", :failure => "fail" }
- #
- # <tt>options</tt> 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 (<tt>options[:url]</tt>) every <tt>options[:frequency]</tt> seconds (default is 10). Usually used to
- # update a specified div (<tt>options[:update]</tt>) with the results
- # of the remote call. The options for specifying the target with <tt>:url</tt>
- # and defining callbacks is the same as link_to_remote.
- # Examples:
- # # Call get_averages and put its results in 'avg' every 10 seconds
- # # Generates:
- # # <script data-periodical='true'
- # # data-url='/get_averages'
- # # type='application/json'
- # # data-update-success='avg'
- # # data-frequency='10'></script>
- # #
- # 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:
- # # <script data-periodical='true'
- # # data-url='/invoice/1'
- # # type='application/json'
- # # data-update-success='invoice'
- # # data-update-failure='error'
- # # data-frequency='10'></script>"
- # #
- # 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:
- # # <script data-periodical='true'
- # # data-url='update'
- # # type='application/json'
- # # data-update-success='news_block'
- # # data-frequency='20'></script>
- # #
- # 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:
- # # "<script type='text/javascript'
- # # data-observe='true'
- # # data-observed='suggest'
- # # data-frequency='0.25'
- # # type='application/json'
- # # data-url='/find_suggestion'
- # # data-update-success='suggest'
- # # data-with='q'></script>"
- # #
- # <%= observe_field :suggest, :url => { :action => :find_suggestion },
- # :frequency => 0.25,
- # :update => :suggest,
- # :with => 'q'
- # %>
- #
- # Required +options+ are either of:
- # <tt>:url</tt>:: +url_for+-style options for the action to call
- # when the field has changed.
- # <tt>:function</tt>:: 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:
- # <tt>:frequency</tt>:: 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.
- # <tt>:update</tt>:: Specifies the DOM ID of the element whose
- # innerHTML should be updated with the
- # XMLHttpRequest response text.
- # <tt>:with</tt>:: 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
- # <em>Common options</em> 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
- # <tt>:with</tt> 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 + '"'}
- "<script " + attributes.join(" ") + "></script>"
- 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)
- 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
- 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
- end
-
- def observe_form(name, options = {})
- html = {}
- set_with_and_condition_attributes(options, html)
- options.merge!(:callbacks => html)
- super
- end
-
- private
- 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)
- end
-
- options.each do |option, value|
- if option.is_a?(Integer)
- html["data-on#{option}"] = 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
- end
- end
- end
- end
-end
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 <tt>:rss</tt> (default) or
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index b81925c7e7..157deebe40 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..fb32f78e5b 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 id="people" name="people"><option value="1">David</option></select>
- #
+ #
# select_tag "people", "<option>David</option>"
# # => <select id="people" name="people"><option>David</option></select>
#
@@ -128,7 +128,7 @@ module ActionView
# Creates a label field
#
- # ==== Options
+ # ==== Options
# * Creates standard HTML attributes for the tag.
#
# ==== Examples
@@ -352,20 +352,20 @@ module ActionView
# # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
#
# submit_tag "Complete sale", :disable_with => "Please wait..."
- # # => <input name="commit" data-disable-with="Please wait..."
+ # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();"
# # type="submit" value="Complete sale" />
#
# submit_tag nil, :class => "form_submit"
# # => <input class="form_submit" name="commit" type="submit" />
#
# submit_tag "Edit", :disable_with => "Editing...", :class => "edit-button"
- # # => <input class="edit-button" data-disable-with="Editing..."
+ # # => <input class="edit-button" onclick="this.disabled=true;this.value='Editing...';this.form.submit();"
# # name="commit" type="submit" value="Edit" />
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)
+ options["data-disable-with"] = disable_with if disable_with
end
if confirm = options.delete("confirm")
@@ -482,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/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index ee6481b86d..7dca9849c0 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -35,98 +35,7 @@ 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 AjaxHelperCompat
-
- # 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:
- # <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
- #
- # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
- # Produces:
- # <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
- # <img src="/images/delete.png?" alt="Delete"/>
- # </a>
- #
- # 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:
- # <a href="#" id="more_link" onclick="try {
- # $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
- # $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
- # }
- # catch (e) {
- # alert('RJS error:\n\n' + e.toString());
- # alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
- # \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
- # throw e
- # };
- # return false;">Show me more</a>
- #
- 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.
- #
- # 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
+ include PrototypeHelper
JS_ESCAPE_MAP = {
'\\' => '\\\\',
@@ -186,65 +95,6 @@ module ActionView
def javascript_cdata_section(content) #:nodoc:
"\n//#{cdata_section("\n#{content}\n//")}\n"
end
-
- protected
- def convert_options_to_javascript!(html_options, url = '')
- confirm, popup = html_options.delete("confirm"), html_options.delete("popup")
-
- 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
- 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
-
- 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 popup_attributes(popup)
- popup.is_a?(Array) ? "{title: '#{popup.first}', options: '#{popup.last}'}" : "true"
- end
-
- 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 d861810f19..d335d89274 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,26 @@ 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 -%>
+ # <div><%= submit_tag 'Recalculate Shipping' %></div>
+ # <% end -%>
+ #
+ # 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 +93,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 +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.
#
@@ -502,32 +552,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 ?
@@ -599,65 +623,57 @@ 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'] << " + '&"
+ protected
+ def options_for_javascript(options)
+ if options.empty?
+ '{}'
else
- js_options['parameters'] = "'"
+ "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
end
- js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
end
- options_for_javascript(js_options)
- end
+ def options_for_ajax(options)
+ js_options = build_callbacks(options)
- def method_option_to_s(method)
- (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
- end
+ 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]
- def build_observer(klass, name, options = {})
- if options[:with] && (options[:with] !~ /[\{=(.]/)
- options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)"
- else
- options[:with] ||= 'value' unless options[:function]
+ 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'] = "'"
+ end
+ js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
+ end
+
+ 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/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
index 04af2781d7..37319cca1b 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 <tt>:toggle_appear</tt>, <tt>:toggle_slide</tt>, 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:
#
# * <tt>:format</tt> - A regular expression to determine what to send as the
# serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>).
- #
+ #
# * <tt>:constraint</tt> - Whether to constrain the dragging to either
# <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained).
- #
+ #
# * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt>
# or <tt>:vertical</tt> direction.
- #
+ #
# * <tt>:tag</tt> - Which children of the container element to treat as
# sortable (default is <tt>li</tt>).
- #
+ #
# * <tt>:containment</tt> - Takes an element or array of elements to treat as
# potential drop targets (defaults to the original target element).
- #
+ #
# * <tt>:only</tt> - A CSS class name or array of class names used to filter
# out child elements as candidates.
- #
+ #
# * <tt>:scroll</tt> - Determines whether to scroll the list during drag
# operations if the list runs past the visual border.
- #
+ #
# * <tt>:tree</tt> - 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.
- #
+ #
# * <tt>:hoverclass</tt> - 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.
+ #
# * <tt>:handle</tt> - 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.
- #
+ #
# * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving
# the original in place until the clone is dropped (default is <tt>false</tt>).
- #
+ #
# * <tt>:dropOnEmpty</tt> - 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 <tt>false</tt>).
- #
+ #
# * <tt>:onChange</tt> - 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.
- #
+ #
# * <tt>:onUpdate</tt> - 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,46 +176,87 @@ module ActionView
#
# Some of these +options+ include:
# * <tt>:accept</tt> - 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+.
- #
+ #
# * <tt>:confirm</tt> - Adds a confirmation dialog. Example:
- #
+ #
# :confirm => "Are you sure you want to do this?"
- #
+ #
# * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have
# this additional CSS class when an accepted +draggable_element+ is
- # hovered over it.
- #
+ # hovered over it.
+ #
# * <tt>:onDrop</tt> - 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.
- #
+ #
# * <tt>:with</tt> - 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]
%(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
+ 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
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 4424dbba42..bd179ef0b3 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
# * <tt>:confirm => 'question?'</tt> - 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.
- # * <tt>:popup => true || array of window options</tt> - 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 <tt>window.open</tt> JavaScript call.
# * <tt>:method => symbol of HTTP verb</tt> - 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 <tt>post?</tt>, <tt>delete?</tt> or <tt>put?</tt>.
# * 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
- # <tt>:popup</tt> and <tt>:method</tt> which will raise an
- # <tt>ActionView::ActionViewError</tt> 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")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
- # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows:
+ # The three options specific to +link_to+ (<tt>:confirm</tt> and <tt>:method</tt>) are used as follows:
#
# link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?"
# # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a>
#
- # link_to "Help", { :action => "help" }, :popup => true
- # # => <a href="/testing/help/" onclick="window.open(this.href);return false;">Help</a>
- #
- # link_to "View Image", @image, :popup => ['new_window_name', 'height=300,width=600']
- # # => <a href="/images/9" onclick="window.open(this.href,'new_window_name','height=300,width=600');return false;">View Image</a>
- #
# link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete
# # => <a href="/images/9" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');
# f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;
@@ -232,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
@@ -305,11 +291,10 @@ module ActionView
request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
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)
@@ -562,6 +547,63 @@ module ActionView
end
private
+ 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")
+
+ 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)
+ 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 add_confirm_to_attributes!(html_options, confirm)
+ html_options["data-confirm"] = confirm if confirm
+ end
+
+ def add_method_to_attributes!(html_options, method)
+ html_options["rel"] = "nofollow" if method && method.to_s.downcase != "get"
+ html_options["data-method"] = method if method
+ end
+
+ 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
+
# 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/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)