actionpack 1.11.2 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (149) hide show
  1. data/CHANGELOG +392 -5
  2. data/lib/action_controller.rb +8 -4
  3. data/lib/action_controller/assertions.rb +9 -10
  4. data/lib/action_controller/base.rb +177 -88
  5. data/lib/action_controller/benchmarking.rb +5 -5
  6. data/lib/action_controller/caching.rb +44 -36
  7. data/lib/action_controller/cgi_ext/cgi_methods.rb +71 -6
  8. data/lib/action_controller/cgi_ext/cookie_performance_fix.rb +1 -1
  9. data/lib/action_controller/cgi_process.rb +36 -24
  10. data/lib/action_controller/components.rb +152 -52
  11. data/lib/action_controller/dependencies.rb +1 -1
  12. data/lib/action_controller/deprecated_redirects.rb +2 -2
  13. data/lib/action_controller/deprecated_request_methods.rb +34 -0
  14. data/lib/action_controller/filters.rb +59 -19
  15. data/lib/action_controller/flash.rb +53 -47
  16. data/lib/action_controller/helpers.rb +2 -2
  17. data/lib/action_controller/integration.rb +524 -0
  18. data/lib/action_controller/layout.rb +58 -23
  19. data/lib/action_controller/mime_responds.rb +163 -0
  20. data/lib/action_controller/mime_type.rb +142 -0
  21. data/lib/action_controller/pagination.rb +13 -7
  22. data/lib/action_controller/request.rb +59 -56
  23. data/lib/action_controller/rescue.rb +1 -1
  24. data/lib/action_controller/routing.rb +29 -10
  25. data/lib/action_controller/scaffolding.rb +8 -0
  26. data/lib/action_controller/session/active_record_store.rb +21 -10
  27. data/lib/action_controller/session/mem_cache_store.rb +18 -12
  28. data/lib/action_controller/session_management.rb +30 -11
  29. data/lib/action_controller/templates/rescues/_trace.rhtml +1 -1
  30. data/lib/action_controller/templates/scaffolds/layout.rhtml +4 -4
  31. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  32. data/lib/action_controller/test_process.rb +189 -118
  33. data/lib/action_controller/vendor/html-scanner/html/node.rb +20 -1
  34. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +3 -0
  35. data/lib/action_controller/vendor/html-scanner/html/version.rb +1 -1
  36. data/lib/action_controller/vendor/xml_node.rb +97 -0
  37. data/lib/action_controller/verification.rb +2 -0
  38. data/lib/action_pack/version.rb +3 -3
  39. data/lib/action_view.rb +0 -2
  40. data/lib/action_view/base.rb +109 -36
  41. data/lib/action_view/compiled_templates.rb +1 -1
  42. data/lib/action_view/helpers/active_record_helper.rb +4 -2
  43. data/lib/action_view/helpers/asset_tag_helper.rb +6 -7
  44. data/lib/action_view/helpers/capture_helper.rb +49 -12
  45. data/lib/action_view/helpers/date_helper.rb +14 -4
  46. data/lib/action_view/helpers/form_helper.rb +136 -20
  47. data/lib/action_view/helpers/form_options_helper.rb +29 -7
  48. data/lib/action_view/helpers/form_tag_helper.rb +22 -20
  49. data/lib/action_view/helpers/java_script_macros_helper.rb +29 -9
  50. data/lib/action_view/helpers/javascript_helper.rb +50 -446
  51. data/lib/action_view/helpers/javascripts/controls.js +95 -30
  52. data/lib/action_view/helpers/javascripts/dragdrop.js +161 -21
  53. data/lib/action_view/helpers/javascripts/effects.js +310 -211
  54. data/lib/action_view/helpers/javascripts/prototype.js +228 -28
  55. data/lib/action_view/helpers/number_helper.rb +9 -9
  56. data/lib/action_view/helpers/pagination_helper.rb +1 -1
  57. data/lib/action_view/helpers/prototype_helper.rb +900 -0
  58. data/lib/action_view/helpers/scriptaculous_helper.rb +135 -0
  59. data/lib/action_view/helpers/text_helper.rb +7 -6
  60. data/lib/action_view/helpers/url_helper.rb +23 -14
  61. data/lib/action_view/partials.rb +12 -4
  62. data/rakefile +13 -5
  63. data/test/abstract_unit.rb +4 -3
  64. data/test/active_record_unit.rb +88 -0
  65. data/test/{controller → activerecord}/active_record_assertions_test.rb +7 -50
  66. data/test/{controller → activerecord}/active_record_store_test.rb +27 -4
  67. data/test/activerecord/pagination_test.rb +161 -0
  68. data/test/controller/action_pack_assertions_test.rb +18 -15
  69. data/test/controller/base_test.rb +31 -42
  70. data/test/controller/benchmark_test.rb +8 -11
  71. data/test/controller/capture_test.rb +33 -1
  72. data/test/controller/cgi_test.rb +33 -0
  73. data/test/controller/custom_handler_test.rb +8 -0
  74. data/test/controller/fake_controllers.rb +9 -17
  75. data/test/controller/filters_test.rb +32 -3
  76. data/test/controller/flash_test.rb +26 -41
  77. data/test/controller/fragment_store_setting_test.rb +1 -1
  78. data/test/controller/layout_test.rb +73 -0
  79. data/test/controller/mime_responds_test.rb +257 -0
  80. data/test/controller/mime_type_test.rb +24 -0
  81. data/test/controller/new_render_test.rb +157 -1
  82. data/test/controller/redirect_test.rb +23 -0
  83. data/test/controller/render_test.rb +54 -56
  84. data/test/controller/request_test.rb +25 -0
  85. data/test/controller/routing_test.rb +74 -66
  86. data/test/controller/test_test.rb +66 -1
  87. data/test/controller/verification_test.rb +3 -1
  88. data/test/controller/webservice_test.rb +255 -0
  89. data/test/fixtures/companies.yml +24 -0
  90. data/test/fixtures/company.rb +9 -0
  91. data/test/fixtures/db_definitions/sqlite.sql +42 -0
  92. data/test/fixtures/developer.rb +7 -0
  93. data/test/fixtures/developers.yml +21 -0
  94. data/test/fixtures/developers_projects.yml +13 -0
  95. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  96. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  97. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  98. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  99. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  100. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  101. data/test/fixtures/project.rb +3 -0
  102. data/test/fixtures/projects.yml +7 -0
  103. data/test/fixtures/replies.yml +13 -0
  104. data/test/fixtures/reply.rb +5 -0
  105. data/test/fixtures/respond_to/all_types_with_layout.rhtml +1 -0
  106. data/test/fixtures/respond_to/all_types_with_layout.rjs +1 -0
  107. data/test/fixtures/respond_to/layouts/standard.rhtml +1 -0
  108. data/test/fixtures/respond_to/using_defaults.rhtml +1 -0
  109. data/test/fixtures/respond_to/using_defaults.rjs +1 -0
  110. data/test/fixtures/respond_to/using_defaults.rxml +1 -0
  111. data/test/fixtures/respond_to/using_defaults_with_type_list.rhtml +1 -0
  112. data/test/fixtures/respond_to/using_defaults_with_type_list.rjs +1 -0
  113. data/test/fixtures/respond_to/using_defaults_with_type_list.rxml +1 -0
  114. data/test/fixtures/test/block_content_for.rhtml +2 -0
  115. data/test/fixtures/test/delete_with_js.rjs +2 -0
  116. data/test/fixtures/test/dot.directory/render_file_with_ivar.rhtml +1 -0
  117. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  118. data/test/fixtures/test/erb_content_for.rhtml +2 -0
  119. data/test/fixtures/test/hello_world.rxml +3 -0
  120. data/test/fixtures/test/hello_world_with_layout_false.rhtml +1 -0
  121. data/test/fixtures/test/non_erb_block_content_for.rxml +4 -0
  122. data/test/fixtures/topic.rb +3 -0
  123. data/test/fixtures/topics.yml +22 -0
  124. data/test/template/active_record_helper_test.rb +4 -0
  125. data/test/template/asset_tag_helper_test.rb +7 -2
  126. data/test/template/date_helper_test.rb +39 -2
  127. data/test/template/form_helper_test.rb +238 -5
  128. data/test/template/form_options_helper_test.rb +78 -0
  129. data/test/template/form_tag_helper_test.rb +11 -0
  130. data/test/template/java_script_macros_helper_test.rb +51 -6
  131. data/test/template/javascript_helper_test.rb +7 -153
  132. data/test/template/number_helper_test.rb +14 -13
  133. data/test/template/prototype_helper_test.rb +423 -0
  134. data/test/template/scriptaculous_helper_test.rb +90 -0
  135. data/test/template/text_helper_test.rb +12 -9
  136. data/test/template/url_helper_test.rb +31 -15
  137. metadata +291 -246
  138. data/lib/action_controller/cgi_ext/multipart_progress.rb +0 -169
  139. data/lib/action_controller/upload_progress.rb +0 -473
  140. data/lib/action_controller/vendor/html-scanner/html/node.rb.rej +0 -17
  141. data/lib/action_view/helpers/upload_progress_helper.rb +0 -433
  142. data/lib/action_view/vendor/builder.rb +0 -13
  143. data/lib/action_view/vendor/builder/blankslate.rb +0 -53
  144. data/lib/action_view/vendor/builder/xmlbase.rb +0 -143
  145. data/lib/action_view/vendor/builder/xmlevents.rb +0 -63
  146. data/lib/action_view/vendor/builder/xmlmarkup.rb +0 -308
  147. data/test/controller/multipart_progress_testx.rb +0 -365
  148. data/test/controller/upload_progress_testx.rb +0 -89
  149. data/test/template/upload_progress_helper_testx.rb +0 -136
@@ -10,7 +10,7 @@ module ActionView
10
10
  #
11
11
  # To use a compiled template module, create a new instance and include it into the class
12
12
  # in which you want the template to be rendered.
13
- class CompiledTemplates < Module
13
+ class CompiledTemplates < Module #:nodoc:
14
14
  attr_reader :method_names
15
15
 
16
16
  def initialize
@@ -103,7 +103,7 @@ module ActionView
103
103
  def error_messages_for(object_name, options = {})
104
104
  options = options.symbolize_keys
105
105
  object = instance_variable_get("@#{object_name}")
106
- unless object.errors.empty?
106
+ if object && !object.errors.empty?
107
107
  content_tag("div",
108
108
  content_tag(
109
109
  options[:header_tag] || "h2",
@@ -113,6 +113,8 @@ module ActionView
113
113
  content_tag("ul", object.errors.full_messages.collect { |msg| content_tag("li", msg) }),
114
114
  "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
115
115
  )
116
+ else
117
+ ""
116
118
  end
117
119
  end
118
120
 
@@ -139,7 +141,7 @@ module ActionView
139
141
  to_input_field_tag("text", options)
140
142
  when :date
141
143
  to_date_select_tag(options)
142
- when :datetime
144
+ when :datetime, :timestamp
143
145
  to_datetime_select_tag(options)
144
146
  when :boolean
145
147
  to_boolean_select_tag(options)
@@ -59,11 +59,10 @@ module ActionView
59
59
  # <tt>controllers/application.rb</tt> and <tt>helpers/application_helper.rb</tt>.
60
60
  def javascript_include_tag(*sources)
61
61
  options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
62
- if sources.first == :defaults
63
- sources = @@javascript_default_sources.dup
64
- if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
65
- sources << 'application'
66
- end
62
+ if sources.include?(:defaults)
63
+ sources = sources[0..(sources.index(:defaults))] + @@javascript_default_sources.dup + sources[(sources.index(:defaults) + 1)..sources.length]
64
+ sources.delete(:defaults)
65
+ sources << "application" if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
67
66
  end
68
67
  sources.collect { |source|
69
68
  source = javascript_path(source)
@@ -131,7 +130,7 @@ module ActionView
131
130
  # * file name, like "rss.gif", that gets expanded to "/images/rss.gif"
132
131
  # * file name without extension, like "logo", that gets expanded to "/images/logo.png"
133
132
  def image_tag(source, options = {})
134
- options.symbolize_keys
133
+ options.symbolize_keys!
135
134
 
136
135
  options[:src] = image_path(source)
137
136
  options[:alt] ||= File.basename(options[:src], '.*').split('.').first.capitalize
@@ -147,7 +146,7 @@ module ActionView
147
146
  private
148
147
  def compute_public_path(source, dir, ext)
149
148
  source = "/#{dir}/#{source}" unless source.first == "/" || source.include?(":")
150
- source = "#{source}.#{ext}" unless source.include?(".")
149
+ source = "#{source}.#{ext}" unless source.split("/").last.include?(".")
151
150
  source = "#{@controller.request.relative_url_root}#{source}" unless %r{^[-a-z]+://} =~ source
152
151
  source = ActionController::Base.asset_host + source unless source.include?(":")
153
152
  source
@@ -43,24 +43,30 @@ module ActionView
43
43
  # instance variable. You can use this instance variable anywhere
44
44
  # in your templates and even in your layout.
45
45
  #
46
- # Example:
46
+ # Example of capture being used in a .rhtml page:
47
47
  #
48
48
  # <% @greeting = capture do %>
49
49
  # Welcome To my shiny new web page!
50
- # <% end %>
50
+ # <% end %>
51
+ #
52
+ # Example of capture being used in a .rxml page:
53
+ #
54
+ # @greeting = capture do
55
+ # 'Welcome To my shiny new web page!'
56
+ # end
51
57
  def capture(*args, &block)
52
58
  # execute the block
53
- buffer = eval("_erbout", block.binding)
54
- pos = buffer.length
55
- block.call(*args)
59
+ begin
60
+ buffer = eval("_erbout", block.binding)
61
+ rescue
62
+ buffer = nil
63
+ end
56
64
 
57
- # extract the block
58
- data = buffer[pos..-1]
59
-
60
- # replace it in the original with empty string
61
- buffer[pos..-1] = ''
62
-
63
- data
65
+ if buffer.nil?
66
+ capture_block(*args, &block)
67
+ else
68
+ capture_erb_with_buffer(buffer, *args, &block)
69
+ end
64
70
  end
65
71
 
66
72
  # Content_for will store the given block
@@ -84,6 +90,37 @@ module ActionView
84
90
  def content_for(name, &block)
85
91
  eval "@content_for_#{name} = (@content_for_#{name} || '') + capture(&block)"
86
92
  end
93
+
94
+ private
95
+ def capture_block(*args, &block)
96
+ block.call(*args)
97
+ end
98
+
99
+ def capture_erb(*args, &block)
100
+ buffer = eval("_erbout", block.binding)
101
+ capture_erb_with_buffer(buffer, *args, &block)
102
+ end
103
+
104
+ def capture_erb_with_buffer(buffer, *args, &block)
105
+ pos = buffer.length
106
+ block.call(*args)
107
+
108
+ # extract the block
109
+ data = buffer[pos..-1]
110
+
111
+ # replace it in the original with empty string
112
+ buffer[pos..-1] = ''
113
+
114
+ data
115
+ end
116
+
117
+ def erb_content_for(name, &block)
118
+ eval "@content_for_#{name} = (@content_for_#{name} || '') + capture_erb(&block)"
119
+ end
120
+
121
+ def block_content_for(name, &block)
122
+ eval "@content_for_#{name} = (@content_for_#{name} || '') + capture_block(&block)"
123
+ end
87
124
  end
88
125
  end
89
126
  end
@@ -76,8 +76,8 @@ module ActionView
76
76
  # date_select("user", "birthday", :order => [:month, :day])
77
77
  #
78
78
  # The selects are prepared for multi-parameter assignment to an Active Record object.
79
- def date_select(object, method, options = {})
80
- InstanceTag.new(object, method, self).to_date_select_tag(options)
79
+ def date_select(object_name, method, options = {})
80
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_date_select_tag(options)
81
81
  end
82
82
 
83
83
  # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based
@@ -87,8 +87,8 @@ module ActionView
87
87
  # datetime_select("post", "written_on", :start_year => 1995)
88
88
  #
89
89
  # The selects are prepared for multi-parameter assignment to an Active Record object.
90
- def datetime_select(object, method, options = {})
91
- InstanceTag.new(object, method, self).to_datetime_select_tag(options)
90
+ def datetime_select(object_name, method, options = {})
91
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_datetime_select_tag(options)
92
92
  end
93
93
 
94
94
  # Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
@@ -293,5 +293,15 @@ module ActionView
293
293
  datetime_select
294
294
  end
295
295
  end
296
+
297
+ class FormBuilder
298
+ def date_select(method, options = {})
299
+ @template.date_select(@object_name, method, options.merge(:object => @object))
300
+ end
301
+
302
+ def datetime_select(method, options = {})
303
+ @template.datetime_select(@object_name, method, options.merge(:object => @object))
304
+ end
305
+ end
296
306
  end
297
307
  end
@@ -47,7 +47,7 @@ module ActionView
47
47
  #
48
48
  # If the object name contains square brackets the id for the object will be inserted. Example:
49
49
  #
50
- # <%= textfield "person[]", "name" %>
50
+ # <%= text_field "person[]", "name" %>
51
51
  #
52
52
  # ...becomes:
53
53
  #
@@ -65,6 +65,90 @@ module ActionView
65
65
  # There's also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html,
66
66
  # link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html
67
67
  module FormHelper
68
+ # Creates a form and a scope around a specific model object, which is then used as a base for questioning about
69
+ # values for the fields. Examples:
70
+ #
71
+ # <% form_for :person, @person, :url => { :action => "update" } do |f| %>
72
+ # First name: <%= f.text_field :first_name %>
73
+ # Last name : <%= f.text_field :last_name %>
74
+ # Biography : <%= f.text_area :biography %>
75
+ # Admin? : <%= f.check_box :admin %>
76
+ # <% end %>
77
+ #
78
+ # Worth noting is that the form_for tag is called in a ERb evaluation block, not a ERb output block. So that's <tt><% %></tt>,
79
+ # not <tt><%= %></tt>. Also worth noting is that the form_for yields a form_builder object, in this example as f, which emulates
80
+ # the API for the stand-alone FormHelper methods, but without the object name. So instead of <tt>text_field :person, :name</tt>,
81
+ # you get away with <tt>f.text_field :name</tt>.
82
+ #
83
+ # That in itself is a modest increase in comfort. The big news is that form_for allows us to more easily escape the instance
84
+ # variable convention, so while the stand-alone approach would require <tt>text_field :person, :name, :object => person</tt>
85
+ # to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with
86
+ # <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>.
87
+ #
88
+ # Also note that form_for doesn't create an exclusive scope. It's still possible to use both the stand-alone FormHelper methods
89
+ # and methods from FormTagHelper. Example:
90
+ #
91
+ # <% form_for :person, @person, :url => { :action => "update" } do |f| %>
92
+ # First name: <%= f.text_field :first_name %>
93
+ # Last name : <%= f.text_field :last_name %>
94
+ # Biography : <%= text_area :person, :biography %>
95
+ # Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %>
96
+ # <% end %>
97
+ #
98
+ # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base.
99
+ # Like collection_select and datetime_select.
100
+ #
101
+ # Html attributes for the form tag can be given as :html => {...}. Example:
102
+ #
103
+ # <% form_for :person, @person, :html => {:id => 'person_form'} do |f| %>
104
+ # ...
105
+ # <% end %>
106
+ #
107
+ # You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers,
108
+ # then use your custom builder like so:
109
+ #
110
+ # <% form_for :person, @person, :url => { :action => "update" }, :builder => LabellingFormBuilder do |f| %>
111
+ # <%= f.text_field :first_name %>
112
+ # <%= f.text_field :last_name %>
113
+ # <%= text_area :person, :biography %>
114
+ # <%= check_box_tag "person[admin]", @person.company.admin? %>
115
+ # <% end %>
116
+ #
117
+ # In many cases you will want to wrap the above in another helper, such as:
118
+ #
119
+ # def labelled_form_for(name, object, options, &proc)
120
+ # form_for(name, object, options.merge(:builder => LabellingFormBuiler), &proc)
121
+ # end
122
+ #
123
+ def form_for(object_name, *args, &proc)
124
+ raise ArgumentError, "Missing block" unless block_given?
125
+ options = args.last.is_a?(Hash) ? args.pop : {}
126
+ concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding)
127
+ fields_for(object_name, *(args << options), &proc)
128
+ concat('</form>', proc.binding)
129
+ end
130
+
131
+ # Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes
132
+ # fields_for suitable for specifying additional model objects in the same form. Example:
133
+ #
134
+ # <% form_for :person, @person, :url => { :action => "update" } do |person_form| %>
135
+ # First name: <%= person_form.text_field :first_name %>
136
+ # Last name : <%= person_form.text_field :last_name %>
137
+ #
138
+ # <% fields_for :permission, @person.permission do |permission_fields| %>
139
+ # Admin? : <%= permission_fields.check_box :admin %>
140
+ # <% end %>
141
+ # <% end %>
142
+ #
143
+ # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base.
144
+ # Like collection_select and datetime_select.
145
+ def fields_for(object_name, *args, &proc)
146
+ raise ArgumentError, "Missing block" unless block_given?
147
+ options = args.last.is_a?(Hash) ? args.pop : {}
148
+ object = args.first
149
+ yield((options[:builder] || FormBuilder).new(object_name, object, self, options, proc))
150
+ end
151
+
68
152
  # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
69
153
  # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
70
154
  # hash with +options+.
@@ -72,23 +156,23 @@ module ActionView
72
156
  # Examples (call, result):
73
157
  # text_field("post", "title", "size" => 20)
74
158
  # <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
75
- def text_field(object, method, options = {})
76
- InstanceTag.new(object, method, self).to_input_field_tag("text", options)
159
+ def text_field(object_name, method, options = {})
160
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("text", options)
77
161
  end
78
162
 
79
163
  # Works just like text_field, but returns an input tag of the "password" type instead.
80
- def password_field(object, method, options = {})
81
- InstanceTag.new(object, method, self).to_input_field_tag("password", options)
164
+ def password_field(object_name, method, options = {})
165
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("password", options)
82
166
  end
83
167
 
84
168
  # Works just like text_field, but returns an input tag of the "hidden" type instead.
85
- def hidden_field(object, method, options = {})
86
- InstanceTag.new(object, method, self).to_input_field_tag("hidden", options)
169
+ def hidden_field(object_name, method, options = {})
170
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("hidden", options)
87
171
  end
88
172
 
89
173
  # Works just like text_field, but returns an input tag of the "file" type instead, which won't have a default value.
90
- def file_field(object, method, options = {})
91
- InstanceTag.new(object, method, self).to_input_field_tag("file", options)
174
+ def file_field(object_name, method, options = {})
175
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("file", options)
92
176
  end
93
177
 
94
178
  # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
@@ -100,8 +184,8 @@ module ActionView
100
184
  # <textarea cols="20" rows="40" id="post_body" name="post[body]">
101
185
  # #{@post.body}
102
186
  # </textarea>
103
- def text_area(object, method, options = {})
104
- InstanceTag.new(object, method, self).to_text_area_tag(options)
187
+ def text_area(object_name, method, options = {})
188
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_text_area_tag(options)
105
189
  end
106
190
 
107
191
  # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
@@ -120,8 +204,8 @@ module ActionView
120
204
  # check_box("puppy", "gooddog", {}, "yes", "no")
121
205
  # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
122
206
  # <input name="puppy[gooddog]" type="hidden" value="no" />
123
- def check_box(object, method, options = {}, checked_value = "1", unchecked_value = "0")
124
- InstanceTag.new(object, method, self).to_check_box_tag(options, checked_value, unchecked_value)
207
+ def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
208
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
125
209
  end
126
210
 
127
211
  # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
@@ -134,8 +218,8 @@ module ActionView
134
218
  # <input type="radio" id="post_category" name="post[category]" value="rails" checked="checked" />
135
219
  # <input type="radio" id="post_category" name="post[category]" value="java" />
136
220
  #
137
- def radio_button(object, method, tag_value, options = {})
138
- InstanceTag.new(object, method, self).to_radio_button_tag(tag_value, options)
221
+ def radio_button(object_name, method, tag_value, options = {})
222
+ InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_radio_button_tag(tag_value, options)
139
223
  end
140
224
  end
141
225
 
@@ -149,9 +233,10 @@ module ActionView
149
233
  DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS)
150
234
  DEFAULT_DATE_OPTIONS = { :discard_type => true }.freeze unless const_defined?(:DEFAULT_DATE_OPTIONS)
151
235
 
152
- def initialize(object_name, method_name, template_object, local_binding = nil)
153
- @object_name, @method_name = object_name.to_s, method_name.to_s
236
+ def initialize(object_name, method_name, template_object, local_binding = nil, object = nil)
237
+ @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
154
238
  @template_object, @local_binding = template_object, local_binding
239
+ @object = object
155
240
  if @object_name.sub!(/\[\]$/,"")
156
241
  @auto_index = @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}").id_before_type_cast
157
242
  end
@@ -186,7 +271,7 @@ module ActionView
186
271
  def to_text_area_tag(options = {})
187
272
  options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
188
273
  add_default_name_and_id(options)
189
- content_tag("textarea", html_escape(value_before_type_cast), options)
274
+ content_tag("textarea", html_escape(options.delete('value') || value_before_type_cast), options)
190
275
  end
191
276
 
192
277
  def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
@@ -240,11 +325,13 @@ module ActionView
240
325
  end
241
326
 
242
327
  def object
243
- @template_object.instance_variable_get "@#{@object_name}"
328
+ @object || @template_object.instance_variable_get("@#{@object_name}")
244
329
  end
245
330
 
246
331
  def value
247
- object.send(@method_name) unless object.nil?
332
+ unless object.nil?
333
+ object.send(@method_name)
334
+ end
248
335
  end
249
336
 
250
337
  def value_before_type_cast
@@ -286,5 +373,34 @@ module ActionView
286
373
  "#{@object_name}_#{index}_#{@method_name}"
287
374
  end
288
375
  end
376
+
377
+ class FormBuilder #:nodoc:
378
+ # The methods which wrap a form helper call.
379
+ class_inheritable_accessor :field_helpers
380
+ self.field_helpers = (FormHelper.instance_methods - ['form_for'])
381
+
382
+ attr_accessor :object_name, :object
383
+
384
+ def initialize(object_name, object, template, options, proc)
385
+ @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
386
+ end
387
+
388
+ (field_helpers - %w(check_box radio_button)).each do |selector|
389
+ src = <<-end_src
390
+ def #{selector}(method, options = {})
391
+ @template.send(#{selector.inspect}, @object_name, method, options.merge(:object => @object))
392
+ end
393
+ end_src
394
+ class_eval src, __FILE__, __LINE__
395
+ end
396
+
397
+ def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
398
+ @template.check_box(@object_name, method, options.merge(:object => @object), checked_value, unchecked_value)
399
+ end
400
+
401
+ def radio_button(method, tag_value, options = {})
402
+ @template.radio_button(@object_name, method, tag_value, options.merge(:object => @object))
403
+ end
404
+ end
289
405
  end
290
406
  end
@@ -47,7 +47,7 @@ module ActionView
47
47
  #
48
48
  # could become:
49
49
  #
50
- # <select name="post[person_id">
50
+ # <select name="post[person_id]">
51
51
  # <option></option>
52
52
  # <option value="1" selected="selected">David</option>
53
53
  # <option value="2">Sam</option>
@@ -59,18 +59,21 @@ module ActionView
59
59
  # to the database. Instead, a second model object is created when the create request is received.
60
60
  # This allows the user to submit a form page more than once with the expected results of creating multiple records.
61
61
  # In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
62
+ #
63
+ # By default, post.person_id is the selected option. Specify :selected => value to use a different selection
64
+ # or :selected => nil to leave all options unselected.
62
65
  def select(object, method, choices, options = {}, html_options = {})
63
- InstanceTag.new(object, method, self).to_select_tag(choices, options, html_options)
66
+ InstanceTag.new(object, method, self, nil, options.delete(:object)).to_select_tag(choices, options, html_options)
64
67
  end
65
68
 
66
69
  # Return select and option tags for the given object and method using options_from_collection_for_select to generate the list of option tags.
67
70
  def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
68
- InstanceTag.new(object, method, self).to_collection_select_tag(collection, value_method, text_method, options, html_options)
71
+ InstanceTag.new(object, method, self, nil, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
69
72
  end
70
73
 
71
74
  # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
72
75
  def country_select(object, method, priority_countries = nil, options = {}, html_options = {})
73
- InstanceTag.new(object, method, self).to_country_select_tag(priority_countries, options, html_options)
76
+ InstanceTag.new(object, method, self, nil, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options)
74
77
  end
75
78
 
76
79
  # Return select and option tags for the given object and method, using
@@ -82,7 +85,7 @@ module ActionView
82
85
  # zone model object. (See #time_zone_options_for_select for more
83
86
  # information.)
84
87
  def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
85
- InstanceTag.new(object, method, self).to_time_zone_select_tag(priority_zones, options, html_options)
88
+ InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
86
89
  end
87
90
 
88
91
  # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
@@ -209,7 +212,7 @@ module ActionView
209
212
  end
210
213
 
211
214
  # Returns a string of option tags for pretty much any time zone in the
212
- # world. Supply a TimeZone object as +selected+ to have it marked as the
215
+ # world. Supply a TimeZone name as +selected+ to have it marked as the
213
216
  # selected option tag. You can also supply an array of TimeZone objects
214
217
  # as +priority_zones+, so that they will be listed above the rest of the
215
218
  # (long) list. (You can use TimeZone.us_zones as a convenience for
@@ -296,7 +299,8 @@ module ActionView
296
299
  def to_select_tag(choices, options, html_options)
297
300
  html_options = html_options.stringify_keys
298
301
  add_default_name_and_id(html_options)
299
- content_tag("select", add_options(options_for_select(choices, value), options, value), html_options)
302
+ selected_value = options.has_key?(:selected) ? options[:selected] : value
303
+ content_tag("select", add_options(options_for_select(choices, selected_value), options, value), html_options)
300
304
  end
301
305
 
302
306
  def to_collection_select_tag(collection, value_method, text_method, options, html_options)
@@ -335,5 +339,23 @@ module ActionView
335
339
  end
336
340
  end
337
341
  end
342
+
343
+ class FormBuilder
344
+ def select(method, choices, options = {}, html_options = {})
345
+ @template.select(@object_name, method, choices, options.merge(:object => @object), html_options)
346
+ end
347
+
348
+ def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
349
+ @template.collection_select(@object_name, method, collection, value_method, text_method, options.merge(:object => @object), html_options)
350
+ end
351
+
352
+ def country_select(method, priority_countries = nil, options = {}, html_options = {})
353
+ @template.country_select(@object_name, method, priority_countries, options.merge(:object => @object), html_options)
354
+ end
355
+
356
+ def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
357
+ @template.time_zone_select(@object_name, method, priority_zones, options.merge(:object => @object), html_options)
358
+ end
359
+ end
338
360
  end
339
361
  end