Skip to content
This repository
Browse code

Re-added mixed-in helper methods for the JavascriptGenerator. Moved J…

…avascriptGenerators methods to a module that is mixed in after the helpers are added. Also fixed that variables set in the enumeration methods like #collect are set correctly. Documentation added for the enumeration methods [Rick Olson].

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@3814 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 043bee3338c83b0c409b5217d2f83aa732c0871a 1 parent 3c78d0b
risk danger olson technoweenie authored
7 actionpack/CHANGELOG
... ... @@ -1,5 +1,12 @@
1 1 *SVN*
2 2
  3 +* Re-added mixed-in helper methods for the JavascriptGenerator. Moved JavascriptGenerators methods to a module that is mixed in after the helpers are added. Also fixed that variables set in the enumeration methods like #collect are set correctly. Documentation added for the enumeration methods [Rick Olson]. Examples:
  4 +
  5 + page.select('#items li').collect('items') do |element|
  6 + element.hide
  7 + end
  8 + # => var items = $$('#items li').collect(function(value, index) { return value.hide(); });
  9 +
3 10 * Added plugin support for parameter parsers, which allows for better support for REST web services. By default, posts submitted with the application/xml content type is handled by creating a XmlSimple hash with the same name as the root element of the submitted xml. More handlers can easily be registered like this:
4 11
5 12 # Assign a new param parser to a new content type
554 actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -360,267 +360,287 @@ def observe_form(form_id, options = {})
360 360 end
361 361 end
362 362
363   - # JavaScriptGenerator generates blocks of JavaScript code that allow you
364   - # to change the content and presentation of multiple DOM elements. Use
365   - # this in your Ajax response bodies, either in a <script> tag or as plain
366   - # JavaScript sent with a Content-type of "text/javascript".
367   - #
368   - # Create new instances with PrototypeHelper#update_page or with
369   - # ActionController::Base#render, then call #insert_html, #replace_html,
370   - # #remove, #show, #hide, #visual_effect, or any other of the built-in
371   - # methods on the yielded generator in any order you like to modify the
372   - # content and appearance of the current page.
373   - #
374   - # Example:
375   - #
376   - # update_page do |page|
377   - # page.insert_html :bottom, 'list', "<li>#{@item.name}</li>"
378   - # page.visual_effect :highlight, 'list'
379   - # page.hide 'status-indicator', 'cancel-link'
380   - # end
381   - #
382   - # generates the following JavaScript:
383   - #
384   - # new Insertion.Bottom("list", "<li>Some item</li>");
385   - # new Effect.Highlight("list");
386   - # ["status-indicator", "cancel-link"].each(Element.hide);
387   - #
388   - # Helper methods can be used in conjunction with JavaScriptGenerator.
389   - # When a helper method is called inside an update block on the +page+
390   - # object, that method will also have access to a +page+ object.
391   - #
392   - # Example:
393   - #
394   - # module ApplicationHelper
395   - # def update_time
396   - # page.replace_html 'time', Time.now.to_s(:db)
397   - # page.visual_effect :highlight, 'time'
398   - # end
399   - # end
400   - #
401   - # # Controller action
402   - # def poll
403   - # render :update { |page| page.update_time }
404   - # end
405   - #
406   - # You can also use PrototypeHelper#update_page_tag instead of
407   - # PrototypeHelper#update_page to wrap the generated JavaScript in a
408   - # <script> tag.
409   - class JavaScriptGenerator
  363 + # All the methods were moved to GeneratorMethods so that
  364 + # #include_helpers_from_context has nothing to overwrite.
  365 + class JavaScriptGenerator #:nodoc:
410 366 def initialize(context, &block) #:nodoc:
411 367 @context, @lines = context, []
412   - # removed because those methods were overriding valid generator methods
413   - # include_helpers_from_context
  368 + include_helpers_from_context
414 369 @context.instance_exec(self, &block)
415 370 end
416   -
417   - def to_s #:nodoc:
418   - @lines * $/
419   - end
420   -
421   - # Returns a element reference by finding it through +id+ in the DOM. This element can then be
422   - # used for further method calls. Examples:
423   - #
424   - # page['blank_slate'] # => $('blank_slate');
425   - # page['blank_slate'].show # => $('blank_slate').show();
426   - # page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
427   - def [](id)
428   - JavaScriptElementProxy.new(self, id)
429   - end
430   -
431   - # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
432   - # used for further method calls. Examples:
433   - #
434   - # page.select('p') # => $$('p');
435   - # page.select('p.welcome b').first # => $$('p.welcome b').first();
436   - # page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
437   - def select(pattern)
438   - JavaScriptElementCollectionProxy.new(self, pattern)
439   - end
440   -
441   - # Inserts HTML at the specified +position+ relative to the DOM element
442   - # identified by the given +id+.
443   - #
444   - # +position+ may be one of:
445   - #
446   - # <tt>:top</tt>:: HTML is inserted inside the element, before the
447   - # element's existing content.
448   - # <tt>:bottom</tt>:: HTML is inserted inside the element, after the
449   - # element's existing content.
450   - # <tt>:before</tt>:: HTML is inserted immediately preceeding the element.
451   - # <tt>:after</tt>:: HTML is inserted immediately following the element.
452   - #
453   - # +options_for_render+ may be either a string of HTML to insert, or a hash
454   - # of options to be passed to ActionView::Base#render. For example:
455   - #
456   - # # Insert the rendered 'navigation' partial just before the DOM
457   - # # element with ID 'content'.
458   - # insert_html :before, 'content', :partial => 'navigation'
459   - #
460   - # # Add a list item to the bottom of the <ul> with ID 'list'.
461   - # insert_html :bottom, 'list', '<li>Last item</li>'
462   - #
463   - def insert_html(position, id, *options_for_render)
464   - insertion = position.to_s.camelize
465   - call "new Insertion.#{insertion}", id, render(*options_for_render)
466   - end
467   -
468   - # Replaces the inner HTML of the DOM element with the given +id+.
469   - #
470   - # +options_for_render+ may be either a string of HTML to insert, or a hash
471   - # of options to be passed to ActionView::Base#render. For example:
472   - #
473   - # # Replace the HTML of the DOM element having ID 'person-45' with the
474   - # # 'person' partial for the appropriate object.
475   - # replace_html 'person-45', :partial => 'person', :object => @person
476   - #
477   - def replace_html(id, *options_for_render)
478   - call 'Element.update', id, render(*options_for_render)
  371 +
  372 + private
  373 + def include_helpers_from_context
  374 + @context.extended_by.each do |mod|
  375 + extend mod unless mod.name =~ /^ActionView::Helpers/
  376 + end
  377 + extend GeneratorMethods
479 378 end
480   -
481   - # Replaces the "outer HTML" (i.e., the entire element, not just its
482   - # contents) of the DOM element with the given +id+.
483   - #
484   - # +options_for_render+ may be either a string of HTML to insert, or a hash
485   - # of options to be passed to ActionView::Base#render. For example:
486   - #
487   - # # Replace the DOM element having ID 'person-45' with the
488   - # # 'person' partial for the appropriate object.
489   - # replace_html 'person-45', :partial => 'person', :object => @person
  379 +
  380 + # JavaScriptGenerator generates blocks of JavaScript code that allow you
  381 + # to change the content and presentation of multiple DOM elements. Use
  382 + # this in your Ajax response bodies, either in a <script> tag or as plain
  383 + # JavaScript sent with a Content-type of "text/javascript".
490 384 #
491   - # This allows the same partial that is used for the +insert_html+ to
492   - # be also used for the input to +replace+ without resorting to
493   - # the use of wrapper elements.
  385 + # Create new instances with PrototypeHelper#update_page or with
  386 + # ActionController::Base#render, then call #insert_html, #replace_html,
  387 + # #remove, #show, #hide, #visual_effect, or any other of the built-in
  388 + # methods on the yielded generator in any order you like to modify the
  389 + # content and appearance of the current page.
494 390 #
495   - # Examples:
  391 + # Example:
496 392 #
497   - # <div id="people">
498   - # <%= render :partial => 'person', :collection => @people %>
499   - # </div>
  393 + # update_page do |page|
  394 + # page.insert_html :bottom, 'list', "<li>#{@item.name}</li>"
  395 + # page.visual_effect :highlight, 'list'
  396 + # page.hide 'status-indicator', 'cancel-link'
  397 + # end
  398 + #
  399 + # generates the following JavaScript:
500 400 #
501   - # # Insert a new person
502   - # page.insert_html :bottom, :partial => 'person', :object => @person
  401 + # new Insertion.Bottom("list", "<li>Some item</li>");
  402 + # new Effect.Highlight("list");
  403 + # ["status-indicator", "cancel-link"].each(Element.hide);
503 404 #
504   - # # Replace an existing person
505   - # page.replace 'person_45', :partial => 'person', :object => @person
  405 + # Helper methods can be used in conjunction with JavaScriptGenerator.
  406 + # When a helper method is called inside an update block on the +page+
  407 + # object, that method will also have access to a +page+ object.
  408 + #
  409 + # Example:
506 410 #
507   - def replace(id, *options_for_render)
508   - call 'Element.replace', id, render(*options_for_render)
509   - end
510   -
511   - # Removes the DOM elements with the given +ids+ from the page.
512   - def remove(*ids)
513   - record "#{javascript_object_for(ids)}.each(Element.remove)"
514   - end
515   -
516   - # Shows hidden DOM elements with the given +ids+.
517   - def show(*ids)
518   - call 'Element.show', *ids
519   - end
520   -
521   - # Hides the visible DOM elements with the given +ids+.
522   - def hide(*ids)
523   - call 'Element.hide', *ids
524   - end
525   -
526   - # Toggles the visibility of the DOM elements with the given +ids+.
527   - def toggle(*ids)
528   - call 'Element.toggle', *ids
529   - end
530   -
531   - # Displays an alert dialog with the given +message+.
532   - def alert(message)
533   - call 'alert', message
534   - end
535   -
536   - # Redirects the browser to the given +location+, in the same form as
537   - # +url_for+.
538   - def redirect_to(location)
539   - assign 'window.location.href', @context.url_for(location)
540   - end
541   -
542   - # Calls the JavaScript +function+, optionally with the given
543   - # +arguments+.
544   - def call(function, *arguments)
545   - record "#{function}(#{arguments_for_call(arguments)})"
546   - end
547   -
548   - # Assigns the JavaScript +variable+ the given +value+.
549   - def assign(variable, value)
550   - record "#{variable} = #{javascript_object_for(value)}"
551   - end
552   -
553   - # Writes raw JavaScript to the page.
554   - def <<(javascript)
555   - @lines << javascript
556   - end
557   -
558   - # Executes the content of the block after a delay of +seconds+. Example:
  411 + # module ApplicationHelper
  412 + # def update_time
  413 + # page.replace_html 'time', Time.now.to_s(:db)
  414 + # page.visual_effect :highlight, 'time'
  415 + # end
  416 + # end
559 417 #
560   - # page.delay(20) do
561   - # page.visual_effect :fade, 'notice'
  418 + # # Controller action
  419 + # def poll
  420 + # render :update { |page| page.update_time }
562 421 # end
563   - def delay(seconds = 1)
564   - record "setTimeout(function() {\n\n"
565   - yield
566   - record "}, #{(seconds * 1000).to_i})"
567   - end
568   -
569   - # Starts a script.aculo.us visual effect. See
570   - # ActionView::Helpers::ScriptaculousHelper for more information.
571   - def visual_effect(name, id = nil, options = {})
572   - record @context.send(:visual_effect, name, id, options)
573   - end
574   -
575   - # Creates a script.aculo.us sortable element. Useful
576   - # to recreate sortable elements after items get added
577   - # or deleted.
578   - # See ActionView::Helpers::ScriptaculousHelper for more information.
579   - def sortable(id, options = {})
580   - record @context.send(:sortable_element_js, id, options)
581   - end
582   -
583   - # Creates a script.aculo.us draggable element.
584   - # See ActionView::Helpers::ScriptaculousHelper for more information.
585   - def draggable(id, options = {})
586   - record @context.send(:draggable_element_js, id, options)
587   - end
588   -
589   - # Creates a script.aculo.us drop receiving element.
590   - # See ActionView::Helpers::ScriptaculousHelper for more information.
591   - def drop_receiving(id, options = {})
592   - record @context.send(:drop_receiving_element_js, id, options)
593   - end
594   -
595   - private
596   - def include_helpers_from_context
597   - @context.extended_by.each do |mod|
598   - extend mod unless mod.name =~ /^ActionView::Helpers/
  422 + #
  423 + # You can also use PrototypeHelper#update_page_tag instead of
  424 + # PrototypeHelper#update_page to wrap the generated JavaScript in a
  425 + # <script> tag.
  426 + module GeneratorMethods
  427 + def to_s #:nodoc:
  428 + @lines * $/
599 429 end
600   - end
601   -
602   - def page
603   - self
604   - end
605   -
606   - def record(line)
607   - returning line = "#{line.to_s.chomp.gsub /\;$/, ''};" do
608   - self << line
  430 +
  431 + # Returns a element reference by finding it through +id+ in the DOM. This element can then be
  432 + # used for further method calls. Examples:
  433 + #
  434 + # page['blank_slate'] # => $('blank_slate');
  435 + # page['blank_slate'].show # => $('blank_slate').show();
  436 + # page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
  437 + def [](id)
  438 + JavaScriptElementProxy.new(self, id)
  439 + end
  440 +
  441 + # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
  442 + # used for further method calls. Examples:
  443 + #
  444 + # page.select('p') # => $$('p');
  445 + # page.select('p.welcome b').first # => $$('p.welcome b').first();
  446 + # page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
  447 + #
  448 + # You can also use prototype enumerations with the collection. Observe:
  449 + #
  450 + # page.select('#items li').each do |value|
  451 + # value.hide
  452 + # end
  453 + # # => $$('#items li').each(function(value) { value.hide(); });
  454 + #
  455 + # Though you can call the block param anything you want, they are always rendered in the
  456 + # javascript as 'value, index.' Other enumerations, like collect() return the last statement:
  457 + #
  458 + # page.select('#items li').collect('hidden') do |item|
  459 + # item.hide
  460 + # end
  461 + # # => var hidden = $$('#items li').collect(function(value, index) { return value.hide(); });
  462 + def select(pattern)
  463 + JavaScriptElementCollectionProxy.new(self, pattern)
  464 + end
  465 +
  466 + # Inserts HTML at the specified +position+ relative to the DOM element
  467 + # identified by the given +id+.
  468 + #
  469 + # +position+ may be one of:
  470 + #
  471 + # <tt>:top</tt>:: HTML is inserted inside the element, before the
  472 + # element's existing content.
  473 + # <tt>:bottom</tt>:: HTML is inserted inside the element, after the
  474 + # element's existing content.
  475 + # <tt>:before</tt>:: HTML is inserted immediately preceeding the element.
  476 + # <tt>:after</tt>:: HTML is inserted immediately following the element.
  477 + #
  478 + # +options_for_render+ may be either a string of HTML to insert, or a hash
  479 + # of options to be passed to ActionView::Base#render. For example:
  480 + #
  481 + # # Insert the rendered 'navigation' partial just before the DOM
  482 + # # element with ID 'content'.
  483 + # insert_html :before, 'content', :partial => 'navigation'
  484 + #
  485 + # # Add a list item to the bottom of the <ul> with ID 'list'.
  486 + # insert_html :bottom, 'list', '<li>Last item</li>'
  487 + #
  488 + def insert_html(position, id, *options_for_render)
  489 + insertion = position.to_s.camelize
  490 + call "new Insertion.#{insertion}", id, render(*options_for_render)
  491 + end
  492 +
  493 + # Replaces the inner HTML of the DOM element with the given +id+.
  494 + #
  495 + # +options_for_render+ may be either a string of HTML to insert, or a hash
  496 + # of options to be passed to ActionView::Base#render. For example:
  497 + #
  498 + # # Replace the HTML of the DOM element having ID 'person-45' with the
  499 + # # 'person' partial for the appropriate object.
  500 + # replace_html 'person-45', :partial => 'person', :object => @person
  501 + #
  502 + def replace_html(id, *options_for_render)
  503 + call 'Element.update', id, render(*options_for_render)
  504 + end
  505 +
  506 + # Replaces the "outer HTML" (i.e., the entire element, not just its
  507 + # contents) of the DOM element with the given +id+.
  508 + #
  509 + # +options_for_render+ may be either a string of HTML to insert, or a hash
  510 + # of options to be passed to ActionView::Base#render. For example:
  511 + #
  512 + # # Replace the DOM element having ID 'person-45' with the
  513 + # # 'person' partial for the appropriate object.
  514 + # replace_html 'person-45', :partial => 'person', :object => @person
  515 + #
  516 + # This allows the same partial that is used for the +insert_html+ to
  517 + # be also used for the input to +replace+ without resorting to
  518 + # the use of wrapper elements.
  519 + #
  520 + # Examples:
  521 + #
  522 + # <div id="people">
  523 + # <%= render :partial => 'person', :collection => @people %>
  524 + # </div>
  525 + #
  526 + # # Insert a new person
  527 + # page.insert_html :bottom, :partial => 'person', :object => @person
  528 + #
  529 + # # Replace an existing person
  530 + # page.replace 'person_45', :partial => 'person', :object => @person
  531 + #
  532 + def replace(id, *options_for_render)
  533 + call 'Element.replace', id, render(*options_for_render)
  534 + end
  535 +
  536 + # Removes the DOM elements with the given +ids+ from the page.
  537 + def remove(*ids)
  538 + record "#{javascript_object_for(ids)}.each(Element.remove)"
  539 + end
  540 +
  541 + # Shows hidden DOM elements with the given +ids+.
  542 + def show(*ids)
  543 + call 'Element.show', *ids
  544 + end
  545 +
  546 + # Hides the visible DOM elements with the given +ids+.
  547 + def hide(*ids)
  548 + call 'Element.hide', *ids
  549 + end
  550 +
  551 + # Toggles the visibility of the DOM elements with the given +ids+.
  552 + def toggle(*ids)
  553 + call 'Element.toggle', *ids
  554 + end
  555 +
  556 + # Displays an alert dialog with the given +message+.
  557 + def alert(message)
  558 + call 'alert', message
  559 + end
  560 +
  561 + # Redirects the browser to the given +location+, in the same form as
  562 + # +url_for+.
  563 + def redirect_to(location)
  564 + assign 'window.location.href', @context.url_for(location)
  565 + end
  566 +
  567 + # Calls the JavaScript +function+, optionally with the given
  568 + # +arguments+.
  569 + def call(function, *arguments)
  570 + record "#{function}(#{arguments_for_call(arguments)})"
  571 + end
  572 +
  573 + # Assigns the JavaScript +variable+ the given +value+.
  574 + def assign(variable, value)
  575 + record "#{variable} = #{javascript_object_for(value)}"
  576 + end
  577 +
  578 + # Writes raw JavaScript to the page.
  579 + def <<(javascript)
  580 + @lines << javascript
  581 + end
  582 +
  583 + # Executes the content of the block after a delay of +seconds+. Example:
  584 + #
  585 + # page.delay(20) do
  586 + # page.visual_effect :fade, 'notice'
  587 + # end
  588 + def delay(seconds = 1)
  589 + record "setTimeout(function() {\n\n"
  590 + yield
  591 + record "}, #{(seconds * 1000).to_i})"
  592 + end
  593 +
  594 + # Starts a script.aculo.us visual effect. See
  595 + # ActionView::Helpers::ScriptaculousHelper for more information.
  596 + def visual_effect(name, id = nil, options = {})
  597 + record @context.send(:visual_effect, name, id, options)
  598 + end
  599 +
  600 + # Creates a script.aculo.us sortable element. Useful
  601 + # to recreate sortable elements after items get added
  602 + # or deleted.
  603 + # See ActionView::Helpers::ScriptaculousHelper for more information.
  604 + def sortable(id, options = {})
  605 + record @context.send(:sortable_element_js, id, options)
  606 + end
  607 +
  608 + # Creates a script.aculo.us draggable element.
  609 + # See ActionView::Helpers::ScriptaculousHelper for more information.
  610 + def draggable(id, options = {})
  611 + record @context.send(:draggable_element_js, id, options)
  612 + end
  613 +
  614 + # Creates a script.aculo.us drop receiving element.
  615 + # See ActionView::Helpers::ScriptaculousHelper for more information.
  616 + def drop_receiving(id, options = {})
  617 + record @context.send(:drop_receiving_element_js, id, options)
  618 + end
  619 +
  620 + private
  621 + def page
  622 + self
  623 + end
  624 +
  625 + def record(line)
  626 + returning line = "#{line.to_s.chomp.gsub /\;$/, ''};" do
  627 + self << line
  628 + end
  629 + end
  630 +
  631 + def render(*options_for_render)
  632 + Hash === options_for_render.first ?
  633 + @context.render(*options_for_render) :
  634 + options_for_render.first.to_s
  635 + end
  636 +
  637 + def javascript_object_for(object)
  638 + object.respond_to?(:to_json) ? object.to_json : object.inspect
  639 + end
  640 +
  641 + def arguments_for_call(arguments)
  642 + arguments.map { |argument| javascript_object_for(argument) }.join ', '
609 643 end
610   - end
611   -
612   - def render(*options_for_render)
613   - Hash === options_for_render.first ?
614   - @context.render(*options_for_render) :
615   - options_for_render.first.to_s
616   - end
617   -
618   - def javascript_object_for(object)
619   - object.respond_to?(:to_json) ? object.to_json : object.inspect
620   - end
621   -
622   - def arguments_for_call(arguments)
623   - arguments.map { |argument| javascript_object_for(argument) }.join ', '
624 644 end
625 645 end
626 646
@@ -769,17 +789,19 @@ def append_to_function_chain!(call)
769 789 class JavaScriptCollectionProxy < JavaScriptProxy #:nodoc:
770 790 ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :findAll, :select, :max, :min, :partition, :reject, :sortBy]
771 791 ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each]
  792 + attr_reader :generator
  793 + delegate :arguments_for_call, :to => :generator
772 794
773 795 def initialize(generator, pattern)
774 796 super(generator, @pattern = pattern)
775 797 end
776 798
777 799 def grep(variable, pattern, &block)
778   - enumerable_method("grep(#{pattern.to_json}, function(value, index) {", variable, %w(value index), &block)
  800 + enumerate :grep, :variable => variable, :return => true, :method_args => [pattern], :yield_args => %w(value index), &block
779 801 end
780 802
781 803 def inject(variable, memo, &block)
782   - enumerable_method("inject(#{memo.to_json}, function(memo, value, index) {", variable, %w(memo value index), &block)
  804 + enumerate :inject, :variable => variable, :method_args => [memo], :yield_args => %w(memo value index), :return => true, &block
783 805 end
784 806
785 807 def pluck(variable, property)
@@ -789,7 +811,7 @@ def pluck(variable, property)
789 811
790 812 def zip(variable, *arguments, &block)
791 813 add_variable_assignment!(variable)
792   - append_enumerable_function!("zip(#{arguments.collect { |a| a.to_json } * ', '}")
  814 + append_enumerable_function!("zip(#{arguments_for_call arguments}")
793 815 if block
794 816 function_chain[-1] += ", function(array) {"
795 817 yield ActiveSupport::JSON::Variable.new('array')
@@ -802,24 +824,36 @@ def zip(variable, *arguments, &block)
802 824
803 825 private
804 826 def method_missing(method, *arguments, &block)
805   - ENUMERABLE_METHODS.include?(method) ? enumerate(method, ENUMERABLE_METHODS_WITH_RETURN.include?(method), &block) : super
806   - end
807   -
808   - def enumerate(enumerable, variable = nil, &block)
809   - enumerable_method("#{enumerable}(function(value, index) {", variable, %w(value index), &block)
  827 + if ENUMERABLE_METHODS.include?(method)
  828 + returnable = ENUMERABLE_METHODS_WITH_RETURN.include?(method)
  829 + variable = arguments.first if returnable
  830 + enumerate(method, {:variable => (arguments.first if returnable), :return => returnable, :yield_args => %w(value index)}, &block)
  831 + else
  832 + super
  833 + end
810 834 end
811 835
812   - def enumerable_method(enumerable, variable, yield_params, &block)
813   - add_variable_assignment!(variable) if variable
814   - append_enumerable_function!(enumerable)
  836 + # Options
  837 + # * variable - name of the variable to set the result of the enumeration to
  838 + # * method_args - array of the javascript enumeration method args that occur before the function
  839 + # * yield_args - array of the javascript yield args
  840 + # * return - true if the enumeration should return the last statement
  841 + def enumerate(enumerable, options = {}, &block)
  842 + options[:method_args] ||= []
  843 + options[:yield_args] ||= []
  844 + yield_args = options[:yield_args] * ', '
  845 + method_args = arguments_for_call options[:method_args] # foo, bar, function
  846 + method_args << ', ' unless method_args.blank?
  847 + add_variable_assignment!(options[:variable]) if options[:variable]
  848 + append_enumerable_function!("#{enumerable}(#{method_args}function(#{yield_args}) {")
815 849 # only yield as many params as were passed in the block
816   - yield *yield_params.collect { |p| JavaScriptVariableProxy.new(@generator, p) }[0..block.arity-1]
817   - add_return_statement! if variable
  850 + yield *options[:yield_args].collect { |p| JavaScriptVariableProxy.new(@generator, p) }[0..block.arity-1]
  851 + add_return_statement! if options[:return]
818 852 @generator << '});'
819 853 end
820 854
821 855 def add_variable_assignment!(variable)
822   - function_chain.push("#{variable} = #{function_chain.pop}")
  856 + function_chain.push("var #{variable} = #{function_chain.pop}")
823 857 end
824 858
825 859 def add_return_statement!
17 actionpack/test/controller/new_render_test.rb
@@ -9,6 +9,12 @@ def hello_world
9 9 end
10 10 end
11 11
  12 +module NewRenderTestHelper
  13 + def rjs_helper_method_from_module
  14 + page.visual_effect :highlight
  15 + end
  16 +end
  17 +
12 18 class NewRenderTestController < ActionController::Base
13 19 layout :determine_layout
14 20
@@ -178,11 +184,18 @@ def @template.name() nil end
178 184 render :action => "potential_conflicts"
179 185 end
180 186
  187 + helper NewRenderTestHelper
  188 + helper do
  189 + def rjs_helper_method(value)
  190 + page.visual_effect :highlight, value
  191 + end
  192 + end
  193 +
181 194 def enum_rjs_test
182 195 render :update do |page|
183 196 page.select('.product').each do |value|
184   - page.visual_effect :highlight
185   - page.visual_effect :highlight, value
  197 + page.rjs_helper_method_from_module
  198 + page.rjs_helper_method(value)
186 199 page.sortable(value, :url => { :action => "order" })
187 200 page.draggable(value)
188 201 end
51 actionpack/test/template/prototype_helper_test.rb
@@ -148,8 +148,6 @@ def test_update_page_tag
148 148 end
149 149 end
150 150
151   -ActionView::Helpers::JavaScriptCollectionProxy.send :public, :enumerate
152   -
153 151 class JavaScriptGeneratorTest < Test::Unit::TestCase
154 152 include BaseTest
155 153
@@ -284,6 +282,15 @@ def test_drop_receiving
284 282 @generator.drop_receiving('blah', :url => { :action => "order" })
285 283 end
286 284
  285 + def test_collection_first_and_last
  286 + @generator.select('p.welcome b').first.hide()
  287 + @generator.select('p.welcome b').last.show()
  288 + assert_equal <<-EOS.strip, @generator.to_s
  289 +$$('p.welcome b').first().hide();
  290 +$$('p.welcome b').last().show();
  291 + EOS
  292 + end
  293 +
287 294 def test_collection_proxy_with_each
288 295 @generator.select('p.welcome b').each do |value|
289 296 value.remove_class_name 'selected'
@@ -301,24 +308,18 @@ def test_collection_proxy_with_each
301 308 EOS
302 309 end
303 310
304   - def test_collection_proxy_on_enumerables_with_return_and_index
305   - iterator = Proc.new { |value| @generator << '(value.className == "welcome")' }
306   - iterator_with_index = Proc.new { |value, index| @generator.call 'alert', index ; @generator << '(value.className == "welcome")' }
307   - ActionView::Helpers::JavaScriptCollectionProxy::ENUMERABLE_METHODS_WITH_RETURN.each do |enum|
308   - @generator.select('p').enumerate(enum, 'a', &iterator)
309   - @generator.select('p').enumerate(enum, 'b', &iterator_with_index)
310   -
311   - assert_equal <<-EOS.strip, @generator.to_s
312   -a = $$('p').#{enum}(function(value, index) {
313   -return (value.className == "welcome");
  311 + def test_collection_proxy_on_collect
  312 + @generator.select('p').collect('a') { |para| para.show }
  313 + @generator.select('p').collect { |para| para.hide }
  314 + assert_equal <<-EOS.strip, @generator.to_s
  315 +var a = $$('p').collect(function(value, index) {
  316 +return value.show();
314 317 });
315   -b = $$('p').#{enum}(function(value, index) {
316   -alert(index);
317   -return (value.className == "welcome");
  318 +$$('p').collect(function(value, index) {
  319 +return value.hide();
318 320 });
319   - EOS
320   - @generator = create_generator
321   - end
  321 + EOS
  322 + @generator = create_generator
322 323 end
323 324
324 325 def test_collection_proxy_with_grep
@@ -331,10 +332,10 @@ def test_collection_proxy_with_grep
331 332 end
332 333
333 334 assert_equal <<-EOS.strip, @generator.to_s
334   -a = $$('p').grep(/^a/, function(value, index) {
  335 +var a = $$('p').grep(/^a/, function(value, index) {
335 336 return (value.className == "welcome");
336 337 });
337   -b = $$('p').grep(/b$/, function(value, index) {
  338 +var b = $$('p').grep(/b$/, function(value, index) {
338 339 alert(value);
339 340 return (value.className == "welcome");
340 341 });
@@ -351,10 +352,10 @@ def test_collection_proxy_with_inject
351 352 end
352 353
353 354 assert_equal <<-EOS.strip, @generator.to_s
354   -a = $$('p').inject([], function(memo, value, index) {
  355 +var a = $$('p').inject([], function(memo, value, index) {
355 356 return (value.className == "welcome");
356 357 });
357   -b = $$('p').inject(null, function(memo, value, index) {
  358 +var b = $$('p').inject(null, function(memo, value, index) {
358 359 alert(memo);
359 360 return (value.className == "welcome");
360 361 });
@@ -363,7 +364,7 @@ def test_collection_proxy_with_inject
363 364
364 365 def test_collection_proxy_with_pluck
365 366 @generator.select('p').pluck('a', 'className')
366   - assert_equal %(a = $$('p').pluck("className");), @generator.to_s
  367 + assert_equal %(var a = $$('p').pluck("className");), @generator.to_s
367 368 end
368 369
369 370 def test_collection_proxy_with_zip
@@ -373,8 +374,8 @@ def test_collection_proxy_with_zip
373 374 end
374 375
375 376 assert_equal <<-EOS.strip, @generator.to_s
376   -a = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
377   -b = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) {
  377 +var a = [1, 2, 3].zip([4, 5, 6], [7, 8, 9]);
  378 +var b = [1, 2, 3].zip([4, 5, 6], [7, 8, 9], function(array) {
378 379 return array.reverse();
379 380 });
380 381 EOS

0 comments on commit 043bee3

Please sign in to comment.
Something went wrong with that request. Please try again.