Browse files

merge forward to current rails/master

  • Loading branch information...
2 parents 8691e25 + fbef982 commit 931f366ffcacc0444fcca2fb2e2b44644db9642f @svenfuchs svenfuchs committed Jul 16, 2008
Showing with 7,526 additions and 3,302 deletions.
  1. +1 −0 .gitignore
  2. +61 −1 Rakefile
  3. +4 −3 actionmailer/Rakefile
  4. +1 −1 actionmailer/lib/action_mailer/base.rb
  5. +1 −2 actionmailer/test/abstract_unit.rb
  6. +57 −0 actionpack/CHANGELOG
  7. +7 −5 actionpack/Rakefile
  8. +29 −78 actionpack/lib/action_controller/assertions/response_assertions.rb
  9. +54 −36 actionpack/lib/action_controller/base.rb
  10. +12 −11 actionpack/lib/action_controller/caching/actions.rb
  11. +9 −9 actionpack/lib/action_controller/caching/fragments.rb
  12. +219 −195 actionpack/lib/action_controller/filters.rb
  13. +2 −2 actionpack/lib/action_controller/layout.rb
  14. +5 −1 actionpack/lib/action_controller/mime_responds.rb
  15. +43 −39 actionpack/lib/action_controller/mime_type.rb
  16. +2 −1 actionpack/lib/action_controller/mime_types.rb
  17. +25 −12 actionpack/lib/action_controller/polymorphic_routes.rb
  18. +30 −5 actionpack/lib/action_controller/rack_process.rb
  19. +41 −8 actionpack/lib/action_controller/request.rb
  20. +1 −1 actionpack/lib/action_controller/rescue.rb
  21. +3 −3 actionpack/lib/action_controller/resources.rb
  22. +4 −0 actionpack/lib/action_controller/routing.rb
  23. +1 −2 actionpack/lib/action_controller/routing/builder.rb
  24. +2 −2 actionpack/lib/action_controller/templates/rescues/diagnostics.erb
  25. +2 −2 actionpack/lib/action_controller/templates/rescues/template_error.erb
  26. +4 −24 actionpack/lib/action_controller/test_process.rb
  27. +1 −1 actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
  28. +5 −3 actionpack/lib/action_view.rb
  29. +108 −102 actionpack/lib/action_view/base.rb
  30. +44 −18 actionpack/lib/action_view/helpers/asset_tag_helper.rb
  31. +1 −2 actionpack/lib/action_view/helpers/cache_helper.rb
  32. +5 −2 actionpack/lib/action_view/helpers/capture_helper.rb
  33. +31 −19 actionpack/lib/action_view/helpers/date_helper.rb
  34. +15 −11 actionpack/lib/action_view/helpers/form_helper.rb
  35. +17 −10 actionpack/lib/action_view/helpers/form_options_helper.rb
  36. +26 −24 actionpack/lib/action_view/helpers/form_tag_helper.rb
  37. +61 −76 actionpack/lib/action_view/helpers/javascript_helper.rb
  38. +194 −190 actionpack/lib/action_view/helpers/prototype_helper.rb
  39. +30 −8 actionpack/lib/action_view/helpers/tag_helper.rb
  40. +3 −7 actionpack/lib/action_view/helpers/text_helper.rb
  41. +4 −6 actionpack/lib/action_view/helpers/url_helper.rb
  42. +11 −9 actionpack/lib/action_view/inline_template.rb
  43. +0 −70 actionpack/lib/action_view/partial_template.rb
  44. +22 −23 actionpack/lib/action_view/partials.rb
  45. +96 −0 actionpack/lib/action_view/paths.rb
  46. +78 −0 actionpack/lib/action_view/renderable.rb
  47. +36 −0 actionpack/lib/action_view/renderable_partial.rb
  48. +65 −63 actionpack/lib/action_view/template.rb
  49. +2 −2 actionpack/lib/action_view/template_error.rb
  50. +0 −88 actionpack/lib/action_view/template_file.rb
  51. +1 −13 actionpack/lib/action_view/template_handler.rb
  52. +5 −15 actionpack/lib/action_view/template_handlers/builder.rb
  53. +2 −110 actionpack/lib/action_view/template_handlers/compilable.rb
  54. +7 −5 actionpack/lib/action_view/template_handlers/erb.rb
  55. +2 −17 actionpack/lib/action_view/template_handlers/rjs.rb
  56. +0 −103 actionpack/lib/action_view/view_load_paths.rb
  57. +3 −0 actionpack/test/abstract_unit.rb
  58. +28 −30 actionpack/test/active_record_unit.rb
  59. +15 −12 actionpack/test/activerecord/render_partial_with_record_identification_test.rb
  60. +8 −35 actionpack/test/controller/action_pack_assertions_test.rb
  61. +0 −3 actionpack/test/controller/addresses_render_test.rb
  62. +25 −53 actionpack/test/controller/caching_test.rb
  63. +0 −2 actionpack/test/controller/capture_test.rb
  64. +78 −9 actionpack/test/controller/cgi_test.rb
  65. +1 −1 actionpack/test/controller/components_test.rb
  66. +25 −13 actionpack/test/controller/content_type_test.rb
  67. +0 −45 actionpack/test/controller/custom_handler_test.rb
  68. +0 −3 actionpack/test/controller/deprecation/deprecated_base_methods_test.rb
  69. +25 −0 actionpack/test/controller/html-scanner/document_test.rb
  70. +3 −2 actionpack/test/controller/layout_test.rb
  71. +30 −28 actionpack/test/controller/mime_responds_test.rb
  72. +80 −62 actionpack/test/controller/new_render_test.rb
  73. +33 −0 actionpack/test/controller/polymorphic_routes_test.rb
  74. +46 −8 actionpack/test/controller/rack_test.rb
  75. +8 −16 actionpack/test/controller/redirect_test.rb
  76. +21 −25 actionpack/test/controller/render_test.rb
  77. +11 −5 actionpack/test/controller/request_test.rb
  78. +15 −17 actionpack/test/controller/resources_test.rb
  79. +20 −0 actionpack/test/controller/routing_test.rb
  80. +0 −4 actionpack/test/controller/send_file_test.rb
  81. +0 −18 actionpack/test/controller/test_test.rb
  82. +22 −40 actionpack/test/controller/view_paths_test.rb
  83. +1 −0 actionpack/test/fixtures/developers/_developer.erb
  84. +1 −0 actionpack/test/fixtures/fun/games/_game.erb
  85. +1 −0 actionpack/test/fixtures/fun/serious/games/_game.erb
  86. +2 −0 actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb
  87. +1 −0 actionpack/test/fixtures/projects/_project.erb
  88. +1 −0 actionpack/test/fixtures/public/javascripts/subdir/subdir.js
  89. +1 −0 actionpack/test/fixtures/public/stylesheets/subdir/subdir.css
  90. +1 −0 actionpack/test/fixtures/replies/_reply.erb
  91. +1 −0 actionpack/test/fixtures/test/_customer_with_var.erb
  92. +1 −0 actionpack/test/fixtures/test/_local_inspector.html.erb
  93. +1 −1 actionpack/test/fixtures/test/hello.builder
  94. +1 −0 actionpack/test/fixtures/test/hyphen-ated.erb
  95. +1 −1 actionpack/test/fixtures/test/non_erb_block_content_for.builder
  96. +45 −1 actionpack/test/template/asset_tag_helper_test.rb
  97. +70 −8 actionpack/test/template/date_helper_test.rb
  98. +1,410 −516 actionpack/test/template/form_options_helper_test.rb
  99. +25 −15 actionpack/test/template/form_tag_helper_test.rb
  100. +6 −10 actionpack/test/template/javascript_helper_test.rb
  101. +72 −71 actionpack/test/template/prototype_helper_test.rb
  102. +14 −13 actionpack/test/template/record_tag_helper_test.rb
  103. +131 −0 actionpack/test/template/render_test.rb
  104. +26 −9 actionpack/test/template/tag_helper_test.rb
  105. +0 −95 actionpack/test/template/template_file_test.rb
  106. +0 −92 actionpack/test/template/template_object_test.rb
  107. +2 −3 actionpack/test/template/text_helper_test.rb
  108. +19 −17 actionpack/test/template/url_helper_test.rb
  109. +13 −5 activemodel/Rakefile
  110. +4 −16 activemodel/lib/active_model.rb
  111. +4 −0 activemodel/lib/active_model/base.rb
  112. +2 −0 activemodel/lib/active_model/callbacks.rb
  113. +7 −0 activemodel/lib/active_model/core.rb
  114. +8 −10 activemodel/lib/active_model/observing.rb
  115. +66 −0 activemodel/lib/active_model/state_machine.rb
  116. +62 −0 activemodel/lib/active_model/state_machine/event.rb
  117. +74 −0 activemodel/lib/active_model/state_machine/machine.rb
  118. +50 −0 activemodel/lib/active_model/state_machine/state.rb
  119. +40 −0 activemodel/lib/active_model/state_machine/state_transition.rb
  120. +2 −0 activemodel/lib/active_model/validations.rb
  121. +0 −120 activemodel/spec/observing_spec.rb
  122. +0 −17 activemodel/spec/spec_helper.rb
  123. +123 −0 activemodel/test/observing_test.rb
  124. +51 −0 activemodel/test/state_machine/event_test.rb
  125. +43 −0 activemodel/test/state_machine/machine_test.rb
  126. +74 −0 activemodel/test/state_machine/state_test.rb
  127. +88 −0 activemodel/test/state_machine/state_transition_test.rb
  128. +324 −0 activemodel/test/state_machine_test.rb
  129. +39 −0 activemodel/test/test_helper.rb
  130. +44 −0 activerecord/CHANGELOG
  131. +4 −4 activerecord/Rakefile
  132. +12 −3 activerecord/lib/active_record/association_preload.rb
  133. +37 −19 activerecord/lib/active_record/associations.rb
  134. +14 −5 activerecord/lib/active_record/associations/association_collection.rb
  135. +5 −1 activerecord/lib/active_record/associations/association_proxy.rb
  136. +4 −3 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  137. +14 −6 activerecord/lib/active_record/associations/has_many_association.rb
  138. +3 −3 activerecord/lib/active_record/associations/has_many_through_association.rb
  139. +12 −3 activerecord/lib/active_record/associations/has_one_association.rb
  140. +25 −18 activerecord/lib/active_record/base.rb
  141. +2 −2 activerecord/lib/active_record/callbacks.rb
  142. +11 −10 activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
  143. +13 −0 activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
  144. +54 −20 activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
  145. +37 −14 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  146. +12 −0 activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
  147. +3 −1 activerecord/lib/active_record/dirty.rb
  148. +8 −7 activerecord/lib/active_record/fixtures.rb
  149. +1 −0 activerecord/lib/active_record/locking/optimistic.rb
  150. +4 −1 activerecord/lib/active_record/migration.rb
  151. +3 −1 activerecord/lib/active_record/named_scope.rb
  152. +4 −5 activerecord/lib/active_record/observer.rb
  153. +4 −0 activerecord/lib/active_record/reflection.rb
  154. +13 −2 activerecord/lib/active_record/test_case.rb
  155. +3 −14 activerecord/lib/active_record/transactions.rb
  156. +14 −9 activerecord/lib/active_record/validations.rb
  157. +6 −0 activerecord/test/cases/adapter_test.rb
  158. +6 −0 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
  159. +60 −1 activerecord/test/cases/associations/has_many_associations_test.rb
  160. +6 −0 activerecord/test/cases/associations/has_many_through_associations_test.rb
  161. +7 −0 activerecord/test/cases/associations/has_one_associations_test.rb
  162. +108 −0 activerecord/test/cases/associations_test.rb
  163. +19 −2 activerecord/test/cases/base_test.rb
  164. +36 −0 activerecord/test/cases/column_definition_test.rb
  165. +46 −0 activerecord/test/cases/dirty_test.rb
  166. +24 −0 activerecord/test/cases/finder_test.rb
  167. +32 −3 activerecord/test/cases/fixtures_test.rb
  168. +5 −5 activerecord/test/cases/helper.rb
  169. +7 −0 activerecord/test/cases/inheritance_test.rb
  170. +10 −2 activerecord/test/cases/lifecycle_test.rb
  171. +20 −0 activerecord/test/cases/locking_test.rb
  172. +10 −0 activerecord/test/cases/method_scoping_test.rb
  173. +111 −16 activerecord/test/cases/migration_test.rb
  174. +25 −0 activerecord/test/cases/multiple_db_test.rb
  175. +56 −1 activerecord/test/cases/named_scope_test.rb
  176. +3 −3 activerecord/test/cases/reflection_test.rb
  177. +46 −0 activerecord/test/cases/schema_dumper_test.rb
  178. +18 −5 activerecord/test/cases/validations_test.rb
  179. +1 −0 activerecord/test/fixtures/companies.yml
  180. +2 −1 activerecord/test/models/author.rb
  181. +1 −0 activerecord/test/models/category.rb
  182. +3 −0 activerecord/test/models/company.rb
  183. +1 −1 activerecord/test/models/person.rb
  184. +12 −1 activerecord/test/models/post.rb
  185. +4 −0 activerecord/test/models/topic.rb
  186. +9 −0 activerecord/test/schema/schema.rb
  187. +5 −4 activeresource/Rakefile
  188. +25 −1 activesupport/CHANGELOG
  189. +6 −6 activesupport/Rakefile
  190. +1 −0 activesupport/lib/active_support.rb
  191. +8 −8 activesupport/lib/active_support/cache.rb
  192. +3 −2 activesupport/lib/active_support/core_ext/array/conversions.rb
  193. +45 −2 activesupport/lib/active_support/core_ext/array/grouping.rb
  194. +3 −1 activesupport/lib/active_support/core_ext/enumerable.rb
  195. +18 −7 activesupport/lib/active_support/core_ext/module/introspection.rb
  196. +3 −2 activesupport/lib/active_support/core_ext/module/model_naming.rb
  197. +1 −1 activesupport/lib/active_support/core_ext/object/instance_variables.rb
  198. +0 −1 activesupport/lib/active_support/core_ext/test.rb
  199. +3 −3 activesupport/lib/active_support/dependencies.rb
  200. +23 −4 activesupport/lib/active_support/inflector.rb
  201. +1 −1 activesupport/lib/active_support/json/encoders/date_time.rb
  202. +35 −0 activesupport/lib/active_support/memoizable.rb
  203. +1 −5 activesupport/lib/active_support/test_case.rb
  204. +6 −0 activesupport/lib/active_support/testing/core_ext/test.rb
  205. +4 −3 activesupport/lib/active_support/{ → testing}/core_ext/test/unit/assertions.rb
  206. +80 −12 activesupport/lib/active_support/testing/performance.rb
  207. +34 −13 activesupport/lib/active_support/time_with_zone.rb
  208. +6 −0 activesupport/lib/active_support/values/time_zone.rb
  209. +6 −0 activesupport/test/caching_test.rb
  210. +46 −4 activesupport/test/core_ext/array_ext_test.rb
  211. +7 −2 activesupport/test/core_ext/enumerable_test.rb
  212. +174 −0 activesupport/test/core_ext/time_with_zone_test.rb
  213. +25 −4 activesupport/test/inflector_test.rb
  214. +49 −0 activesupport/test/memoizable_test.rb
  215. +7 −0 activesupport/test/time_zone_test.rb
  216. +0 −1 cleanlogs.sh
  217. +613 −0 doc/template/horo.rb
  218. +19 −0 railties/CHANGELOG
  219. +10 −4 railties/Rakefile
  220. +2 −1 railties/bin/about
  221. +2 −0 railties/configs/routes.rb
  222. +4 −4 railties/environments/boot.rb
  223. +1 −5 railties/helpers/performance_test_helper.rb
  224. +1 −0 railties/lib/commands/about.rb
  225. +33 −16 railties/lib/commands/plugin.rb
  226. +12 −2 railties/lib/commands/server.rb
  227. +25 −0 railties/lib/commands/servers/thin.rb
  228. +1 −1 railties/lib/console_with_helpers.rb
  229. +47 −33 railties/lib/initializer.rb
  230. +6 −0 railties/lib/performance_test_help.rb
  231. +5 −1 railties/lib/rails/gem_dependency.rb
  232. +11 −10 railties/lib/rails/plugin.rb
  233. +5 −4 railties/lib/rails/plugin/locator.rb
  234. +10 −17 railties/lib/rails_generator/commands.rb
  235. +1 −1 railties/lib/rails_generator/generators/components/observer/templates/unit_test.rb
  236. +8 −0 railties/lib/rails_generator/generators/components/performance_test/USAGE
  237. +16 −0 railties/lib/rails_generator/generators/components/performance_test/performance_test_generator.rb
  238. +8 −0 railties/lib/rails_generator/generators/components/performance_test/templates/performance_test.rb
  239. +8 −1 railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb
  240. +1 −1 railties/lib/rails_generator/lookup.rb
  241. +8 −11 railties/lib/tasks/annotations.rake
  242. +20 −14 railties/lib/tasks/databases.rake
  243. +1 −1 railties/lib/tasks/misc.rake
  244. +10 −4 railties/lib/tasks/testing.rake
  245. +19 −0 railties/test/generators/rails_controller_generator_test.rb
  246. +42 −0 railties/test/generators/rails_scaffold_generator_test.rb
  247. +1 −1 railties/test/plugin_loader_test.rb
View
1 .gitignore
@@ -1,4 +1,5 @@
debug.log
+doc/rdoc
activeresource/doc
activerecord/doc
actionpack/doc
View
62 Rakefile
@@ -1,4 +1,6 @@
require 'rake'
+require 'rake/rdoctask'
+require 'rake/contrib/sshpublisher'
env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
@@ -11,11 +13,69 @@ end
desc 'Run all tests by default'
task :default => :test
-%w(test rdoc package pgem release).each do |task_name|
+%w(test rdoc pgem package release).each do |task_name|
desc "Run #{task_name} task for all projects"
task task_name do
PROJECTS.each do |project|
system %(cd #{project} && #{env} #{$0} #{task_name})
end
end
end
+
+
+desc "Generate documentation for the Rails framework"
+Rake::RDocTask.new do |rdoc|
+ rdoc.rdoc_dir = 'doc/rdoc'
+ rdoc.title = "Ruby on Rails Documentation"
+
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.options << '-A cattr_accessor=object'
+ rdoc.options << '--charset' << 'utf-8'
+
+ rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : './doc/template/horo'
+
+ rdoc.rdoc_files.include('railties/CHANGELOG')
+ rdoc.rdoc_files.include('railties/MIT-LICENSE')
+ rdoc.rdoc_files.include('railties/README')
+ rdoc.rdoc_files.include('railties/lib/{*.rb,commands/*.rb,rails/*.rb,rails_generator/*.rb}')
+
+ rdoc.rdoc_files.include('activerecord/README')
+ rdoc.rdoc_files.include('activerecord/CHANGELOG')
+ rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb')
+ rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*')
+
+ rdoc.rdoc_files.include('activeresource/README')
+ rdoc.rdoc_files.include('activeresource/CHANGELOG')
+ rdoc.rdoc_files.include('activeresource/lib/active_resource.rb')
+ rdoc.rdoc_files.include('activeresource/lib/active_resource/*')
+
+ rdoc.rdoc_files.include('actionpack/README')
+ rdoc.rdoc_files.include('actionpack/CHANGELOG')
+ rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
+ rdoc.rdoc_files.include('actionpack/lib/action_view/**/*.rb')
+ rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
+
+ rdoc.rdoc_files.include('actionmailer/README')
+ rdoc.rdoc_files.include('actionmailer/CHANGELOG')
+ rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
+ rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
+
+ rdoc.rdoc_files.include('activesupport/README')
+ rdoc.rdoc_files.include('activesupport/CHANGELOG')
+ rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb')
+ rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*')
+end
+
+# Enhance rdoc task to copy referenced images also
+task :rdoc do
+ FileUtils.mkdir_p "doc/rdoc/files/examples/"
+ FileUtils.copy "activerecord/examples/associations.png", "doc/rdoc/files/examples/associations.png"
+end
+
+desc "Publish API docs for Rails as a whole and for each component"
+task :pdoc => :rdoc do
+ Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/api", "doc/rdoc").upload
+ PROJECTS.each do |project|
+ system %(cd #{project} && #{env} #{$0} pdoc)
+ end
+end
View
7 actionmailer/Rakefile
@@ -35,7 +35,7 @@ Rake::RDocTask.new { |rdoc|
rdoc.title = "Action Mailer -- Easy email delivery and testing"
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
rdoc.options << '--charset' << 'utf-8'
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
+ rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
rdoc.rdoc_files.include('README', 'CHANGELOG')
rdoc.rdoc_files.include('lib/action_mailer.rb')
rdoc.rdoc_files.include('lib/action_mailer/*.rb')
@@ -76,12 +76,13 @@ end
desc "Publish the API documentation"
task :pgem => [:package] do
- Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+ Rake::SshFilePublisher.new("wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+ `ssh wrath.rubyonrails.org './gemupdate.sh'`
end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
- Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/am", "doc").upload
+ Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload
end
desc "Publish the release files to RubyForge."
View
2 actionmailer/lib/action_mailer/base.rb
@@ -426,7 +426,7 @@ def register_template_extension(extension)
end
def template_root=(root)
- write_inheritable_attribute(:template_root, ActionView::ViewLoadPaths.new(Array(root)))
+ write_inheritable_attribute(:template_root, ActionView::PathSet.new(Array(root)))
end
end
View
3 actionmailer/test/abstract_unit.rb
@@ -32,8 +32,7 @@ def self.start(*args)
# Wrap tests that use Mocha and skip if unavailable.
def uses_mocha(test_name)
- gem 'mocha', ">=0.5"
- require 'stubba'
+ gem 'mocha', ">=0.9.0"
yield
rescue Gem::LoadError
$stderr.puts "Skipping #{test_name} tests (Mocha >= 0.5 is required). `gem install mocha` and try again."
View
57 actionpack/CHANGELOG
@@ -1,5 +1,62 @@
*Edge*
+* Get buffer for fragment cache from template's @output_buffer [Josh Peek]
+
+* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached [Josh Peek]
+
+* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class [Josh Peek]
+
+* Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {}) [Josh Peek]
+
+* Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent with link_to_remote (submit_to_remote still works as an alias) #8994 [clemens]
+
+* Add :recursive option to javascript_include_tag and stylesheet_link_tag to be used along with :all. #480 [Damian Janowski]
+
+* Allow users to disable the use of the Accept header [Michael Koziarski]
+
+ The accept header is poorly implemented by browsers and causes strange
+ errors when used on public sites where crawlers make requests too. You
+ can use formatted urls (e.g. /people/1.xml) to support API clients in a
+ much simpler way.
+
+ To disable the header you need to set:
+
+ config.action_controller.use_accept_header = false
+
+* Do not stat template files in production mode before rendering. You will no longer be able to modify templates in production mode without restarting the server [Josh Peek]
+
+* Deprecated TemplateHandler line offset [Josh Peek]
+
+* Allow caches_action to accept cache store options. #416. [José Valim]. Example:
+
+ caches_action :index, :redirected, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour
+
+* Remove define_javascript_functions, javascript_include_tag and friends are far superior. [Michael Koziarski]
+
+* Deprecate :use_full_path render option. The supplying the option no longer has an effect [Josh Peek]
+
+* Add :as option to render a collection of partials with a custom local variable name. #509 [Simon Jefford, Pratik Naik]
+
+ render :partial => 'other_people', :collection => @people, :as => :person
+
+ This will let you access objects of @people as 'person' local variable inside 'other_people' partial template.
+
+* time_zone_select: support for regexp matching of priority zones. Resolves #195 [Ernie Miller]
+
+* Made ActionView::Base#render_file private [Josh Peek]
+
+* Refactor and simplify the implementation of assert_redirected_to. Arguments are now normalised relative to the controller being tested, not the root of the application. [Michael Koziarski]
+
+ This could cause some erroneous test failures if you were redirecting between controllers
+ in different namespaces and wrote your assertions relative to the root of the application.
+
+* Remove follow_redirect from controller functional tests.
+
+ If you want to follow redirects you can use integration tests. The functional test
+ version was only useful if you were using redirect_to :id=>...
+
+* Fix polymorphic_url with singleton resources. #461 [Tammer Saleh]
+
* Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek]
* Added block-call style to link_to [Sam Stephenson/DHH]. Example:
View
12 actionpack/Rakefile
@@ -49,12 +49,14 @@ Rake::RDocTask.new { |rdoc|
rdoc.title = "Action Pack -- On rails from request to response"
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.options << '--charset' << 'utf-8'
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
+ rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo'
if ENV['DOC_FILES']
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
else
rdoc.rdoc_files.include('README', 'RUNNING_UNIT_TESTS', 'CHANGELOG')
- rdoc.rdoc_files.include('lib/**/*.rb')
+ rdoc.rdoc_files.include(Dir['lib/**/*.rb'] -
+ Dir['lib/*/vendor/**/*.rb'])
+ rdoc.rdoc_files.exclude('lib/actionpack.rb')
end
}
@@ -132,13 +134,13 @@ task :update_js => [ :update_scriptaculous ]
desc "Publish the API documentation"
task :pgem => [:package] do
- Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
- `ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'`
+ Rake::SshFilePublisher.new("wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
+ `ssh wrath.rubyonrails.org './gemupdate.sh'`
end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
- Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/ap", "doc").upload
+ Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/ap", "doc").upload
end
desc "Publish the release files to RubyForge."
View
107 actionpack/lib/action_controller/assertions/response_assertions.rb
@@ -56,74 +56,24 @@ def assert_response(type, message = nil)
# # assert that the redirection was to the named route login_url
# assert_redirected_to login_url
#
+ # # assert that the redirection was to the url for @customer
+ # assert_redirected_to @customer
+ #
def assert_redirected_to(options = {}, message=nil)
clean_backtrace do
assert_response(:redirect, message)
return true if options == @response.redirected_to
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
-
- begin
- url = {}
- original = { :expected => options, :actual => @response.redirected_to.is_a?(Symbol) ? @response.redirected_to : @response.redirected_to.dup }
- original.each do |key, value|
- if value.is_a?(Symbol)
- value = @controller.respond_to?(value, true) ? @controller.send(value) : @controller.send("hash_for_#{value}_url")
- end
-
- unless value.is_a?(Hash)
- request = case value
- when NilClass then nil
- when /^\w+:\/\// then recognized_request_for(%r{^(\w+://.*?(/|$|\?))(.*)$} =~ value ? $3 : nil)
- else recognized_request_for(value)
- end
- value = request.path_parameters if request
- end
-
- if value.is_a?(Hash) # stringify 2 levels of hash keys
- if name = value.delete(:use_route)
- route = ActionController::Routing::Routes.named_routes[name]
- value.update(route.parameter_shell)
- end
-
- value.stringify_keys!
- value.values.select { |v| v.is_a?(Hash) }.collect { |v| v.stringify_keys! }
- if key == :expected && value['controller'] == @controller.controller_name && original[:actual].is_a?(Hash)
- original[:actual].stringify_keys!
- value.delete('controller') if original[:actual]['controller'].nil? || original[:actual]['controller'] == value['controller']
- end
- end
-
- if value.respond_to?(:[]) && value['controller']
- value['controller'] = value['controller'].to_s
- if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
- new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
- value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path) && @response.redirected_to.is_a?(Hash)
- end
- value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash
- end
- url[key] = value
- end
-
- @response_diff = url[:actual].diff(url[:expected]) if url[:actual]
- msg = build_message(message, "expected a redirect to <?>, found one to <?>, a difference of <?> ", url[:expected], url[:actual], @response_diff)
-
- assert_block(msg) do
- url[:expected].keys.all? do |k|
- if k == :controller then url[:expected][k] == ActionController::Routing.controller_relative_to(url[:actual][k], @controller.class.controller_path)
- else parameterize(url[:expected][k]) == parameterize(url[:actual][k])
- end
- end
- end
- rescue ActionController::RoutingError # routing failed us, so match the strings only.
- msg = build_message(message, "expected a redirect to <?>, found one to <?>", options, @response.redirect_url)
- url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$}
- eurl, epath, url, path = [options, @response.redirect_url].collect do |url|
- u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url]
- [u, (p.first == '/') ? p : '/' + p]
- end.flatten
+
+ # Support partial arguments for hash redirections
+ if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
+ return true if options.all? {|(key, value)| @response.redirected_to[key] == value}
+ end
+
+ redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to)
+ options_after_normalisation = normalize_argument_to_redirection(options)
- assert_equal(eurl, url, msg) if eurl && url
- assert_equal(epath, path, msg) if epath && path
+ if redirected_to_after_normalisation != options_after_normalisation
+ flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"
end
end
end
@@ -137,36 +87,37 @@ def assert_redirected_to(options = {}, message=nil)
#
def assert_template(expected = nil, message=nil)
clean_backtrace do
- rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file
+ rendered = @response.rendered_template
msg = build_message(message, "expecting <?> but rendering with <?>", expected, rendered)
assert_block(msg) do
if expected.nil?
- !@response.rendered_with_file?
+ @response.rendered_template.nil?
else
- expected == rendered
+ rendered.to_s.match(expected)
end
end
end
end
private
- # Recognizes the route for a given path.
- def recognized_request_for(path, request_method = nil)
- path = "/#{path}" unless path.first == '/'
-
- # Assume given controller
- request = ActionController::TestRequest.new({}, {}, nil)
- request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
- request.path = path
-
- ActionController::Routing::Routes.recognize(request)
- request
- end
# Proxy to to_param if the object will respond to it.
def parameterize(value)
value.respond_to?(:to_param) ? value.to_param : value
end
+
+ def normalize_argument_to_redirection(fragment)
+ after_routing = @controller.url_for(fragment)
+ if after_routing =~ %r{^\w+://.*}
+ after_routing
+ else
+ # FIXME - this should probably get removed.
+ if after_routing.first != '/'
+ after_routing = '/' + after_routing
+ end
+ @request.protocol + @request.host_with_port + after_routing
+ end
+ end
end
end
end
View
90 actionpack/lib/action_controller/base.rb
@@ -340,6 +340,16 @@ class Base
cattr_accessor :optimise_named_routes
self.optimise_named_routes = true
+ # Indicates whether the response format should be determined by examining the Accept HTTP header,
+ # or by using the simpler params + ajax rules.
+ #
+ # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept
+ # header into account. If it is set to false then the request format will be determined solely
+ # by examining params[:format]. If params format is missing, the format will be either HTML or
+ # Javascript depending on whether the request is an AJAX request.
+ cattr_accessor :use_accept_header
+ self.use_accept_header = true
+
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
class_inheritable_accessor :allow_forgery_protection
self.allow_forgery_protection = true
@@ -402,26 +412,26 @@ def controller_path
# More methods can be hidden using <tt>hide_actions</tt>.
def hidden_actions
unless read_inheritable_attribute(:hidden_actions)
- write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map(&:to_s))
+ write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map { |m| m.to_s })
end
read_inheritable_attribute(:hidden_actions)
end
# Hide each of the given methods from being callable as actions.
def hide_action(*names)
- write_inheritable_attribute(:hidden_actions, hidden_actions | names.map(&:to_s))
+ write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s })
end
- ## View load paths determine the bases from which template references can be made. So a call to
- ## render("test/template") will be looked up in the view load paths array and the closest match will be
- ## returned.
+ # View load paths determine the bases from which template references can be made. So a call to
+ # render("test/template") will be looked up in the view load paths array and the closest match will be
+ # returned.
def view_paths
@view_paths || superclass.view_paths
end
def view_paths=(value)
- @view_paths = ActionView::ViewLoadPaths.new(Array(value)) if value
+ @view_paths = ActionView::Base.process_view_paths(value) if value
end
# Adds a view_path to the front of the view_paths array.
@@ -603,7 +613,8 @@ def process(request, response, method = :perform_action, *arguments) #:nodoc:
#
# This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
# would have slashed-off the path components after the changed action.
- def url_for(options = {}) #:doc:
+ def url_for(options = {})
+ options ||= {}
case options
when String
options
@@ -641,7 +652,7 @@ def view_paths
end
def view_paths=(value)
- @template.view_paths = ViewLoadPaths.new(value)
+ @template.view_paths = ActionView::Base.process_view_paths(value)
end
# Adds a view_path to the front of the view_paths array.
@@ -702,6 +713,9 @@ def append_view_path(path)
# # builds the complete response.
# render :partial => "person", :collection => @winners
#
+ # # Renders a collection of partials but with a custom local variable name
+ # render :partial => "admin_person", :collection => @winners, :as => :person
+ #
# # Renders the same collection of partials, but also renders the
# # person_divider partial between each person partial.
# render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
@@ -733,6 +747,9 @@ def append_view_path(path)
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
# render :template => "weblog/show"
#
+ # # Renders the template with a local variable
+ # render :template => "weblog/show", :locals => {:customer => Customer.new}
+ #
# === Rendering a file
#
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
@@ -852,22 +869,21 @@ def render(options = nil, extra_options = {}, &block) #:doc:
else
if file = options[:file]
- render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
+ render_for_file(file, options[:status], nil, options[:locals] || {})
elsif template = options[:template]
- render_for_file(template, options[:status], true)
+ render_for_file(template, options[:status], true, options[:locals] || {})
elsif inline = options[:inline]
add_variables_to_assigns
- tmpl = ActionView::InlineTemplate.new(@template, options[:inline], options[:locals], options[:type])
- render_for_text(@template.render_template(tmpl), options[:status])
+ render_for_text(@template.render(options), options[:status])
elsif action_name = options[:action]
template = default_template_name(action_name.to_s)
if options[:layout] && !template_exempt_from_layout?(template)
- render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
+ render_with_a_layout(:file => template, :status => options[:status], :layout => true)
else
- render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
+ render_with_no_layout(:file => template, :status => options[:status])
end
elsif xml = options[:xml]
@@ -887,12 +903,12 @@ def render(options = nil, extra_options = {}, &block) #:doc:
if collection = options[:collection]
render_for_text(
@template.send!(:render_partial_collection, partial, collection,
- options[:spacer_template], options[:locals]), options[:status]
+ options[:spacer_template], options[:locals], options[:as]), options[:status]
)
else
render_for_text(
@template.send!(:render_partial, partial,
- ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]), options[:status]
+ options[:object], options[:locals]), options[:status]
)
end
@@ -1037,29 +1053,31 @@ def redirect_to(options = {}, response_status = {}) #:doc:
status = 302
end
+ response.redirected_to= options
+ logger.info("Redirected to #{options}") if logger && logger.info?
+
case options
when %r{^\w+://.*}
- raise DoubleRenderError if performed?
- logger.info("Redirected to #{options}") if logger && logger.info?
- response.redirect(options, interpret_status(status))
- response.redirected_to = options
- @performed_redirect = true
-
+ redirect_to_full_url(options, status)
when String
- redirect_to(request.protocol + request.host_with_port + options, :status=>status)
-
+ redirect_to_full_url(request.protocol + request.host_with_port + options, status)
when :back
- request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"], :status=>status) : raise(RedirectBackError)
-
- when Hash
- redirect_to(url_for(options), :status=>status)
- response.redirected_to = options
-
+ if referer = request.headers["Referer"]
+ redirect_to(referer, :status=>status)
+ else
+ raise RedirectBackError
+ end
else
- redirect_to(url_for(options), :status=>status)
+ redirect_to_full_url(url_for(options), status)
end
end
+ def redirect_to_full_url(url, status)
+ raise DoubleRenderError if performed?
+ response.redirect(url, interpret_status(status))
+ @performed_redirect = true
+ end
+
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
# intermediate caches shouldn't cache the response.
#
@@ -1092,10 +1110,10 @@ def reset_session #:doc:
private
- def render_for_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
+ def render_for_file(template_path, status = nil, use_full_path = nil, locals = {}) #:nodoc:
add_variables_to_assigns
logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
- render_for_text(@template.render_file(template_path, use_full_path, locals), status)
+ render_for_text(@template.render(:file => template_path, :locals => locals), status)
end
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
@@ -1183,7 +1201,7 @@ def action_methods
end
def self.action_methods
- @action_methods ||= Set.new(public_instance_methods.map(&:to_s)) - hidden_actions
+ @action_methods ||= Set.new(public_instance_methods.map { |m| m.to_s }) - hidden_actions
end
def add_variables_to_assigns
@@ -1230,8 +1248,8 @@ def template_public?(template_name = default_template_name)
end
def template_exempt_from_layout?(template_name = default_template_name)
- template_name = @template.send(:template_file_from_name, template_name) if @template
- @@exempt_from_layout.any? { |ext| template_name.to_s =~ ext }
+ template_name = @template.pick_template(template_name).to_s if @template
+ @@exempt_from_layout.any? { |ext| template_name =~ ext }
end
def default_template_name(action_name = self.action_name)
View
23 actionpack/lib/action_controller/caching/actions.rb
@@ -27,13 +27,15 @@ module Caching
# You can set modify the default action cache path by passing a :cache_path option. This will be passed directly to ActionCachePath.path_for. This is handy
# for actions with multiple possible routes that should be cached differently. If a block is given, it is called with the current controller instance.
#
- # And you can also use :if to pass a Proc that specifies when the action should be cached.
+ # And you can also use :if (or :unless) to pass a Proc that specifies when the action should be cached.
+ #
+ # Finally, if you are using memcached, you can also pass :expires_in.
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
# caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
- # caches_action :show, :cache_path => { :project => 1 }
+ # caches_action :show, :cache_path => { :project => 1 }, :expires_in => 1.hour
# caches_action :feed, :cache_path => Proc.new { |controller|
# controller.params[:user_id] ?
# controller.send(:user_list_url, c.params[:user_id], c.params[:id]) :
@@ -56,8 +58,10 @@ module ClassMethods
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
- cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path))
- around_filter(cache_filter, {:only => actions}.merge(options))
+ filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) }
+
+ cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
+ around_filter(cache_filter, filter_options)
end
end
@@ -80,8 +84,8 @@ def initialize(options, &block)
end
def before(controller)
- cache_path = ActionCachePath.new(controller, path_options_for(controller, @options))
- if cache = controller.read_fragment(cache_path.path)
+ cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path)))
+ if cache = controller.read_fragment(cache_path.path, @options[:store_options])
controller.rendered_action_cache = true
set_content_type!(controller, cache_path.extension)
options = { :text => cache }
@@ -96,7 +100,7 @@ def before(controller)
def after(controller)
return if controller.rendered_action_cache || !caching_allowed(controller)
action_content = cache_layout? ? content_for_layout(controller) : controller.response.body
- controller.write_fragment(controller.action_cache_path.path, action_content)
+ controller.write_fragment(controller.action_cache_path.path, action_content, @options[:store_options])
end
private
@@ -162,10 +166,7 @@ def extract_extension(request)
# If there's no extension in the path, check request.format
if extension.nil?
- extension = request.format.to_sym.to_s
- if extension=='all'
- extension = nil
- end
+ extension = request.cache_format
end
extension
end
View
18 actionpack/lib/action_controller/caching/fragments.rb
@@ -60,17 +60,17 @@ def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
- def fragment_for(block, name = {}, options = nil) #:nodoc:
- unless perform_caching then block.call; return end
-
- buffer = yield
-
- if cache = read_fragment(name, options)
- buffer.concat(cache)
+ def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
+ if perform_caching
+ if cache = read_fragment(name, options)
+ buffer.concat(cache)
+ else
+ pos = buffer.length
+ block.call
+ write_fragment(name, buffer[pos..-1], options)
+ end
else
- pos = buffer.length
block.call
- write_fragment(name, buffer[pos..-1], options)
end
end
View
414 actionpack/lib/action_controller/filters.rb
@@ -7,6 +7,225 @@ def self.included(base)
end
end
+ class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
+ def append_filter_to_chain(filters, filter_type, &block)
+ pos = find_filter_append_position(filters, filter_type)
+ update_filter_chain(filters, filter_type, pos, &block)
+ end
+
+ def prepend_filter_to_chain(filters, filter_type, &block)
+ pos = find_filter_prepend_position(filters, filter_type)
+ update_filter_chain(filters, filter_type, pos, &block)
+ end
+
+ def create_filters(filters, filter_type, &block)
+ filters, conditions = extract_options(filters, &block)
+ filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
+ filters
+ end
+
+ def skip_filter_in_chain(*filters, &test)
+ filters, conditions = extract_options(filters)
+ filters.each do |filter|
+ if callback = find(filter) then delete(callback) end
+ end if conditions.empty?
+ update_filter_in_chain(filters, :skip => conditions, &test)
+ end
+
+ private
+ def update_filter_chain(filters, filter_type, pos, &block)
+ new_filters = create_filters(filters, filter_type, &block)
+ insert(pos, new_filters).flatten!
+ end
+
+ def find_filter_append_position(filters, filter_type)
+ # appending an after filter puts it at the end of the call chain
+ # before and around filters go before the first after filter in the chain
+ unless filter_type == :after
+ each_with_index do |f,i|
+ return i if f.after?
+ end
+ end
+ return -1
+ end
+
+ def find_filter_prepend_position(filters, filter_type)
+ # prepending a before or around filter puts it at the front of the call chain
+ # after filters go before the first after filter in the chain
+ if filter_type == :after
+ each_with_index do |f,i|
+ return i if f.after?
+ end
+ return -1
+ end
+ return 0
+ end
+
+ def find_or_create_filter(filter, filter_type, options = {})
+ update_filter_in_chain([filter], options)
+
+ if found_filter = find(filter) { |f| f.type == filter_type }
+ found_filter
+ else
+ filter_kind = case
+ when filter.respond_to?(:before) && filter_type == :before
+ :before
+ when filter.respond_to?(:after) && filter_type == :after
+ :after
+ else
+ :filter
+ end
+
+ case filter_type
+ when :before
+ BeforeFilter.new(filter_kind, filter, options)
+ when :after
+ AfterFilter.new(filter_kind, filter, options)
+ else
+ AroundFilter.new(filter_kind, filter, options)
+ end
+ end
+ end
+
+ def update_filter_in_chain(filters, options, &test)
+ filters.map! { |f| block_given? ? find(f, &test) : find(f) }
+ filters.compact!
+
+ map! do |filter|
+ if filters.include?(filter)
+ new_filter = filter.dup
+ new_filter.update_options!(options)
+ new_filter
+ else
+ filter
+ end
+ end
+ end
+ end
+
+ class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
+ def initialize(kind, method, options = {})
+ super
+ update_options! options
+ end
+
+ def before?
+ self.class == BeforeFilter
+ end
+
+ def after?
+ self.class == AfterFilter
+ end
+
+ def around?
+ self.class == AroundFilter
+ end
+
+ # Make sets of strings from :only/:except options
+ def update_options!(other)
+ if other
+ convert_only_and_except_options_to_sets_of_strings(other)
+ if other[:skip]
+ convert_only_and_except_options_to_sets_of_strings(other[:skip])
+ end
+ end
+
+ options.update(other)
+ end
+
+ private
+ def should_not_skip?(controller)
+ if options[:skip]
+ !included_in_action?(controller, options[:skip])
+ else
+ true
+ end
+ end
+
+ def included_in_action?(controller, options)
+ if options[:only]
+ options[:only].include?(controller.action_name)
+ elsif options[:except]
+ !options[:except].include?(controller.action_name)
+ else
+ true
+ end
+ end
+
+ def should_run_callback?(controller)
+ should_not_skip?(controller) && included_in_action?(controller, options) && super
+ end
+
+ def convert_only_and_except_options_to_sets_of_strings(opts)
+ [:only, :except].each do |key|
+ if values = opts[key]
+ opts[key] = Array(values).map(&:to_s).to_set
+ end
+ end
+ end
+ end
+
+ class AroundFilter < Filter #:nodoc:
+ def type
+ :around
+ end
+
+ def call(controller, &block)
+ if should_run_callback?(controller)
+ method = filter_responds_to_before_and_after? ? around_proc : self.method
+
+ # For around_filter do |controller, action|
+ if method.is_a?(Proc) && method.arity == 2
+ evaluate_method(method, controller, block)
+ else
+ evaluate_method(method, controller, &block)
+ end
+ else
+ block.call
+ end
+ end
+
+ private
+ def filter_responds_to_before_and_after?
+ method.respond_to?(:before) && method.respond_to?(:after)
+ end
+
+ def around_proc
+ Proc.new do |controller, action|
+ method.before(controller)
+
+ if controller.send!(:performed?)
+ controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
+ else
+ begin
+ action.call
+ ensure
+ method.after(controller)
+ end
+ end
+ end
+ end
+ end
+
+ class BeforeFilter < Filter #:nodoc:
+ def type
+ :before
+ end
+
+ def call(controller, &block)
+ super
+ if controller.send!(:performed?)
+ controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
+ end
+ end
+ end
+
+ class AfterFilter < Filter #:nodoc:
+ def type
+ :after
+ end
+ end
+
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
# authentication, caching, or auditing before the intended action is performed. Or to do localization or output
# compression after the action has been performed. Filters have access to the request, response, and all the instance
@@ -245,201 +464,6 @@ def self.included(base)
# filter and controller action will not be run. If +before+ renders or redirects,
# the second half of +around+ and will still run but +after+ and the
# action will not. If +around+ fails to yield, +after+ will not be run.
-
- class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
- def append_filter_to_chain(filters, filter_type, &block)
- pos = find_filter_append_position(filters, filter_type)
- update_filter_chain(filters, filter_type, pos, &block)
- end
-
- def prepend_filter_to_chain(filters, filter_type, &block)
- pos = find_filter_prepend_position(filters, filter_type)
- update_filter_chain(filters, filter_type, pos, &block)
- end
-
- def create_filters(filters, filter_type, &block)
- filters, conditions = extract_options(filters, &block)
- filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
- filters
- end
-
- def skip_filter_in_chain(*filters, &test)
- filters, conditions = extract_options(filters)
- filters.each do |filter|
- if callback = find(filter) then delete(callback) end
- end if conditions.empty?
- update_filter_in_chain(filters, :skip => conditions, &test)
- end
-
- private
- def update_filter_chain(filters, filter_type, pos, &block)
- new_filters = create_filters(filters, filter_type, &block)
- insert(pos, new_filters).flatten!
- end
-
- def find_filter_append_position(filters, filter_type)
- # appending an after filter puts it at the end of the call chain
- # before and around filters go before the first after filter in the chain
- unless filter_type == :after
- each_with_index do |f,i|
- return i if f.after?
- end
- end
- return -1
- end
-
- def find_filter_prepend_position(filters, filter_type)
- # prepending a before or around filter puts it at the front of the call chain
- # after filters go before the first after filter in the chain
- if filter_type == :after
- each_with_index do |f,i|
- return i if f.after?
- end
- return -1
- end
- return 0
- end
-
- def find_or_create_filter(filter, filter_type, options = {})
- update_filter_in_chain([filter], options)
-
- if found_filter = find(filter) { |f| f.type == filter_type }
- found_filter
- else
- filter_kind = case
- when filter.respond_to?(:before) && filter_type == :before
- :before
- when filter.respond_to?(:after) && filter_type == :after
- :after
- else
- :filter
- end
-
- case filter_type
- when :before
- BeforeFilter.new(filter_kind, filter, options)
- when :after
- AfterFilter.new(filter_kind, filter, options)
- else
- AroundFilter.new(filter_kind, filter, options)
- end
- end
- end
-
- def update_filter_in_chain(filters, options, &test)
- filters.map! { |f| block_given? ? find(f, &test) : find(f) }
- filters.compact!
-
- map! do |filter|
- if filters.include?(filter)
- new_filter = filter.dup
- new_filter.options.merge!(options)
- new_filter
- else
- filter
- end
- end
- end
- end
-
- class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
- def before?
- self.class == BeforeFilter
- end
-
- def after?
- self.class == AfterFilter
- end
-
- def around?
- self.class == AroundFilter
- end
-
- private
- def should_not_skip?(controller)
- if options[:skip]
- !included_in_action?(controller, options[:skip])
- else
- true
- end
- end
-
- def included_in_action?(controller, options)
- if options[:only]
- Array(options[:only]).map(&:to_s).include?(controller.action_name)
- elsif options[:except]
- !Array(options[:except]).map(&:to_s).include?(controller.action_name)
- else
- true
- end
- end
-
- def should_run_callback?(controller)
- should_not_skip?(controller) && included_in_action?(controller, options) && super
- end
- end
-
- class AroundFilter < Filter #:nodoc:
- def type
- :around
- end
-
- def call(controller, &block)
- if should_run_callback?(controller)
- method = filter_responds_to_before_and_after? ? around_proc : self.method
-
- # For around_filter do |controller, action|
- if method.is_a?(Proc) && method.arity == 2
- evaluate_method(method, controller, block)
- else
- evaluate_method(method, controller, &block)
- end
- else
- block.call
- end
- end
-
- private
- def filter_responds_to_before_and_after?
- method.respond_to?(:before) && method.respond_to?(:after)
- end
-
- def around_proc
- Proc.new do |controller, action|
- method.before(controller)
-
- if controller.send!(:performed?)
- controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
- else
- begin
- action.call
- ensure
- method.after(controller)
- end
- end
- end
- end
- end
-
- class BeforeFilter < Filter #:nodoc:
- def type
- :before
- end
-
- def call(controller, &block)
- super
- if controller.send!(:performed?)
- controller.send!(:halt_filter_chain, method, :rendered_or_redirected)
- end
- end
- end
-
- class AfterFilter < Filter #:nodoc:
- def type
- :after
- end
- end
-
module ClassMethods
# The passed <tt>filters</tt> will be appended to the filter_chain and
# will execute before the action on this controller is performed.
View
4 actionpack/lib/action_controller/layout.rb
@@ -254,7 +254,7 @@ def render_with_a_layout(options = nil, extra_options = {}, &block) #:nodoc:
@template.instance_variable_set("@content_for_layout", content_for_layout)
response.layout = layout
status = template_with_options ? options[:status] : nil
- render_for_text(@template.render_file(layout, true), status)
+ render_for_text(@template.render(layout), status)
else
render_with_no_layout(options, extra_options, &block)
end
@@ -304,7 +304,7 @@ def action_has_layout?
end
def layout_directory?(layout_name)
- @template.view_paths.find_template_file_for_path("#{File.join('layouts', layout_name)}.#{@template.template_format}.erb") ? true : false
+ @template.file_exists?("#{File.join('layouts', layout_name)}.#{@template.template_format}")
end
end
end
View
6 actionpack/lib/action_controller/mime_responds.rb
@@ -114,7 +114,11 @@ def initialize(controller)
@request = controller.request
@response = controller.response
- @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
+ if ActionController::Base.use_accept_header
+ @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
+ else
+ @mime_type_priority = [@request.format]
+ end
@order = []
@responses = {}
View
82 actionpack/lib/action_controller/mime_type.rb
@@ -72,57 +72,61 @@ def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], s
end
def parse(accept_header)
- # keep track of creation order to keep the subsequent sort stable
- list = []
- accept_header.split(/,/).each_with_index do |header, index|
- params, q = header.split(/;\s*q=/)
- if params
- params.strip!
- list << AcceptItem.new(index, params, q) unless params.empty?
+ if accept_header !~ /,/
+ [Mime::Type.lookup(accept_header)]
+ else
+ # keep track of creation order to keep the subsequent sort stable
+ list = []
+ accept_header.split(/,/).each_with_index do |header, index|
+ params, q = header.split(/;\s*q=/)
+ if params
+ params.strip!
+ list << AcceptItem.new(index, params, q) unless params.empty?
+ end
end
- end
- list.sort!
+ list.sort!
- # Take care of the broken text/xml entry by renaming or deleting it
- text_xml = list.index("text/xml")
- app_xml = list.index(Mime::XML.to_s)
+ # Take care of the broken text/xml entry by renaming or deleting it
+ text_xml = list.index("text/xml")
+ app_xml = list.index(Mime::XML.to_s)
- if text_xml && app_xml
- # set the q value to the max of the two
- list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
+ if text_xml && app_xml
+ # set the q value to the max of the two
+ list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
- # make sure app_xml is ahead of text_xml in the list
- if app_xml > text_xml
- list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
- app_xml, text_xml = text_xml, app_xml
- end
+ # make sure app_xml is ahead of text_xml in the list
+ if app_xml > text_xml
+ list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
+ app_xml, text_xml = text_xml, app_xml
+ end
- # delete text_xml from the list
- list.delete_at(text_xml)
+ # delete text_xml from the list
+ list.delete_at(text_xml)
- elsif text_xml
- list[text_xml].name = Mime::XML.to_s
- end
+ elsif text_xml
+ list[text_xml].name = Mime::XML.to_s
+ end
- # Look for more specific XML-based types and sort them ahead of app/xml
+ # Look for more specific XML-based types and sort them ahead of app/xml
- if app_xml
- idx = app_xml
- app_xml_type = list[app_xml]
+ if app_xml
+ idx = app_xml
+ app_xml_type = list[app_xml]
- while(idx < list.length)
- type = list[idx]
- break if type.q < app_xml_type.q
- if type.name =~ /\+xml$/
- list[app_xml], list[idx] = list[idx], list[app_xml]
- app_xml = idx
+ while(idx < list.length)
+ type = list[idx]
+ break if type.q < app_xml_type.q
+ if type.name =~ /\+xml$/
+ list[app_xml], list[idx] = list[idx], list[app_xml]
+ app_xml = idx
+ end
+ idx += 1
end
- idx += 1
end
- end
- list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
- list
+ list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
+ list
+ end
end
end
View
3 actionpack/lib/action_controller/mime_types.rb
@@ -17,4 +17,5 @@
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
# http://www.ietf.org/rfc/rfc4627.txt
-Mime::Type.register "application/json", :json, %w( text/x-json )
+# http://www.json.org/JSONRequest.html
+Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
View
37 actionpack/lib/action_controller/polymorphic_routes.rb
@@ -48,6 +48,9 @@ module PolymorphicRoutes
#
# # calls post_url(post)
# polymorphic_url(post) # => "http://example.com/posts/1"
+ # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
+ # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
+ # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
#
# ==== Options
#
@@ -83,8 +86,6 @@ def polymorphic_url(record_or_hash_or_array, options = {})
else [ record_or_hash_or_array ]
end
- args << format if format
-
inflection =
case
when options[:action].to_s == "new"
@@ -96,6 +97,9 @@ def polymorphic_url(record_or_hash_or_array, options = {})
else
:singular
end
+
+ args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
+ args << format if format
named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
send!(named_route, *args)
@@ -136,11 +140,19 @@ def build_named_route_call(records, namespace, inflection, options = {})
else
record = records.pop
route = records.inject("") do |string, parent|
- string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
+ if parent.is_a?(Symbol) || parent.is_a?(String)
+ string << "#{parent}_"
+ else
+ string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
+ end
end
end
- route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
+ if record.is_a?(Symbol) || record.is_a?(String)
+ route << "#{record}_"
+ else
+ route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
+ end
action_prefix(options) + namespace + route + routing_type(options).to_s
end
@@ -163,16 +175,17 @@ def extract_format(record_or_hash_or_array, options)
end
end
+ # Remove the first symbols from the array and return the url prefix
+ # implied by those symbols.
def extract_namespace(record_or_hash_or_array)
- returning "" do |namespace|
- if record_or_hash_or_array.is_a?(Array)
- record_or_hash_or_array.delete_if do |record_or_namespace|
- if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol)
- namespace << "#{record_or_namespace}_"
- end
- end
- end
+ return "" unless record_or_hash_or_array.is_a?(Array)
+
+ namespace_keys = []
+ while (key = record_or_hash_or_array.first) && key.is_a?(String) || key.is_a?(Symbol)
+ namespace_keys << record_or_hash_or_array.shift
end
+
+ namespace_keys.map {|k| "#{k}_"}.join
end
end
end
View
35 actionpack/lib/action_controller/rack_process.rb
@@ -24,6 +24,19 @@ def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
super()
end
+ %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
+ PATH_TRANSLATED QUERY_STRING REMOTE_HOST
+ REMOTE_IDENT REMOTE_USER SCRIPT_NAME
+ SERVER_NAME SERVER_PROTOCOL
+
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
+ @env[env]
+ end
+ end
+
# The request body is an IO input stream. If the RAW_POST_DATA environment
# variable is already set, wrap it in a StringIO.
def body
@@ -35,7 +48,7 @@ def body
end
def key?(key)
- @env.key? key
+ @env.key?(key)
end
def query_parameters
@@ -85,6 +98,18 @@ def remote_addr
@env['REMOTE_ADDR']
end
+ def request_method
+ @env['REQUEST_METHOD'].downcase.to_sym
+ end
+
+ def server_port
+ @env['SERVER_PORT'].to_i
+ end
+
+ def server_software
+ @env['SERVER_SOFTWARE'].split("/").first
+ end
+
def session
unless defined?(@session)
if @session_options == false
@@ -178,9 +203,9 @@ def out(output = $stdout, &block)
normalize_headers(@headers)
if [204, 304].include?(@status.to_i)
@headers.delete "Content-Type"
- [status.to_i, @headers.to_hash, []]
+ [status, @headers.to_hash, []]
else
- [status.to_i, @headers.to_hash, self]
+ [status, @headers.to_hash, self]
end
end
alias to_a out
@@ -225,8 +250,8 @@ def normalize_headers(options = "text/html")
headers['Content-Language'] = options.delete('language') if options['language']
headers['Expires'] = options.delete('expires') if options['expires']
- @status = options.delete('Status') if options['Status']
- @status ||= 200
+ @status = options['Status'] || "200 OK"
+
# Convert 'cookie' header to 'Set-Cookie' headers.
# Because Set-Cookie header can appear more the once in the response body,
# we store it in a line break seperated string that will be translated to
View
49 actionpack/lib/action_controller/request.rb
@@ -82,21 +82,34 @@ def content_type
# Returns the accepted MIME type for the request
def accepts
@accepts ||=
- if @env['HTTP_ACCEPT'].to_s.strip.empty?
- [ content_type, Mime::ALL ].compact # make sure content_type being nil is not included
- else
- Mime::Type.parse(@env['HTTP_ACCEPT'])
+ begin
+ header = @env['HTTP_ACCEPT'].to_s.strip
+
+ if header.empty?
+ [content_type, Mime::ALL].compact
+ else
+ Mime::Type.parse(header)
+ end
end
end
- # Returns the Mime type for the format used in the request. If there is no format available, the first of the
- # accept types will be used. Examples:
+ # Returns the Mime type for the format used in the request.
#
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
- # GET /posts/5 | request.format => request.accepts.first (usually Mime::HTML for browsers)
+ # GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
def format
- @format ||= parameters[:format] ? Mime::Type.lookup_by_extension(parameters[:format]) : accepts.first
+ @format ||= begin
+ if parameters[:format]
+ Mime::Type.lookup_by_extension(parameters[:format])
+ elsif ActionController::Base.use_accept_header
+ accepts.first
+ elsif xhr?
+ Mime::Type.lookup_by_extension("js")
+ else
+ Mime::Type.lookup_by_extension("html")
+ end
+ end
end
@@ -116,6 +129,26 @@ def format=(extension)
@format = Mime::Type.lookup_by_extension(parameters[:format])
end
+ # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
+ # If no format is given it returns <tt>:js</tt>for AJAX requests and <tt>:html</tt>
+ # otherwise.
+ def template_format
+ parameter_format = parameters[:format]
+
+ if parameter_format
+ parameter_format.to_sym
+ elsif xhr?
+ :js
+ else
+ :html
+ end
+ end
+
+ def cache_format
+ parameter_format = parameters[:format]
+ parameter_format && parameter_format.to_sym
+ end
+
# Returns true if the request's "X-Requested-With" header contains
# "XMLHttpRequest". (The Prototype Javascript library sends this header with
# every Ajax request.)
View
2 actionpack/lib/action_controller/rescue.rb
@@ -178,7 +178,7 @@ def rescue_action_locally(exception)
@template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub")))
@template.send!(:assign_variables_from_controller)
- @template.instance_variable_set("@contents", @template.render_file(template_path_for_local_rescue(exception), false))
+ @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception), :use_full_path => false))
response.content_type = Mime::HTML
render_for_file(rescues_path("layout"), response_code_for_rescue(exception))
View
6 actionpack/lib/action_controller/resources.rb
@@ -72,17 +72,17 @@ def requirements(with_id = false)
end
def conditions
- @conditions = @options[:conditions] || {}
+ @conditions ||= @options[:conditions] || {}
end
def path
@path ||= "#{path_prefix}/#{path_segment}"
end
def new_path
- new_action = self.options[:path_names][:new] if self.options[:path_names]
+ new_action = self.options[:path_names][:new] if self.options[:path_names]
new_action ||= Base.resources_path_names[:new]
- @new_path ||= "#{path}/#{new_action}"
+ @new_path ||= "#{path}/#{new_action}"
end
def member_path
View
4 actionpack/lib/action_controller/routing.rb
@@ -88,6 +88,10 @@ module ActionController
#
# map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }
#
+ # Note: The default routes, as provided by the Rails generator, make all actions in every
+ # controller accessible via GET requests. You should consider removing them or commenting
+ # them out if you're using named routes and resources.
+ #
# == Named routes
#
# Routes can be named with the syntax <tt>map.name_of_route options</tt>,
View
3 actionpack/lib/action_controller/routing/builder.rb
@@ -67,10 +67,9 @@ def divide_route_options(segments, options)
options = options.dup
if options[:namespace]
- options[:controller] = "#{options[:path_prefix]}/#{options[:controller]}"
+ options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}"
options.delete(:path_prefix)
options.delete(:name_prefix)
- options.delete(:namespace)
end
requirements = (options.delete(:requirements) || {}).dup
View
4 actionpack/lib/action_controller/templates/rescues/diagnostics.erb
@@ -6,6 +6,6 @@
</h1>
<pre><%=h @exception.clean_message %></pre>
-<%= render_file(@rescues_path + "/_trace.erb", false) %>
+<%= render(:file => @rescues_path + "/_trace.erb", :use_full_path => false) %>
-<%= render_file(@rescues_path + "/_request_and_response.erb", false) %>
+<%= render(:file => @rescues_path + "/_request_and_response.erb", :use_full_path => false) %>
</