Permalink
Browse files

Took first stab at reimplementing form_remote_tag helpers

  • Loading branch information...
foobarfighter authored and stefanpenner committed Sep 25, 2009
1 parent b225de9 commit 5316e77db1c34dca15f83dd6a10e78e847c356c3
Showing with 149 additions and 96 deletions.
  1. +26 −96 actionpack/lib/action_view/helpers/ajax_helper.rb
  2. +123 −0 actionpack/test/javascript/ajax_test.rb
@@ -3,46 +3,38 @@ module Helpers
module AjaxHelper
include UrlHelper
- def extract_remote_attributes!(options)
- attributes = options.delete(:html) || {}
-
- attributes.merge!(extract_update_attributes!(options))
- attributes.merge!(extract_request_attributes!(options))
- attributes["data-js-type"] = options.delete(:js_type) || "remote"
-
- attributes
- end
-
- def remote_form_for(record_or_name_or_array, *args, &proc)
- options = args.extract_options!
- 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
-
def form_remote_tag(options = {}, &block)
attributes = {}
attributes.merge!(extract_remote_attributes!(options))
attributes.merge!(options)
- url = attributes.delete("data-url")
- form_tag(attributes.delete(:action) || url, attributes, &block)
+ 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)
+ attributes["data-update-success"] = update[:success]
+ attributes["data-update-failure"] = update[:failure]
+ else
+ attributes["data-update-success"] = update
+ end
+
+ 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)
- html["data-update-position"] = options.delete(:position)
- html["data-method"] = options.delete(:method)
- html["data-remote"] = "true"
-
- html.merge!(options)
-
url = url_for(url) if url.is_a?(Hash)
link_to(name, url, attributes)
end
@@ -118,17 +110,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|
@@ -159,66 +140,15 @@ def button_to_remote(name, options = {}, html_options = {})
private
- def extract_request_attributes!(options)
- attributes = {}
- attributes["data-method"] = options.delete(:method)
-
- url = options.delete(:url)
- attributes["data-url"] = url.is_a?(Hash) ? url_for(url) : url
-
- #TODO: Remove all references to prototype - BR
- if options.delete(:form)
- attributes["data-parameters"] = 'Form.serialize(this)'
- elsif submit = options.delete(:submit)
- attributes["data-parameters"] = "Form.serialize('#{submit}')"
- elsif with = options.delete(:with)
- if with !~ /[\{=(.]/
- attributes["data-with"] = "'#{with}=' + encodeURIComponent(value)"
- else
- attributes["data-with"] = with
- end
- end
-
- 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
+ # TODO: Move to javascript helpers - BR
+ class JSFunction
+ def initialize(statements, *arguments)
+ @statements, @arguments = statements, arguments
end
- attributes["data-update-position"] = options.delete(:position)
- purge_unused_attributes!(attributes)
- end
-
- def extract_observer_attributes!(options)
- attributes = extract_remote_attributes!(options)
- attributes["data-observed"] = options.delete(:observed)
-
- callback = options.delete(:function)
- frequency = options.delete(:frequency)
- if callback
- attributes["data-observer-code"] = create_js_function(callback, "element", "value")
- end
- if frequency && frequency != 0
- attributes["data-frequency"] = frequency.to_i
+ def as_json(options = nil)
+ "function(#{@arguments.join(", ")}) {#{@statements}}"
end
-
- purge_unused_attributes!(attributes)
- end
-
- def purge_unused_attributes!(attributes)
- attributes.delete_if {|key, value| value.nil? }
- attributes
- end
-
- def create_js_function(statements, *arguments)
- "function(#{arguments.join(", ")}) {#{statements}}"
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 5316e77

Please sign in to comment.