aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwycats <wycats@gmail.com>2010-04-10 17:22:52 -0400
committerwycats <wycats@gmail.com>2010-04-10 17:22:52 -0400
commit87f7093ee3306f417e1136d947eba200d40ff8e7 (patch)
treeba70dbdaf67e12fc067bb5d8343d7681932452ef
parentee8e9d548472fb8cb8792a569e579c6513be77d6 (diff)
parent381f877bbbbf81d679f5be3b7ac7e961d41502bd (diff)
downloadrails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.gz
rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.bz2
rails-87f7093ee3306f417e1136d947eba200d40ff8e7.zip
Merge branch 'master' into docrails_master
-rw-r--r--actionmailer/lib/action_mailer/version.rb2
-rw-r--r--actionpack/CHANGELOG19
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb4
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb28
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb2
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb3
-rw-r--r--actionpack/lib/action_controller/metal/verification.rb130
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb2
-rw-r--r--actionpack/lib/action_controller/railtie.rb6
-rw-r--r--actionpack/lib/action_controller/test_case.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb17
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb19
-rw-r--r--actionpack/lib/action_pack/version.rb2
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb285
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb53
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb12
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb24
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb10
-rw-r--r--actionpack/lib/action_view/locale/en.yml8
-rw-r--r--actionpack/lib/action_view/lookup_context.rb4
-rw-r--r--actionpack/lib/action_view/render/layouts.rb2
-rw-r--r--actionpack/test/controller/localized_templates_test.rb22
-rw-r--r--actionpack/test/controller/test_test.rb6
-rw-r--r--actionpack/test/controller/verification_test.rb270
-rw-r--r--actionpack/test/dispatch/routing_test.rb28
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb9
-rw-r--r--actionpack/test/fixtures/localized/hello_world.de.html1
-rw-r--r--actionpack/test/fixtures/localized/hello_world.en.html1
-rw-r--r--actionpack/test/fixtures/test/_utf8_partial.html.erb1
-rw-r--r--actionpack/test/fixtures/test/_utf8_partial_magic.html.erb2
-rw-r--r--actionpack/test/fixtures/test/utf8.html.erb2
-rw-r--r--actionpack/test/fixtures/test/utf8_magic.html.erb2
-rw-r--r--actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb5
-rw-r--r--actionpack/test/template/active_model_helper_i18n_test.rb42
-rw-r--r--actionpack/test/template/active_model_helper_test.rb296
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb12
-rw-r--r--actionpack/test/template/form_helper_test.rb41
-rw-r--r--actionpack/test/template/render_test.rb72
-rw-r--r--actionpack/test/template/url_helper_test.rb20
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb4
-rw-r--r--activemodel/lib/active_model/callbacks.rb6
-rw-r--r--activemodel/lib/active_model/errors.rb5
-rw-r--r--activemodel/lib/active_model/version.rb2
-rw-r--r--activemodel/test/cases/validations_test.rb6
-rw-r--r--activerecord/Rakefile1
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb8
-rwxr-xr-xactiverecord/lib/active_record/base.rb76
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/railties/databases.rake10
-rw-r--r--activerecord/lib/active_record/version.rb2
-rw-r--r--activerecord/test/cases/active_schema_test_postgresql.rb7
-rw-r--r--activerecord/test/cases/adapter_test.rb6
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb8
-rw-r--r--activerecord/test/cases/dirty_test.rb95
-rw-r--r--activerecord/test/cases/migration_test.rb64
-rw-r--r--activerecord/test/cases/named_scope_test.rb10
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb12
-rw-r--r--activeresource/lib/active_resource/base.rb4
-rw-r--r--activeresource/lib/active_resource/http_mock.rb4
-rw-r--r--activeresource/lib/active_resource/version.rb2
-rw-r--r--activesupport/CHANGELOG5
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb8
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb6
-rw-r--r--activesupport/lib/active_support/cache/strategy/local_cache.rb2
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/aliasing.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/module/synchronization.rb2
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb85
-rw-r--r--activesupport/lib/active_support/ordered_hash.rb54
-rw-r--r--activesupport/lib/active_support/version.rb2
-rw-r--r--activesupport/test/clean_backtrace_test.rb7
-rw-r--r--activesupport/test/multibyte_chars_test.rb73
-rw-r--r--railties/CHANGELOG2
-rw-r--r--railties/guides/assets/stylesheets/main.css2
-rw-r--r--railties/guides/source/routing.textile825
-rw-r--r--railties/lib/rails.rb9
-rw-r--r--railties/lib/rails/application/configuration.rb24
-rw-r--r--railties/lib/rails/engine/configuration.rb2
-rw-r--r--railties/lib/rails/generators.rb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb11
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb2
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb17
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt14
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js73
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt (renamed from railties/lib/rails/generators/rails/app/templates/test/test_helper.rb)2
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb1
-rw-r--r--railties/lib/rails/generators/rails/stylesheets/USAGE5
-rw-r--r--railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb9
-rw-r--r--railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css (renamed from railties/lib/rails/generators/rails/app/templates/public/stylesheets/application.css)11
-rw-r--r--railties/lib/rails/test_unit/testing.rake18
-rw-r--r--railties/lib/rails/version.rb2
-rw-r--r--railties/test/application/configuration_test.rb21
-rw-r--r--railties/test/generators/app_generator_test.rb4
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb1
-rw-r--r--railties/test/generators/scaffold_generator_test.rb8
-rw-r--r--railties/test/generators/stylesheets_generator_test.rb17
-rw-r--r--railties/test/railties/railtie_test.rb11
-rw-r--r--version.rb2
111 files changed, 1312 insertions, 1892 deletions
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 5954cfcede..e66172c8d3 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -3,7 +3,7 @@ module ActionMailer
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 84481d0194..708683747b 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,4 +1,21 @@
-*Rails 3.0.0 [Edge] (pending)*
+*Rails 3.0.0 [beta 3] (pending)*
+
+* Removed verify method in controllers. [JV]
+ It's now available as a plugin at http://github.com/rails/verification
+
+* Removed input, form, error_messages_for and error_message_on from views. [JV]
+ It's now available as a plugin at http://github.com/rails/dynamic_form
+
+* Routes can be scoped by controller module. [Jeremy Kemper]
+
+ # /session => Auth::SessionsController
+ scope :module => 'auth' do
+ resource :session
+ end
+
+* Added #favicon_link_tag, it uses #image_path so in particular the favicon gets an asset ID [fxn]
+
+* Fixed that default locale templates should be used if the current locale template is missing [DHH]
* Added all the new HTML5 form types as individual form tag methods (search, url, number, etc) #3646 [Stephen Celis]
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 536154fc6b..c14393dda7 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -33,7 +33,6 @@ module ActionController
autoload :Streaming
autoload :Testing
autoload :UrlFor
- autoload :Verification
end
autoload :Dispatcher, 'action_controller/deprecated/dispatcher'
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 1dfc240029..2e94a20d9d 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -30,7 +30,6 @@ module ActionController
Cookies,
Flash,
- Verification,
RequestForgeryProtection,
Streaming,
RecordIdentifier,
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index fe95f0e0d7..2a0a1107eb 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -38,22 +38,22 @@ module ActionController #:nodoc:
extend ActiveSupport::Concern
included do
- @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
##
# :singleton-method:
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
# web server to look in the new location for cached files.
+ @@page_cache_directory = ''
cattr_accessor :page_cache_directory
- @@page_cache_extension = '.html'
##
# :singleton-method:
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
+ @@page_cache_extension = '.html'
cattr_accessor :page_cache_extension
end
diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb
index 05551ffee4..5d9cfb153a 100644
--- a/actionpack/lib/action_controller/deprecated/base.rb
+++ b/actionpack/lib/action_controller/deprecated/base.rb
@@ -6,15 +6,6 @@ module ActionController
deprecated_config_writer(option, message)
end
- # This method has been moved to ActionDispatch::Request.filter_parameters
- def filter_parameter_logging(*args, &block)
- ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
- filter = Rails.application.config.filter_parameters
- filter.concat(args)
- filter << block if block
- filter
- end
-
def deprecated_config_reader(option, message = nil)
message ||= "Reading #{option} directly from ActionController::Base is deprecated. " \
"Please read it from config.#{option}"
@@ -136,6 +127,25 @@ module ActionController
end
end
+ module DeprecatedBehavior
+ # This method has been moved to ActionDispatch::Request.filter_parameters
+ def filter_parameter_logging(*args, &block)
+ ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
+ filter = Rails.application.config.filter_parameters
+ filter.concat(args)
+ filter << block if block
+ filter
+ end
+
+ # This was moved to a plugin
+ def verify(*args)
+ ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " <<
+ "Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
+ end
+ end
+
+ extend DeprecatedBehavior
+
deprecated_config_writer :session_store
deprecated_config_writer :session_options
deprecated_config_accessor :relative_url_root, "relative_url_root is ineffective. Please stop using it"
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index e6cea483bb..02722360f1 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -32,8 +32,6 @@ module ActionController
def rescue_action(env)
raise env["action_dispatch.rescue.exception"]
end
-
- self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
end
# For old tests
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index d97c10a293..6ad9a23542 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -135,7 +135,6 @@ module ActionController #:nodoc:
def to_format
default_render
rescue ActionView::MissingTemplate => e
- raise unless resourceful?
api_behavior(e)
end
@@ -154,6 +153,8 @@ module ActionController #:nodoc:
# This is the common behavior for "API" requests, like :xml and :json.
def api_behavior(error)
+ raise error unless resourceful?
+
if get?
display resource
elsif has_errors?
diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb
deleted file mode 100644
index b7fc2b7421..0000000000
--- a/actionpack/lib/action_controller/metal/verification.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-module ActionController #:nodoc:
- module Verification #:nodoc:
- extend ActiveSupport::Concern
-
- include AbstractController::Callbacks, Flash, Rendering
-
- # This module provides a class-level method for specifying that certain
- # actions are guarded against being called without certain prerequisites
- # being met. This is essentially a special kind of before_filter.
- #
- # An action may be guarded against being invoked without certain request
- # parameters being set, or without certain session values existing.
- #
- # When a verification is violated, values may be inserted into the flash, and
- # a specified redirection is triggered. If no specific action is configured,
- # verification failures will by default result in a 400 Bad Request response.
- #
- # Usage:
- #
- # class GlobalController < ActionController::Base
- # # Prevent the #update_settings action from being invoked unless
- # # the 'admin_privileges' request parameter exists. The
- # # settings action will be redirected to in current controller
- # # if verification fails.
- # verify :params => "admin_privileges", :only => :update_post,
- # :redirect_to => { :action => "settings" }
- #
- # # Disallow a post from being updated if there was no information
- # # submitted with the post, and if there is no active post in the
- # # session, and if there is no "note" key in the flash. The route
- # # named category_url will be redirected to if verification fails.
- #
- # verify :params => "post", :session => "post", "flash" => "note",
- # :only => :update_post,
- # :add_flash => { "alert" => "Failed to create your message" },
- # :redirect_to => :category_url
- #
- # Note that these prerequisites are not business rules. They do not examine
- # the content of the session or the parameters. That level of validation should
- # be encapsulated by your domain model or helper methods in the controller.
- module ClassMethods
- # Verify the given actions so that if certain prerequisites are not met,
- # the user is redirected to a different action. The +options+ parameter
- # is a hash consisting of the following key/value pairs:
- #
- # <tt>:params</tt>::
- # a single key or an array of keys that must be in the <tt>params</tt>
- # hash in order for the action(s) to be safely called.
- # <tt>:session</tt>::
- # a single key or an array of keys that must be in the <tt>session</tt>
- # in order for the action(s) to be safely called.
- # <tt>:flash</tt>::
- # a single key or an array of keys that must be in the flash in order
- # for the action(s) to be safely called.
- # <tt>:method</tt>::
- # a single key or an array of keys--any one of which must match the
- # current request method in order for the action(s) to be safely called.
- # (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
- # example.)
- # <tt>:xhr</tt>::
- # true/false option to ensure that the request is coming from an Ajax
- # call or not.
- # <tt>:add_flash</tt>::
- # a hash of name/value pairs that should be merged into the session's
- # flash if the prerequisites cannot be satisfied.
- # <tt>:add_headers</tt>::
- # a hash of name/value pairs that should be merged into the response's
- # headers hash if the prerequisites cannot be satisfied.
- # <tt>:redirect_to</tt>::
- # the redirection parameters to be used when redirecting if the
- # prerequisites cannot be satisfied. You can redirect either to named
- # route or to the action in some controller.
- # <tt>:render</tt>::
- # the render parameters to be used when the prerequisites cannot be satisfied.
- # <tt>:only</tt>::
- # only apply this verification to the actions specified in the associated
- # array (may also be a single value).
- # <tt>:except</tt>::
- # do not apply this verification to the actions specified in the associated
- # array (may also be a single value).
- def verify(options={})
- before_filter :only => options[:only], :except => options[:except] do
- verify_action options
- end
- end
- end
-
- private
-
- def verify_action(options) #:nodoc:
- if prereqs_invalid?(options)
- flash.update(options[:add_flash]) if options[:add_flash]
- response.headers.merge!(options[:add_headers]) if options[:add_headers]
- apply_remaining_actions(options) unless performed?
- end
- end
-
- def prereqs_invalid?(options) # :nodoc:
- verify_presence_of_keys_in_hash_flash_or_params(options) ||
- verify_method(options) ||
- verify_request_xhr_status(options)
- end
-
- def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
- [*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
- [*options[:session]].find { |v| session[v].nil? } ||
- [*options[:flash] ].find { |v| flash[v].nil? }
- end
-
- def verify_method(options) # :nodoc:
- [*options[:method]].all? { |v| request.method_symbol != v.to_sym } if options[:method]
- end
-
- def verify_request_xhr_status(options) # :nodoc:
- request.xhr? != options[:xhr] unless options[:xhr].nil?
- end
-
- def apply_redirect_to(redirect_to_option) # :nodoc:
- (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option
- end
-
- def apply_remaining_actions(options) # :nodoc:
- case
- when options[:render] ; render(options[:render])
- when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to]))
- else head(:bad_request)
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index ae363e300c..7f2eb4306b 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -120,7 +120,7 @@ module ActionController
end
%w(edit new).each do |action|
- module_eval <<-EOT, __FILE__, __LINE__
+ module_eval <<-EOT, __FILE__, __LINE__ + 1
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
polymorphic_url( # polymorphic_url(
record_or_hash, # record_or_hash,
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 030ba4ec48..b029434004 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -44,6 +44,12 @@ module ActionController
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
end
+ initializer "action_controller.page_cache_directory" do
+ ActiveSupport.on_load(:action_controller) do
+ self.page_cache_directory = Rails.public_path
+ end
+ end
+
initializer "action_controller.set_configs" do |app|
paths = app.config.paths
ac = app.config.action_controller
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 2d4cf2fafb..dfd8e75bcc 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -284,6 +284,8 @@ module ActionController
include ActionDispatch::TestProcess
include ActionController::TemplateAssertions
+ attr_reader :response, :request
+
# Executes a request simulating GET HTTP method and set/volley the response
def get(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "GET")
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 18a3688bb0..1524b00d5b 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -36,17 +36,16 @@ module ActionDispatch
when Proc
strategy.call(request.raw_post)
when :xml_simple, :xml_node
- request.body.size == 0 ? {} : Hash.from_xml(request.raw_post).with_indifferent_access
+ data = Hash.from_xml(request.body) || {}
+ request.body.rewind if request.body.respond_to?(:rewind)
+ data.with_indifferent_access
when :yaml
YAML.load(request.raw_post)
when :json
- if request.body.size == 0
- {}
- else
- data = ActiveSupport::JSON.decode(request.raw_post)
- data = {:_json => data} unless data.is_a?(Hash)
- data.with_indifferent_access
- end
+ data = ActiveSupport::JSON.decode(request.body)
+ request.body.rewind if request.body.respond_to?(:rewind)
+ data = {:_json => data} unless data.is_a?(Hash)
+ data.with_indifferent_access
else
false
end
@@ -76,4 +75,4 @@ module ActionDispatch
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 839df50999..09ff052fd0 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -6,7 +6,7 @@
<% end %>
<%
- clean_params = @request.parameters.clone
+ clean_params = @request.filtered_parameters.clone
clean_params.delete("action")
clean_params.delete("controller")
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 925e91f081..7035e360ec 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -324,7 +324,8 @@ module ActionDispatch
end
def namespace(path)
- scope(path.to_s, :name_prefix => path.to_s, :controller_namespace => path.to_s) { yield }
+ path = path.to_s
+ scope(:path => path, :name_prefix => path, :module => path) { yield }
end
def constraints(constraints = {})
@@ -363,15 +364,15 @@ module ActionDispatch
parent ? "#{parent}_#{child}" : child
end
- def merge_controller_namespace_scope(parent, child)
+ def merge_module_scope(parent, child)
parent ? "#{parent}/#{child}" : child
end
def merge_controller_scope(parent, child)
- @scope[:controller_namespace] ? "#{@scope[:controller_namespace]}/#{child}" : child
+ @scope[:module] ? "#{@scope[:module]}/#{child}" : child
end
- def merge_resources_path_names_scope(parent, child)
+ def merge_path_names_scope(parent, child)
merge_options_scope(parent, child)
end
@@ -520,7 +521,7 @@ module ActionDispatch
def initialize(*args) #:nodoc:
super
- @scope[:resources_path_names] = @set.resources_path_names
+ @scope[:path_names] = @set.resources_path_names
end
def resource(*resources, &block)
@@ -636,7 +637,7 @@ module ActionDispatch
return self
end
- resources_path_names = options.delete(:path_names)
+ path_names = options.delete(:path_names)
if args.first.is_a?(Symbol)
action = args.first
@@ -653,7 +654,7 @@ module ActionDispatch
end
else
with_exclusive_name_prefix(action) do
- return match("#{action_path(action, resources_path_names)}(.:format)", options.reverse_merge(:to => action))
+ return match("#{action_path(action, path_names)}(.:format)", options.reverse_merge(:to => action))
end
end
end
@@ -681,7 +682,7 @@ module ActionDispatch
private
def action_path(name, path_names = nil)
- path_names ||= @scope[:resources_path_names]
+ path_names ||= @scope[:path_names]
path_names[name.to_sym] || name.to_s
end
@@ -692,7 +693,7 @@ module ActionDispatch
end
if path_names = options.delete(:path_names)
- scope(:resources_path_names => path_names) do
+ scope(:path_names => path_names) do
send(method, resources.pop, options, &block)
end
return true
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 0b40465a79..8f0c5d939f 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -3,7 +3,7 @@ module ActionPack
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index 44e193f18e..a7650c0050 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -1,8 +1,6 @@
-require 'cgi'
require 'action_view/helpers/form_helper'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/object/blank'
module ActionView
@@ -14,252 +12,29 @@ module ActionView
end
module Helpers
- # The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+
- # method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
- # is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
- # In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html
module ActiveModelHelper
- # Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt>
- # has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World":
- #
- # input("post", "title")
- # # => <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
- def input(record_name, method, options = {})
- InstanceTag.new(record_name, method, self).to_tag(options)
- end
-
- # Returns an entire form with all needed input tags for a specified Active Record object. For example, if <tt>@post</tt>
- # has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then
- #
- # form("post")
- #
- # would yield a form like the following (modulus formatting):
- #
- # <form action='/posts/create' method='post'>
- # <p>
- # <label for="post_title">Title</label><br />
- # <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
- # </p>
- # <p>
- # <label for="post_body">Body</label><br />
- # <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
- # </p>
- # <input name="commit" type="submit" value="Create" />
- # </form>
- #
- # It's possible to specialize the form builder by using a different action name and by supplying another
- # block renderer. For example, if <tt>@entry</tt> has an attribute +message+ of type +VARCHAR+ then
- #
- # form("entry",
- # :action => "sign",
- # :input_block => Proc.new { |record, column|
- # "#{column.human_name}: #{input(record, column.name)}<br />"
- # })
- #
- # would yield a form like the following (modulus formatting):
- #
- # <form action="/entries/sign" method="post">
- # Message:
- # <input id="entry_message" name="entry[message]" size="30" type="text" /><br />
- # <input name="commit" type="submit" value="Sign" />
- # </form>
- #
- # It's also possible to add additional content to the form by giving it a block, such as:
- #
- # form("entry", :action => "sign") do |form|
- # form << content_tag("b", "Department")
- # form << collection_select("department", "id", @departments, "id", "name")
- # end
- #
- # The following options are available:
- #
- # * <tt>:action</tt> - The action used when submitting the form (default: +create+ if a new record, otherwise +update+).
- # * <tt>:input_block</tt> - Specialize the output using a different block, see above.
- # * <tt>:method</tt> - The method used when submitting the form (default: +post+).
- # * <tt>:multipart</tt> - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+).
- # * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update").
- def form(record_name, options = {})
- record = instance_variable_get("@#{record_name}")
- record = convert_to_model(record)
-
- options = options.symbolize_keys
- options[:action] ||= record.persisted? ? "update" : "create"
- action = url_for(:action => options[:action], :id => record)
-
- submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
-
- contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
- contents.safe_concat hidden_field(record_name, :id) if record.persisted?
- contents.safe_concat all_input_tags(record, record_name, options)
- yield contents if block_given?
- contents.safe_concat submit_tag(submit_value)
- contents.safe_concat('</form>')
- end
-
- # Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
- # This error message is wrapped in a <tt>DIV</tt> tag by default or with <tt>:html_tag</tt> if specified,
- # which can be extended to include a <tt>:prepend_text</tt> and/or <tt>:append_text</tt> (to properly explain
- # the error), and a <tt>:css_class</tt> to style it accordingly. +object+ should either be the name of an
- # instance variable or the actual object. The method can be passed in either as a string or a symbol.
- # As an example, let's say you have a model <tt>@post</tt> that has an error message on the +title+ attribute:
- #
- # <%= error_message_on "post", "title" %>
- # # => <div class="formError">can't be empty</div>
- #
- # <%= error_message_on @post, :title %>
- # # => <div class="formError">can't be empty</div>
- #
- # <%= error_message_on "post", "title",
- # :prepend_text => "Title simply ",
- # :append_text => " (or it won't work).",
- # :html_tag => "span",
- # :css_class => "inputError" %>
- # # => <span class="inputError">Title simply can't be empty (or it won't work).</span>
- def error_message_on(object, method, *args)
- options = args.extract_options!
- unless args.empty?
- ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' +
- 'prepend_text, append_text, html_tag, and css_class arguments', caller)
-
- options[:prepend_text] = args[0] || ''
- options[:append_text] = args[1] || ''
- options[:html_tag] = args[2] || 'div'
- options[:css_class] = args[3] || 'formError'
- end
- options.reverse_merge!(:prepend_text => '', :append_text => '', :html_tag => 'div', :css_class => 'formError')
-
- object = convert_to_model(object)
-
- if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
- (errors = obj.errors[method]).presence
- content_tag(options[:html_tag],
- (options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]),
- :class => options[:css_class]
- )
- else
- ''
- end
- end
-
- # Returns a string with a <tt>DIV</tt> containing all of the error messages for the objects located as instance variables by the names
- # given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are
- # provided.
- #
- # This <tt>DIV</tt> can be tailored by the following options:
- #
- # * <tt>:header_tag</tt> - Used for the header of the error div (default: "h2").
- # * <tt>:id</tt> - The id of the error div (default: "errorExplanation").
- # * <tt>:class</tt> - The class of the error div (default: "errorExplanation").
- # * <tt>:object</tt> - The object (or array of objects) for which to display errors,
- # if you need to escape the instance variable convention.
- # * <tt>:object_name</tt> - The object name to use in the header, or any text that you prefer.
- # If <tt>:object_name</tt> is not set, the name of the first object will be used.
- # * <tt>:header_message</tt> - The message in the header of the error div. Pass +nil+
- # or an empty string to avoid the header message altogether. (Default: "X errors
- # prohibited this object from being saved").
- # * <tt>:message</tt> - The explanation message after the header message and before
- # the error list. Pass +nil+ or an empty string to avoid the explanation message
- # altogether. (Default: "There were problems with the following fields:").
- #
- # To specify the display for one object, you simply provide its name as a parameter.
- # For example, for the <tt>@user</tt> model:
- #
- # error_messages_for 'user'
- #
- # You can also supply an object:
- #
- # error_messages_for @user
- #
- # This will use the last part of the model name in the presentation. For instance, if
- # this is a MyKlass::User object, this will use "user" as the name in the String. This
- # is taken from MyKlass::User.model_name.human, which can be overridden.
- #
- # To specify more than one object, you simply list them; optionally, you can add an extra <tt>:object_name</tt> parameter, which
- # will be the name used in the header message:
- #
- # error_messages_for 'user_common', 'user', :object_name => 'user'
- #
- # You can also use a number of objects, which will have the same naming semantics
- # as a single object.
- #
- # error_messages_for @user, @post
- #
- # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual
- # object (or array of objects to use):
- #
- # error_messages_for 'user', :object => @question.user
- #
- # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what
- # you need is significantly different from the default presentation, it makes plenty of sense to access the <tt>object.errors</tt>
- # instance yourself and set it up. View the source of this method to see how easy it is.
- def error_messages_for(*params)
- options = params.extract_options!.symbolize_keys
-
- objects = Array.wrap(options.delete(:object) || params).map do |object|
- object = instance_variable_get("@#{object}") unless object.respond_to?(:to_model)
- object = convert_to_model(object)
-
- if object.class.respond_to?(:model_name)
- options[:object_name] ||= object.class.model_name.human.downcase
+ %w(input form error_messages_for error_message_on).each do |method|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{method}(*args)
+ ActiveSupport::Deprecation.warn "#{method} was removed from Rails and is now available as a plugin. " <<
+ "Please install it with `rails plugin install git://github.com/rails/dynamic_form.git`.", caller
end
+ RUBY
+ end
+ end
- object
- end
-
- objects.compact!
- count = objects.inject(0) {|sum, object| sum + object.errors.count }
-
- unless count.zero?
- html = {}
- [:id, :class].each do |key|
- if options.include?(key)
- value = options[key]
- html[key] = value unless value.blank?
- else
- html[key] = 'errorExplanation'
- end
- end
- options[:object_name] ||= params.first
-
- I18n.with_options :locale => options[:locale], :scope => [:errors, :template] do |locale|
- header_message = if options.include?(:header_message)
- options[:header_message]
- else
- locale.t :header, :count => count, :model => options[:object_name].to_s.gsub('_', ' ')
- end
-
- message = options.include?(:message) ? options[:message] : locale.t(:body)
-
- error_messages = objects.sum do |object|
- object.errors.full_messages.map do |msg|
- content_tag(:li, msg)
- end
- end.join.html_safe
-
- contents = ''
- contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
- contents << content_tag(:p, message) unless message.blank?
- contents << content_tag(:ul, error_messages)
-
- content_tag(:div, contents.html_safe, html)
+ module ActiveModelFormBuilder
+ %w(error_messages error_message_on).each do |method|
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{method}(*args)
+ ActiveSupport::Deprecation.warn "f.#{method} was removed from Rails and is now available as a plugin. " <<
+ "Please install it with `rails plugin install git://github.com/rails/dynamic_form.git`.", caller
end
- else
- ''
- end
+ RUBY
end
-
- private
- def all_input_tags(record, record_name, options)
- input_block = options[:input_block] || default_input_block
- record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
- end
-
- def default_input_block
- Proc.new { |record, column| %(<p><label for="#{record}_#{column.name}">#{column.human_name}</label><br />#{input(record, column.name)}</p>) }
- end
end
- module ActiveRecordInstanceTag
+ module ActiveModelInstanceTag
def object
@active_model_object ||= begin
object = super
@@ -267,26 +42,6 @@ module ActionView
end
end
- def to_tag(options = {})
- case column_type
- when :string
- field_type = @method_name.include?("password") ? "password" : "text"
- to_input_field_tag(field_type, options)
- when :text
- to_text_area_tag(options)
- when :integer, :float, :decimal
- to_input_field_tag("text", options)
- when :date
- to_date_select_tag(options)
- when :datetime, :timestamp
- to_datetime_select_tag(options)
- when :time
- to_time_select_tag(options)
- when :boolean
- to_boolean_select_tag(options)
- end
- end
-
%w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
module_eval "def #{meth}(*) error_wrapping(super) end"
end
@@ -302,14 +57,14 @@ module ActionView
def error_message
object.errors[@method_name]
end
+ end
- def column_type
- object.send(:column_for_attribute, @method_name).type
- end
+ class FormBuilder
+ include ActiveModelFormBuilder
end
class InstanceTag
- include ActiveRecordInstanceTag
+ include ActiveModelInstanceTag
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index e4ec17467e..563d9ec319 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -501,16 +501,53 @@ module ActionView
end
end
+ # Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document
+ # root of your application and it changes later, clients that have it in their cache
+ # won't see the update. Using this helper prevents that because it appends an asset ID:
+ #
+ # <%= favicon_link_tag %>
+ #
+ # generates
+ #
+ # <link href="/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ #
+ # You may specify a different file in the first argument:
+ #
+ # <%= favicon_link_tag 'favicon.ico' %>
+ #
+ # That's passed to +path_to_image+ as is, so it gives
+ #
+ # <link href="/images/favicon.ico?4649789979" rel="shortcut icon" type="image/vnd.microsoft.icon" />
+ #
+ # The helper accepts an additional options hash where you can override "rel" and "type".
+ #
+ # For example, Mobile Safari looks for a different LINK tag, pointing to an image that
+ # will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
+ # The following call would generate such a tag:
+ #
+ # <%= favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png' %>
+ #
+ def favicon_link_tag(source='/favicon.ico', options={})
+ tag('link', {
+ :rel => 'shortcut icon',
+ :type => 'image/vnd.microsoft.icon',
+ :href => path_to_image(source)
+ }.merge(options.symbolize_keys))
+ end
+
# Computes the path to an image asset in the public images directory.
# Full paths from the document root will be passed through.
- # Used internally by +image_tag+ to build the image path.
+ # Used internally by +image_tag+ to build the image path:
#
- # ==== Examples
- # image_path("edit") # => /images/edit
- # image_path("edit.png") # => /images/edit.png
- # image_path("icons/edit.png") # => /images/icons/edit.png
- # image_path("/icons/edit.png") # => /icons/edit.png
- # image_path("http://www.railsapplication.com/img/edit.png") # => http://www.railsapplication.com/img/edit.png
+ # image_path("edit") # => "/images/edit"
+ # image_path("edit.png") # => "/images/edit.png"
+ # image_path("icons/edit.png") # => "/images/icons/edit.png"
+ # image_path("/icons/edit.png") # => "/icons/edit.png"
+ # image_path("http://www.railsapplication.com/img/edit.png") # => "http://www.railsapplication.com/img/edit.png"
+ #
+ # If you have images as application resources this method may conflict with their named routes.
+ # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
+ # plugin authors are encouraged to do so.
def image_path(source)
compute_public_path(source, 'images')
end
@@ -590,7 +627,7 @@ module ActionView
end
if mouseover = options.delete(:mouseover)
- options[:onmouseover] = "this.src='#{image_path(mouseover)}'"
+ options[:onmouseover] = "this.src='#{path_to_image(mouseover)}'"
options[:onmouseout] = "this.src='#{src}'"
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index fc02d959d4..a3453cc47a 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -1105,7 +1105,7 @@ module ActionView
end
(field_helpers - %w(label check_box radio_button fields_for hidden_field)).each do |selector|
- src, file, line = <<-end_src, __FILE__, __LINE__ + 1
+ src, line = <<-end_src, __LINE__ + 1
def #{selector}(method, options = {}) # def text_field(method, options = {})
@template.send( # @template.send(
#{selector.inspect}, # "text_field",
@@ -1114,7 +1114,7 @@ module ActionView
objectify_options(options)) # objectify_options(options))
end # end
end_src
- class_eval src, file, line
+ class_eval src, __FILE__, line
end
def fields_for(record_or_name_or_array, *args, &block)
@@ -1169,14 +1169,6 @@ module ActionView
@template.hidden_field(@object_name, method, objectify_options(options))
end
- def error_message_on(method, *args)
- @template.error_message_on(@object, method, *args)
- end
-
- def error_messages(options = {})
- @template.error_messages_for(@object_name, objectify_options(options))
- end
-
# Add the submit button for the given form. When no value is given, it checks
# if the object is a new resource or not to create the proper label:
#
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 11c6351bd3..105f4565e6 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -217,8 +217,6 @@ module ActionView
InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
end
-
-
# Return select and option tags for the given object and method, using
# #time_zone_options_for_select to generate the list of option tags.
#
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 081c317e0c..b840f77fb5 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -199,8 +199,8 @@ module ActionView
# file_field_tag 'attachment'
# # => <input id="attachment" name="attachment" type="file" />
#
- # file_field_tag 'avatar', :class => 'profile-input'
- # # => <input class="profile-input" id="avatar" name="avatar" type="file" />
+ # file_field_tag 'avatar', :class => 'profile_input'
+ # # => <input class="profile_input" id="avatar" name="avatar" type="file" />
#
# file_field_tag 'picture', :disabled => true
# # => <input disabled="disabled" id="picture" name="picture" type="file" />
@@ -244,8 +244,8 @@ module ActionView
# password_field_tag 'confirm_pass', nil, :disabled => true
# # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
#
- # password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin-input"
- # # => <input class="pin-input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
+ # password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin_input"
+ # # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
def password_field_tag(name = "password", value = nil, options = {})
text_field_tag(name, value, options.update("type" => "password"))
end
@@ -374,8 +374,8 @@ module ActionView
# submit_tag nil, :class => "form_submit"
# # => <input class="form_submit" name="commit" type="submit" />
#
- # submit_tag "Edit", :disable_with => "Editing...", :class => "edit-button"
- # # => <input class="edit-button" data-disable_with="Editing..."
+ # submit_tag "Edit", :disable_with => "Editing...", :class => "edit_button"
+ # # => <input class="edit_button" data-disable_with="Editing..."
# # name="commit" type="submit" value="Edit" />
#
# submit_tag "Save", :confirm => "Are you sure?"
@@ -386,7 +386,7 @@ module ActionView
options.stringify_keys!
if disable_with = options.delete("disable_with")
- options["data-disable-with"] = disable_with if disable_with
+ options["data-disable-with"] = disable_with
end
if confirm = options.delete("confirm")
@@ -398,7 +398,7 @@ module ActionView
# Displays an image which when clicked will submit the form.
#
- # <tt>source</tt> is passed to AssetTagHelper#image_path
+ # <tt>source</tt> is passed to AssetTagHelper#path_to_image
#
# ==== Options
# * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
@@ -414,11 +414,11 @@ module ActionView
# image_submit_tag("purchase.png", :disabled => true)
# # => <input disabled="disabled" src="/images/purchase.png" type="image" />
#
- # image_submit_tag("search.png", :class => 'search-button')
- # # => <input class="search-button" src="/images/search.png" type="image" />
+ # image_submit_tag("search.png", :class => 'search_button')
+ # # => <input class="search_button" src="/images/search.png" type="image" />
#
- # image_submit_tag("agree.png", :disabled => true, :class => "agree-disagree-button")
- # # => <input class="agree-disagree-button" disabled="disabled" src="/images/agree.png" type="image" />
+ # image_submit_tag("agree.png", :disabled => true, :class => "agree_disagree_button")
+ # # => <input class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
def image_submit_tag(source, options = {})
options.stringify_keys!
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 5925faf810..0b748d700b 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -265,7 +265,7 @@ module ActionView
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
# the +link_to+ documentation.
#
- # The generated form element has a class name of <tt>button-to</tt>
+ # The generated form element has a class name of <tt>button_to</tt>
# to allow styling of the form itself and its children. You can control
# the form submission and input element behavior using +html_options+.
# This method accepts the <tt>:method</tt> and <tt>:confirm</tt> modifiers
@@ -289,14 +289,14 @@ module ActionView
#
# ==== Examples
# <%= button_to "New", :action => "new" %>
- # # => "<form method="post" action="/controller/new" class="button-to">
+ # # => "<form method="post" action="/controller/new" class="button_to">
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
#
# <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
# :confirm => "Are you sure?", :method => :delete %>
- # # => "<form method="post" action="/images/delete/1" class="button-to">
+ # # => "<form method="post" action="/images/delete/1" class="button_to">
# # <div>
# # <input type="hidden" name="_method" value="delete" />
# # <input data-confirm='Are you sure?' value="Delete" type="submit" />
@@ -306,7 +306,7 @@ module ActionView
#
# <%= button_to('Destroy', 'http://www.example.com', :confirm => 'Are you sure?',
# :method => "delete", :remote => true, :disable_with => 'loading...') %>
- # # => "<form class='button-to' method='post' action='http://www.example.com' data-remote='true'>
+ # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
# # <input value='Destroy' type='submit' disable_with='loading...' data-confirm='Are you sure?' />
@@ -338,7 +338,7 @@ module ActionView
html_options.merge!("type" => "submit", "value" => name)
- ("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button-to\"><div>" +
+ ("<form method=\"#{form_method}\" action=\"#{escape_once url}\" #{"data-remote=\"true\"" if remote} class=\"button_to\"><div>" +
method_tag + tag("input", html_options) + request_token_tag + "</div></form>").html_safe
end
diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml
index a3e2230f6f..187e010e30 100644
--- a/actionpack/lib/action_view/locale/en.yml
+++ b/actionpack/lib/action_view/locale/en.yml
@@ -141,14 +141,6 @@
minute: "Minute"
second: "Seconds"
- errors:
- template:
- header:
- one: "1 error prohibited this {{model}} from being saved"
- other: "{{count}} errors prohibited this {{model}} from being saved"
- # The variable :count is also available
- body: "There were problems with the following fields:"
-
helpers:
select:
# Default value for :prompt => true in FormOptionsHelper
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 9b59aac0eb..6e61d85dcc 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -38,7 +38,7 @@ module ActionView
end
register_detail(:formats) { Mime::SET.symbols }
- register_detail(:locale) { [I18n.locale] }
+ register_detail(:locale) { [I18n.locale, I18n.default_locale] }
class DetailsKey #:nodoc:
alias :eql? :equal?
@@ -160,7 +160,7 @@ module ActionView
config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
config.locale = value
end
- super(I18n.locale)
+ super(_locale_defaults)
end
# Update the details keys by merging the given hash into the current
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
index 7311730a19..31b09d9f0a 100644
--- a/actionpack/lib/action_view/render/layouts.rb
+++ b/actionpack/lib/action_view/render/layouts.rb
@@ -48,7 +48,7 @@ module ActionView
#
def _layout_for(name = nil, &block) #:nodoc:
if !block || name
- @_content_for[name || :layout]
+ @_content_for[name || :layout].html_safe
else
capture(&block)
end
diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb
new file mode 100644
index 0000000000..41ff2f3809
--- /dev/null
+++ b/actionpack/test/controller/localized_templates_test.rb
@@ -0,0 +1,22 @@
+require 'abstract_unit'
+
+class LocalizedController < ActionController::Base
+ def hello_world
+ end
+end
+
+class LocalizedTemplatesTest < ActionController::TestCase
+ tests LocalizedController
+
+ def test_localized_template_is_used
+ I18n.locale = :de
+ get :hello_world
+ assert_equal "Gutten Tag", @response.body
+ end
+
+ def test_default_locale_template_is_used_when_locale_is_missing
+ I18n.locale = :dk
+ get :hello_world
+ assert_equal "Hello World", @response.body
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb
index 8910454b8b..6f1ce2fef7 100644
--- a/actionpack/test/controller/test_test.rb
+++ b/actionpack/test/controller/test_test.rb
@@ -189,6 +189,12 @@ XML
assert_equal Hash.new, @request.session.to_hash
end
+ def test_response_and_request_have_nice_accessors
+ process :no_op
+ assert_equal @response, response
+ assert_equal @request, request
+ end
+
def test_process_with_request_uri_with_no_params
process :test_uri
assert_equal "/test_test/test/test_uri", @response.body
diff --git a/actionpack/test/controller/verification_test.rb b/actionpack/test/controller/verification_test.rb
deleted file mode 100644
index 0600ec2ec1..0000000000
--- a/actionpack/test/controller/verification_test.rb
+++ /dev/null
@@ -1,270 +0,0 @@
-require 'abstract_unit'
-
-class VerificationTest < ActionController::TestCase
- class TestController < ActionController::Base
- verify :only => :guarded_one, :params => "one",
- :add_flash => { :error => 'unguarded' },
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :guarded_two, :params => %w( one two ),
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :guarded_with_flash, :params => "one",
- :add_flash => { :notice => "prereqs failed" },
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :guarded_in_session, :session => "one",
- :redirect_to => { :action => "unguarded" }
-
- verify :only => [:multi_one, :multi_two], :session => %w( one two ),
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :guarded_by_method, :method => :post,
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :guarded_by_xhr, :xhr => true,
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :guarded_by_not_xhr, :xhr => false,
- :redirect_to => { :action => "unguarded" }
-
- before_filter :unconditional_redirect, :only => :two_redirects
- verify :only => :two_redirects, :method => :post,
- :redirect_to => { :action => "unguarded" }
-
- verify :only => :must_be_post, :method => :post, :render => { :status => 405, :text => "Must be post" }, :add_headers => { "Allow" => "POST" }
-
- verify :only => :guarded_one_for_named_route_test, :params => "one",
- :redirect_to => :foo_url
-
- verify :only => :no_default_action, :params => "santa"
-
- verify :only => :guarded_with_back, :method => :post,
- :redirect_to => :back
-
- def guarded_one
- render :text => "#{params[:one]}"
- end
-
- def guarded_one_for_named_route_test
- render :text => "#{params[:one]}"
- end
-
- def guarded_with_flash
- render :text => "#{params[:one]}"
- end
-
- def guarded_two
- render :text => "#{params[:one]}:#{params[:two]}"
- end
-
- def guarded_in_session
- render :text => "#{session["one"]}"
- end
-
- def multi_one
- render :text => "#{session["one"]}:#{session["two"]}"
- end
-
- def multi_two
- render :text => "#{session["two"]}:#{session["one"]}"
- end
-
- def guarded_by_method
- render :text => "#{request.method.downcase}"
- end
-
- def guarded_by_xhr
- render :text => "#{request.xhr?}"
- end
-
- def guarded_by_not_xhr
- render :text => "#{request.xhr?}"
- end
-
- def unguarded
- render :text => "#{params[:one]}"
- end
-
- def two_redirects
- render :nothing => true
- end
-
- def must_be_post
- render :text => "Was a post!"
- end
-
- def guarded_with_back
- render :text => "#{params[:one]}"
- end
-
- def no_default_action
- # Will never run
- end
-
- protected
-
- def unconditional_redirect
- redirect_to :action => "unguarded"
- end
- end
-
- tests TestController
-
- def test_using_symbol_back_with_no_referrer
- assert_raise(ActionController::RedirectBackError) { get :guarded_with_back }
- end
-
- def test_using_symbol_back_redirects_to_referrer
- @request.env["HTTP_REFERER"] = "/foo"
- get :guarded_with_back
- assert_redirected_to '/foo'
- end
-
- def test_no_deprecation_warning_for_named_route
- assert_not_deprecated do
- with_routing do |set|
- set.draw do |map|
- match 'foo', :to => 'test#foo', :as => :foo
- match 'verification_test/:action', :to => ::VerificationTest::TestController
- end
- get :guarded_one_for_named_route_test, :two => "not one"
- assert_redirected_to '/foo'
- end
- end
- end
-
- def test_guarded_one_with_prereqs
- get :guarded_one, :one => "here"
- assert_equal "here", @response.body
- end
-
- def test_guarded_one_without_prereqs
- get :guarded_one
- assert_redirected_to :action => "unguarded"
- assert_equal 'unguarded', flash[:error]
- end
-
- def test_guarded_with_flash_with_prereqs
- get :guarded_with_flash, :one => "here"
- assert_equal "here", @response.body
- assert flash.empty?
- end
-
- def test_guarded_with_flash_without_prereqs
- get :guarded_with_flash
- assert_redirected_to :action => "unguarded"
- assert_equal "prereqs failed", flash[:notice]
- end
-
- def test_guarded_two_with_prereqs
- get :guarded_two, :one => "here", :two => "there"
- assert_equal "here:there", @response.body
- end
-
- def test_guarded_two_without_prereqs_one
- get :guarded_two, :two => "there"
- assert_redirected_to :action => "unguarded"
- end
-
- def test_guarded_two_without_prereqs_two
- get :guarded_two, :one => "here"
- assert_redirected_to :action => "unguarded"
- end
-
- def test_guarded_two_without_prereqs_both
- get :guarded_two
- assert_redirected_to :action => "unguarded"
- end
-
- def test_unguarded_with_params
- get :unguarded, :one => "here"
- assert_equal "here", @response.body
- end
-
- def test_unguarded_without_params
- get :unguarded
- assert @response.body.blank?
- end
-
- def test_guarded_in_session_with_prereqs
- get :guarded_in_session, {}, "one" => "here"
- assert_equal "here", @response.body
- end
-
- def test_guarded_in_session_without_prereqs
- get :guarded_in_session
- assert_redirected_to :action => "unguarded"
- end
-
- def test_multi_one_with_prereqs
- get :multi_one, {}, "one" => "here", "two" => "there"
- assert_equal "here:there", @response.body
- end
-
- def test_multi_one_without_prereqs
- get :multi_one
- assert_redirected_to :action => "unguarded"
- end
-
- def test_multi_two_with_prereqs
- get :multi_two, {}, "one" => "here", "two" => "there"
- assert_equal "there:here", @response.body
- end
-
- def test_multi_two_without_prereqs
- get :multi_two
- assert_redirected_to :action => "unguarded"
- end
-
- def test_guarded_by_method_with_prereqs
- post :guarded_by_method
- assert_equal "post", @response.body
- end
-
- def test_guarded_by_method_without_prereqs
- get :guarded_by_method
- assert_redirected_to :action => "unguarded"
- end
-
- def test_guarded_by_xhr_with_prereqs
- xhr :post, :guarded_by_xhr
- assert_equal "true", @response.body
- end
-
- def test_guarded_by_xhr_without_prereqs
- get :guarded_by_xhr
- assert_redirected_to :action => "unguarded"
- end
-
- def test_guarded_by_not_xhr_with_prereqs
- get :guarded_by_not_xhr
- assert_equal "false", @response.body
- end
-
- def test_guarded_by_not_xhr_without_prereqs
- xhr :post, :guarded_by_not_xhr
- assert_redirected_to :action => "unguarded"
- end
-
- def test_guarded_post_and_calls_render_succeeds
- post :must_be_post
- assert_equal "Was a post!", @response.body
- end
-
- def test_default_failure_should_be_a_bad_request
- post :no_default_action
- assert_response :bad_request
- end
-
- def test_guarded_post_and_calls_render_fails_and_sets_allow_header
- get :must_be_post
- assert_response 405
- assert_equal "Must be post", @response.body
- assert_equal "POST", @response.headers["Allow"]
- end
-
- def test_second_redirect
- assert_nothing_raised { get :two_redirects }
- end
-end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 6ff478aec1..d38c48bfd4 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -186,6 +186,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
+
+ scope :module => 'api' do
+ resource :token
+ end
+
+ scope :path => 'api' do
+ resource :me
+ match '/' => 'mes#index'
+ end
end
end
@@ -942,6 +951,25 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_module_scope
+ with_test_routes do
+ get '/token'
+ assert_equal 'api/tokens#show', @response.body
+ assert_equal '/token', token_path
+ end
+ end
+
+ def test_path_scope
+ with_test_routes do
+ get '/api/me'
+ assert_equal 'mes#show', @response.body
+ assert_equal '/api/me', me_path
+
+ get '/api'
+ assert_equal 'mes#index', @response.body
+ end
+ end
+
private
def with_test_routes
yield
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 97da680f17..b447b0715c 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -106,4 +106,13 @@ class ShowExceptionsTest < ActionController::IntegrationTest
assert_response 405
assert_match /ActionController::MethodNotAllowed/, body
end
+
+ test "does not show filtered parameters" do
+ @app = DevelopmentApp
+
+ get "/", {"foo"=>"bar"}, {'action_dispatch.show_exceptions' => true,
+ 'action_dispatch.parameter_filter' => [:foo]}
+ assert_response 500
+ assert_match "&quot;foo&quot;=&gt;&quot;[FILTERED]&quot;", body
+ end
end
diff --git a/actionpack/test/fixtures/localized/hello_world.de.html b/actionpack/test/fixtures/localized/hello_world.de.html
new file mode 100644
index 0000000000..4727d7a7e0
--- /dev/null
+++ b/actionpack/test/fixtures/localized/hello_world.de.html
@@ -0,0 +1 @@
+Gutten Tag \ No newline at end of file
diff --git a/actionpack/test/fixtures/localized/hello_world.en.html b/actionpack/test/fixtures/localized/hello_world.en.html
new file mode 100644
index 0000000000..5e1c309dae
--- /dev/null
+++ b/actionpack/test/fixtures/localized/hello_world.en.html
@@ -0,0 +1 @@
+Hello World \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/_utf8_partial.html.erb b/actionpack/test/fixtures/test/_utf8_partial.html.erb
new file mode 100644
index 0000000000..8d717fd427
--- /dev/null
+++ b/actionpack/test/fixtures/test/_utf8_partial.html.erb
@@ -0,0 +1 @@
+<%= "текст" %>
diff --git a/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb b/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb
new file mode 100644
index 0000000000..4e2224610a
--- /dev/null
+++ b/actionpack/test/fixtures/test/_utf8_partial_magic.html.erb
@@ -0,0 +1,2 @@
+<%# encoding: utf-8 -%>
+<%= "текст" %>
diff --git a/actionpack/test/fixtures/test/utf8.html.erb b/actionpack/test/fixtures/test/utf8.html.erb
index 14fe12debc..ac98c2f012 100644
--- a/actionpack/test/fixtures/test/utf8.html.erb
+++ b/actionpack/test/fixtures/test/utf8.html.erb
@@ -1,4 +1,4 @@
-Русский текст
+Русский <%= render :partial => 'test/utf8_partial' %>
<%= "日".encoding %>
<%= @output_buffer.encoding %>
<%= __ENCODING__ %>
diff --git a/actionpack/test/fixtures/test/utf8_magic.html.erb b/actionpack/test/fixtures/test/utf8_magic.html.erb
index 58cd03b439..257279c29f 100644
--- a/actionpack/test/fixtures/test/utf8_magic.html.erb
+++ b/actionpack/test/fixtures/test/utf8_magic.html.erb
@@ -1,5 +1,5 @@
<%# encoding: utf-8 -%>
-Русский текст
+Русский <%= render :partial => 'test/utf8_partial_magic' %>
<%= "日".encoding %>
<%= @output_buffer.encoding %>
<%= __ENCODING__ %>
diff --git a/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb b/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb
new file mode 100644
index 0000000000..cb22692f9a
--- /dev/null
+++ b/actionpack/test/fixtures/test/utf8_magic_with_bare_partial.html.erb
@@ -0,0 +1,5 @@
+<%# encoding: utf-8 -%>
+Русский <%= render :partial => 'test/utf8_partial' %>
+<%= "日".encoding %>
+<%= @output_buffer.encoding %>
+<%= __ENCODING__ %>
diff --git a/actionpack/test/template/active_model_helper_i18n_test.rb b/actionpack/test/template/active_model_helper_i18n_test.rb
deleted file mode 100644
index 4eb2f262bd..0000000000
--- a/actionpack/test/template/active_model_helper_i18n_test.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'abstract_unit'
-
-class ActiveModelHelperI18nTest < Test::Unit::TestCase
- include ActionView::Context
- include ActionView::Helpers::ActiveModelHelper
-
- attr_reader :request
-
- def setup
- @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages'])
- @object.stubs :to_model => @object
- @object.stubs :class => stub(:model_name => stub(:human => ""))
-
- @object_name = 'book_seller'
- @object_name_without_underscore = 'book seller'
-
- stubs(:content_tag).returns 'content_tag'
-
- I18n.stubs(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns "1 error prohibited this from being saved"
- I18n.stubs(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
- end
-
- def test_error_messages_for_given_a_header_option_it_does_not_translate_header_message
- I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').never
- error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en')
- end
-
- def test_error_messages_for_given_no_header_option_it_translates_header_message
- I18n.expects(:t).with(:'header', :locale => 'en', :scope => [:errors, :template], :count => 1, :model => '').returns 'header message'
- error_messages_for(:object => @object, :locale => 'en')
- end
-
- def test_error_messages_for_given_a_message_option_it_does_not_translate_message
- I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).never
- error_messages_for(:object => @object, :message => 'message', :locale => 'en')
- end
-
- def test_error_messages_for_given_no_message_option_it_translates_message
- I18n.expects(:t).with(:'body', :locale => 'en', :scope => [:errors, :template]).returns 'There were problems with the following fields:'
- error_messages_for(:object => @object, :locale => 'en')
- end
-end
diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb
index 47eb620f7a..8deeb78eab 100644
--- a/actionpack/test/template/active_model_helper_test.rb
+++ b/actionpack/test/template/active_model_helper_test.rb
@@ -4,132 +4,25 @@ class ActiveModelHelperTest < ActionView::TestCase
tests ActionView::Helpers::ActiveModelHelper
silence_warnings do
- class Post < Struct.new(:title, :author_name, :body, :secret, :written_on)
- extend ActiveModel::Naming
+ class Post < Struct.new(:author_name, :body)
include ActiveModel::Conversion
- end
-
- class User < Struct.new(:email)
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- end
-
- class Column < Struct.new(:type, :name, :human_name)
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- end
- end
+ include ActiveModel::Validations
- class DirtyPost
- class Errors
- def empty?
+ def persisted?
false
end
-
- def count
- 1
- end
-
- def full_messages
- ["Author name can't be <em>empty</em>"]
- end
-
- def [](field)
- ["can't be <em>empty</em>"]
- end
- end
-
- def errors
- Errors.new
end
end
- def setup_post
- @post = Post.new
- def @post.errors
- Class.new {
- def [](field)
- case field.to_s
- when "author_name"
- ["can't be empty"]
- when "body"
- ['foo']
- else
- []
- end
- end
- def empty?() false end
- def count() 1 end
- def full_messages() [ "Author name can't be empty" ] end
- }.new
- end
-
- def @post.persisted?() false end
- def @post.to_param() nil end
-
- def @post.column_for_attribute(attr_name)
- Post.content_columns.select { |column| column.name == attr_name }.first
- end
-
- silence_warnings do
- def Post.content_columns() [ Column.new(:string, "title", "Title"), Column.new(:text, "body", "Body") ] end
- end
-
- @post.title = "Hello World"
- @post.author_name = ""
- @post.body = "Back to the hill and over it again!"
- @post.secret = 1
- @post.written_on = Date.new(2004, 6, 15)
- end
-
- def setup_user
- @user = User.new
- def @user.errors
- Class.new {
- def [](field) field == "email" ? ['nonempty'] : [] end
- def empty?() false end
- def count() 1 end
- def full_messages() [ "User email can't be empty" ] end
- }.new
- end
-
- def @user.new_record?() true end
- def @user.to_param() nil end
-
- def @user.column_for_attribute(attr_name)
- User.content_columns.select { |column| column.name == attr_name }.first
- end
-
- silence_warnings do
- def User.content_columns() [ Column.new(:string, "email", "Email") ] end
- end
-
- @user.email = ""
- end
-
- def protect_against_forgery?
- @protect_against_forgery ? true : false
- end
- attr_accessor :request_forgery_protection_token, :form_authenticity_token
-
def setup
super
- setup_post
- setup_user
-
- @response = ActionController::TestResponse.new
- end
-
- def url_for(options)
- options = options.symbolize_keys
- [options[:action], options[:id].to_param].compact.join('/')
- end
+ @post = Post.new
+ @post.errors[:author_name] << "can't be empty"
+ @post.errors[:body] << "foo"
- def test_generic_input_tag
- assert_dom_equal(
- %(<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />), input("post", "title")
- )
+ @post.author_name = ""
+ @post.body = "Back to the hill and over it again!"
end
def test_text_area_with_errors
@@ -160,176 +53,11 @@ class ActiveModelHelperTest < ActionView::TestCase
ActionView::Base.field_error_proc = old_proc if old_proc
end
- def test_form_with_string
- assert_dom_equal(
- %(<form action="create" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
- form("post")
- )
-
- silence_warnings do
- class << @post
- def persisted?() true end
- def to_param() id end
- def id() 1 end
+ def test_deprecations
+ %w(input form error_messages_for error_message_on).each do |method|
+ assert_deprecated do
+ send(method, "post")
end
end
-
- assert_dom_equal(
- %(<form action="update/1" method="post"><input id="post_id" name="post[id]" type="hidden" value="1" /><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Update" /></form>),
- form("post")
- )
- end
-
- def test_form_with_protect_against_forgery
- @protect_against_forgery = true
- @request_forgery_protection_token = 'authenticity_token'
- @form_authenticity_token = '123'
- assert_dom_equal(
- %(<form action="create" method="post"><div style='margin:0;padding:0;display:inline'><input type='hidden' name='authenticity_token' value='123' /></div><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
- form("post")
- )
- end
-
- def test_form_with_method_option
- assert_dom_equal(
- %(<form action="create" method="get"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
- form("post", :method=>'get')
- )
- end
-
- def test_form_with_action_option
- output_buffer << form("post", :action => "sign")
- assert_select "form[action=sign]" do |form|
- assert_select "input[type=submit][value=Sign]"
- end
- end
-
- def test_form_with_date
- silence_warnings do
- def Post.content_columns() [ Column.new(:date, "written_on", "Written on") ] end
- end
-
- assert_dom_equal(
- %(<form action="create" method="post"><p><label for="post_written_on">Written on</label><br /><select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n<select id="post_written_on_2i" name="post[written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n<select id="post_written_on_3i" name="post[written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n</p><input name="commit" type="submit" value="Create" /></form>),
- form("post")
- )
- end
-
- def test_form_with_datetime
- silence_warnings do
- def Post.content_columns() [ Column.new(:datetime, "written_on", "Written on") ] end
- end
- @post.written_on = Time.gm(2004, 6, 15, 16, 30)
-
- assert_dom_equal(
- %(<form action="create" method="post"><p><label for="post_written_on">Written on</label><br /><select id="post_written_on_1i" name="post[written_on(1i)]">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n<select id="post_written_on_2i" name="post[written_on(2i)]">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6" selected="selected">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n</select>\n<select id="post_written_on_3i" name="post[written_on(3i)]">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15" selected="selected">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n</select>\n &mdash; <select id="post_written_on_4i" name="post[written_on(4i)]">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n</select>\n : <select id="post_written_on_5i" name="post[written_on(5i)]">\n<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30" selected="selected">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n</select>\n</p><input name="commit" type="submit" value="Create" /></form>),
- form("post")
- )
- end
-
- def test_error_for_block
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post")
- assert_equal %(<div class="errorDeathByClass" id="errorDeathById"><h1>1 error prohibited this post from being saved</h1><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :class => "errorDeathByClass", :id => "errorDeathById", :header_tag => "h1")
- assert_equal %(<div id="errorDeathById"><h1>1 error prohibited this post from being saved</h1><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :class => nil, :id => "errorDeathById", :header_tag => "h1")
- assert_equal %(<div class="errorDeathByClass"><h1>1 error prohibited this post from being saved</h1><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :class => "errorDeathByClass", :id => nil, :header_tag => "h1")
- end
-
- def test_error_messages_for_escapes_html
- @dirty_post = DirtyPost.new
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this dirty post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be &lt;em&gt;empty&lt;/em&gt;</li></ul></div>), error_messages_for("dirty_post")
- end
-
- def test_error_messages_for_handles_nil
- assert_equal "", error_messages_for("notthere")
- end
-
- def test_error_message_on_escapes_html
- @dirty_post = DirtyPost.new
- assert_dom_equal "<div class=\"formError\">can't be &lt;em&gt;empty&lt;/em&gt;</div>", error_message_on(:dirty_post, :author_name)
- end
-
- def test_error_message_on_handles_nil
- assert_equal "", error_message_on("notthere", "notthere")
- end
-
- def test_error_message_on
- assert_dom_equal "<div class=\"formError\">can't be empty</div>", error_message_on(:post, :author_name)
- end
-
- def test_error_message_on_no_instance_variable
- other_post = @post
- assert_dom_equal "<div class=\"formError\">can't be empty</div>", error_message_on(other_post, :author_name)
- end
-
- def test_error_message_on_with_options_hash
- assert_dom_equal "<div class=\"differentError\">beforecan't be emptyafter</div>", error_message_on(:post, :author_name, :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
- end
-
- def test_error_message_on_with_tag_option_in_options_hash
- assert_dom_equal "<span class=\"differentError\">beforecan't be emptyafter</span>", error_message_on(:post, :author_name, :html_tag => "span", :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after')
- end
-
- def test_error_message_on_handles_empty_errors
- assert_equal "", error_message_on(@post, :tag)
- end
-
- def test_error_messages_for_many_objects
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li><li>User email can't be empty</li></ul></div>), error_messages_for("post", "user")
-
- # reverse the order, error order changes and so does the title
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post")
-
- # add the default to put post back in the title
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object_name => "post")
-
- # symbols work as well
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => :post)
-
- # any default works too
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this monkey from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => "monkey")
-
- # should space object name
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this chunky bacon from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :object_name => "chunky_bacon")
-
- # hide header and explanation messages with nil or empty string
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :header_message => nil, :message => "")
-
- # override header and explanation messages
- header_message = "Yikes! Some errors"
- message = "Please fix the following fields and resubmit:"
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>#{header_message}</h2><p>#{message}</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for(:user, :post, :header_message => header_message, :message => message)
- end
-
- def test_error_messages_for_non_instance_variable
- actual_user = @user
- actual_post = @post
- @user = nil
- @post = nil
-
- #explicitly set object
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>), error_messages_for("post", :object => actual_post)
-
- #multiple objects
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>), error_messages_for("user", "post", :object => [actual_user, actual_post])
-
- #nil object
- assert_equal '', error_messages_for('user', :object => nil)
- end
-
- def test_error_messages_for_model_objects
- error = error_messages_for(@post)
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>),
- error
-
- error = error_messages_for(@user, @post)
- assert_dom_equal %(<div class="errorExplanation" id="errorExplanation"><h2>2 errors prohibited this user from being saved</h2><p>There were problems with the following fields:</p><ul><li>User email can't be empty</li><li>Author name can't be empty</li></ul></div>),
- error
- end
-
- def test_form_with_string_multipart
- assert_dom_equal(
- %(<form action="create" enctype="multipart/form-data" method="post"><p><label for="post_title">Title</label><br /><input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /></p>\n<p><label for="post_body">Body</label><br /><div class="fieldWithErrors"><textarea cols="40" id="post_body" name="post[body]" rows="20">Back to the hill and over it again!</textarea></div></p><input name="commit" type="submit" value="Create" /></form>),
- form("post", :multipart => true)
- )
end
end
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 223a430f92..124bf734ac 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -157,6 +157,14 @@ class AssetTagHelperTest < ActionView::TestCase
%(image_tag("mouse.png", :mouseover => image_path("mouse_over.png"))) => %(<img alt="Mouse" onmouseover="this.src='/images/mouse_over.png'" onmouseout="this.src='/images/mouse.png'" src="/images/mouse.png" />)
}
+ FaviconLinkToTag = {
+ %(favicon_link_tag) => %(<link href="/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
+ %(favicon_link_tag 'favicon.ico') => %(<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />),
+ %(favicon_link_tag 'favicon.ico', :rel => 'foo') => %(<link href="/images/favicon.ico" rel="foo" type="image/vnd.microsoft.icon" />),
+ %(favicon_link_tag 'favicon.ico', :rel => 'foo', :type => 'bar') => %(<link href="/images/favicon.ico" rel="foo" type="bar" />),
+ %(favicon_link_tag 'mb-icon.png', :rel => 'apple-touch-icon', :type => 'image/png') => %(<link href="/images/mb-icon.png" rel="apple-touch-icon" type="image/png" />)
+ }
+
VideoPathToTag = {
%(video_path("xml")) => %(/videos/xml),
%(video_path("xml.ogg")) => %(/videos/xml.ogg),
@@ -331,6 +339,10 @@ class AssetTagHelperTest < ActionView::TestCase
ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
+ def test_favicon_link_tag
+ FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
+ end
+
def test_image_tag_windows_behaviour
old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
# This simulates the behaviour of File#exist? on windows when testing a file ending in "."
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 64dfbde0b0..28a13b07be 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -1314,12 +1314,12 @@ class FormHelperTest < ActionView::TestCase
class LabelledFormBuilder < ActionView::Helpers::FormBuilder
(field_helpers - %w(hidden_field)).each do |selector|
- src = <<-END_SRC
+ src, line = <<-END_SRC, __LINE__ + 1
def #{selector}(field, *args, &proc)
("<label for='\#{field}'>\#{field.to_s.humanize}:</label> " + super + "<br/>").html_safe
end
END_SRC
- class_eval src, __FILE__, __LINE__
+ class_eval src, __FILE__, line
end
end
@@ -1366,43 +1366,6 @@ class FormHelperTest < ActionView::TestCase
ActionView::Base.default_form_builder = old_default_form_builder
end
- def test_default_form_builder_with_active_record_helpers
- assert_deprecated do
- form_for(:post, @post) do |f|
- concat f.error_message_on('author_name')
- concat f.error_messages
- end
- end
-
- expected = %(<form action='http://www.example.com' method='post'>) +
- %(<div class='formError'>can't be empty</div>) +
- %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) +
- %(</form>)
-
- assert_dom_equal expected, output_buffer
-
- end
-
- def test_default_form_builder_no_instance_variable
- post = @post
- @post = nil
-
- assert_deprecated do
- form_for(:post, post) do |f|
- concat f.error_message_on('author_name')
- concat f.error_messages
- end
- end
-
- expected = %(<form action='http://www.example.com' method='post'>) +
- %(<div class='formError'>can't be empty</div>) +
- %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) +
- %(</form>)
-
- assert_dom_equal expected, output_buffer
-
- end
-
def test_fields_for_with_labelled_builder
output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f|
concat f.text_field(:title)
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 5f33c933db..c9a50da418 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -245,31 +245,6 @@ module RenderTestCases
assert_equal %(\n<title>title</title>\n\n),
@view.render(:file => "test/layout_render_file.erb")
end
-
- if '1.9'.respond_to?(:force_encoding)
- def test_render_utf8_template_with_magic_comment
- with_external_encoding Encoding::ASCII_8BIT do
- result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield")
- assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result
- assert_equal Encoding::UTF_8, result.encoding
- end
- end
-
- def test_render_utf8_template_with_default_external_encoding
- with_external_encoding Encoding::UTF_8 do
- result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
- assert_equal "Русский текст\nUTF-8\nUTF-8\nUTF-8\n", result
- assert_equal Encoding::UTF_8, result.encoding
- end
- end
-
- def with_external_encoding(encoding)
- old, Encoding.default_external = Encoding.default_external, encoding
- yield
- ensure
- Encoding.default_external = old
- end
- end
end
class CachedViewRenderTest < ActiveSupport::TestCase
@@ -302,4 +277,51 @@ class LazyViewRenderTest < ActiveSupport::TestCase
def teardown
GC.start
end
+
+ if '1.9'.respond_to?(:force_encoding)
+ def test_render_utf8_template_with_magic_comment
+ with_external_encoding Encoding::ASCII_8BIT do
+ result = @view.render(:file => "test/utf8_magic.html.erb", :layouts => "layouts/yield")
+ assert_equal Encoding::UTF_8, result.encoding
+ assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result
+ end
+ end
+
+ def test_render_utf8_template_with_default_external_encoding
+ with_external_encoding Encoding::UTF_8 do
+ result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
+ assert_equal Encoding::UTF_8, result.encoding
+ assert_equal "Русский текст\n\nUTF-8\nUTF-8\nUTF-8\n", result
+ end
+ end
+
+ def test_render_utf8_template_with_incompatible_external_encoding
+ with_external_encoding Encoding::SJIS do
+ begin
+ result = @view.render(:file => "test/utf8.html.erb", :layouts => "layouts/yield")
+ flunk 'Should have raised incompatible encoding error'
+ rescue ActionView::Template::Error => error
+ assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message
+ end
+ end
+ end
+
+ def test_render_utf8_template_with_partial_with_incompatible_encoding
+ with_external_encoding Encoding::SJIS do
+ begin
+ result = @view.render(:file => "test/utf8_magic_with_bare_partial.html.erb", :layouts => "layouts/yield")
+ flunk 'Should have raised incompatible encoding error'
+ rescue ActionView::Template::Error => error
+ assert_match 'invalid byte sequence in Shift_JIS', error.original_exception.message
+ end
+ end
+ end
+
+ def with_external_encoding(encoding)
+ old, Encoding.default_external = Encoding.default_external, encoding
+ yield
+ ensure
+ Encoding.default_external = old
+ end
+ end
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 35e73fbf1e..de63030714 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -74,56 +74,56 @@ class UrlHelperTest < ActiveSupport::TestCase
# todo: missing test cases
def test_button_to_with_straight_url
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
+ assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
end
def test_button_to_with_query
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
+ assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
end
def test_button_to_with_escaped_query
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2")
+ assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2")
end
def test_button_to_with_query_and_no_name
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button-to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
+ assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
end
def test_button_to_with_javascript_confirm
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
)
end
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
)
end
def test_button_to_enabled_disabled
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :disabled => false)
)
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :disabled => true)
)
end
def test_button_to_with_method_delete
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
+ "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :method => :delete)
)
end
def test_button_to_with_method_get
assert_dom_equal(
- "<form method=\"get\" action=\"http://www.example.com\" class=\"button-to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
+ "<form method=\"get\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
button_to("Hello", "http://www.example.com", :method => :get)
)
end
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index f04829ef09..b7c368ad8d 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -221,7 +221,7 @@ module ActiveModel
def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
- module_eval <<-STR, __FILE__, __LINE__+1
+ module_eval <<-STR, __FILE__, __LINE__ + 1
def #{matcher.method_name(new_name)}(*args)
send(:#{matcher.method_name(old_name)}, *args)
end
@@ -265,7 +265,7 @@ module ActiveModel
else
method_name = matcher.method_name(attr_name)
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__+1
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
if method_defined?(:#{method_name})
undef :#{method_name}
end
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index d4e98de57b..ad12600d7c 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -105,7 +105,7 @@ module ActiveModel
end
def _define_before_model_callback(klass, callback) #:nodoc:
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.before_#{callback}(*args, &block)
set_callback(:#{callback}, :before, *args, &block)
end
@@ -113,7 +113,7 @@ module ActiveModel
end
def _define_around_model_callback(klass, callback) #:nodoc:
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.around_#{callback}(*args, &block)
set_callback(:#{callback}, :around, *args, &block)
end
@@ -121,7 +121,7 @@ module ActiveModel
end
def _define_after_model_callback(klass, callback) #:nodoc:
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
def self.after_#{callback}(*args, &block)
options = args.extract_options!
options[:prepend] = true
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index 64b28f6def..e6c86c7843 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -142,6 +142,11 @@ module ActiveModel
to_a.size
end
+ # Returns true if there are any errors, false if not.
+ def empty?
+ all? { |k, v| v && v.empty? }
+ end
+
# Returns an xml formatted representation of the Errors hash.
#
# p.errors.add(:name, "can't be blank")
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index a33657626f..aac47d5f81 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -3,7 +3,7 @@ module ActiveModel
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb
index 9fedd84c73..925a68da91 100644
--- a/activemodel/test/cases/validations_test.rb
+++ b/activemodel/test/cases/validations_test.rb
@@ -97,6 +97,12 @@ class ValidationsTest < ActiveModel::TestCase
assert_equal 2, r.errors.count
end
+ def test_errors_empty_after_errors_on_check
+ t = Topic.new
+ assert t.errors[:id].empty?
+ assert t.errors.empty?
+ end
+
def test_validates_each
hits = 0
Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
diff --git a/activerecord/Rakefile b/activerecord/Rakefile
index e638da0f3e..bf05389eae 100644
--- a/activerecord/Rakefile
+++ b/activerecord/Rakefile
@@ -50,6 +50,7 @@ end
t.libs << "test" << connection_path
t.test_files=Dir.glob( "test/cases/**/*_test{,_#{adapter_short}}.rb" ).sort
t.verbose = true
+ t.warning = true
}
task "isolated_test_#{adapter}" do
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 5ecdf1ac8d..b808f8c306 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -105,6 +105,7 @@ module ActiveRecord
def reset
reset_target!
+ reset_named_scopes_cache!
@loaded = false
end
@@ -146,7 +147,7 @@ module ActiveRecord
# has_many :books
# end
#
- # Author.find(:first).books.transaction do
+ # Author.first.books.transaction do
# # same effect as calling Book.transaction
# end
def transaction(*args)
@@ -162,6 +163,7 @@ module ActiveRecord
load_target
delete(@target)
reset_target!
+ reset_named_scopes_cache!
end
# Calculate sum using SQL, not Enumerable
@@ -250,6 +252,7 @@ module ActiveRecord
load_target
destroy(@target)
reset_target!
+ reset_named_scopes_cache!
end
def create(attrs = {})
@@ -406,8 +409,8 @@ module ActiveRecord
super
end
elsif @reflection.klass.scopes[method]
- @_scopes ||= {}
- @_scopes[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
+ @_named_scopes_cache ||= {}
+ @_named_scopes_cache[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) }
else
with_scope(construct_scope) do
if block_given?
@@ -428,6 +431,10 @@ module ActiveRecord
@target = Array.new
end
+ def reset_named_scopes_cache!
+ @_named_scopes_cache = {}
+ end
+
def find_target
records =
if @reflection.options[:finder_sql]
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 435aea9b09..36f2a9777c 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -52,6 +52,8 @@ module ActiveRecord
@changed_attributes.delete(attr) unless field_changed?(attr, old, value)
else
old = clone_attribute_value(:read_attribute, attr)
+ # Save Time objects as TimeWithZone if time_zone_aware_attributes == true
+ old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old)
@changed_attributes[attr] = old if field_changed?(attr, old, value)
end
@@ -84,6 +86,10 @@ module ActiveRecord
old != value
end
+
+ def clone_with_time_zone_conversion_attribute?(attr, old)
+ old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index a8e3e28a7a..783d61383b 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -17,7 +17,7 @@ module ActiveRecord
# This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone.
def define_method_attribute(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body = <<-EOV
+ method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}(reload = false)
cached = @attributes_cache['#{attr_name}']
return cached if cached && !reload
@@ -25,7 +25,7 @@ module ActiveRecord
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
end
EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
super
end
@@ -35,7 +35,7 @@ module ActiveRecord
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
def define_method_attribute=(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
- method_body = <<-EOV
+ method_body, line = <<-EOV, __LINE__ + 1
def #{attr_name}=(time)
unless time.acts_like?(:time)
time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
@@ -44,7 +44,7 @@ module ActiveRecord
write_attribute(:#{attr_name}, time)
end
EOV
- generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__)
+ generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
super
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index f0b107255c..d544c48a4c 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -56,15 +56,15 @@ module ActiveRecord #:nodoc:
#
# class User < ActiveRecord::Base
# def self.authenticate_unsafely(user_name, password)
- # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'")
+ # where("user_name = '#{user_name}' AND password = '#{password}'").first
# end
#
# def self.authenticate_safely(user_name, password)
- # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ])
+ # where("user_name = ? AND password = ?", user_name, password).first
# end
#
# def self.authenticate_safely_simply(user_name, password)
- # find(:first, :conditions => { :user_name => user_name, :password => password })
+ # where(:user_name => user_name, :password => password).first
# end
# end
#
@@ -77,30 +77,30 @@ module ActiveRecord #:nodoc:
# question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing
# the question marks with symbols and supplying a hash with values for the matching symbol keys:
#
- # Company.find(:first, :conditions => [
+ # Company.where(
# "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
# { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' }
- # ])
+ # ).first
#
# Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
# operator. For instance:
#
- # Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 })
- # Student.find(:all, :conditions => params[:student])
+ # Student.where(:first_name => "Harvey", :status => 1)
+ # Student.where(params[:student])
#
# A range may be used in the hash to use the SQL BETWEEN operator:
#
- # Student.find(:all, :conditions => { :grade => 9..12 })
+ # Student.where(:grade => 9..12)
#
# An array may be used in the hash to use the SQL IN operator:
#
- # Student.find(:all, :conditions => { :grade => [9,11,12] })
+ # Student.where(:grade => [9,11,12])
#
# When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a
# particular condition. For instance:
#
- # Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools)
- # Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools)
+ # Student.joins(:schools).where(:schools => { :type => 'public' })
+ # Student.joins(:schools).where('schools.type' => 'public' )
#
# == Overwriting default accessors
#
@@ -153,18 +153,18 @@ module ActiveRecord #:nodoc:
# Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by
# appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>,
# <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing
- # <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
- # And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
+ # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>.
+ # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>.
#
# It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like
# <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing
- # <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do
+ # <tt>Person.where(:user_name => user_name, :password => password).first</tt>, you just do
# <tt>Person.find_by_user_name_and_password(user_name, password)</tt>.
#
- # It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt>
- # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is
- # actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>.
- # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options.
+ # It's even possible to call these dynamic finder methods on relations and named scopes. For example :
+ #
+ # Payment.order("created_on").find_all_by_amount(50)
+ # Payment.pending.find_last_by_amount(100)
#
# The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with
# <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example:
@@ -224,7 +224,7 @@ module ActiveRecord #:nodoc:
# class PriorityClient < Client; end
#
# When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then
- # fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object.
+ # fetch this row again using <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object.
#
# If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just
# like normal subclasses with no special magic for differentiating between them or reloading the right type with find.
@@ -1117,10 +1117,6 @@ module ActiveRecord #:nodoc:
# It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+
# is actually <tt>find_all_by_amount(amount, options)</tt>.
#
- # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that
- # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password])
- # respectively.
- #
# Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future
# attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
@@ -1138,7 +1134,7 @@ module ActiveRecord #:nodoc:
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
if match.scope?
- self.class_eval %{
+ self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
options = args.extract_options! # options = args.extract_options!
attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments(
@@ -1147,7 +1143,7 @@ module ActiveRecord #:nodoc:
#
scoped(:conditions => attributes) # scoped(:conditions => attributes)
end # end
- }, __FILE__, __LINE__
+ METHOD
send(method_id, *arguments)
end
else
@@ -1196,12 +1192,12 @@ module ActiveRecord #:nodoc:
protected
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
- # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
- # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash.
+ # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while
+ # <tt>:create</tt> parameters are an attributes hash.
#
# class Article < ActiveRecord::Base
# def self.create_with_scope
- # with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
+ # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
# find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
# a = create(1)
# a.blog_id # => 1
@@ -1210,20 +1206,20 @@ module ActiveRecord #:nodoc:
# end
#
# In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of
- # <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged.
+ # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged.
#
- # <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing
+ # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing
# problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the
# array of strings format for your joins.
#
# class Article < ActiveRecord::Base
# def self.find_with_scope
- # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do
- # with_scope(:find => { :limit => 10 }) do
- # find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
+ # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
+ # with_scope(:find => limit(10)) do
+ # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
# end
- # with_scope(:find => { :conditions => "author_id = 3" }) do
- # find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
+ # with_scope(:find => where(:author_id => 3)) do
+ # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
# end
# end
# end
@@ -1233,9 +1229,9 @@ module ActiveRecord #:nodoc:
#
# class Article < ActiveRecord::Base
# def self.find_with_exclusive_scope
- # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do
- # with_exclusive_scope(:find => { :limit => 10 })
- # find(:all) # => SELECT * from articles LIMIT 10
+ # with_scope(:find => where(:blog_id => 1).limit(1)) do
+ # with_exclusive_scope(:find => limit(10))
+ # all # => SELECT * from articles LIMIT 10
# end
# end
# end
@@ -1318,9 +1314,9 @@ module ActiveRecord #:nodoc:
modularized_name = type_name_with_module(type_name)
silence_warnings do
begin
- class_eval(modularized_name, __FILE__, __LINE__)
+ class_eval(modularized_name, __FILE__)
rescue NameError
- class_eval(type_name, __FILE__, __LINE__)
+ class_eval(type_name, __FILE__)
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 1e095110f2..e5d100b51b 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -15,7 +15,7 @@ module ActiveRecord
def dirties_query_cache(base, *method_names)
method_names.each do |method_name|
- base.class_eval <<-end_code, __FILE__, __LINE__
+ base.class_eval <<-end_code, __FILE__, __LINE__ + 1
def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args)
clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled
#{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 2395a744a3..68ee88bba4 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -658,27 +658,6 @@ module ActiveRecord
end
end
- # Creates a schema for the given user
- #
- # Example:
- # create_schema('products', 'postgres')
- def create_schema(schema_name, pg_username)
- execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"")
- end
-
- # Drops a schema
- #
- # Example:
- # drop_schema('products')
- def drop_schema(schema_name)
- execute("DROP SCHEMA \"#{schema_name}\"")
- end
-
- # Returns an array of all schemas in the database
- def all_schemas
- query('SELECT schema_name FROM information_schema.schemata').flatten
- end
-
# Returns the list of all tables in the schema search path or a specified schema.
def tables(name = nil)
query(<<-SQL, name).map { |row| row[0] }
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index a107befef3..2b53afc68b 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -84,15 +84,9 @@ namespace :db do
end
end
when 'postgresql'
- @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
- schema_search_path = config['schema_search_path'] || 'public'
- first_in_schema_search_path = schema_search_path.split(',').first.strip
+ @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
begin
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
- unless ActiveRecord::Base.connection.all_schemas.include?(first_in_schema_search_path)
- ActiveRecord::Base.connection.create_schema(first_in_schema_search_path, config['username'])
- $stderr.puts "Schema #{first_in_schema_search_path} has been created."
- end
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
ActiveRecord::Base.establish_connection(config)
rescue
@@ -452,6 +446,8 @@ namespace :db do
end
end
+task 'test:prepare' => 'db:test:prepare'
+
def drop_database(config)
case config['adapter']
when 'mysql'
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index cf78ec6e12..bf6b995a37 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -3,7 +3,7 @@ module ActiveRecord
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/activerecord/test/cases/active_schema_test_postgresql.rb b/activerecord/test/cases/active_schema_test_postgresql.rb
index 67c662d694..af80f724f2 100644
--- a/activerecord/test/cases/active_schema_test_postgresql.rb
+++ b/activerecord/test/cases/active_schema_test_postgresql.rb
@@ -17,13 +17,6 @@ class PostgresqlActiveSchemaTest < Test::Unit::TestCase
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
end
- def test_create_schema
- assert_equal %(CREATE SCHEMA "rizwan" AUTHORIZATION "postgres"), create_schema(:rizwan, :postgres)
- end
-
- def test_drop_schema
- assert_equal %(DROP SCHEMA "rizwan"), drop_schema(:rizwan)
- end
private
def method_missing(method_symbol, *arguments)
ActiveRecord::Base.connection.send(method_symbol, *arguments)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 9f78ae008c..9b28766405 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -81,12 +81,6 @@ class AdapterTest < ActiveRecord::TestCase
def test_encoding
assert_not_nil @connection.encoding
end
-
- def test_all_schemas
- @connection.create_schema(:test_schema, :postgres)
- assert @connection.all_schemas.include?('test_schema')
- @connection.drop_schema(:test_schema)
- end
end
def test_table_alias
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 62121b93cb..be77ee4bf3 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -35,11 +35,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_belongs_to_with_primary_key_joins_on_correct_column
sql = Client.joins(:firm_with_primary_key).to_sql
if current_adapter?(:MysqlAdapter)
- assert_no_match /`firm_with_primary_keys_companies`\.`id`/, sql
- assert_match /`firm_with_primary_keys_companies`\.`name`/, sql
+ assert_no_match(/`firm_with_primary_keys_companies`\.`id`/, sql)
+ assert_match(/`firm_with_primary_keys_companies`\.`name`/, sql)
else
- assert_no_match /"firm_with_primary_keys_companies"\."id"/, sql
- assert_match /"firm_with_primary_keys_companies"\."name"/, sql
+ assert_no_match(/"firm_with_primary_keys_companies"\."id"/, sql)
+ assert_match(/"firm_with_primary_keys_companies"\."name"/, sql)
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index a64ccb2120..7a17ef1ee0 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -54,6 +54,89 @@ class DirtyTest < ActiveRecord::TestCase
assert_nil pirate.catchphrase_change
end
+ def test_time_attributes_changes_with_time_zone
+ in_time_zone 'Paris' do
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ # New record - no changes.
+ pirate = target.new
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Saved - no changes.
+ pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.save!
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Change created_on.
+ old_created_on = pirate.created_on
+ pirate.created_on = Time.now - 1.day
+ assert pirate.created_on_changed?
+ assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
+ assert_equal old_created_on, pirate.created_on_was
+ end
+ end
+
+ def test_time_attributes_changes_without_time_zone_by_skip
+ in_time_zone 'Paris' do
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ target.skip_time_zone_conversion_for_attributes = [:created_on]
+
+ # New record - no changes.
+ pirate = target.new
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Saved - no changes.
+ pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.save!
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Change created_on.
+ old_created_on = pirate.created_on
+ pirate.created_on = Time.now + 1.day
+ assert pirate.created_on_changed?
+ # kind_of does not work because
+ # ActiveSupport::TimeWithZone.name == 'Time'
+ assert_equal Time, pirate.created_on_was.class
+ assert_equal old_created_on, pirate.created_on_was
+ end
+ end
+
+ def test_time_attributes_changes_without_time_zone
+
+ target = Class.new(ActiveRecord::Base)
+ target.table_name = 'pirates'
+
+ target.time_zone_aware_attributes = false
+
+ # New record - no changes.
+ pirate = target.new
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Saved - no changes.
+ pirate.catchphrase = 'arrrr, time zone!!'
+ pirate.save!
+ assert !pirate.created_on_changed?
+ assert_nil pirate.created_on_change
+
+ # Change created_on.
+ old_created_on = pirate.created_on
+ pirate.created_on = Time.now + 1.day
+ assert pirate.created_on_changed?
+ # kind_of does not work because
+ # ActiveSupport::TimeWithZone.name == 'Time'
+ assert_equal Time, pirate.created_on_was.class
+ assert_equal old_created_on, pirate.created_on_was
+ end
+
+
def test_aliased_attribute_changes
# the actual attribute here is name, title is an
# alias setup via alias_attribute
@@ -406,4 +489,16 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal %w(parrot_id), pirate.changed
assert_nil pirate.parrot_id_was
end
+
+ def in_time_zone(zone)
+ old_zone = Time.zone
+ old_tz = ActiveRecord::Base.time_zone_aware_attributes
+
+ Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
+ ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
+ yield
+ ensure
+ Time.zone = old_zone
+ ActiveRecord::Base.time_zone_aware_attributes = old_tz
+ end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index e213986ede..7a26ee072d 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1136,21 +1136,6 @@ if ActiveRecord::Base.connection.supports_migrations?
load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
end
- def test_migrator_interleaved_migrations
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
-
- assert_nothing_raised do
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
- end
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- assert_nothing_raised do
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
- end
- end
-
def test_migrator_db_has_no_schema_migrations_table
# Oracle adapter raises error if semicolon is present as last character
if current_adapter?(:OracleAdapter)
@@ -1362,16 +1347,6 @@ if ActiveRecord::Base.connection.supports_migrations?
end
end
- def test_migration_should_be_run_without_logger
- previous_logger = ActiveRecord::Base.logger
- ActiveRecord::Base.logger = nil
- assert_nothing_raised do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- end
- ensure
- ActiveRecord::Base.logger = previous_logger
- end
-
protected
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
@@ -1457,6 +1432,45 @@ if ActiveRecord::Base.connection.supports_migrations?
end # SexyMigrationsTest
+ class MigrationLoggerTest < ActiveRecord::TestCase
+ def setup
+ Object.send(:remove_const, :InnocentJointable)
+ end
+
+ def test_migration_should_be_run_without_logger
+ previous_logger = ActiveRecord::Base.logger
+ ActiveRecord::Base.logger = nil
+ assert_nothing_raised do
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
+ end
+ ensure
+ ActiveRecord::Base.logger = previous_logger
+ end
+ end
+
+ class InterleavedMigrationsTest < ActiveRecord::TestCase
+ def setup
+ Object.send(:remove_const, :PeopleHaveLastNames)
+ end
+
+ def test_migrator_interleaved_migrations
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
+
+ assert_nothing_raised do
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
+ end
+
+ Person.reset_column_information
+ assert Person.column_methods_hash.include?(:last_name)
+
+ Object.send(:remove_const, :PeopleHaveLastNames)
+ Object.send(:remove_const, :InnocentJointable)
+ assert_nothing_raised do
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
+ end
+ end
+ end
+
class ChangeTableMigrationsTest < ActiveRecord::TestCase
def setup
@connection = Person.connection
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 3d5ebb6cb8..e4cafad11e 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -422,6 +422,16 @@ class NamedScopeTest < ActiveRecord::TestCase
post.comments.containing_the_letter_e.all # force load
assert_no_queries { post.comments.containing_the_letter_e.all }
end
+
+ def test_named_scopes_are_reset_on_association_reload
+ post = posts(:welcome)
+
+ [:destroy_all, :reset, :delete_all].each do |method|
+ before = post.comments.containing_the_letter_e
+ post.comments.send(method)
+ assert before.object_id != post.comments.containing_the_letter_e.object_id, "AssociationCollection##{method} should reset the named scopes cache"
+ end
+ end
end
class DynamicScopeMatchTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index e1ad5c1685..2849ff11b7 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -130,10 +130,20 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
- fixtures :authors, :posts
+ fixtures :authors, :posts, :projects
+
# to_xml used to mess with the hash the user provided which
# caused the builder to be reused. This meant the document kept
# getting appended to.
+
+ def test_modules
+ projects = MyApplication::Business::Project.all
+ xml = projects.to_xml
+ root = projects.first.class.to_s.underscore.pluralize.tr('/','_').dasherize
+ assert_match "<#{root} type=\"array\">", xml
+ assert_match "</#{root}>", xml
+ end
+
def test_passing_hash_shouldnt_reuse_builder
options = {:include=>:posts}
david = authors(:david)
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index 1e81fc099c..1dd5af8098 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -588,11 +588,11 @@ module ActiveResource
@prefix_parameters = nil
# Redefine the new methods.
- code = <<-end_code
+ code, line = <<-end_code, __LINE__ + 1
def prefix_source() "#{value}" end
def prefix(options={}) "#{prefix_call}" end
end_code
- silence_warnings { instance_eval code, __FILE__, __LINE__ }
+ silence_warnings { instance_eval code, __FILE__, line }
rescue
logger.error "Couldn't set prefix: #{$!}\n #{code}" if logger
raise
diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb
index e5891300a6..1ed3804017 100644
--- a/activeresource/lib/active_resource/http_mock.rb
+++ b/activeresource/lib/active_resource/http_mock.rb
@@ -57,7 +57,7 @@ module ActiveResource
# def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
# @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
# end
- module_eval <<-EOE, __FILE__, __LINE__
+ module_eval <<-EOE, __FILE__, __LINE__ + 1
def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
@responses << [Request.new(:#{method}, path, nil, request_headers), Response.new(body || "", status, response_headers)]
end
@@ -125,7 +125,7 @@ module ActiveResource
# self.class.requests << request
# self.class.responses.assoc(request).try(:second) || raise(InvalidRequestError.new("No response recorded for #{request}"))
# end
- module_eval <<-EOE, __FILE__, __LINE__
+ module_eval <<-EOE, __FILE__, __LINE__ + 1
def #{method}(path, #{'body, ' if has_body}headers)
request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers)
self.class.requests << request
diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb
index d01ebd017f..6b9b8d3a9d 100644
--- a/activeresource/lib/active_resource/version.rb
+++ b/activeresource/lib/active_resource/version.rb
@@ -3,7 +3,7 @@ module ActiveResource
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index b9f565c71d..a5a7a9b904 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,3 +1,8 @@
+*Rails 3.0.0 [beta 3] (pending)*
+
+* Speed up and add Ruby 1.9 support for ActiveSupport::Multibyte::Chars#tidy_bytes. #4350 [Norman Clarke]
+
+
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
* Reduced load time by deferring configuration of classes using
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 6fab565646..8465bc1e10 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -4,7 +4,9 @@ module ActiveSupport
# context, so only the relevant lines are included.
#
# If you need to reconfigure an existing BacktraceCleaner, like the one in Rails, to show as much as possible, you can always
- # call BacktraceCleaner#remove_silencers!
+ # call BacktraceCleaner#remove_silencers! Also, if you need to reconfigure an existing BacktraceCleaner so that it does not
+ # filter or modify the paths of any lines of the backtrace, you can call BacktraceCleaner#remove_filters! These two methods
+ # will give you a completely untouched backtrace.
#
# Example:
#
@@ -60,6 +62,10 @@ module ActiveSupport
@silencers = []
end
+ def remove_filters!
+ @filters = []
+ end
+
private
def filter(backtrace)
@filters.each do |f|
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index d84a62ca2d..c56fedc12e 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -64,7 +64,7 @@ module ActiveSupport
@data.get(key, raw?(options))
end
rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}")
+ logger.error("MemCacheError (#{e}): #{e.message}") if logger
nil
end
@@ -85,7 +85,7 @@ module ActiveSupport
response == Response::STORED
end
rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}")
+ logger.error("MemCacheError (#{e}): #{e.message}") if logger
false
end
@@ -95,7 +95,7 @@ module ActiveSupport
response == Response::DELETED
end
rescue MemCache::MemCacheError => e
- logger.error("MemCacheError (#{e}): #{e.message}")
+ logger.error("MemCacheError (#{e}): #{e.message}") if logger
false
end
diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb
index 86c7703c27..bbbd643736 100644
--- a/activesupport/lib/active_support/cache/strategy/local_cache.rb
+++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb
@@ -18,7 +18,7 @@ module ActiveSupport
def middleware
@middleware ||= begin
klass = Class.new
- klass.class_eval(<<-EOS, __FILE__, __LINE__)
+ klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
def initialize(app)
@app = app
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index c669630e47..5a7b94ead7 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -387,7 +387,7 @@ module ActiveSupport
send("_update_#{symbol}_superclass_callbacks")
body = send("_#{symbol}_callbacks").compile(nil)
- body, line = <<-RUBY_EVAL, __LINE__
+ body, line = <<-RUBY_EVAL, __LINE__ + 1
def _run_#{symbol}_callbacks(key = nil, &blk)
if self.class.send("_update_#{symbol}_superclass_callbacks")
self.class.__define_runner(#{symbol.inspect})
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 2119322bfe..5d8e78e6e5 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -131,7 +131,7 @@ class Array
require 'builder' unless defined?(Builder)
options = options.dup
- options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)) : "records"
+ options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)).tr('/', '_') : "records"
options[:children] ||= options[:root].singularize
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
diff --git a/activesupport/lib/active_support/core_ext/module/aliasing.rb b/activesupport/lib/active_support/core_ext/module/aliasing.rb
index 3cad164148..ce481f0e84 100644
--- a/activesupport/lib/active_support/core_ext/module/aliasing.rb
+++ b/activesupport/lib/active_support/core_ext/module/aliasing.rb
@@ -61,7 +61,7 @@ class Module
# e.subject = "Megastars"
# e.title # => "Megastars"
def alias_attribute(new_name, old_name)
- module_eval <<-STR, __FILE__, __LINE__+1
+ module_eval <<-STR, __FILE__, __LINE__ + 1
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
diff --git a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
index 4d0198f028..28ac89dab9 100644
--- a/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
+++ b/activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
@@ -21,7 +21,7 @@ class Module
def attr_accessor_with_default(sym, default = nil, &block)
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
- module_eval(<<-EVAL, __FILE__, __LINE__)
+ module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
def #{sym}=(value) # def age=(value)
class << self; attr_reader :#{sym} end # class << self; attr_reader :age end
@#{sym} = value # @age = value
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index b73f4c2b59..40a1866428 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -126,7 +126,7 @@ class Module
%(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
end
- module_eval(<<-EOS, file, line)
+ module_eval(<<-EOS, file, line - 5)
if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}")
remove_possible_method("#{prefix}#{method}")
end
diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb
index 115b8abd4e..de76a069d6 100644
--- a/activesupport/lib/active_support/core_ext/module/synchronization.rb
+++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb
@@ -28,7 +28,7 @@ class Module
raise ArgumentError, "#{method} is already synchronized. Double synchronization is not currently supported."
end
- module_eval(<<-EOS, __FILE__, __LINE__)
+ module_eval(<<-EOS, __FILE__, __LINE__ + 1)
def #{aliased_method}_with_synchronization#{punctuation}(*args, &block) # def expire_with_synchronization(*args, &block)
#{with}.synchronize do # @@lock.synchronize do
#{aliased_method}_without_synchronization#{punctuation}(*args, &block) # expire_without_synchronization(*args, &block)
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index 3eb0bf31f8..38007fd4e7 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -19,7 +19,7 @@ module ActiveSupport #:nodoc:
# bad.explicit_checking_method "T".mb_chars.downcase.to_s
#
# The default Chars implementation assumes that the encoding of the string is UTF-8, if you want to handle different
- # encodings you can write your own multibyte string handler and configure it through
+ # encodings you can write your own multibyte string handler and configure it through
# ActiveSupport::Multibyte.proxy_class.
#
# class CharsForUTF32
@@ -458,8 +458,10 @@ module ActiveSupport #:nodoc:
end
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
- def tidy_bytes
- chars(self.class.tidy_bytes(@wrapped_string))
+ #
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP1252 or ISO-8859-1.
+ def tidy_bytes(force = false)
+ chars(self.class.tidy_bytes(@wrapped_string, force))
end
%w(lstrip rstrip strip reverse upcase downcase tidy_bytes capitalize).each do |method|
@@ -528,7 +530,7 @@ module ActiveSupport #:nodoc:
unpacked << codepoints[marker..pos-1]
marker = pos
end
- end
+ end
unpacked
end
@@ -644,33 +646,80 @@ module ActiveSupport #:nodoc:
codepoints
end
+ def tidy_byte(byte)
+ if byte < 160
+ [UCD.cp1252[byte] || byte].pack("U").unpack("C*")
+ elsif byte < 192
+ [194, byte]
+ else
+ [195, byte - 64]
+ end
+ end
+ private :tidy_byte
+
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string.
- def tidy_bytes(string)
- string.split(//u).map do |c|
- c.force_encoding(Encoding::ASCII) if c.respond_to?(:force_encoding)
-
- if !ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'].match(c)
- n = c.unpack('C')[0]
- n < 128 ? n.chr :
- n < 160 ? [UCD.cp1252[n] || n].pack('U') :
- n < 192 ? "\xC2" + n.chr : "\xC3" + (n-64).chr
+ #
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP-1252 or ISO-8859-1.
+ def tidy_bytes(string, force = false)
+ if force
+ return string.unpack("C*").map do |b|
+ tidy_byte(b)
+ end.flatten.compact.pack("C*").unpack("U*").pack("U*")
+ end
+
+ bytes = string.unpack("C*")
+ conts_expected = 0
+ last_lead = 0
+
+ bytes.each_index do |i|
+
+ byte = bytes[i]
+ is_ascii = byte < 128
+ is_cont = byte > 127 && byte < 192
+ is_lead = byte > 191 && byte < 245
+ is_unused = byte > 240
+ is_restricted = byte > 244
+
+ # Impossible or highly unlikely byte? Clean it.
+ if is_unused || is_restricted
+ bytes[i] = tidy_byte(byte)
+ elsif is_cont
+ # Not expecting contination byte? Clean up. Otherwise, now expect one less.
+ conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
else
- c
+ if conts_expected > 0
+ # Expected continuation, but got ASCII or leading? Clean backwards up to
+ # the leading byte.
+ (1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
+ conts_expected = 0
+ end
+ if is_lead
+ # Final byte is leading? Clean it.
+ if i == bytes.length - 1
+ bytes[i] = tidy_byte(bytes.last)
+ else
+ # Valid leading byte? Expect continuations determined by position of
+ # first zero bit, with max of 3.
+ conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
+ last_lead = i
+ end
+ end
end
- end.join
+ end
+ bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
end
end
protected
-
+
def translate_offset(byte_offset) #:nodoc:
return nil if byte_offset.nil?
return 0 if @wrapped_string == ''
-
+
if @wrapped_string.respond_to?(:force_encoding)
@wrapped_string = @wrapped_string.dup.force_encoding(Encoding::ASCII_8BIT)
end
-
+
begin
@wrapped_string[0...byte_offset].unpack('U*').length
rescue ArgumentError => e
diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb
index 57ead35827..e1a2866863 100644
--- a/activesupport/lib/active_support/ordered_hash.rb
+++ b/activesupport/lib/active_support/ordered_hash.rb
@@ -1,13 +1,28 @@
require 'yaml'
+YAML.add_builtin_type("omap") do |type, val|
+ ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
+end
+
# OrderedHash is namespaced to prevent conflicts with other implementations
module ActiveSupport
- # Hash is ordered in Ruby 1.9!
- if RUBY_VERSION >= '1.9'
- class OrderedHash < ::Hash #:nodoc:
+ class OrderedHash < ::Hash #:nodoc:
+ def to_yaml_type
+ "!tag:yaml.org,2002:omap"
end
- else
- class OrderedHash < Hash #:nodoc:
+
+ def to_yaml(opts = {})
+ YAML.quick_emit(self, opts) do |out|
+ out.seq(taguri, to_yaml_style) do |seq|
+ each do |k, v|
+ seq.add(k => v)
+ end
+ end
+ end
+ end
+
+ # Hash is ordered in Ruby 1.9!
+ if RUBY_VERSION < '1.9'
def initialize(*args, &block)
super
@keys = []
@@ -55,7 +70,7 @@ module ActiveSupport
end
super
end
-
+
def delete_if
super
sync_keys!
@@ -134,31 +149,10 @@ module ActiveSupport
"#<OrderedHash #{super}>"
end
- private
-
- def sync_keys!
- @keys.delete_if {|k| !has_key?(k)}
- end
- end
- end
-
- class OrderedHash #:nodoc:
- def to_yaml_type
- "!tag:yaml.org,2002:omap"
- end
-
- def to_yaml(opts = {})
- YAML.quick_emit(self, opts) do |out|
- out.seq(taguri, to_yaml_style) do |seq|
- each do |k, v|
- seq.add(k => v)
- end
+ private
+ def sync_keys!
+ @keys.delete_if {|k| !has_key?(k)}
end
- end
end
end
-
- YAML.add_builtin_type("omap") do |type, val|
- ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
- end
end
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 3ce11e59d2..538a8b87c6 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -3,7 +3,7 @@ module ActiveSupport
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/activesupport/test/clean_backtrace_test.rb b/activesupport/test/clean_backtrace_test.rb
index ddbc258df1..86838a7f9a 100644
--- a/activesupport/test/clean_backtrace_test.rb
+++ b/activesupport/test/clean_backtrace_test.rb
@@ -9,6 +9,11 @@ class BacktraceCleanerFilterTest < ActiveSupport::TestCase
test "backtrace should not contain prefix when it has been filtered out" do
assert_equal "/my/class.rb", @bc.clean([ "/my/prefix/my/class.rb" ]).first
end
+
+ test "backtrace cleaner should allow removing filters" do
+ @bc.remove_filters!
+ assert_equal "/my/prefix/my/class.rb", @bc.clean(["/my/prefix/my/class.rb"]).first
+ end
test "backtrace should contain unaltered lines if they dont match a filter" do
assert_equal "/my/other_prefix/my/class.rb", @bc.clean([ "/my/other_prefix/my/class.rb" ]).first
@@ -44,4 +49,4 @@ class BacktraceCleanerFilterAndSilencerTest < ActiveSupport::TestCase
test "backtrace should not silence lines that has first had their silence hook filtered out" do
assert_equal [ "/class.rb" ], @bc.clean([ "/mongrel/class.rb" ])
end
-end \ No newline at end of file
+end
diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb
index 0e489c10e1..1b8d13c024 100644
--- a/activesupport/test/multibyte_chars_test.rb
+++ b/activesupport/test/multibyte_chars_test.rb
@@ -107,7 +107,7 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase
# Ruby 1.9 only supports basic whitespace
@whitespace = "\n\t ".force_encoding(Encoding::UTF_8)
end
-
+
@byte_order_mark = [65279].pack('U')
end
@@ -468,14 +468,6 @@ end
class MultibyteCharsExtrasTest < Test::Unit::TestCase
include MultibyteTestHelpers
- if RUBY_VERSION >= '1.9'
- def test_tidy_bytes_is_broken_on_1_9_0
- assert_raise(ArgumentError) do
- assert_equal_codepoints [0xfffd].pack('U'), chars("\xef\xbf\xbd").tidy_bytes
- end
- end
- end
-
def test_upcase_should_be_unicode_aware
assert_equal "АБВГД\0F", chars("аБвгд\0f").upcase
assert_equal 'こにちわ', chars('こにちわ').upcase
@@ -504,7 +496,7 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
def test_limit_should_work_on_a_multibyte_string
example = chars(UNICODE_STRING)
bytesize = UNICODE_STRING.respond_to?(:bytesize) ? UNICODE_STRING.bytesize : UNICODE_STRING.size
-
+
assert_equal UNICODE_STRING, example.limit(bytesize)
assert_equal '', example.limit(0)
assert_equal '', example.limit(1)
@@ -531,7 +523,7 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
assert example.limit(limit).to_s.length <= limit
end
end
-
+
def test_composition_exclusion_is_set_up_properly
# Normalization of DEVANAGARI LETTER QA breaks when composition exclusion isn't used correctly
qa = [0x915, 0x93c].pack('U*')
@@ -607,28 +599,57 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase
end
def test_tidy_bytes_should_tidy_bytes
+
+ single_byte_cases = {
+ "\x21" => "!", # Valid ASCII byte, low
+ "\x41" => "A", # Valid ASCII byte, mid
+ "\x7E" => "~", # Valid ASCII byte, high
+ "\x80" => "€", # Continuation byte, low (cp125)
+ "\x94" => "”", # Continuation byte, mid (cp125)
+ "\x9F" => "Ÿ", # Continuation byte, high (cp125)
+ "\xC0" => "À", # Overlong encoding, start of 2-byte sequence, but codepoint < 128
+ "\xC1" => "Á", # Overlong encoding, start of 2-byte sequence, but codepoint < 128
+ "\xC2" => "Â", # Start of 2-byte sequence, low
+ "\xC8" => "È", # Start of 2-byte sequence, mid
+ "\xDF" => "ß", # Start of 2-byte sequence, high
+ "\xE0" => "à", # Start of 3-byte sequence, low
+ "\xE8" => "è", # Start of 3-byte sequence, mid
+ "\xEF" => "ï", # Start of 3-byte sequence, high
+ "\xF0" => "ð", # Start of 4-byte sequence
+ "\xF1" => "ñ", # Unused byte
+ "\xFF" => "ÿ", # Restricted byte
+ "\x00" => "\x00" # null char
+ }
+
+ single_byte_cases.each do |bad, good|
+ assert_equal good, chars(bad).tidy_bytes.to_s
+ assert_equal "#{good}#{good}", chars("#{bad}#{bad}").tidy_bytes
+ assert_equal "#{good}#{good}#{good}", chars("#{bad}#{bad}#{bad}").tidy_bytes
+ assert_equal "#{good}a", chars("#{bad}a").tidy_bytes
+ assert_equal "#{good}á", chars("#{bad}á").tidy_bytes
+ assert_equal "a#{good}a", chars("a#{bad}a").tidy_bytes
+ assert_equal "á#{good}á", chars("á#{bad}á").tidy_bytes
+ assert_equal "a#{good}", chars("a#{bad}").tidy_bytes
+ assert_equal "á#{good}", chars("á#{bad}").tidy_bytes
+ end
+
byte_string = "\270\236\010\210\245"
tidy_string = [0xb8, 0x17e, 0x8, 0x2c6, 0xa5].pack('U*')
- ascii_padding = 'aa'
- utf8_padding = 'éé'
-
assert_equal_codepoints tidy_string, chars(byte_string).tidy_bytes
-
- assert_equal_codepoints ascii_padding.dup.insert(1, tidy_string),
- chars(ascii_padding.dup.insert(1, byte_string)).tidy_bytes
- assert_equal_codepoints utf8_padding.dup.insert(2, tidy_string),
- chars(utf8_padding.dup.insert(2, byte_string)).tidy_bytes
assert_nothing_raised { chars(byte_string).tidy_bytes.to_s.unpack('U*') }
- assert_equal_codepoints "\xC3\xA7", chars("\xE7").tidy_bytes # iso_8859_1: small c cedilla
- assert_equal_codepoints "\xE2\x80\x9C", chars("\x93").tidy_bytes # win_1252: left smart quote
- assert_equal_codepoints "\xE2\x82\xAC", chars("\x80").tidy_bytes # win_1252: euro
- assert_equal_codepoints "\x00", chars("\x00").tidy_bytes # null char
- assert_equal_codepoints [0xfffd].pack('U'), chars("\xef\xbf\xbd").tidy_bytes # invalid char
- rescue ArgumentError => e
- raise e if RUBY_VERSION < '1.9'
+ # UTF-8 leading byte followed by too few continuation bytes
+ assert_equal_codepoints "\xc3\xb0\xc2\xa5\xc2\xa4\x21", chars("\xf0\xa5\xa4\x21").tidy_bytes
+ end
+
+ def test_tidy_bytes_should_forcibly_tidy_bytes_if_specified
+ byte_string = "\xF0\xA5\xA4\xA4" # valid as both CP-1252 and UTF-8, but with different interpretations.
+ assert_not_equal "𥤤", chars(byte_string).tidy_bytes
+ # Forcible conversion to UTF-8
+ assert_equal "𥤤", chars(byte_string).tidy_bytes(true)
end
+
private
def string_from_classes(classes)
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 261333628d..82684e4614 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,3 @@
-* A new application now comes with a layout and a stylesheet. [JV]
-
* Renamed config.cookie_secret to config.secret_token and pass it as env key. [JV]
*Rails 3.0.0 [beta 2] (April 1st, 2010)*
diff --git a/railties/guides/assets/stylesheets/main.css b/railties/guides/assets/stylesheets/main.css
index 7ccae2c87e..bab0b7a9d9 100644
--- a/railties/guides/assets/stylesheets/main.css
+++ b/railties/guides/assets/stylesheets/main.css
@@ -437,7 +437,7 @@ div.code_container, div.important, div.caution, div.warning, div.note, div.info
/* Remove bottom margin of paragraphs in special boxes, otherwise they get a
spurious blank area below with the box background. */
div.important p, div.caution p, div.warning p, div.note p, div.info p {
- margin-bottom: 0px;
+ margin-bottom: 1em;
}
/* Edge Badge
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index cf71e700dc..32b1e30502 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -2,180 +2,111 @@ h2. Rails Routing from the Outside In
This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to:
-* Understand the purpose of routing
-* Decipher the code in +routes.rb+
-* Construct your own routes, using either the @match@ method or the preferred RESTful style
-* Identify how a route will map to a controller and action
+* Understand the code in +routes.rb+
+* Construct your own routes, using either the preferred resourceful style or with the @match@ method
+* Identify what parameters to expect an action to receive
+* Automatically create URLs using route helpers
+* Use advanced techniques such as constraints and Rack endpoints
endprologue.
-h3. The Dual Purpose of Routing
+h3. The Purpose of the Rails Router
-Rails routing is a two-way piece of machinery - rather as if you could turn trees into paper, and then turn paper back into trees. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings.
+The Rails router recognizes URLs and dispatches them to a controller's action. It can also generate URLs, avoiding the need to hardcode URL strings in your views.
h4. Connecting URLs to Code
-When your Rails application receives an incoming HTTP request, say
+When your Rails application receives an incoming request
-<pre>
+<plain>
GET /patients/17
-</pre>
-
-the routing engine within Rails is the piece of code that dispatches the request to the appropriate spot in your application. In this case, the application would most likely end up running the +show+ action within the +patients+ controller, displaying the details of the patient whose ID is 17.
-
-h4. Generating URLs from Code
+</plain>
-Routing also works in reverse. If your application contains this code:
+it asks the router to match it to a controller action. If the first matching route is
<ruby>
-@patient = Patient.find(17)
+match "/patients/:id" => "patients#show"
</ruby>
-<erb>
-<%= link_to "Patient Record", patient_path(@patient) %>
-</erb>
-
-Then the routing engine is the piece that translates that to a link to a URL such as +http://example.com/patients/17+. By using routing in this way, you can reduce the brittleness of your application as compared to one with hard-coded URLs, and make your code easier to read and understand.
-
-NOTE: Patient needs to be declared as a Restful resource for this style of translation to be available.
+the request is dispatched to the +patients+ controller's +show+ action with <tt>{ :id => "17" }</tt> in +params+.
-h3. Quick Tour of +routes.rb+
-
-There are two components to routing in Rails: the routing engine itself, which is supplied as part of Rails, and the file +config/routes.rb+, which contains the actual routes that will be used by your application. Learning exactly what you can put in +routes.rb+ is the main topic of this guide, but before we dig in let's get a quick overview.
-
-h4. Processing the File
-
-In format, +routes.rb+ is nothing more than one big block sent to +ApplicationName::Application.routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file:
-
-* RESTful Routes
-* Named Routes
-* Nested Routes
-* Regular Routes
-* Default Routes
-
-Each of these types of route is covered in more detail later in this guide.
-
-The +routes.rb+ file is processed from top to bottom when a request comes in. The request will be dispatched to the first matching route, and then proceeds to the next. If there is no matching route, then Rails returns HTTP status 404 to the caller.
-
-h4. RESTful Routes
-
-RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information with a single declaration. A RESTful route looks like this:
-
-<ruby>
-resources :books
-</ruby>
-
-h4(#quick-tour-named-routes). Named Routes
-
-Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route:
-
-<ruby>
-match 'login' => 'sessions#new', :as => 'login'
-</ruby>
+h4. Generating URLs from Code
-If you're coming from Rails 2, this route will be equivalent to:
+You can also generate routes. If your application contains this code:
<ruby>
-map.login '/login', :controller => 'sessions', :action => 'new'
+@patient = Patient.find(17)
</ruby>
-You will also notice that +sessions#new+ is a shorthand for +:controller => 'sessions', :action => 'new'+. By declaring a named route such as this, you can use +login_path+ or +login_url+ in your controllers and views to generate the URLs for this route. A RESTful generates named routes without the need to explicitly generate a named route via +as+ key.
+<erb>
+<%= link_to "Patient Record", patients_path(@patient.id) %>
+</erb>
-h4. Nested Routes
+The router will generate the path +/patients/17+. This reduces the brittleness of your view and makes your code easier to understand.
-Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration:
+h3. Resource Routing: the Rails Default
-<ruby>
-resources :assemblies do
- resources :parts
-end
-</ruby>
+Resource routing allows you to quickly declare all of the common routes for a given resourceful controller. Instead of declaring separate routes for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+ actions, a resourceful route declares them in a single line of code.
-h4(#quick-tour-regular-routes). Regular Routes
+h4. Resources on the Web
-In many applications, you'll also see non-RESTful routing, which explicitly connects the parts of a URL to a particular action. For example,
+Browsers request pages from Rails by making a request for a URL using a specific HTTP method, such as +GET+, +POST+, +PUT+ and +DELETE+. Each method is a request to perform an operation on the resource. A resource route maps a number of related request to the actions in a single controller.
-<ruby>
-match 'parts/:number' => 'inventory#show'
-</ruby>
+When your Rails application receives an incoming request for
-h4. Default Routes
+<plain>
+DELETE /photos/17
+</plain>
-The default route is a safety net that catches otherwise-unrouted requests. Many Rails applications will contain this default route:
+it asks the router to map it to a controller action. If the first matching route is
<ruby>
-match ':controller(/:action(/:id(.:format)))'
+resources :photos
</ruby>
-In Rails 3, this route is commented out advising to use RESTful routes as much as possible. So if you're using RESTful routing for everything in your application, you will probably want to leave it like that.
-
-h3. RESTful Routing: the Rails Default
-
-RESTful routing is the current standard for routing in Rails, and it's the one that you should prefer for new applications. It can take a little while to understand how RESTful routing works, but it's worth the effort; your code will be easier to read and you'll be working with Rails, rather than fighting against it, when you use this style of routing.
-
-h4. What is REST?
-
-The foundation of RESTful routing is generally considered to be Roy Fielding's doctoral thesis, "Architectural Styles and the Design of Network-based Software Architectures":http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes:
-
-* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources
-* Transferring representations of the state of that resource between system components.
-
-For example, to a Rails application a request such as this:
-
-<pre>
-DELETE /photos/17
-</pre>
-
-would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities.
+Rails would dispatch that request to the +destroy+ method on the +photos+ controller with <tt>{ :id => "17" }</tt> in +params+.
h4. CRUD, Verbs, and Actions
-In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as
+In Rails, a resourceful route provides a mapping between HTTP verbs and URLs and controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as
<ruby>
resources :photos
</ruby>
-creates seven different routes in your application:
+creates seven different routes in your application, all mapping to the +Photos+ controller:
-|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
-|GET |/photos |Photos |index |display a list of all photos|
-|GET |/photos/new |Photos |new |return an HTML form for creating a new photo|
-|POST |/photos |Photos |create |create a new photo|
-|GET |/photos/1 |Photos |show |display a specific photo|
-|GET |/photos/1/edit |Photos |edit |return an HTML form for editing a photo|
-|PUT |/photos/1 |Photos |update |update a specific photo|
-|DELETE |/photos/1 |Photos |destroy |delete a specific photo|
-
-For the specific routes (those that reference just a single resource), the identifier for the resource will be available within the corresponding controller action as +params[:id]+.
+|_. Verb |_.URL |_.action |_.used for|
+|GET |/photos |index |display a list of all photos|
+|GET |/photos/new |new |return an HTML form for creating a new photo|
+|POST |/photos |create |create a new photo|
+|GET |/photos/:id |show |display a specific photo|
+|GET |/photos/:id/edit |edit |return an HTML form for editing a photo|
+|PUT |/photos/:id |update |update a specific photo|
+|DELETE |/photos/:id |destroy |delete a specific photo|
h4. URLs and Paths
-Creating a RESTful route will also make available a pile of helpers within your application, something that requires explicit mention otherwise:
+Creating a resourceful route will also expose a number of helpers to the controllers in your application. In the case of +resources :photos+:
-* +photos_url+ and +photos_path+ map to the path for the index and create actions
-* +new_photo_url+ and +new_photo_path+ map to the path for the new action
-* +edit_photo_url+ and +edit_photo_path+ map to the path for the edit action
-* +photo_url+ and +photo_path+ map to the path for the show, update, and destroy actions
+* +photos_path+ returns +/photos+
+* +new_photo_path+ returns +/photos/new+
+* +edit_photo_path+ returns +/photos/edit+
+* +photo_path(id)+ returns +/photos/:id+ (for instance, +photo_path(10)+ returns +/photos/10+)
-NOTE: Because routing makes use of the HTTP verb as well as the path in the request to dispatch requests, the seven routes generated by a RESTful routing entry only give rise to four pairs of helpers.
+Each of these helpers has a corresponding +_url+ helper (such as +photos_url+) which returns the same path prefixed with the current host, port and path prefix.
-In each case, the +_url+ helper generates a string containing the entire URL that the application will understand, while the +_path+ helper generates a string containing the relative path from the root of the application. For example:
-
-<ruby>
-photos_url # => "http://www.example.com/photos"
-photos_path # => "/photos"
-</ruby>
+NOTE: Because the router uses the HTTP verb and URL to match inbound requests, four URLs map to seven different actions.
h4. Defining Multiple Resources at the Same Time
-If you need to create routes for more than one RESTful resource, you can save a bit of typing by defining them all with a single call to +resources+:
+If you need to create routes for more than one resource, you can save a bit of typing by defining them all with a single call to +resources+:
<ruby>
resources :photos, :books, :videos
</ruby>
-This has exactly the same effect as
+This works exactly the same as
<ruby>
resources :photos
@@ -185,205 +116,91 @@ resources :videos
h4. Singular Resources
-You can also apply RESTful routing to singleton resources within your application. In this case, you use +resource+ instead of +resources+ and the route generation is slightly different. For example, a routing entry of
+Sometimes, you have a resource that clients always look up without referencing an ID. A common example, +/profile+ always shows the profile of the currently logged in user. In this case, you can use a singular resource to map +/profile+ (rather than +/profile/:id+) to the +show+ action.
<ruby>
resource :geocoder
</ruby>
-creates six different routes in your application:
-
-|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
-|GET |/geocoder/new |Geocoders |new |return an HTML form for creating the new geocoder|
-|POST |/geocoder |Geocoders |create |create the new geocoder|
-|GET |/geocoder |Geocoders |show |display the one and only geocoder resource|
-|GET |/geocoder/edit |Geocoders |edit |return an HTML form for editing the geocoder|
-|PUT |/geocoder |Geocoders |update |update the one and only geocoder resource|
-|DELETE |/geocoder |Geocoders |destroy |delete the geocoder resource|
-
-NOTE: Even though the name of the resource is singular in +routes.rb+, the matching controller is still plural.
-
-A singular RESTful route generates an abbreviated set of helpers:
-
-* +new_geocoder_url+ and +new_geocoder_path+ map to the path for the new action
-* +edit_geocoder_url+ and +edit_geocoder_path+ map to the path for the edit action
-* +geocoder_url+ and +geocoder_path+ map to the path for the create, show, update, and destroy actions
-
-h4. Customizing Resources
+creates six different routes in your application, all mapping to the +Geocoders+ controller:
-Although the conventions of RESTful routing are likely to be sufficient for many applications, there are a number of ways to customize the way that RESTful routes work. These options include:
+|_. Verb |_.URL |_.action |_.used for|
+|GET |/geocoder/new |new |return an HTML form for creating the geocoder|
+|POST |/geocoder |create |create the new geocoder|
+|GET |/geocoder |show |display the one and only geocoder resource|
+|GET |/geocoder/edit |edit |return an HTML form for editing the geocoder|
+|PUT |/geocoder |update |update the one and only geocoder resource|
+|DELETE |/geocoder |destroy |delete the geocoder resource|
-* +:controller+
-* +:singular+
-* +:constraints+
-* +:as+
-* +:path_names+
-* +:only+
-* +:except+
+NOTE: Because you might want to use the same controller for a singular route (+/account+) and a plural route (+/accounts/45+), singular resources map to plural controllers.
-You can also add additional routes via the +member+ and +collection+ blocks, which are discussed later in this guide.
+A singular resourceful route generates these helpers:
-h5. Using +:controller+
+* +new_geocoder_path+ returns +/geocoder/new+
+* +edit_geocoder_path+ returns +/geocoder/edit+
+* +geocoder_path+ returns +/geocoder+
-The +:controller+ option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry:
-
-<ruby>
-resources :photos, :controller => "images"
-</ruby>
-
-will recognize incoming URLs containing +photo+ but route the requests to the Images controller:
-
-|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
-|GET |/photos |Images |index |display a list of all images|
-|GET |/photos/new |Images |new |return an HTML form for creating a new image|
-|POST |/photos |Images |create |create a new image|
-|GET |/photos/1 |Images |show |display a specific image|
-|GET |/photos/1/edit |Images |edit |return an HTML form for editing an image|
-|PUT |/photos/1 |Images |update |update a specific image|
-|DELETE |/photos/1 |Images |destroy |delete a specific image|
-
-NOTE: The helpers will be generated with the name of the resource, not the name of the controller. So in this case, you'd still get +photos_path+, +new_photo_path+, and so on.
+As with plural resources, the same helpers ending in +_url+ will also include the host, port and path prefix.
h4. Controller Namespaces and Routing
-Rails allows you to group your controllers into namespaces by saving them in folders underneath +app/controllers+. The +:controller+ option provides a convenient way to use these routes. For example, you might have a resource whose controller is purely for admin users in the +admin+ folder:
+You may wish to organize groups of controllers under a namespace. Most commonly, you might group a number of administrative controllers under an +Admin::+ namespace. You would place these controllers under the +app/controllers/admin+ directory, and you can group them together in your router:
<ruby>
-resources :photos, :controller => "admin/photos"
-</ruby>
-
-If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +photo_path+ helper, and you follow a link generated with +&lt;%= link_to "show", photo_path(1) %&gt;+ you will end up on the view generated by +admin/photos/show+, but you will also end up in the same place if you have +&lt;%= link_to "show", {:controller => "photos", :action => "show"} %&gt;+ because Rails will generate the show URL relative to the current URL.
-
-TIP: If you want to guarantee that a link goes to a top-level controller, use a preceding slash to anchor the controller name: +&lt;%= link_to "show", {:controller => "/photos", :action => "show"} %&gt;+
-
-You can also specify a controller namespace with the +namespace+ method instead of a path. This can be especially useful when mapping multiple namespaced routes together:
-
-<ruby>
-namespace :admin do
- resources :photos, :videos
+namespace "admin" do
+ resources :posts, :comments
end
</ruby>
-That would give you routing for +admin/photos+ and +admin/videos+ controllers.
-
-The difference between generating routes through +namespace+ and the +:controller+ key is that the +namespace+ will add +admin+ to the generated helpers as well, so the above route generates +admin_photos_path+.
-
-h5. Using +:singular+
-
-If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the +:singular+ option:
-
-<ruby>
-resources :teeth, :singular => "tooth"
-</ruby>
-
-TIP: Depending on the other code in your application, you may prefer to add additional rules to the +Inflector+ class instead.
-
-h5. Using +:constraints+
-
-You can use the +:constraints+ option in a RESTful route to impose a format on the implied parameter in routes. For example:
-
-<ruby>
-resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/}
-</ruby>
-
-This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, +/photos/1+ would no longer be recognized by this route, but +/photos/RR27+ would.
-
-h5. Using +:as+
-
-The +:as+ option lets you override the normal naming for the actual generated paths. For example:
-
-<ruby>
-resources :photos, :as => "images"
-</ruby>
-
-will recognize incoming URLs containing +image+ but route the requests to the Photos controller:
-
-|_.HTTP verb|_.URL |_.controller|_.action |_:used for|
-|GET |/images |Photos |index |display a list of all photos|
-|GET |/images/new |Photos |new |return an HTML form for creating a new photo|
-|POST |/images |Photos |create |create a new photo|
-|GET |/images/1 |Photos |show |display a specific photo|
-|GET |/images/1/edit |Photos |edit |return an HTML form for editing a photo|
-|PUT |/images/1 |Photos |update |update a specific photo|
-|DELETE |/images/1 |Photos |destroy |delete a specific photo|
+This will create a number of routes for each of the +posts+ and +comments+ controller. For +Admin::PostsController+, Rails will create:
-NOTE: The helpers will be generated with the name of the resource, not the path name. So in this case, you'd still get +photos_path+, +new_photo_path+, and so on.
+|_. Verb |_.URL |_.action |_. helper |
+|GET |/admin/photos |index | admin_photos_path |
+|GET |/admin/photos/new |new | new_admin_photos_path |
+|POST |/admin/photos |create | admin_photos_path |
+|GET |/admin/photos/1 |show | admin_photo_path(id) |
+|GET |/admin/photos/1/edit |edit | edit_admin_photo_path(id) |
+|PUT |/admin/photos/1 |update | admin_photo_path(id) |
+|DELETE |/admin/photos/1 |destroy | admin_photo_path(id) |
-h5. Using +:path_names+
-
-The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs:
+If you want to route +/photos+ (without the prefix +/admin+) to +Admin::PostsController+, you could use
<ruby>
-resources :photos, :path_names => { :new => 'make', :edit => 'change' }
-</ruby>
-
-This would cause the routing to recognize URLs such as
-
-<pre>
-/photos/make
-/photos/1/change
-</pre>
-
-NOTE: The actual action names aren't changed by this option; the two URLs shown would still route to the new and edit actions.
-
-TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can set a default in your environment:
-
-<ruby>
-config.action_controller.resources_path_names = { :new => 'make', :edit => 'change' }
+scope :module => "admin" do
+ resources :posts, :comments
+end
</ruby>
-h5. Using +:name_prefix+
-
-You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example:
+or, for a single case
<ruby>
-resources :photos :name_prefix => 'photographer'
+resources :posts, :module => "admin"
</ruby>
-This combination will give you route helpers such as +photographer_photos_path+ to use in your code.
-
-NOTE: You can also use +:name_prefix+ with non-RESTful routes.
-
-h5. Using +:only+ and +:except+
-
-By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option specifies that only certain routes should be generated:
+If you want to route +/admin/photos+ to +PostsController+ (without the +Admin::+ module prefix), you could use
<ruby>
-resources :photos, :only => [:index, :show]
-</ruby>
-
-With this declaration, a +GET+ request to +/photos+ would succeed, but a +POST+ request to +/photos+ (which would ordinarily be routed to the create action) will fail.
-
-The +:except+ option specifies a route or list of routes that should _not_ be generated:
-
-<ruby>
-resources :photos, :except => :destroy
+scope "/admin" do
+ resources :posts, :comments
+end
</ruby>
-In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/<em>id</em>+) will be generated.
-
-TIP: If your application has many RESTful routes, using +:only+ and +:except+ to generate only the routes that you actually need can cut down on memory use and speed up the routing process.
-
-h5. Changing Path Names for Resources
-
-Using +scope+, we can alter path names generated by resources:
+or, for a single case
<ruby>
-scope(:resources_path_names => { :new => "neu", :edit => "bearbeiten" }) do
- resources :categories, :path => "kategorien"
-end
+resources :posts, :path => "/admin"
</ruby>
-With +scope+ defined, it now generates routes with customized path names.
+In each of these cases, the named routes remain the same as if you did not use +scope+. In the last case, the following URLs map to +PostsController+:
-|_.HTTP verb|_.URL |_.controller |_.action |_:used for|
-|GET |/kategorien |Categories |index |display a list of all categories|
-|GET |/kategorien/neu |Categories |new |return an HTML form for creating a new category|
-|POST |/kategorien |Categories |create |create a new category|
-|GET |/kategorien/1 |Categories |show |display a specific category|
-|GET |/kategorien/:id/bearbeiten |Categories |edit |return an HTML form for editing a category|
-|PUT |/kategorien/1 |Categories |update |update a specific category|
-|DELETE |/kategorien/1 |Categories |destroy |delete a specific category|
+|_. Verb |_.URL |_.action |_. helper |
+|GET |photos |index | photos_path |
+|GET |photos/new |new | photos_path |
+|POST |photos |create | photos_path |
+|GET |photos/1 |show | photo_path(id) |
+|GET |photos/1/edit |edit | dmin_photo_path(id) |
+|PUT |photos/1 |update | photo_path(id) |
+|DELETE |photos/1 |destroy | photo_path(id) |
h4. Nested Resources
@@ -399,7 +216,7 @@ class Ad < ActiveRecord::Base
end
</ruby>
-Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration:
+Nested routes allow you to capture this relationship in your routing. In this case, you could include this route declaration:
<ruby>
resources :magazines do
@@ -407,31 +224,19 @@ resources :magazines do
end
</ruby>
-In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL:
-
-|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
-|GET |/magazines/1/ads |Ads |index |display a list of all ads for a specific magazine|
-|GET |/magazines/1/ads/new |Ads |new |return an HTML form for creating a new ad belonging to a specific magazine|
-|POST |/magazines/1/ads |Ads |create |create a new ad belonging to a specific magazine|
-|GET |/magazines/1/ads/1 |Ads |show |display a specific ad belonging to a specific magazine|
-|GET |/magazines/1/ads/1/edit |Ads |edit |return an HTML form for editing an ad belonging to a specific magazine|
-|PUT |/magazines/1/ads/1 |Ads |update |update a specific ad belonging to a specific magazine|
-|DELETE |/magazines/1/ads/1 |Ads |destroy |delete a specific ad belonging to a specific magazine|
-
+In addition to the routes for magazines, this declaration will also route ads to an +AdsController+. The ad URLs require a magazine:
-This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+.
+|_.Verb |_.URL |_.action |_.used for|
+|GET |/magazines/1/ads |index |display a list of all ads for a specific magazine|
+|GET |/magazines/1/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine|
+|POST |/magazines/1/ads |create |create a new ad belonging to a specific magazine|
+|GET |/magazines/1/ads/1 |show |display a specific ad belonging to a specific magazine|
+|GET |/magazines/1/ads/1/edit |edit |return an HTML form for editing an ad belonging to a specific magazine|
+|PUT |/magazines/1/ads/1 |update |update a specific ad belonging to a specific magazine|
+|DELETE |/magazines/1/ads/1 |destroy |delete a specific ad belonging to a specific magazine|
-h5(#nested-name-prefix). Using +:name_prefix+
-
-The +:name_prefix+ option overrides the automatically-generated prefix in nested route helpers. For example,
-
-<ruby>
-resources :magazines do
- resources :ads, :name_prefix => 'periodical'
-end
-</ruby>
-This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+.
+This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+. These helpers take an instance of Magazine as the first parameter (+magazine_ads_url(@magazine)+).
h5. Limits to Nesting
@@ -455,33 +260,9 @@ The corresponding route helper would be +publisher_magazine_photo_url+, requirin
TIP: _Resources should never be nested more than 1 level deep._
-h5. Shallow Nesting
+h4. Creating URLs From Objects
-The +:shallow+ option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an +:id+ parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes:
-
-<ruby>
-resources :publishers, :shallow => true do
- resources :magazines do
- resources :photos
- end
-end
-</ruby>
-
-This will enable recognition of (among others) these routes:
-
-<pre>
-/publishers/1 ==> publisher_path(1)
-/publishers/1/magazines ==> publisher_magazines_path(1)
-/magazines/2 ==> magazine_path(2)
-/magazines/2/photos ==> magazines_photos_path(2)
-/photos/3 ==> photo_path(3)
-</pre>
-
-With shallow nesting, you need only supply enough information to uniquely identify the resource that you want to work with.
-
-h4. Route Generation from Arrays
-
-In addition to using the generated routing helpers, Rails can also generate RESTful routes from an array of parameters. For example, suppose you have a set of routes generated with these entries in routes.rb:
+In addition to using the routing helpers, Rails can also create URLs from an array of parameters. For example, suppose you have this set of routes:
<ruby>
resources :magazines do
@@ -489,49 +270,35 @@ resources :magazines do
end
</ruby>
-Rails will generate helpers such as magazine_ad_path that you can use in building links:
+When using +magazine_ad_path+, you can pass in instances of +Magazine+ and +Ad+ instead of the numeric IDs.
-<ruby>
+<erb>
<%= link_to "Ad details", magazine_ad_path(@magazine, @ad) %>
-</ruby>
-
-Another way to refer to the same route is with an array of objects:
-
-<ruby>
-<%= link_to "Ad details", [@magazine, @ad] %>
-</ruby>
+</erb>
-This format is especially useful when you might not know until runtime which of several types of object will be used in a particular link.
+You can also use +url_for+ with a set of objects, and Rails will automatically determine which route you want:
-h4. Namespaced Resources
+<erb>
+<%= link_to "Ad details", url_for(@magazine, @ad) %>
+</erb>
-It's possible to do some quite complex things by combining +scope+ and +:name_prefix+. For example, you can use the combination of these two options to move administrative resources to their own folder in your application:
+In this case, Rails will see that +@magazine+ is a +Magazine+ and +@ad+ is an +Ad+ and will therefore use the +magazine_ad_path+ helper. In helpers like +link_to+, you can specify just the object in place of the full +url_for+ call:
-<ruby>
-scope 'admin' do
- resources :photos, :name_prefix => "admin", :controller => 'admin/photos'
- scope 'photos' do
- resources :tags, :name_prefix => 'admin_photo', :controller => 'admin/photo_tags'
- resources :ratings, :name_prefix => 'admin_photo', :controller => 'admin/photo_ratings'
- end
-end
-</ruby>
+<erb>
+<%= link_to "Ad details", [@magazine, @ad] %>
+</erb>
-The good news is that if you find yourself using this level of complexity, you can stop. Rails supports _namespaced resources_ to make placing resources in their own folder a snap. Here's the namespaced version of those same three routes:
+If you wanted to link to just a magazine, you could leave out the +Array+:
-<ruby>
-namespace :admin do
- resources :photos do
- resources :tags, :ratings
- end
-end
-</ruby>
+<erb>
+<%= link_to "Magazine details", @magazine %>
+</erb>
-As you can see, the namespaced version is much more succinct than the one that spells everything out - but it still creates the same routes. For example, you'll get +admin_photos_url+ that expects to find an +Admin::PhotosController+ and that matches +admin/photos+, and +admin_photos_ratings_path+ that matches +/admin/photos/_photo_id_/ratings+, expecting to use +Admin::RatingsController+. Even though you're not specifying +path_prefix+ explicitly, the routing code will calculate the appropriate +path_prefix+ from the route nesting.
+This allows you to treat instances of your models as URLs, and is a key advantage to using the resourceful style.
h4. Adding More RESTful Actions
-You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional member routes (those which apply to a single instance of the resource), additional new routes (those that apply to creating a new resource), or additional collection routes (those which apply to the collection of resources as a whole).
+You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional routes that apply to the collection or individual members of the collection.
h5. Adding Member Routes
@@ -545,9 +312,9 @@ resources :photos do
end
</ruby>
-This will enable Rails to recognize URLs such as +/photos/1/preview+ using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create the +preview_photo_url+ and +preview_photo_path+ route helpers.
+This will recognize +/photos/1/preview+ with GET, and route to the +preview+ action of +PhotosController+. It will also create the +preview_photo_url+ and +preview_photo_path+ helpers.
-Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use +get+, +put+, +post+, or +delete+ here. If you don't have multiple +member+ route, you can also passing +:on+ to the routing.
+Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use +get+, +put+, +post+, or +delete+ here. If you don't have multiple +member+ routes, you can also passing +:on+ to a route.
<ruby>
resources :photos do
@@ -557,7 +324,7 @@ end
h5. Adding Collection Routes
-To add a collection route, use the +:collection+ option:
+To add a route to the collection:
<ruby>
resources :photos do
@@ -567,9 +334,9 @@ resources :photos do
end
</ruby>
-This will enable Rails to recognize URLs such as +/photos/search+ using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create the +search_photos_url+ and +search_photos_path+ route helpers.
+This will enable Rails to recognize URLs such as +/photos/search+ with GET, and route to the +search+ action of +PhotosController+. It will also create the +search_photos_url+ and +search_photos_path+ route helpers.
-Just as with member routes, you can passing +:on+ to the routing.
+Just as with member routes, you can pass +:on+ to a route.
<ruby>
resources :photos do
@@ -577,24 +344,17 @@ resources :photos do
end
</ruby>
-h5. Adding New Routes
-
-As of writing, Rails 3 has deprecated +:new+ option from routing. You will need to explicit define the route using +match+ method
-
-<ruby>
-resources :photos
-match 'photos/new/upload' => 'photos#upload', :as => 'upload_new_photos'
-</ruby>
-
h5. A Note of Caution
-If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +member+ and +collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points.
+If you find yourself adding many extra actions to a resourceful route, it's time to stop and ask yourself whether you're disguising the presence of another resource.
-h3. Regular Routes
+h3. Non-Resourceful Routes
-In addition to RESTful routing, Rails supports regular routing - a way to map URLs to controllers and actions. With regular routing, you don't get the masses of routes automatically generated by RESTful routing. Instead, you must set up each route within your application separately.
+In addition to resource routing, Rails has powerful support for routing arbitrary URLs to actions. Here, you don't get groups of routes automatically generated by resourceful routing. Instead, you set up each route within your application separately.
-While RESTful routing has become the Rails standard, there are still plenty of places where the simpler regular routing works fine. You can even mix the two styles within a single application. In general, you should prefer RESTful routing _when possible_, because it will make parts of your application easier to write. But there's no need to try to shoehorn every last piece of your application into a RESTful framework if that's not a good fit.
+While you should usually use resourceful routing, there are still many places where the simpler routing is more appropriate. There's no need to try to shoehorn every last piece of your application into a resourceful framework if that's not a good fit.
+
+In particular, simple routing makes it very easy to map legacy URLs to new Rails actions.
h4. Bound Parameters
@@ -604,80 +364,121 @@ When you set up a regular route, you supply a series of symbols that Rails maps
match ':controller(/:action(/:id))'
</ruby>
-If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +Photos+ controller, and to make the final parameter (1) available as +params[:id]+. This route will also route the incoming request of +/photos+ to PhotosController, since +:action+ and +:id+ are optional parameters, denoted by parenthesis.
+If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +PhotosController+, and to make the final parameter +"1"+ available as +params[:id]+. This route will also route the incoming request of +/photos+ to +PhotosController+, since +:action+ and +:id+ are optional parameters, denoted by parentheses.
-h4. Wildcard Components
+h4. Dynamic Segments
-You can set up as many wildcard symbols within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the matching action as part of the params hash. So, if you set up this route:
+You can set up as many dynamic segments within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the action as part of +params+. If you set up this route:
<ruby>
match ':controller/:action/:id/:user_id'
</ruby>
-An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be set to 2.
+An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+.
-h4. Static Text
+h4. Static Segments
-You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests:
+You can specify static segments when creating a route.
<ruby>
match ':controller/:action/:id/with_user/:user_id'
</ruby>
-This route would respond to URLs such as +/photos/show/1/with_user/2+.
+This route would respond to URLs such as +/photos/show/1/with_user/2+. In this case, +params+ would be <tt>{ :controller => "photos", :action => "show", :id => "1", :user_id => "2" }</tt>.
-h4. Querystring Parameters
+h4. The Query String
-Rails routing automatically picks up querystring parameters and makes them available in the +params+ hash. For example, with this route:
+The +params+ will also include any parameters from the query string. For example, with this route:
<ruby>
match ':controller/:action/:id
</ruby>
-An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be equal to 2.
+An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params+ will be <tt>{ :controller => "photos", :action => "show", :id => "1", :user_id => "2" }</tt>.
h4. Defining Defaults
-You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters by putting it after +=>+:
+You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply them as defaults:
<ruby>
match 'photos/:id' => 'photos#show'
</ruby>
-With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller.
+With this route, Rails will match an incoming URL of +/photos/12+ to the +show+ action of +PhotosController+.
-You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example:
+You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that you do not specify as dynamic segments. For example:
<ruby>
match 'photos/:id' => 'photos#show', :defaults => { :format => 'jpg' }
</ruby>
-With this route, an incoming URL of +photos/12+ would be dispatched to the +show+ action within the +Photos+ controller, and +params[:format]+ will be set to +jpg+.
+Rails would match +photos/12+ to the +show+ action of +PhotosController+, and set +params[:format]+ to +"jpg"+.
-h4. Named Routes
+h4. Naming Routes
-Regular routes need not use the +connect+ method. You can use any other name here to create a _named route_. For example,
+You can specify a name for any route using the +:as+ option.
<ruby>
match 'logout' => 'sessions#destroy', :as => :logout
</ruby>
-This will do two things. First, requests to +/logout+ will be sent to the +destroy+ action of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code.
+This will create +logout_path+ and +logout_url+ as named helpers in your application. Calling +logout_path+ will return +/logout+
-h4. Route Constraints
+h4. Segment Constraints
-You can use the +:constraints+ option to enforce a format for any parameter in a route:
+You can use the +:constraints+ option to enforce a format for a dynamic segment:
<ruby>
match 'photo/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ }
</ruby>
-This route would respond to URLs such as +/photo/A12345+. You can more succinctly express the same route this way:
+This route would match URLs such as +/photo/A12345+. You can more succinctly express the same route this way:
<ruby>
match 'photo/:id' => 'photos#show', :id => /[A-Z]\d{5}/
</ruby>
+h4. Request-Based Constraints
+
+You can also constrain a route based on any method on the <a href="action_controller_overview.html#the-request-object">Request</a> object that returns a +String+.
+
+You specify a request-based constraint the same way that you specify a segment constraint:
+
+<ruby>
+match "photo", :constraints => {:subdomain => "admin"}
+</ruby>
+
+You can also specify constrains in a block form:
+
+<ruby>
+namespace "admin" do
+ constraints :subdomain => "admin" do
+ resources :photos
+ end
+end
+</ruby>
+
+h4. Advanced Constraints
+
+If you have a more advanced constraint, you can provide an object that responds to +matches?+ that Rails should use. Let's say you wanted to route all users on a blacklist to the +BlacklistController+. You could do:
+
+<ruby>
+class BlacklistConstraint
+ def initialize
+ @ips = Blacklist.retrieve_ips
+ end
+
+ def matches?(request)
+ @ips.include?(request.remote_ip)
+ end
+end
+
+TwitterClone::Application.routes.draw do
+ match "*path" => "blacklist#index",
+ :constraints => BlacklistConstraint.new
+end
+</ruby>
+
h4. Route Globbing
Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example
@@ -686,89 +487,244 @@ Route globbing is a way to specify that a particular parameter should be matched
match 'photo/*other' => 'photos#unknown'
</ruby>
-This route would match +photo/12+ or +/photo/long/path/to/12+ equally well, creating an array of path segments as the value of +params[:other]+.
+This route would match +photo/12+ or +/photo/long/path/to/12+, setting +params[:other]+ to +"12"+ or +"long/path/to/12"+.
+
+h4. Redirection
+
+You can redirect any path to another path using the +redirect+ helper in your router:
+
+<ruby>
+match "/stories" => redirect("/posts")
+</ruby>
+
+You can also reuse dynamic segments from the match in the path to redirect to:
+
+<ruby>
+match "/stories/:name" => redirect("/posts/%{name}")
+</ruby>
+
+You can also provide a block to redirect, which receives the params and (optionally) the request object:
-h3. Formats and +respond_to+
+<ruby>
+match "/stories/:name" => redirect {|params| "/posts/#{params[:name].pluralize}" }
+match "/stories" => redirect {|p, req| "/posts/#{req.subdomain}" }
+</ruby>
+
+In all of these cases, if you don't provide the leading host (+http://www.example.com+), Rails will take those details from the current request.
-There's one more way in which routing can do different things depending on differences in the incoming HTTP request: by issuing a response that corresponds to what the request specifies that it will accept. In Rails routing, you can control this with the special +:format+ parameter in the route.
+h4. Routing to Rack Applications
-For instance, consider the second of the default routes in the boilerplate +routes.rb+ file:
+Instead of a String, like +"posts#index"+, which corresponds to the +index+ action in the +PostsController+, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher.
<ruby>
-match ':controller(/:action(/:id(.:format)))'
+match "/application.js" => Sprockets
</ruby>
-This route matches requests such as +/photo/edit/1.xml+ or +/photo/show/2.rss+. Within the appropriate action code, you can issue different responses depending on the requested format:
+As long as +Sprockets+ responds to +call+ and returns a <tt>[status, headers, body]</tt>, the router won't know the difference between the Rack application and an action.
+
+NOTE: For the curious, +"posts#index"+ actually expands out to +PostsController.action(:index)+, which returns a valid Rack application.
+
+h4. Using +root+
+
+You can specify what Rails should route +"/"+ to with the +root+ method:
<ruby>
-respond_to do |format|
- format.html # return the default template for HTML
- format.xml { render :xml => @photo.to_xml }
+root :to => 'pages#main'
+</ruby>
+
+You should put the +root+ route at the end of the file.
+
+h3. Customizing Resourceful Routes
+
+While the default routes and helpers generated by +resources :posts+ will usually serve you well, you may want to customize them in some way. Rails allows you to customize virtually any generic part of the resourceful helpers.
+
+h4. Specifying a Controller to Use
+
+The +:controller+ option lets you explicitly specify a controller to use for the resource. For example:
+
+<ruby>
+resources :photos, :controller => "images"
+</ruby>
+
+will recognize incoming URLs beginning with +/photo+ but route to the +Images+ controller:
+
+|_. Verb |_.URL |_.action |
+|GET |/photos |index |
+|GET |/photos/new |new |
+|POST |/photos |create |
+|GET |/photos/1 |show |
+|GET |/photos/1/edit |edit |
+|PUT |/photos/1 |update |
+|DELETE |/photos/1 |destroy |
+
+NOTE: Use +photos_path+, +new_photos_path+, etc. to generate URLs for this resource.
+
+h4. Specifying Constraints
+
+You can use the +:constraints+ option to specify a required format on the implicit +id+. For example:
+
+<ruby>
+resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/}
+</ruby>
+
+This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, the router would no longer match +/photos/1+ to this route. Instead, +/photos/RR27+ would match.
+
+You can specify a single constraint to a apply to a number of routes by using the block form:
+
+<ruby>
+constraints(:id => /[A-Z][A-Z][0-9]+/) do
+ resources :photos
+ resources :accounts
+end
+</ruby>
+
+NOTE: Of course, you can use the more advanced constraints available in non-resourceful routes in this context
+
+h4. Overriding the Named Helpers
+
+The +:as+ option lets you override the normal naming for the named route helpers. For example:
+
+<ruby>
+resources :photos, :as => "images"
+</ruby>
+
+will recognize incoming URLs beginning with +/photos+ and route the requests to +PhotosController+:
+
+|_.HTTP verb|_.URL |_.action |_.named helper |
+|GET |/photos |index | images_path_ |
+|GET |/photos/new |new | new_image_path |
+|POST |/photos |create | images_path |
+|GET |/photos/1 |show | image_path |
+|GET |/photos/1/edit |edit | edit_image_path |
+|PUT |/photos/1 |update | image_path |
+|DELETE |/photos/1 |destroy | image_path |
+
+h4. Overriding the +new+ and +edit+ Segments
+
+The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs:
+
+<ruby>
+resources :photos, :path_names => { :new => 'make', :edit => 'change' }
+</ruby>
+
+This would cause the routing to recognize URLs such as
+
+<pre>
+/photos/make
+/photos/1/change
+</pre>
+
+NOTE: The actual action names aren't changed by this option. The two URLs shown would still route to the new and edit actions.
+
+TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can use a scope:
+
+<ruby>
+scope :path_names => { :new => "make" } do
+ # rest of your routes
end
</ruby>
-h4. Specifying the Format with an HTTP Header
+h4. Overriding the Named Helper Prefix
-If there is no +:format+ parameter in the route, Rails will automatically look at the HTTP Accept header to determine the desired format.
+You can use the :name_prefix option to add a prefix to the named route helpers that Rails generates for a route. You can use this option to prevent collisions between routes using a path scope.
-h4. Recognized MIME types
+<ruby>
+scope "admin" do
+ resources :photos, :name_prefix => "admin"
+end
-By default, Rails recognizes +html+, +text+, +json+, +csv+, +xml+, +rss+, +atom+, and +yaml+ as acceptable response types. If you need types beyond this, you can register them in your environment:
+resources :photos
+</ruby>
+
+This will provide route helpers such as +photographer_photos_path+.
+
+You could specify a name prefix to use for a group of routes in the scope:
<ruby>
-Mime::Type.register "image/jpg", :jpg
+scope "admin", :name_prefix => "admin" do
+ resources :photos, :accounts
+end
+
+resources :photos, :accounts
</ruby>
-h3. The Default Routes
+NOTE: The +namespace+ scope will automatically add a +:name_prefix+ as well as +:module+ and +:path+ prefixes.
+
+h4. Restricting the Routes Created
-When you create a new Rails application, +routes.rb+ is initialized with a default route:
+By default, Rails creates routes for all seven of the default actions (index, show, new, create, edit, update, and destroy) for every RESTful route in your application. You can use the +:only+ and +:except+ options to fine-tune this behavior. The +:only+ option tells Rails to create only the specified routes:
<ruby>
-match ':controller(/:action(/:id(.:format)))'
+resources :photos, :only => [:index, :show]
</ruby>
-These routes provide reasonable defaults for many URLs, if you're not using RESTful routing.
+Now, a +GET+ request to +/photos+ would succeed, but a +POST+ request to +/photos+ (which would ordinarily be routed to the +create+ action) will fail.
+
+The +:except+ option specifies a route or list of routes that Rails should _not_ create:
-NOTE: The default routes will make every action of every controller in your application accessible to GET requests. If you've designed your application to make consistent use of RESTful and named routes, you should comment out the default routes to prevent access to your controllers through the wrong verbs. If you've had the default routes enabled during development, though, you need to be sure that you haven't unwittingly depended on them somewhere in your application - otherwise you may find mysterious failures when you disable them.
+<ruby>
+resources :photos, :except => :destroy
+</ruby>
-h3. The Empty Route
+In this case, Rails will create all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/:id+).
-Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to +http://example.com+ or +http://example.com/+ will be handled by the empty route.
+TIP: If your application has many RESTful routes, using +:only+ and +:except+ to generate only the routes that you actually need can cut down on memory use and speed up the routing process.
-h4. Using +root+
+h4. Translated Paths
-The preferred way to set up the empty route is with the +root+ command:
+Using +scope+, we can alter path names generated by resources:
<ruby>
-root :to => 'pages#main'
+scope(:path_names => { :new => "neu", :edit => "bearbeiten" }) do
+ resources :categories, :path => "kategorien"
+end
</ruby>
-The use of the +root+ method tells Rails that this route applies to requests for the root of the site.
+Rails now creates routes to the +CategoriesControlleR+.
-Because of the top-down processing of the file, the named route must be specified _before_ the call to +root+.
+|_.HTTP verb|_.URL |_.action |
+|GET |/kategorien |index |
+|GET |/kategorien/neu |new |
+|POST |/kategorien |create |
+|GET |/kategorien/1 |show |
+|GET |/kategorien/:id/bearbeiten |edit |
+|PUT |/kategorien/1 |update |
+|DELETE |/kategorien/1 |destroy |
-h4. Connecting the Empty String
+h4. Overriding the Singular Form
-You can also specify an empty route by explicitly connecting the empty string:
+If you want to customize the singular name of the route in the named helpers, you can use the +:singular+ option.
<ruby>
-match '' => 'pages#main'
+resources :teeth, :singular => "tooth"
+</ruby>
+
+TIP: If you want to define the singular form of a word for your entire application, you should add additional rules to the +Inflector+ instead.
+
+h4(#nested-name-prefix). Using +:name_prefix+ in Nested Resources
+
+The +:name_prefix+ option overrides the automatically-generated prefix for the parent resource in nested route helpers. For example,
+
+<ruby>
+resources :magazines do
+ resources :ads, :name_prefix => 'periodical'
+end
</ruby>
-TIP: If the empty route does not seem to be working in your application, make sure that you have deleted the file +public/index.html+ from your Rails tree.
+This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+.
h3. Inspecting and Testing Routes
-Routing in your application should not be a "black box" that you never open. Rails offers built-in tools for both inspecting and testing routes.
+Rails offers facilities for inspecting and testing your routes.
h4. Seeing Existing Routes with +rake+
-If you want a complete list of all of the available routes in your application, run the +rake routes+ command. This will dump all of your routes to the console, in the same order that they appear in +routes.rb+. For each route, you'll see:
+If you want a complete list of all of the available routes in your application, run +rake routes+ command. This will print all of your routes, in the same order that they appear in +routes.rb+. For each route, you'll see:
* The route name (if any)
* The HTTP verb used (if the route doesn't respond to all verbs)
-* The URL pattern
-* The routing parameters that will be generated by this URL
+* The URL pattern to match
+* The routing parameters for the route
For example, here's a small section of the +rake routes+ output for a RESTful route:
@@ -812,7 +768,7 @@ You can supply a +:method+ argument to specify the HTTP verb:
assert_recognizes({ :controller => "photos", :action => "create" }, { :path => "photos", :method => :post })
</ruby>
-You can also use the RESTful helpers to test recognition of a RESTful route:
+You can also use the resourceful helpers to test recognition of a RESTful route:
<ruby>
assert_recognizes new_photo_url, { :path => "photos", :method => :post }
@@ -830,6 +786,7 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3
+* April 10, 2010: Updated guide to remove outdated and superfluous information, and to provide information about new features, by "Yehuda Katz":http://www.yehudakatz.com
* April 2, 2010: Updated guide to match new Routing DSL in Rails 3, by "Rizwan Reza":http://www.rizwanreza.com/
* Febuary 1, 2010: Modifies the routing documentation to match new routing DSL in Rails 3, by Prem Sichanugrist
* October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes, by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index 9d02da104d..0611b2a9f5 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -79,6 +79,10 @@ module Rails
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
end
+ def env=(environment)
+ @_env = ActiveSupport::StringInquirer.new(environment)
+ end
+
def cache
RAILS_CACHE
end
@@ -88,11 +92,12 @@ module Rails
end
def public_path
- @@public_path ||= self.root ? File.join(self.root, "public") : "public"
+ application && application.paths.public.to_a.first
end
def public_path=(path)
- @@public_path = path
+ ActiveSupport::Deprecation.warn "Setting Rails.public_path= is deprecated. " <<
+ "Please set paths.public = in config/application.rb instead.", caller
end
end
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index d3e4742e8a..7ce3494fa6 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -6,21 +6,29 @@ module Rails
include ::Rails::Configuration::Deprecated
attr_accessor :allow_concurrency, :cache_classes, :cache_store,
- :secret_token, :consider_all_requests_local, :dependency_loading,
+ :encoding, :consider_all_requests_local, :dependency_loading,
:filter_parameters, :log_level, :logger, :metals,
:plugins, :preload_frameworks, :reload_engines, :reload_plugins,
- :serve_static_assets, :time_zone, :whiny_nils
+ :secret_token, :serve_static_assets, :time_zone, :whiny_nils
def initialize(*)
super
- @allow_concurrency = false
- @filter_parameters = []
- @dependency_loading = true
+ @allow_concurrency = false
+ @consider_all_requests_local = false
+ @encoding = "utf-8"
+ @filter_parameters = []
+ @dependency_loading = true
@serve_static_assets = true
- @time_zone = "UTC"
- @consider_all_requests_local = true
@session_store = :cookie_store
@session_options = {}
+ @time_zone = "UTC"
+ end
+
+ def encoding=(value)
+ @encoding = value
+ if defined?(Encoding) && Encoding.respond_to?(:default_external=)
+ Encoding.default_external = value
+ end
end
def middleware
@@ -129,7 +137,7 @@ module Rails
def default_middleware_stack
ActionDispatch::MiddlewareStack.new.tap do |middleware|
- middleware.use('::ActionDispatch::Static', lambda { Rails.public_path }, :if => lambda { serve_static_assets })
+ middleware.use('::ActionDispatch::Static', lambda { paths.public.to_a.first }, :if => lambda { serve_static_assets })
middleware.use('::Rack::Lock', :if => lambda { !allow_concurrency })
middleware.use('::Rack::Runtime')
middleware.use('::Rails::Rack::Logger')
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 28e7ef947d..c5411a0331 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -43,7 +43,7 @@ module Rails
end
def load_once_paths
- @eager_load_paths ||= paths.load_once
+ @load_once_paths ||= paths.load_once
end
def load_paths
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 5f93dbf18f..9bc019b152 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -26,6 +26,7 @@ module Rails
:orm => '-o',
:resource_controller => '-c',
:scaffold_controller => '-c',
+ :stylesheets => '-y',
:template_engine => '-e',
:test_framework => '-t'
},
@@ -50,6 +51,7 @@ module Rails
:resource_controller => :controller,
:scaffold_controller => :scaffold_controller,
:singleton => false,
+ :stylesheets => true,
:test_framework => nil,
:template_engine => :erb
},
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
index 01ec58c615..0615a34a85 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
@@ -1,5 +1,14 @@
<%%= form_for(@<%= singular_name %>) do |f| %>
- <%%= f.error_messages %>
+ <%% if @<%= singular_name %>.errors.any? %>
+ <div id="errorExplanation">
+ <h2><%%= pluralize(@<%= singular_name %>.errors.count, "error") %> prohibited this <%= singular_name %> from being saved:</h2>
+ <ul>
+ <%% @<%= singular_name %>.errors.full_messages.each do |msg| %>
+ <li><%%= msg %></li>
+ <%% end %>
+ </ul>
+ </div>
+ <%% end %>
<% for attribute in attributes -%>
<div class="field">
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
index 24f13fc0f8..4dd2e6bf8c 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
@@ -1,3 +1,5 @@
+<p class="notice"><%%= notice %></p>
+
<% for attribute in attributes -%>
<p>
<b><%= attribute.human_name %>:</b>
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index df6e98f38d..bb2a080286 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -78,7 +78,7 @@ module Rails::Generators
end
def create_app_files
- directory "app"
+ directory 'app'
end
def create_config_files
@@ -137,7 +137,7 @@ module Rails::Generators
end
def create_public_stylesheets_files
- directory "public/stylesheets"
+ empty_directory_with_gitkeep "public/stylesheets"
end
def create_prototype_files
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb
deleted file mode 100644
index 6b87d9d3ec..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title><%= controller_name.humanize %>: <%= action_name %></title>
- <%= stylesheet_link_tag 'application' %>
- <%= javascript_include_tag :defaults %>
- <%= csrf_meta_tag %>
-</head>
-<body>
-
-<p class="notice"><%= notice %></p>
-<p class="alert"><%= alert %></p>
-
-<%=raw yield %>
-
-</body>
-</html>
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
new file mode 100644
index 0000000000..1dd112b4a6
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title><%= app_const_base %></title>
+ <%%= stylesheet_link_tag :all %>
+ <%%= javascript_include_tag :defaults %>
+ <%%= csrf_meta_tag %>
+</head>
+<body>
+
+<%%= yield %>
+
+</body>
+</html>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index bd4fedcdec..0066e2b0c2 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -46,7 +46,10 @@ module <%= app_const_base %>
# g.test_framework :test_unit, :fixture => true
# end
+ # Configure the default encoding used in templates for Ruby 1.9.
+ config.encoding = "utf-8"
+
# Configure sensitive parameters which will be filtered from the log file.
- config.filter_parameters << :password
+ config.filter_parameters += [:password]
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js
index 7342e1b830..c5fa02ae35 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js
+++ b/railties/lib/rails/generators/rails/app/templates/public/javascripts/rails.js
@@ -1,15 +1,8 @@
document.observe("dom:loaded", function() {
- var authToken = $$('meta[name=csrf-token]').first().readAttribute('content'),
- authParam = $$('meta[name=csrf-param]').first().readAttribute('content'),
- formTemplate = '<form method="#{method}" action="#{action}">\
- #{realmethod}<input name="#{param}" value="#{token}" type="hidden">\
- </form>',
- realmethodTemplate = '<input name="_method" value="#{method}" type="hidden">';
-
function handleRemote(element) {
var method, url, params;
- if (element.tagName.toLowerCase() == 'form') {
+ if (element.tagName.toLowerCase() === 'form') {
method = element.readAttribute('method') || 'post';
url = element.readAttribute('action');
params = element.serialize(true);
@@ -39,65 +32,81 @@ document.observe("dom:loaded", function() {
element.fire("ajax:after");
}
+ function handleMethod(element) {
+ var method, url, token_name, token;
+
+ method = element.readAttribute('data-method');
+ url = element.readAttribute('href');
+ csrf_param = $$('meta[name=csrf-param]').first();
+ csrf_token = $$('meta[name=csrf-token]').first();
+
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
+ element.parentNode.appendChild(form);
+
+ if (method != 'post') {
+ var field = new Element('input', { type: 'hidden', name: '_method', value: method });
+ form.appendChild(field);
+ }
+
+ if (csrf_param) {
+ var param = csrf_param.readAttribute('content');
+ var token = csrf_token.readAttribute('content');
+ var field = new Element('input', { type: 'hidden', name: param, value: token });
+ form.appendChild(field);
+ }
+
+ form.submit();
+ }
+
$(document.body).observe("click", function(event) {
- var message = event.element().readAttribute('data-confirm');
+ var message = event.findElement().readAttribute('data-confirm');
if (message && !confirm(message)) {
event.stop();
return false;
}
- var element = event.findElement("a[data-remote=true]");
+ var element = event.findElement("a[data-remote]");
if (element) {
handleRemote(element);
event.stop();
+ return true;
}
var element = event.findElement("a[data-method]");
- if (element && element.readAttribute('data-remote') != 'true') {
- var method = element.readAttribute('data-method'),
- piggyback = method.toLowerCase() != 'post',
- formHTML = formTemplate.interpolate({
- method: 'POST',
- realmethod: piggyback ? realmethodTemplate.interpolate({ method: method }) : '',
- action: element.readAttribute('href'),
- token: authToken,
- param: authParam
- });
-
- var form = new Element('div').update(formHTML).down().hide();
- this.insert({ bottom: form });
-
- form.submit();
+ if (element) {
+ handleMethod(element);
event.stop();
+ return true;
}
});
// TODO: I don't think submit bubbles in IE
$(document.body).observe("submit", function(event) {
- var message = event.element().readAttribute('data-confirm');
+ var element = event.findElement(),
+ message = element.readAttribute('data-confirm');
if (message && !confirm(message)) {
event.stop();
return false;
}
- var inputs = event.element().select("input[type=submit][data-disable-with]");
+ var inputs = element.select("input[type=submit][data-disable-with]");
inputs.each(function(input) {
input.disabled = true;
input.writeAttribute('data-original-value', input.value);
input.value = input.readAttribute('data-disable-with');
});
- var element = event.findElement("form[data-remote=true]");
+ var element = event.findElement("form[data-remote]");
if (element) {
handleRemote(element);
event.stop();
}
});
- $(document.body).observe("ajax:complete", function(event) {
- var element = event.element();
+ $(document.body).observe("ajax:after", function(event) {
+ var element = event.findElement();
- if (element.tagName.toLowerCase() == 'form') {
+ if (element.tagName.toLowerCase() === 'form') {
var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
inputs.each(function(input) {
input.value = input.readAttribute('data-original-value');
@@ -106,4 +115,4 @@ document.observe("dom:loaded", function() {
});
}
});
-});
+}); \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory b/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/public/stylesheets/.empty_directory
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
index 8bf1192ffe..86564031f5 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
@@ -3,11 +3,13 @@ require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
class ActiveSupport::TestCase
+<% unless options[:skip_activerecord] -%>
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
+<% end -%>
# Add more helper methods to be used by all tests here...
end
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index bd156f399c..779f933785 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -7,6 +7,7 @@ module Rails
remove_class_option :actions
hook_for :scaffold_controller, :required => true
+ hook_for :stylesheets
end
end
end
diff --git a/railties/lib/rails/generators/rails/stylesheets/USAGE b/railties/lib/rails/generators/rails/stylesheets/USAGE
new file mode 100644
index 0000000000..59e5495d0b
--- /dev/null
+++ b/railties/lib/rails/generators/rails/stylesheets/USAGE
@@ -0,0 +1,5 @@
+Description:
+ Copies scaffold stylesheets to public/stylesheets/.
+
+Examples:
+ `rails generate stylesheets`
diff --git a/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb b/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb
new file mode 100644
index 0000000000..ce68443c39
--- /dev/null
+++ b/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb
@@ -0,0 +1,9 @@
+module Rails
+ module Generators
+ class StylesheetsGenerator < Base
+ def copy_stylesheets_file
+ template "scaffold.css", "public/stylesheets/scaffold.css" if behavior == :invoke
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/application.css b/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
index ea3dc9b8b5..f3f46d8b98 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/stylesheets/application.css
+++ b/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
@@ -35,10 +35,10 @@ div.field, div.actions {
}
#errorExplanation {
- width: 400px;
+ width: 450px;
border: 2px solid red;
padding: 7px;
- padding-bottom: 12px;
+ padding-bottom: 0;
margin-bottom: 20px;
background-color: #f0f0f0;
}
@@ -49,16 +49,11 @@ div.field, div.actions {
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px;
+ margin-bottom: 0px;
background-color: #c00;
color: #fff;
}
-#errorExplanation p {
- color: #333;
- margin-bottom: 0;
- padding: 5px;
-}
-
#errorExplanation ul li {
font-size: 12px;
list-style: square;
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 57857fb911..23b8f92abd 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -52,7 +52,11 @@ task :test do
end
namespace :test do
- Rake::TestTask.new(:recent => "db:test:prepare") do |t|
+ task :prepare do
+ # Placeholder task for other Railtie and plugins to enhance. See Active Record for an example.
+ end
+
+ Rake::TestTask.new(:recent => "test:prepare") do |t|
since = TEST_CHANGES_SINCE
touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
recent_tests('app/models/**/*.rb', 'test/unit', since) +
@@ -63,7 +67,7 @@ namespace :test do
end
Rake::Task['test:recent'].comment = "Test recent changes"
- Rake::TestTask.new(:uncommitted => "db:test:prepare") do |t|
+ Rake::TestTask.new(:uncommitted => "test:prepare") do |t|
def t.file_list
if File.directory?(".svn")
changed_since_checkin = silence_stderr { `svn status` }.map { |path| path.chomp[7 .. -1] }
@@ -86,32 +90,32 @@ namespace :test do
end
Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)"
- Rake::TestTask.new(:units => "db:test:prepare") do |t|
+ Rake::TestTask.new(:units => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/unit/**/*_test.rb'
end
Rake::Task['test:units'].comment = "Run the unit tests in test/unit"
- Rake::TestTask.new(:functionals => "db:test:prepare") do |t|
+ Rake::TestTask.new(:functionals => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/functional/**/*_test.rb'
end
Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional"
- Rake::TestTask.new(:integration => "db:test:prepare") do |t|
+ Rake::TestTask.new(:integration => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/integration/**/*_test.rb'
end
Rake::Task['test:integration'].comment = "Run the integration tests in test/integration"
- Rake::TestTask.new(:benchmark => 'db:test:prepare') do |t|
+ Rake::TestTask.new(:benchmark => 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
t.options = '-- --benchmark'
end
Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests'
- Rake::TestTask.new(:profile => 'db:test:prepare') do |t|
+ Rake::TestTask.new(:profile => 'test:prepare') do |t|
t.libs << 'test'
t.pattern = 'test/performance/**/*_test.rb'
end
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 7c47cbeabd..c10876134a 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -3,7 +3,7 @@ module Rails
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index 90f2e2b370..8bf0f09d6b 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -172,6 +172,27 @@ module ApplicationTests
assert $prepared
end
+ test "config.encoding sets the default encoding" do
+ add_to_config <<-RUBY
+ config.encoding = "utf-8"
+ RUBY
+
+ require "#{app_path}/config/application"
+
+ unless RUBY_VERSION < '1.9'
+ assert_equal Encoding.find("utf-8"), Encoding.default_external
+ end
+ end
+
+ test "config.paths.public sets Rails.public_path" do
+ add_to_config <<-RUBY
+ config.paths.public = "somewhere"
+ RUBY
+
+ require "#{app_path}/config/application"
+ assert_equal File.join(app_path, "somewhere"), Rails.public_path
+ end
+
def make_basic_app
require "rails"
require "action_controller/railtie"
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 64579c1205..24e6d541c2 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -54,8 +54,8 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_application_controller_and_layout_files
run_generator
assert_file "app/controllers/application_controller.rb", /layout 'application'/
- assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag 'application'/
- assert_file "public/stylesheets/application.css"
+ assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag :all/
+ assert_no_file "public/stylesheets/application.css"
end
def test_name_collision_raises_an_error
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index f5af137ced..8040b22fe6 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -66,6 +66,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
new
show
).each { |view| assert_file "app/views/users/#{view}.html.erb" }
+ assert_no_file "app/views/layouts/users.html.erb"
end
def test_functional_tests
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 3cad65f503..e8e622fe5c 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -70,10 +70,14 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
show
_form
).each { |view| assert_file "app/views/product_lines/#{view}.html.erb" }
+ assert_no_file "app/views/layouts/product_lines.html.erb"
# Helpers
assert_file "app/helpers/product_lines_helper.rb"
assert_file "test/unit/helpers/product_lines_helper_test.rb"
+
+ # Stylesheets
+ assert_file "public/stylesheets/scaffold.css"
end
def test_scaffold_on_revoke
@@ -97,9 +101,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Views
assert_no_file "app/views/product_lines"
+ assert_no_file "app/views/layouts/product_lines.html.erb"
# Helpers
assert_no_file "app/helpers/product_lines_helper.rb"
assert_no_file "test/unit/helpers/product_lines_helper_test.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "public/stylesheets/scaffold.css"
end
end
diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb
new file mode 100644
index 0000000000..aaeb686daa
--- /dev/null
+++ b/railties/test/generators/stylesheets_generator_test.rb
@@ -0,0 +1,17 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/stylesheets/stylesheets_generator'
+
+class StylesheetsGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+
+ def test_copy_stylesheets
+ run_generator
+ assert_file "public/stylesheets/scaffold.css"
+ end
+
+ def test_stylesheets_are_not_deleted_on_revoke
+ run_generator
+ run_generator [], :behavior => :revoke
+ assert_file "public/stylesheets/scaffold.css"
+ end
+end
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 546bf5e143..2accaca855 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -125,5 +125,16 @@ module RailtiesTest
require "#{app_path}/config/environment"
assert $ran_block
end
+
+ test "we can change our environment if we want to" do
+ begin
+ original_env = Rails.env
+ Rails.env = 'foo'
+ assert_equal('foo', Rails.env)
+ ensure
+ Rails.env = original_env
+ assert_equal(original_env, Rails.env)
+ end
+ end
end
end
diff --git a/version.rb b/version.rb
index 7c47cbeabd..c10876134a 100644
--- a/version.rb
+++ b/version.rb
@@ -3,7 +3,7 @@ module Rails
MAJOR = 3
MINOR = 0
TINY = 0
- BUILD = "beta2"
+ BUILD = "beta3"
STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end