Permalink
Browse files

Took first stab at reimplementing form_remote_tag helpers

  • Loading branch information...
1 parent 0955f57 commit 118a720a018fdf7358e619fc9cd949d5f336bf07 @foobarfighter foobarfighter committed with stefanpenner Sep 25, 2009
Showing with 164 additions and 24 deletions.
  1. +41 −24 actionpack/lib/action_view/helpers/ajax_helper.rb
  2. +123 −0 actionpack/test/javascript/ajax_test.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 @@ def observe_field(name, options = {})
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 @@ def button_to_remote(name, options = {}, html_options = {})
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
@@ -2,14 +2,35 @@
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|
assert_match Regexp.new(Regexp.escape(match)), html
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 @@ def link(options)
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)

0 comments on commit 118a720

Please sign in to comment.