diff options
author | David Heinemeier Hansson <david@loudthinking.com> | 2006-03-03 19:34:23 +0000 |
---|---|---|
committer | David Heinemeier Hansson <david@loudthinking.com> | 2006-03-03 19:34:23 +0000 |
commit | 2e67f1adc8e52121270d1139665a965e3f0792d8 (patch) | |
tree | ccaf6b866c3895fede3fd00bf849a1f542394cf9 /actionpack | |
parent | 116658e69b9c4a722e6ae8717629b8cd0057db89 (diff) | |
download | rails-2e67f1adc8e52121270d1139665a965e3f0792d8.tar.gz rails-2e67f1adc8e52121270d1139665a965e3f0792d8.tar.bz2 rails-2e67f1adc8e52121270d1139665a965e3f0792d8.zip |
RJS now does enumerations, baby! (closes #3876) [Rick Olson]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3754 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/lib/action_view/helpers/prototype_helper.rb | 108 | ||||
-rw-r--r-- | actionpack/test/template/prototype_helper_test.rb | 104 |
2 files changed, 201 insertions, 11 deletions
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 4c6c020ebe..db36f3de5f 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -434,7 +434,7 @@ module ActionView # page.select('p.welcome b').first # => $$('p.welcome b').first(); # page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide(); def select(pattern) - JavaScriptCollectionProxy.new(self, pattern) + JavaScriptElementCollectionProxy.new(self, pattern) end # Inserts HTML at the specified +position+ relative to the DOM element @@ -687,9 +687,9 @@ module ActionView # Converts chained method calls on DOM proxy elements into JavaScript chains class JavaScriptProxy < Builder::BlankSlate #:nodoc: - def initialize(generator, root) + def initialize(generator, root = nil) @generator = generator - @generator << root + @generator << root if root end private @@ -697,7 +697,7 @@ module ActionView if method.to_s =~ /(.*)=$/ assign($1, arguments.first) else - call(method, *arguments) + call("#{method.to_s.first}#{method.to_s.classify[1..-1]}", *arguments) end end @@ -707,7 +707,7 @@ module ActionView end def assign(variable, value) - append_to_function_chain! "#{variable} = #{@generator.send(:javascript_object_for, value)}" + append_to_function_chain!("#{variable} = #{@generator.send(:javascript_object_for, value)}") end def function_chain @@ -715,7 +715,7 @@ module ActionView end def append_to_function_chain!(call) - function_chain[-1] = function_chain[-1][0..-2] if function_chain[-1][-1..-1] == ";" # strip last ; + function_chain[-1].chomp!(';') function_chain[-1] += ".#{call};" end end @@ -737,15 +737,105 @@ module ActionView def reload replace :partial => @id.to_s end + + end + + class JavaScriptVariableProxy < JavaScriptProxy #:nodoc: + def initialize(generator, variable) + @variable = variable + @empty = true # only record lines if we have to. gets rid of unnecessary linebreaks + super(generator) + end + + # The JSON Encoder calls this to check for the #to_json method + # Since it's a blank slate object, I suppose it responds to anything. + def respond_to?(method) + true + end + + def to_json + @variable + end + + private + def append_to_function_chain!(call) + @generator << @variable if @empty + @empty = false + super + end end class JavaScriptCollectionProxy < JavaScriptProxy #:nodoc: + ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :findAll, :select, :max, :min, :partition, :reject, :sortBy] + ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each] + def initialize(generator, pattern) - @pattern = pattern - super(generator, "$$('#{pattern}')") + super(generator, @pattern = pattern) + end + + def grep(variable, pattern, &block) + enumerable_method("grep(#{pattern.to_json}, function(value, index) {", variable, %w(value index), &block) + end + + def inject(variable, memo, &block) + enumerable_method("inject(#{memo.to_json}, function(memo, value, index) {", variable, %w(memo value index), &block) + end + + def pluck(variable, property) + add_variable_assignment!(variable) + append_enumerable_function!("pluck(#{property.to_json});") + end + + def zip(variable, *arguments, &block) + add_variable_assignment!(variable) + append_enumerable_function!("zip(#{arguments.collect { |a| a.to_json } * ', '}") + if block + function_chain[-1] += ", function(array) {" + yield @generator, ActiveSupport::JSON::Variable.new('array') + add_return_statement! + @generator << '});' + else + function_chain[-1] += ');' + end end - # TODO: Implement funky stuff like .each + private + def method_missing(method, *arguments, &block) + ENUMERABLE_METHODS.include?(method) ? enumerate(method, ENUMERABLE_METHODS_WITH_RETURN.include?(method), &block) : super + end + + def enumerate(enumerable, variable = nil, &block) + enumerable_method("#{enumerable}(function(value, index) {", variable, %w(value index), &block) + end + + def enumerable_method(enumerable, variable, yield_params, &block) + add_variable_assignment!(variable) if variable + append_enumerable_function!(enumerable) + yield *([@generator] + yield_params.collect { |p| JavaScriptVariableProxy.new(@generator, p) }) + add_return_statement! if variable + @generator << '});' + end + + def add_variable_assignment!(variable) + function_chain.push("#{variable} = #{function_chain.pop}") + end + + def add_return_statement! + unless function_chain.last =~ /return/ + function_chain.push("return #{function_chain.pop.chomp(';')};") + end + end + + def append_enumerable_function!(call) + function_chain[-1].chomp!(';') + function_chain[-1] += ".#{call}" + end + end + + class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\ + def initialize(generator, pattern) + super(generator, "$$('#{pattern}')") + end end end end diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 5a4ec61b39..8ae7a98108 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -148,6 +148,8 @@ class PrototypeHelperTest < Test::Unit::TestCase end end +ActionView::Helpers::JavaScriptCollectionProxy.send :public, :enumerate + class JavaScriptGeneratorTest < Test::Unit::TestCase include BaseTest @@ -244,8 +246,8 @@ Element.update("baz", "<p>This is a test</p>"); end def test_element_proxy_two_deep - @generator['hello'].hide("first").display - assert_equal %($('hello').hide("first").display();), @generator.to_s + @generator['hello'].hide("first").clean_whitespace + assert_equal %($('hello').hide("first").cleanWhitespace();), @generator.to_s end def test_select_access @@ -281,4 +283,102 @@ Element.update("baz", "<p>This is a test</p>"); assert_equal %(Droppables.add('blah', {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});), @generator.drop_receiving('blah', :url => { :action => "order" }) end + + def test_collection_proxy_with_each + @generator.select('p.welcome b').each do |page, value| + value.remove_class_name 'selected' + end + @generator.select('p.welcome b').each do |page, value, index| + page.call 'alert', index + page.call 'alert', value, 'selected' + end + assert_equal <<-EOS.strip, @generator.to_s +$$('p.welcome b').each(function(value, index) { +value.removeClassName("selected"); +}); +$$('p.welcome b').each(function(value, index) { +alert(index); +alert(value, "selected"); +}); + EOS + end + + def test_collection_proxy_on_enumerables_with_return_and_index + iterator = Proc.new { |page, value| page << '(value.className == "welcome")' } + iterator_with_index = Proc.new { |page, value, index| page.call 'alert', index ; page << '(value.className == "welcome")' } + ActionView::Helpers::JavaScriptCollectionProxy::ENUMERABLE_METHODS_WITH_RETURN.each do |enum| + @generator.select('p').enumerate(enum, 'a', &iterator) + @generator.select('p').enumerate(enum, 'b', &iterator_with_index) + + assert_equal <<-EOS.strip, @generator.to_s +a = $$('p').#{enum}(function(value, index) { +return (value.className == "welcome"); +}); +b = $$('p').#{enum}(function(value, index) { +alert(index); +return (value.className == "welcome"); +}); + EOS + @generator = create_generator + end + end + + def test_collection_proxy_with_grep + @generator.select('p').grep 'a', /^a/ do |page, value| + page << '(value.className == "welcome")' + end + @generator.select('p').grep 'b', /b$/ do |page, value, index| + page.call 'alert', value + page << '(value.className == "welcome")' + end + + assert_equal <<-EOS.strip, @generator.to_s +a = $$('p').grep(/^a/, function(value, index) { +return (value.className == "welcome"); +}); +b = $$('p').grep(/b$/, function(value, index) { +alert(value); +return (value.className == "welcome"); +}); + EOS + end + + def test_collection_proxy_with_inject + @generator.select('p').inject 'a', [] do |page, memo, value| + page << '(value.className == "welcome")' + end + @generator.select('p').inject 'b', nil do |page, memo, value, index| + page.call 'alert', memo + page << '(value.className == "welcome")' + end + + assert_equal <<-EOS.strip, @generator.to_s +a = $$('p').inject([], function(memo, value, index) { +return (value.className == "welcome"); +}); +b = $$('p').inject(null, function(memo, value, index) { +alert(memo); +return (value.className == "welcome"); +}); + EOS + end + + def test_collection_proxy_with_pluck + @generator.select('p').pluck('a', 'className') + assert_equal %(a = $$('p').pluck("className");), @generator.to_s + end + + def test_collection_proxy_with_zip + ActionView::Helpers::JavaScriptCollectionProxy.new(@generator, '[1, 2, 3]').zip('a', [4, 5, 6], [7, 8, 9]) + ActionView::Helpers::JavaScriptCollectionProxy.new(@generator, '[1, 2, 3]').zip('b', [4, 5, 6], [7, 8, 9]) do |page, array| + page.call 'array.reverse' + end + + assert_equal <<-EOS.strip, @generator.to_s +a = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]); +b = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) { +return array.reverse(); +}); + EOS + end end
\ No newline at end of file |