diff options
-rw-r--r-- | actionpack/lib/action_view/helpers/ajax_helper.rb | 65 | ||||
-rw-r--r-- | actionpack/test/javascript/ajax_test.rb | 123 |
2 files changed, 164 insertions, 24 deletions
diff --git a/actionpack/lib/action_view/helpers/ajax_helper.rb b/actionpack/lib/action_view/helpers/ajax_helper.rb index 688a5262c7..e127641f4f 100644 --- a/actionpack/lib/action_view/helpers/ajax_helper.rb +++ b/actionpack/lib/action_view/helpers/ajax_helper.rb @@ -2,26 +2,41 @@ module ActionView module Helpers module AjaxHelper include UrlHelper - - def link_to_remote(name, url, options = {}) - html = options.delete(:html) || {} + + def form_remote_tag(options = {}, &block) + attributes = {} + attributes.merge!(extract_remote_attributes!(options)) + attributes.merge!(options) + + url = attributes.delete(:url) + form_tag(attributes.delete(:action) || url_for(url), attributes, &block) + end + + def extract_remote_attributes!(options) + attributes = options.delete(:html) || {} update = options.delete(:update) if update.is_a?(Hash) - html["data-update-success"] = update[:success] - html["data-update-failure"] = update[:failure] + attributes["data-update-success"] = update[:success] + attributes["data-update-failure"] = update[:failure] else - html["data-update-success"] = update + attributes["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) + attributes["data-update-position"] = options.delete(:position) + attributes["data-method"] = options.delete(:method) + attributes["data-remote"] = true + + attributes + end + + def link_to_remote(name, url, options = {}) + attributes = {} + attributes.merge!(extract_remote_attributes!(options)) + attributes.merge!(options) url = url_for(url) if url.is_a?(Hash) - link_to(name, url, html) + link_to(name, url, attributes) end def button_to_remote(name, options = {}, html_options = {}) @@ -72,17 +87,6 @@ module ActionView SCRIPT end - # TODO: Move to javascript helpers - BR - class JSFunction - def initialize(statements, *arguments) - @statements, @arguments = statements, arguments - end - - def as_json(options = nil) - "function(#{@arguments.join(", ")}) {#{@statements}}" - end - end - module Rails2Compatibility def set_callbacks(options, html) [:complete, :failure, :success, :interactive, :loaded, :loading].each do |type| @@ -111,7 +115,20 @@ module ActionView super end end - + + private + + # TODO: Move to javascript helpers - BR + class JSFunction + def initialize(statements, *arguments) + @statements, @arguments = statements, arguments + end + + def as_json(options = nil) + "function(#{@arguments.join(", ")}) {#{@statements}}" + end + end + end end end
\ No newline at end of file diff --git a/actionpack/test/javascript/ajax_test.rb b/actionpack/test/javascript/ajax_test.rb index dc1f7c14fd..49942f6681 100644 --- a/actionpack/test/javascript/ajax_test.rb +++ b/actionpack/test/javascript/ajax_test.rb @@ -2,7 +2,22 @@ require "abstract_unit" class AjaxTestCase < ActiveSupport::TestCase include ActionView::Helpers::AjaxHelper + + # TODO: Ask Katz: Should these be included by the AjaxHelper? - BR include ActionView::Helpers::TagHelper + include ActionView::Helpers::FormTagHelper + + # TODO: Replace with the real url_for method - BR + def url_for(url) + case url + when Hash + "/url/hash" + when String + url + else + raise TypeError.new("Unsupported url type (#{url.class}) for this test helper") + end + end def assert_html(html, matches) matches.each do |match| @@ -10,6 +25,12 @@ class AjaxTestCase < ActiveSupport::TestCase end end + def assert_html_not_present(html, matches) + matches.each do |match| + assert_no_match Regexp.new(Regexp.escape(match)), html + end + end + def extract_json_from_data_element(data_element) root = HTML::Document.new(data_element).root script = root.find(:tag => "script") @@ -98,6 +119,108 @@ class LinkToRemoteTest < AjaxTestCase end end +class FormRemoteTagTest < AjaxTestCase + + def protect_against_forgery? + false + end + + def request_forgery_protection_token + "token_name" + end + + def form_authenticity_token + "t0k3n" + end + + def authenticity_input_attributes + %w(input type="hidden" name="token_name" value="t0k3n") + end + + # TODO: Play with using assert_dom_equal + test "basic" do + assert_html form_remote_tag(:update => "#glass_of_beer", :url => { :action => :fast }), + %w(form action="/url/hash" method="post" data-remote="true" data-update-success="#glass_of_beer") + end + + test "when protect_against_forgery? is true" do + def protect_against_forgery? + true + end + + expected_form_attributes = %w(form action="/url/hash" method="post" data-remote="true" data-update-success="#glass_of_beer") + expected_patterns = expected_form_attributes + authenticity_input_attributes + + assert_equal true, protect_against_forgery? + assert_html form_remote_tag(:update => "#glass_of_beer", :url => { :action => :fast }), expected_patterns + end + + test ":action is used when it is present" do + html = form_remote_tag(:update => "#glass_of_beer", :action => "foo") + + assert_html html, %w(form action="foo" method="post" data-remote="true" data-update-success="#glass_of_beer") + assert_no_match /url="foo"/, html + end + + test ":url is used when :action is not present" do + html = form_remote_tag(:update => "#glass_of_beer", :url => "bar") + + assert_html html, %w(form action="bar" method="post" data-remote="true" data-update-success="#glass_of_beer") + assert_no_match /url="bar"/, html + end + + test "when protect_against_forgery? is false" do + assert_equal false, protect_against_forgery? + assert_html_not_present form_remote_tag(:update => "#glass_of_beer", :url => { :action => :fast }), + authenticity_input_attributes + end + + test "update callbacks" do + assert_html form_remote_tag(:update => { :success => "#glass_of_beer" }, :url => { :action => :fast }), + %w(form action="/url/hash" method="post" data-remote="true" data-update-success="#glass_of_beer") + + assert_html form_remote_tag(:update => { :failure => "#glass_of_water" }, :url => { :action => :fast }), + %w(form action="/url/hash" method="post" data-remote="true" data-update-failure="#glass_of_water") + + assert_html form_remote_tag(:update => { :success => "#glass_of_beer", :failure => "#glass_of_water" }, :url => { :action => :fast }), + %w(form action="/url/hash" method="post" data-remote="true" data-update-success="#glass_of_beer" data-update-failure="#glass_of_water") + end + + test "using a :method option" do + expected_form_attributes = %w(form action="/url/hash" method="post" data-remote="true" data-update-success="#glass_of_beer") + # TODO: Ask Katz: Why does rails do this? Some web servers don't allow PUT or DELETE from what I remember... - BR + expected_input_attributes = %w(input name="_method" type="hidden" value="put") + + assert_html form_remote_tag(:update => "#glass_of_beer", :url => { :action => :fast }, :html => { :method => :put }), + expected_form_attributes + expected_input_attributes + end + + + # FIXME: This test is janky as hell. We are essentially rewriting capture and concat and they don't really work right + # because output is out of order. This test passes because it's only doing a regex match on the buffer, but this really + # needs to be fixed by using the real helper methods that rails provides. capture, concat, url_for etc. should be + # implemented by their *real* methods or we need to find a better workaround so that our tests aren't written so + # poorly. - BR + test "form_remote_tag with block in erb" do + def capture(*args, &block) + @buffer = [] + block.call(*args) if block_given? + end + def concat(str) + @buffer << str + end + + expected_form_attributes = %w(form action="/url/hash" method="post" data-remote="true" data-update-success="#glass_of_beer" /form) + expected_inner_html = %w(w00t!) + + form_remote_tag(:update => "#glass_of_beer", :url => { :action => :fast }) { concat expected_inner_html } + assert_html @buffer.to_s, + expected_form_attributes + expected_inner_html + end + + +end + class ButtonToRemoteTest < AjaxTestCase def button(options, html = {}) button_to_remote("Remote outpost", options, html) |