Skip to content
Browse files

Merge branch '3-0-stable' of github.com:rails/rails into 3-0-stable

  • Loading branch information...
2 parents b7b28d5 + f2fc81f commit c09c8be3566f16a9c85f051c52952b71a75cb97e @dhh dhh committed Dec 18, 2010
Showing with 1,291 additions and 561 deletions.
  1. +1 −1 Gemfile
  2. +6 −4 actionmailer/lib/action_mailer/base.rb
  3. +3 −4 actionpack/lib/abstract_controller/base.rb
  4. +3 −6 actionpack/lib/abstract_controller/layouts.rb
  5. +46 −32 actionpack/lib/action_controller/caching/actions.rb
  6. +14 −7 actionpack/lib/action_controller/caching/pages.rb
  7. +7 −3 actionpack/lib/action_controller/metal/request_forgery_protection.rb
  8. +10 −3 actionpack/lib/action_dispatch/middleware/cookies.rb
  9. +69 −23 actionpack/lib/action_dispatch/routing/mapper.rb
  10. +2 −2 actionpack/lib/action_view/base.rb
  11. +1 −1 actionpack/lib/action_view/helpers/form_helper.rb
  12. +6 −2 actionpack/lib/action_view/helpers/form_tag_helper.rb
  13. +3 −2 actionpack/lib/action_view/helpers/number_helper.rb
  14. +14 −0 actionpack/test/controller/caching_test.rb
  15. +2 −1 actionpack/test/controller/mime_responds_test.rb
  16. +1 −1 actionpack/test/lib/controller/fake_models.rb
  17. +5 −0 actionpack/test/template/number_helper_test.rb
  18. +2 −2 activemodel/lib/active_model/attribute_methods.rb
  19. +7 −1 activemodel/lib/active_model/errors.rb
  20. +14 −14 activemodel/lib/active_model/lint.rb
  21. +1 −0 activemodel/lib/active_model/serializers/xml.rb
  22. +5 −0 activemodel/test/cases/errors_test.rb
  23. +8 −7 activemodel/test/cases/serializeration/json_serialization_test.rb
  24. +2 −2 activemodel/test/cases/validations_test.rb
  25. +1 −1 activerecord/lib/active_record/aggregations.rb
  26. +1 −1 activerecord/lib/active_record/associations.rb
  27. +10 −10 activerecord/lib/active_record/associations/association_collection.rb
  28. +3 −3 activerecord/lib/active_record/associations/association_proxy.rb
  29. +3 −3 activerecord/lib/active_record/associations/belongs_to_association.rb
  30. +1 −1 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
  31. +1 −1 activerecord/lib/active_record/associations/has_many_through_association.rb
  32. +6 −6 activerecord/lib/active_record/associations/has_one_association.rb
  33. +1 −1 activerecord/lib/active_record/associations/has_one_through_association.rb
  34. +1 −1 activerecord/lib/active_record/attribute_methods.rb
  35. +3 −4 activerecord/lib/active_record/attribute_methods/primary_key.rb
  36. +2 −1 activerecord/lib/active_record/attribute_methods/read.rb
  37. +7 −7 activerecord/lib/active_record/autosave_association.rb
  38. +9 −11 activerecord/lib/active_record/base.rb
  39. +7 −6 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
  40. +1 −1 activerecord/lib/active_record/locking/optimistic.rb
  41. +1 −1 activerecord/lib/active_record/locking/pessimistic.rb
  42. +4 −26 activerecord/lib/active_record/observer.rb
  43. +5 −5 activerecord/lib/active_record/persistence.rb
  44. +1 −1 activerecord/lib/active_record/relation.rb
  45. +1 −1 activerecord/lib/active_record/relation/calculations.rb
  46. +1 −1 activerecord/lib/active_record/relation/finder_methods.rb
  47. +1 −1 activerecord/lib/active_record/relation/predicate_builder.rb
  48. +1 −1 activerecord/lib/active_record/relation/query_methods.rb
  49. +4 −4 activerecord/lib/active_record/session_store.rb
  50. +17 −14 activerecord/lib/active_record/transactions.rb
  51. +1 −1 activerecord/lib/active_record/validations.rb
  52. +1 −1 activerecord/lib/active_record/validations/uniqueness.rb
  53. +6 −6 activerecord/test/cases/associations/belongs_to_associations_test.rb
  54. +13 −13 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
  55. +6 −6 activerecord/test/cases/associations/has_many_associations_test.rb
  56. +2 −2 activerecord/test/cases/associations/has_one_associations_test.rb
  57. +17 −0 activerecord/test/cases/associations/has_one_through_associations_test.rb
  58. +8 −8 activerecord/test/cases/associations/join_model_test.rb
  59. +9 −0 activerecord/test/cases/attribute_methods_test.rb
  60. +43 −43 activerecord/test/cases/autosave_association_test.rb
  61. +22 −6 activerecord/test/cases/base_test.rb
  62. +21 −21 activerecord/test/cases/finder_test.rb
  63. +16 −0 activerecord/test/cases/lifecycle_test.rb
  64. +8 −8 activerecord/test/cases/nested_attributes_test.rb
  65. +13 −6 activerecord/test/cases/relations_test.rb
  66. +2 −2 activerecord/test/cases/session_store/sql_bypass.rb
  67. +8 −8 activerecord/test/cases/transactions_test.rb
  68. +21 −0 activerecord/test/cases/xml_serialization_test.rb
  69. +9 −0 activerecord/test/cases/yaml_serialization_test.rb
  70. +1 −1 activerecord/test/models/pirate.rb
  71. +1 −1 activerecord/test/models/subject.rb
  72. +1 −1 activerecord/test/models/topic.rb
  73. +3 −3 activesupport/lib/active_support/cache.rb
  74. +13 −1 activesupport/lib/active_support/configurable.rb
  75. +1 −1 activesupport/lib/active_support/dependencies.rb
  76. +0 −2 railties/guides/source/action_controller_overview.textile
  77. +8 −10 railties/guides/source/action_mailer_basics.textile
  78. +9 −55 railties/guides/source/active_record_querying.textile
  79. +1 −1 railties/guides/source/active_record_validations_callbacks.textile
  80. +43 −0 railties/guides/source/active_support_core_extensions.textile
  81. +2 −0 railties/guides/source/command_line.textile
  82. +332 −62 railties/guides/source/configuring.textile
  83. +7 −2 railties/guides/source/contributing_to_rails.textile
  84. +261 −6 railties/guides/source/generators.textile
  85. +1 −1 railties/guides/source/i18n.textile
  86. +10 −14 railties/guides/source/layouts_and_rendering.textile
  87. +1 −1 railties/guides/source/performance_testing.textile
  88. +12 −12 railties/guides/source/routing.textile
  89. +3 −3 railties/guides/source/security.textile
  90. +16 −10 railties/guides/source/testing.textile
  91. +1 −1 railties/lib/rails/generators/actions.rb
  92. +1 −1 railties/lib/rails/generators/rails/app/app_generator.rb
  93. +3 −10 railties/lib/rails/generators/rails/app/templates/config/boot.rb
  94. +9 −0 railties/test/generators/app_generator_test.rb
View
2 Gemfile
@@ -1,6 +1,6 @@
source 'http://rubygems.org'
-gem "rails", :path => File.dirname(__FILE__)
+gemspec
gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"
View
10 actionmailer/lib/action_mailer/base.rb
@@ -234,8 +234,8 @@ module ActionMailer #:nodoc:
# default :sender => 'system@example.com'
# end
#
- # You can pass in any header value that a <tt>Mail::Message</tt>, out of the box, <tt>ActionMailer::Base</tt>
- # sets the following:
+ # You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
+ # <tt>ActionMailer::Base</tt> sets the following:
#
# * <tt>:mime_version => "1.0"</tt>
# * <tt>:charset => "UTF-8",</tt>
@@ -273,7 +273,7 @@ module ActionMailer #:nodoc:
# = Configuration options
#
# These options are specified on the class level, like
- # <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
+ # <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
#
# * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
# per the above section.
@@ -290,7 +290,9 @@ module ActionMailer #:nodoc:
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
# authentication type here.
- # This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
+ # This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
+ # send password BASE64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
+ # information and a cryptographic Message Digest 5 algorithm to hash important information)
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
# and starts to use it.
#
View
7 actionpack/lib/abstract_controller/base.rb
@@ -31,10 +31,9 @@ def abstract!
# A list of all internal methods for a controller. This finds the first
# abstract superclass of a controller, and gets a list of all public
# instance methods on that abstract class. Public instance methods of
- # a controller would normally be considered action methods, so we
- # are removing those methods on classes declared as abstract
- # (ActionController::Metal and ActionController::Base are defined
- # as abstract)
+ # a controller would normally be considered action methods, so methods
+ # declared on abstract classes are being removed.
+ # (ActionController::Metal and ActionController::Base are defined as abstract)
def internal_methods
controller = self
controller = controller.superclass until controller.abstract?
View
9 actionpack/lib/abstract_controller/layouts.rb
@@ -235,13 +235,10 @@ def _implied_layout_name
controller_path
end
- # Takes the specified layout and creates a _layout method to be called
- # by _default_layout
+ # Creates a _layout method to be called by _default_layout .
#
- # If there is no explicit layout specified:
- # If a layout is found in the view paths with the controller's
- # name, return that string. Otherwise, use the superclass'
- # layout (which might also be implied)
+ # If a layout is not explicitly mentioned then look for a layout with the controller's name.
+ # if nothing is found then try same procedure to find super class's layout.
def _write_layout_method
remove_possible_method(:_layout)
View
78 actionpack/lib/action_controller/caching/actions.rb
@@ -4,29 +4,30 @@ module ActionController #:nodoc:
module Caching
# Action caching is similar to page caching by the fact that the entire
# output of the response is cached, but unlike page caching, every
- # request still goes through the Action Pack. The key benefit
- # of this is that filters are run before the cache is served, which
- # allows for authentication and other restrictions on whether someone
- # is allowed to see the cache. Example:
+ # request still goes through Action Pack. The key benefit of this is
+ # that filters run before the cache is served, which allows for
+ # authentication and other restrictions on whether someone is allowed
+ # to execute such action. Example:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
+ #
# caches_page :public
- # caches_action :index, :show, :feed
+ # caches_action :index, :show
# end
#
- # In this example, the public action doesn't require authentication,
- # so it's possible to use the faster page caching method. But both
- # the show and feed action are to be shielded behind the authenticate
- # filter, so we need to implement those as action caches.
- #
- # Action caching internally uses the fragment caching and an around
- # filter to do the job. The fragment cache is named according to both
- # the current host and the path. So a page that is accessed at
- # http://david.somewhere.com/lists/show/1 will result in a fragment named
- # "david.somewhere.com/lists/show/1". This allows the cacher to
- # differentiate between "david.somewhere.com/lists/" and
- # "jamis.somewhere.com/lists/" -- which is a helpful way of assisting
+ # In this example, the +public+ action doesn't require authentication
+ # so it's possible to use the faster page caching. On the other hand
+ # +index+ and +show+ require authentication. They can still be cached,
+ # but we need action caching for them.
+ #
+ # Action caching uses fragment caching internally and an around
+ # filter to do the job. The fragment cache is named according to
+ # the host and path of the request. A page that is accessed at
+ # <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
+ # <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
+ # differentiate between <tt>david.example.com/lists/</tt> and
+ # <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
# the subdomain-as-account-key pattern.
#
# Different representations of the same resource, e.g.
@@ -38,39 +39,52 @@ module Caching
# <tt>:action => 'list', :format => :xml</tt>.
#
# You can set modify the default action cache path by passing a
- # :cache_path option. This will be passed directly to
- # ActionCachePath.path_for. This is handy for actions with multiple
- # possible routes that should be cached differently. If a block is
- # given, it is called with the current controller instance.
+ # <tt>:cache_path</tt> option. This will be passed directly to
+ # <tt>ActionCachePath.path_for</tt>. This is handy for actions with
+ # multiple possible routes that should be cached differently. If a
+ # block is given, it is called with the current controller instance.
#
- # And you can also use :if (or :unless) to pass a Proc that
- # specifies when the action should be cached.
+ # And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
+ # proc that specifies when the action should be cached.
#
- # Finally, if you are using memcached, you can also pass :expires_in.
+ # Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
+ #
+ # The following example depicts some of the points made above:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
- # caches_page :public
+ #
+ # caches_page :public
+ #
# caches_action :index, :if => proc do |c|
# !c.request.format.json? # cache if is not a JSON request
# end
#
# caches_action :show, :cache_path => { :project => 1 },
# :expires_in => 1.hour
#
- # caches_action :feed, :cache_path => proc do |controller|
- # if controller.params[:user_id]
- # controller.send(:user_list_url,
- # controller.params[:user_id], controller.params[:id])
+ # caches_action :feed, :cache_path => proc do |c|
+ # if c.params[:user_id]
+ # c.send(:user_list_url,
+ # c.params[:user_id], c.params[:id])
# else
- # controller.send(:list_url, controller.params[:id])
+ # c.send(:list_url, c.params[:id])
# end
# end
# end
#
- # If you pass :layout => false, it will only cache your action
- # content. It is useful when your layout has dynamic information.
+ # If you pass <tt>:layout => false</tt>, it will only cache your action
+ # content. That's useful when your layout has dynamic information.
+ #
+ # Warning: If the format of the request is determined by the Accept HTTP
+ # header the Content-Type of the cached response could be wrong because
+ # no information about the MIME type is stored in the cache key. So, if
+ # you first ask for MIME type M in the Accept header, a cache entry is
+ # created, and then perform a second resquest to the same resource asking
+ # for a different MIME type, you'd get the content cached for M.
#
+ # The <tt>:format</tt> parameter is taken into account though. The safest
+ # way to cache by MIME type is to pass the format in the route.
module Actions
extend ActiveSupport::Concern
View
21 actionpack/lib/action_controller/caching/pages.rb
@@ -71,9 +71,9 @@ def expire_page(path)
# Manually cache the +content+ in the key determined by +path+. Example:
# cache_page "I'm the cached content", "/lists/show"
- def cache_page(content, path)
+ def cache_page(content, path, extension = nil)
return unless perform_caching
- path = page_cache_path(path)
+ path = page_cache_path(path, extension)
instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
@@ -98,14 +98,16 @@ def caches_page(*actions)
end
private
- def page_cache_file(path)
+ def page_cache_file(path, extension)
name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
- name << page_cache_extension unless (name.split('/').last || name).include? '.'
+ unless (name.split('/').last || name).include? '.'
+ name << (extension || self.page_cache_extension)
+ end
return name
end
- def page_cache_path(path)
- page_cache_directory + page_cache_file(path)
+ def page_cache_path(path, extension = nil)
+ page_cache_directory + page_cache_file(path, extension)
end
def instrument_page_cache(name, path)
@@ -146,7 +148,12 @@ def cache_page(content = nil, options = nil)
request.path
end
- self.class.cache_page(content || response.body, path)
+
+ if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
+ extension = ".#{type_symbol}"
+ end
+
+ self.class.cache_page(content || response.body, path, extension)
end
private
View
10 actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -71,9 +71,13 @@ module ClassMethods
# class FooController < ApplicationController
# protect_from_forgery :except => :index
#
- # # you can disable csrf protection on controller-by-controller basis:
- # skip_before_filter :verify_authenticity_token
- # end
+ # You can disable csrf protection on controller-by-controller basis:
+ #
+ # skip_before_filter :verify_authenticity_token
+ #
+ # It can also be disabled for specific controller actions:
+ #
+ # skip_before_filter :verify_authenticity_token, :except => [:create]
#
# Valid Options:
#
View
13 actionpack/lib/action_dispatch/middleware/cookies.rb
@@ -16,24 +16,31 @@ def cookie_jar
# Examples for writing:
#
# # Sets a simple session cookie.
+ # # This cookie will be deleted when the user's browser is closed.
# cookies[:user_name] = "david"
#
+ # # Assign an array of values to a cookie.
+ # cookies[:lat_lon] = [47.68, -122.37]
+ #
# # Sets a cookie that expires in 1 hour.
# cookies[:login] = { :value => "XJ-122", :expires => 1.hour.from_now }
#
# # Sets a signed cookie, which prevents a user from tampering with its value.
- # # You must specify a value in ActionController::Base.cookie_verifier_secret.
- # cookies.signed[:remember_me] = [current_user.id, current_user.salt]
+ # # The cookie is signed by your app's <tt>config.secret_token</tt> value.
+ # # Rails generates this value by default when you create a new Rails app.
+ # cookies.signed[:user_id] = current_user.id
#
# # Sets a "permanent" cookie (which expires in 20 years from now).
# cookies.permanent[:login] = "XJ-122"
+ #
# # You can also chain these methods:
# cookies.permanent.signed[:login] = "XJ-122"
#
# Examples for reading:
#
# cookies[:user_name] # => "david"
# cookies.size # => 2
+ # cookies[:lat_lon] # => [47.68, -122.37]
#
# Example for deleting:
#
@@ -275,7 +282,7 @@ def ensure_secret_secure(secret)
"integrity hash for cookie session data. Use " +
"config.secret_token = \"some secret phrase of at " +
"least #{SECRET_MIN_LENGTH} characters\"" +
- "in config/application.rb"
+ "in config/initializers/secret_token.rb"
end
if secret.length < SECRET_MIN_LENGTH
View
92 actionpack/lib/action_dispatch/routing/mapper.rb
@@ -246,7 +246,11 @@ def initialize(set) #:nodoc:
#
# root :to => 'pages#main'
#
- # You should put the root route at the end of <tt>config/routes.rb</tt>.
+ # For options, see the +match+ method's documentation, as +root+ uses it internally.
+ #
+ # You should put the root route at the top of <tt>config/routes.rb</tt>,
+ # because this means it will be matched first. As this is the most popular route
+ # of most Rails applications, this is beneficial.
def root(options = {})
match '/', options.reverse_merge(:as => :root)
end
@@ -268,18 +272,18 @@ def match(path, options=nil)
# Mount a Rack-based application to be used within the application.
#
- # mount SomeRackApp, :at => "some_route"
+ # mount SomeRackApp, :at => "some_route"
#
# Alternatively:
#
- # mount(SomeRackApp => "some_route")
+ # mount(SomeRackApp => "some_route")
#
# All mounted applications come with routing helpers to access them.
# These are named after the class specified, so for the above example
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
# To customize this helper's name, use the +:as+ option:
#
- # mount(SomeRackApp => "some_route", :as => "exciting")
+ # mount(SomeRackApp => "some_route", :as => "exciting")
#
# This will generate the +exciting_path+ and +exciting_url+ helpers
# which can be used to navigate to this mounted app.
@@ -448,8 +452,18 @@ def initialize(*args) #:nodoc:
super
end
- # Used to route <tt>/photos</tt> (without the prefix <tt>/admin</tt>)
- # to Admin::PostsController:
+ # Used to scope a set of routes to particular constraints.
+ #
+ # Take the following route definition as an example:
+ #
+ # scope :path => ":account_id", :as => "account" do
+ # resources :projects
+ # end
+ #
+ # This generates helpers such as +account_projects_path+, just like +resources+ does.
+ # The difference here being that the routes generated are like /rails/projects/2,
+ # rather than /accounts/rails/projects/2.
+ #
# === Supported options
# [:module]
# If you want to route /posts (without the prefix /admin) to
@@ -555,38 +569,38 @@ def controller(controller, options={})
#
# This generates the following routes:
#
- # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
- # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
- # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
- # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
- # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
- # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
- # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
+ # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
+ # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
+ # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
+ # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
+ # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
+ # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
+ # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
# === Supported options
#
- # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ all default to the name of the namespace.
+ # The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+ options all default to the name of the namespace.
#
# [:path]
# The path prefix for the routes.
#
- # namespace :admin, :path => "sekret" do
- # resources :posts
- # end
+ # namespace :admin, :path => "sekret" do
+ # resources :posts
+ # end
#
# All routes for the above +resources+ will be accessible through +/sekret/posts+, rather than +/admin/posts+
#
# [:module]
# The namespace for the controllers.
#
- # namespace :admin, :module => "sekret" do
- # resources :posts
- # end
+ # namespace :admin, :module => "sekret" do
+ # resources :posts
+ # end
#
# The +PostsController+ here should go in the +Sekret+ namespace and so it should be defined like this:
#
- # class Sekret::PostsController < ApplicationController
- # # code go here
- # end
+ # class Sekret::PostsController < ApplicationController
+ # # code go here
+ # end
#
# [:as]
# Changes the name used in routing helpers for this namespace.
@@ -935,6 +949,22 @@ def resource(*resources, &block)
# GET /photos/:id/edit
# PUT /photos/:id
# DELETE /photos/:id
+ #
+ # Resources can also be nested infinitely by using this block syntax:
+ #
+ # resources :photos do
+ # resources :comments
+ # end
+ #
+ # This generates the following comments routes:
+ #
+ # GET /photos/:id/comments/new
+ # POST /photos/:id/comments
+ # GET /photos/:id/comments/:id
+ # GET /photos/:id/comments/:id/edit
+ # PUT /photos/:id/comments/:id
+ # DELETE /photos/:id/comments/:id
+ #
# === Supported options
# [:path_names]
# Allows you to change the paths of the seven default actions.
@@ -943,6 +973,21 @@ def resource(*resources, &block)
# resources :posts, :path_names => { :new => "brand_new" }
#
# The above example will now change /posts/new to /posts/brand_new
+ #
+ # [:module]
+ # Set the module where the controller can be found. Defaults to nothing.
+ #
+ # resources :posts, :module => "admin"
+ #
+ # All requests to the posts resources will now go to +Admin::PostsController+.
+ #
+ # [:path]
+ #
+ # Set a path prefix for this resource.
+ #
+ # resources :posts, :path => "admin"
+ #
+ # All actions for this resource will now be at +/admin/posts+.
def resources(*resources, &block)
options = resources.extract_options!
@@ -1054,6 +1099,7 @@ def nested
end
end
+ # See ActionDispatch::Routing::Mapper::Scoping#namespace
def namespace(path, options = {})
if resource_scope?
nested { super }
View
4 actionpack/lib/action_view/base.rb
@@ -79,8 +79,8 @@ class NonConcattingString < ActiveSupport::SafeBuffer
#
# === Template caching
#
- # By default, Rails will compile each template to a method in order to render it. When you alter a template, Rails will
- # check the file's modification time and recompile it.
+ # By default, Rails will compile each template to a method in order to render it. When you alter a template,
+ # Rails will check the file's modification time and recompile it in development mode.
#
# == Builder
#
View
2 actionpack/lib/action_view/helpers/form_helper.rb
@@ -1097,7 +1097,7 @@ class InstanceTag
include InstanceTagMethods
end
- class FormBuilder #:nodoc:
+ class FormBuilder
# The methods which wrap a form helper call.
class_inheritable_accessor :field_helpers
self.field_helpers = (FormHelper.instance_method_names - ['form_for'])
View
8 actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -38,7 +38,7 @@ module FormTagHelper
# form_tag('/upload', :multipart => true)
# # => <form action="/upload" method="post" enctype="multipart/form-data">
#
- # <%= form_tag('/posts')do -%>
+ # <%= form_tag('/posts') do -%>
# <div><%= submit_tag 'Save' %></div>
# <% end -%>
# # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
@@ -67,7 +67,7 @@ def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
- # select_tag "people", options_from_collection_for_select(@people, "name", "id")
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name")
# # <select id="people" name="people"><option value="1">David</option></select>
#
# select_tag "people", "<option>David</option>"
@@ -115,6 +115,7 @@ def select_tag(name, option_tags = nil, options = {})
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
+ # * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
@@ -124,6 +125,9 @@ def select_tag(name, option_tags = nil, options = {})
# text_field_tag 'query', 'Enter your search query here'
# # => <input id="query" name="query" type="text" value="Enter your search query here" />
#
+ # text_field_tag 'search', nil, :placeholder => 'Enter search term...'
+ # # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
+ #
# text_field_tag 'request', nil, :class => 'special_input'
# # => <input class="special_input" id="request" name="request" type="text" />
#
View
5 actionpack/lib/action_view/helpers/number_helper.rb
@@ -272,12 +272,13 @@ def number_with_precision(number, options = {})
digits, rounded_number = 1, 0
else
digits = (Math.log10(number.abs) + 1).floor
- rounded_number = BigDecimal.new((number / 10 ** (digits - precision)).to_s).round.to_f * 10 ** (digits - precision)
+ rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new((10 ** (digits - precision)).to_f.to_s)).round.to_f * 10 ** (digits - precision)
+ digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
end
precision = precision - digits
precision = precision > 0 ? precision : 0 #don't let it be negative
else
- rounded_number = BigDecimal.new((number * (10 ** precision)).to_s).round.to_f / 10 ** precision
+ rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
end
formatted_number = number_with_delimiter("%01.#{precision}f" % rounded_number, options)
if strip_insignificant_zeros
View
14 actionpack/test/controller/caching_test.rb
@@ -16,7 +16,14 @@ class CachingController < ActionController::Base
class PageCachingTestController < CachingController
caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
caches_page :found, :not_found
+ caches_page :about_me
+ def about_me
+ respond_to do |format|
+ format.html {render :text => 'I am html'}
+ format.xml {render :text => 'I am xml'}
+ end
+ end
def ok
head :ok
@@ -74,6 +81,13 @@ def teardown
@controller.perform_caching = false
end
+ def test_should_obey_http_accept_attribute
+ @request.env['HTTP_ACCEPT'] = 'text/xml'
+ get :about_me
+ assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/about_me.xml")
+ assert_equal 'I am xml', @response.body
+ end
+
def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
with_routing do |set|
set.draw do |map|
View
3 actionpack/test/controller/mime_responds_test.rb
@@ -884,7 +884,8 @@ def test_render_json_object_responds_to_str_still_produce_json
@controller = RenderJsonRespondWithController.new
@request.accept = "application/json"
get :index, :format => :json
- assert_equal %Q{{"message":"boom","error":"RenderJsonTestException"}}, @response.body
+ assert_match(/"message":"boom"/, @response.body)
+ assert_match(/"error":"RenderJsonTestException"/, @response.body)
end
def test_no_double_render_is_raised
View
2 actionpack/test/lib/controller/fake_models.rb
@@ -162,6 +162,6 @@ def each
class RenderJsonTestException < Exception
def to_json(options = nil)
- return { :error => self.class.name, :message => self.to_str }.to_json
+ return { :error => self.class.name, :message => self.to_s }.to_json
end
end
View
5 actionpack/test/template/number_helper_test.rb
@@ -100,6 +100,8 @@ def test_number_with_precision
assert_equal("0", number_with_precision(0, :precision => 0))
assert_equal("0.00100", number_with_precision(0.001, :precision => 5))
assert_equal("0.001", number_with_precision(0.00111, :precision => 3))
+ assert_equal("10.00", number_with_precision(9.995, :precision => 2))
+ assert_equal("11.00", number_with_precision(10.995, :precision => 2))
end
def test_number_with_precision_with_custom_delimiter_and_separator
@@ -125,6 +127,9 @@ def test_number_with_precision_with_significant_digits
assert_equal "0.0001", number_with_precision(0.0001, :precision => 1, :significant => true )
assert_equal "0.000100", number_with_precision(0.0001, :precision => 3, :significant => true )
assert_equal "0.0001", number_with_precision(0.0001111, :precision => 1, :significant => true )
+ assert_equal "10.0", number_with_precision(9.995, :precision => 3, :significant => true)
+ assert_equal "9.99", number_with_precision(9.994, :precision => 3, :significant => true)
+ assert_equal "11.0", number_with_precision(10.995, :precision => 3, :significant => true)
end
def test_number_with_precision_with_strip_insignificant_zeros
View
4 activemodel/lib/active_model/attribute_methods.rb
@@ -46,8 +46,8 @@ class MissingAttributeError < NoMethodError
# end
# end
#
- # Notice that whenever you include ActiveModel::AttributeMethods in your class,
- # it requires you to implement a <tt>attributes</tt> methods which returns a hash
+ # Note that whenever you include ActiveModel::AttributeMethods in your class,
+ # it requires you to implement an <tt>attributes</tt> method which returns a hash
# with each attribute name in your model as hash key and the attribute value as
# hash value.
#
View
8 activemodel/lib/active_model/errors.rb
@@ -167,7 +167,13 @@ def to_xml(options={})
# Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
def as_json(options=nil)
- self
+ to_hash
+ end
+
+ def to_hash
+ hash = ActiveSupport::OrderedHash.new
+ each { |k, v| (hash[k] ||= []) << v }
+ hash
end
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
View
28 activemodel/lib/active_model/lint.rb
@@ -1,19 +1,19 @@
-# == Active Model Lint Tests
-#
-# You can test whether an object is compliant with the Active Model API by
-# including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will include
-# tests that tell you whether your object is fully compliant, or if not,
-# which aspects of the API are not implemented.
-#
-# These tests do not attempt to determine the semantic correctness of the
-# returned values. For instance, you could implement valid? to always
-# return true, and the tests would pass. It is up to you to ensure that
-# the values are semantically meaningful.
-#
-# Objects you pass in are expected to return a compliant object from a
-# call to to_model. It is perfectly fine for to_model to return self.
module ActiveModel
module Lint
+ # == Active Model Lint Tests
+ #
+ # You can test whether an object is compliant with the Active Model API by
+ # including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will include
+ # tests that tell you whether your object is fully compliant, or if not,
+ # which aspects of the API are not implemented.
+ #
+ # These tests do not attempt to determine the semantic correctness of the
+ # returned values. For instance, you could implement valid? to always
+ # return true, and the tests would pass. It is up to you to ensure that
+ # the values are semantically meaningful.
+ #
+ # Objects you pass in are expected to return a compliant object from a
+ # call to to_model. It is perfectly fine for to_model to return self.
module Tests
# == Responds to <tt>to_key</tt>
View
1 activemodel/lib/active_model/serializers/xml.rb
@@ -17,6 +17,7 @@ class Attribute #:nodoc:
def initialize(name, serializable, raw_value=nil)
@name, @serializable = name, serializable
+ raw_value = raw_value.in_time_zone if raw_value.respond_to?(:in_time_zone)
@value = raw_value || @serializable.send(name)
@type = compute_type
end
View
5 activemodel/test/cases/errors_test.rb
@@ -62,4 +62,9 @@ def self.lookup_ancestors
end
+ test 'to_hash should return an ordered hash' do
+ person = Person.new
+ person.errors.add(:name, "can not be blank")
+ assert_instance_of ActiveSupport::OrderedHash, person.errors.to_hash
+ end
end
View
15 activemodel/test/cases/serializeration/json_serialization_test.rb
@@ -6,6 +6,7 @@
class Contact
extend ActiveModel::Naming
include ActiveModel::Serializers::JSON
+ include ActiveModel::Validations
def attributes
instance_values
@@ -105,15 +106,15 @@ def @contact.favorite_quote; "Constraints are liberating"; end
end
test "should return OrderedHash for errors" do
- car = Automobile.new
-
- # run the validation
- car.valid?
+ contact = Contact.new
+ contact.errors.add :name, "can't be blank"
+ contact.errors.add :name, "is too short (minimum is 2 characters)"
+ contact.errors.add :age, "must be 16 or over"
hash = ActiveSupport::OrderedHash.new
- hash[:make] = "can't be blank"
- hash[:model] = "is too short (minimum is 2 characters)"
- assert_equal hash.to_json, car.errors.to_json
+ hash[:name] = ["can't be blank", "is too short (minimum is 2 characters)"]
+ hash[:age] = ["must be 16 or over"]
+ assert_equal hash.to_json, contact.errors.to_json
end
test "serializable_hash should not modify options passed in argument" do
View
4 activemodel/test/cases/validations_test.rb
@@ -174,8 +174,8 @@ def test_errors_conversions
assert_match %r{<error>Content can't be blank</error>}, xml
hash = ActiveSupport::OrderedHash.new
- hash[:title] = "can't be blank"
- hash[:content] = "can't be blank"
+ hash[:title] = ["can't be blank"]
+ hash[:content] = ["can't be blank"]
assert_equal t.errors.to_json, hash.to_json
end
View
2 activerecord/lib/active_record/aggregations.rb
@@ -6,7 +6,7 @@ module Aggregations # :nodoc:
def clear_aggregation_cache #:nodoc:
self.class.reflect_on_all_aggregations.to_a.each do |assoc|
instance_variable_set "@#{assoc.name}", nil
- end if self.persisted?
+ end unless self.new_record?
end
# Active Record implements aggregation through a macro-like class method called +composed_of+
View
2 activerecord/lib/active_record/associations.rb
@@ -118,7 +118,7 @@ module Associations # :nodoc:
def clear_association_cache #:nodoc:
self.class.reflect_on_all_associations.to_a.each do |assoc|
instance_variable_set "@#{assoc.name}", nil
- end if self.persisted?
+ end unless self.new_record?
end
private
View
20 activerecord/lib/active_record/associations/association_collection.rb
@@ -127,13 +127,13 @@ def build(attributes = {}, &block)
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
def <<(*records)
result = true
- load_target unless @owner.persisted?
+ load_target if @owner.new_record?
transaction do
flatten_deeper(records).each do |record|
raise_on_type_mismatch(record)
add_record_to_target_with_callbacks(record) do |r|
- result &&= insert_record(record) if @owner.persisted?
+ result &&= insert_record(record) unless @owner.new_record?
end
end
end
@@ -291,12 +291,12 @@ def create!(attrs = {})
# This method is abstract in the sense that it relies on
# +count_records+, which is a method descendants have to provide.
def size
- if !@owner.persisted? || (loaded? && !@reflection.options[:uniq])
+ if @owner.new_record? || (loaded? && !@reflection.options[:uniq])
@target.size
elsif !loaded? && @reflection.options[:group]
load_target.size
elsif !loaded? && !@reflection.options[:uniq] && @target.is_a?(Array)
- unsaved_records = @target.reject { |r| r.persisted? }
+ unsaved_records = @target.select { |r| r.new_record? }
unsaved_records.size + count_records
else
count_records
@@ -363,7 +363,7 @@ def replace(other_array)
def include?(record)
return false unless record.is_a?(@reflection.klass)
- return include_in_memory?(record) unless record.persisted?
+ return include_in_memory?(record) if record.new_record?
load_target if @reflection.options[:finder_sql] && !loaded?
return @target.include?(record) if loaded?
exists?(record)
@@ -390,7 +390,7 @@ def construct_counter_sql
end
def load_target
- if @owner.persisted? || foreign_key_present
+ if !@owner.new_record? || foreign_key_present
begin
if !loaded?
if @target.is_a?(Array) && @target.any?
@@ -521,7 +521,7 @@ def remove_records(*records)
transaction do
records.each { |record| callback(:before_remove, record) }
- old_records = records.select { |r| r.persisted? }
+ old_records = records.reject { |r| r.new_record? }
yield(records, old_records)
records.each { |record| callback(:after_remove, record) }
end
@@ -546,14 +546,14 @@ def callbacks_for(callback_name)
end
def ensure_owner_is_not_new
- unless @owner.persisted?
+ if @owner.new_record?
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
end
end
def fetch_first_or_last_using_find?(args)
- args.first.kind_of?(Hash) || !(loaded? || !@owner.persisted? || @reflection.options[:finder_sql] ||
- !@target.all? { |record| record.persisted? } || args.first.kind_of?(Integer))
+ args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
+ @target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
end
def include_in_memory?(record)
View
6 activerecord/lib/active_record/associations/association_proxy.rb
@@ -174,10 +174,10 @@ def sanitize_sql(sql, table_name = @reflection.klass.table_name)
# If the association is polymorphic the type of the owner is also set.
def set_belongs_to_association_for(record)
if @reflection.options[:as]
- record["#{@reflection.options[:as]}_id"] = @owner.id if @owner.persisted?
+ record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record?
record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s
else
- if @owner.persisted?
+ unless @owner.new_record?
primary_key = @reflection.options[:primary_key] || :id
record[@reflection.primary_key_name] = @owner.send(primary_key)
end
@@ -233,7 +233,7 @@ def method_missing(method, *args)
def load_target
return nil unless defined?(@loaded)
- if !loaded? and (@owner.persisted? || foreign_key_present)
+ if !loaded? and (!@owner.new_record? || foreign_key_present)
@target = find_target
end
View
6 activerecord/lib/active_record/associations/belongs_to_association.rb
@@ -14,21 +14,21 @@ def replace(record)
counter_cache_name = @reflection.counter_cache_column
if record.nil?
- if counter_cache_name && @owner.persisted?
+ if counter_cache_name && !@owner.new_record?
@reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
end
@target = @owner[@reflection.primary_key_name] = nil
else
raise_on_type_mismatch(record)
- if counter_cache_name && @owner.persisted? && record.id != @owner[@reflection.primary_key_name]
+ if counter_cache_name && !@owner.new_record? && record.id != @owner[@reflection.primary_key_name]
@reflection.klass.increment_counter(counter_cache_name, record.id)
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
end
@target = (AssociationProxy === record ? record.target : record)
- @owner[@reflection.primary_key_name] = record_id(record) if record.persisted?
+ @owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
@updated = true
end
View
2 activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb
@@ -34,7 +34,7 @@ def count_records
end
def insert_record(record, force = true, validate = true)
- unless record.persisted?
+ if record.new_record?
if force
record.save!
else
View
2 activerecord/lib/active_record/associations/has_many_through_association.rb
@@ -59,7 +59,7 @@ def construct_find_options!(options)
end
def insert_record(record, force = true, validate = true)
- unless record.persisted?
+ if record.new_record?
if force
record.save!
else
View
12 activerecord/lib/active_record/associations/has_one_association.rb
@@ -35,18 +35,18 @@ def replace(obj, dont_save = false)
if dependent? && !dont_save
case @reflection.options[:dependent]
when :delete
- @target.delete if @target.persisted?
+ @target.delete unless @target.new_record?
@owner.clear_association_cache
when :destroy
- @target.destroy if @target.persisted?
+ @target.destroy unless @target.new_record?
@owner.clear_association_cache
when :nullify
@target[@reflection.primary_key_name] = nil
- @target.save if @owner.persisted? && @target.persisted?
+ @target.save unless @owner.new_record? || @target.new_record?
end
else
@target[@reflection.primary_key_name] = nil
- @target.save if @owner.persisted? && @target.persisted?
+ @target.save unless @owner.new_record? || @target.new_record?
end
end
@@ -61,7 +61,7 @@ def replace(obj, dont_save = false)
set_inverse_instance(obj, @owner)
@loaded = true
- unless !@owner.persisted? or obj.nil? or dont_save
+ unless @owner.new_record? or obj.nil? or dont_save
return (obj.save ? self : false)
else
return (obj.nil? ? nil : self)
@@ -120,7 +120,7 @@ def new_record(replace_existing)
if replace_existing
replace(record, true)
else
- record[@reflection.primary_key_name] = @owner.id if @owner.persisted?
+ record[@reflection.primary_key_name] = @owner.id unless @owner.new_record?
self.target = record
set_inverse_instance(record, @owner)
end
View
2 activerecord/lib/active_record/associations/has_one_through_association.rb
@@ -21,7 +21,7 @@ def create_through_record(new_value) #nodoc:
if current_object
new_value ? current_object.update_attributes(construct_join_attributes(new_value)) : current_object.destroy
elsif new_value
- unless @owner.persisted?
+ if @owner.new_record?
self.target = new_value
through_association = @owner.send(:association_instance_get, @reflection.through_reflection.name)
through_association.build(construct_join_attributes(new_value))
View
2 activerecord/lib/active_record/attribute_methods.rb
@@ -54,7 +54,7 @@ def respond_to?(*args)
protected
def attribute_method?(attr_name)
- attr_name == 'id' || attributes.include?(attr_name)
+ attr_name == 'id' || (defined?(@attributes) && @attributes.include?(attr_name))
end
end
end
View
7 activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -3,11 +3,10 @@ module AttributeMethods
module PrimaryKey
extend ActiveSupport::Concern
- # Returns this record's primary key value wrapped in an Array or nil if
- # the record is not persisted? or has just been destroyed.
+ # Returns this record's primary key value wrapped in an Array
+ # or nil if the record is a new_record?
def to_key
- key = send(self.class.primary_key)
- [key] if key
+ new_record? ? nil : [ id ]
end
module ClassMethods
View
3 activerecord/lib/active_record/attribute_methods/read.rb
@@ -75,7 +75,8 @@ def define_read_method(symbol, attr_name, column)
def read_attribute(attr_name)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id'
- if !(value = @attributes[attr_name]).nil?
+ value = @attributes[attr_name]
+ unless value.nil?
if column = column_for_attribute(attr_name)
if unserializable_attribute?(attr_name, column)
unserialize_attribute(attr_name)
View
14 activerecord/lib/active_record/autosave_association.rb
@@ -208,7 +208,7 @@ def marked_for_destruction?
# Returns whether or not this record has been changed in any way (including whether
# any of its nested autosave associations are likewise changed)
def changed_for_autosave?
- !persisted? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
+ new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
end
private
@@ -222,7 +222,7 @@ def associated_records_to_validate_or_save(association, new_record, autosave)
elsif autosave
association.target.find_all { |record| record.changed_for_autosave? }
else
- association.target.find_all { |record| !record.persisted? }
+ association.target.find_all { |record| record.new_record? }
end
end
@@ -248,7 +248,7 @@ def validate_single_association(reflection)
# +reflection+.
def validate_collection_association(reflection)
if association = association_instance_get(reflection.name)
- if records = associated_records_to_validate_or_save(association, !persisted?, reflection.options[:autosave])
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
records.each { |record| association_valid?(reflection, record) }
end
end
@@ -277,7 +277,7 @@ def association_valid?(reflection, association)
# Is used as a before_save callback to check while saving a collection
# association whether or not the parent was a new record before saving.
def before_save_collection_association
- @new_record_before_save = !persisted?
+ @new_record_before_save = new_record?
true
end
@@ -299,7 +299,7 @@ def save_collection_association(reflection)
if autosave && record.marked_for_destruction?
association.destroy(record)
- elsif autosave != false && (@new_record_before_save || !record.persisted?)
+ elsif autosave != false && (@new_record_before_save || record.new_record?)
if autosave
saved = association.send(:insert_record, record, false, false)
else
@@ -334,7 +334,7 @@ def save_has_one_association(reflection)
association.destroy
else
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
- if autosave != false && (!persisted? || !association.persisted? || association[reflection.primary_key_name] != key || autosave)
+ if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
association[reflection.primary_key_name] = key
saved = association.save(:validate => !autosave)
raise ActiveRecord::Rollback if !saved && autosave
@@ -354,7 +354,7 @@ def save_belongs_to_association(reflection)
if autosave && association.marked_for_destruction?
association.destroy
elsif autosave != false
- saved = association.save(:validate => !autosave) if !association.persisted? || autosave
+ saved = association.save(:validate => !autosave) if association.new_record? || autosave
if association.updated?
association_id = association.send(reflection.options[:primary_key] || :id)
View
20 activerecord/lib/active_record/base.rb
@@ -205,7 +205,7 @@ module ActiveRecord #:nodoc:
#
# # No 'Winter' tag exists
# winter = Tag.find_or_initialize_by_name("Winter")
- # winter.persisted? # false
+ # winter.new_record? # true
#
# To find by a subset of the attributes to be used for instantiating a new object, pass a hash instead of
# a list of parameters.
@@ -1393,7 +1393,7 @@ def encode_quoted_value(value) #:nodoc:
def initialize(attributes = nil)
@attributes = attributes_from_column_definition
@attributes_cache = {}
- @persisted = false
+ @new_record = true
@readonly = false
@destroyed = false
@marked_for_destruction = false
@@ -1428,7 +1428,7 @@ def initialize_copy(other)
clear_aggregation_cache
clear_association_cache
@attributes_cache = {}
- @persisted = false
+ @new_record = true
ensure_proper_type
populate_with_current_scope_attributes
@@ -1447,8 +1447,7 @@ def initialize_copy(other)
def init_with(coder)
@attributes = coder['attributes']
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
- @readonly = @destroyed = @marked_for_destruction = false
- @persisted = true
+ @new_record = @readonly = @destroyed = @marked_for_destruction = false
_run_find_callbacks
_run_initialize_callbacks
end
@@ -1489,7 +1488,7 @@ def to_param
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
def cache_key
case
- when !persisted?
+ when new_record?
"#{self.class.model_name.cache_key}/new"
when timestamp = self[:updated_at]
"#{self.class.model_name.cache_key}/#{id}-#{timestamp.to_s(:number)}"
@@ -1618,9 +1617,8 @@ def column_for_attribute(name)
# models are still comparable.
def ==(comparison_object)
comparison_object.equal?(self) ||
- comparison_object.instance_of?(self.class) &&
- id.present? &&
- comparison_object.id == id
+ (comparison_object.instance_of?(self.class) &&
+ comparison_object.id == id && !comparison_object.new_record?)
end
# Delegates to ==
@@ -1665,7 +1663,7 @@ def readonly!
# Returns the contents of the record as a nicely formatted string.
def inspect
attributes_as_nice_string = self.class.column_names.collect { |name|
- if has_attribute?(name) || !persisted?
+ if has_attribute?(name) || new_record?
"#{name}: #{attribute_for_inspect(name)}"
end
}.compact.join(", ")
@@ -1717,7 +1715,7 @@ def arel_attributes_values(include_primary_key = true, include_readonly_attribut
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
value = read_attribute(name)
- if value && self.class.serialized_attributes.key?(name)
+ if !value.nil? && self.class.serialized_attributes.key?(name)
value = YAML.dump value
end
attrs[self.class.arel_table[name]] = value
View
13 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -378,10 +378,7 @@ def quoted_date(value) #:nodoc:
# REFERENTIAL INTEGRITY ====================================
def supports_disable_referential_integrity?() #:nodoc:
- version = query("SHOW server_version")[0][0].split('.')
- version[0].to_i >= 8 && version[1].to_i >= 1
- rescue
- return false
+ postgresql_version >= 80100
end
def disable_referential_integrity #:nodoc:
@@ -897,8 +894,12 @@ def postgresql_version
else
# Mimic PGconn.server_version behavior
begin
- query('SELECT version()')[0][0] =~ /PostgreSQL (\d+)\.(\d+)\.(\d+)/
- ($1.to_i * 10000) + ($2.to_i * 100) + $3.to_i
+ if query('SELECT version()')[0][0] =~ /PostgreSQL ([0-9.]+)/
+ major, minor, tiny = $1.split(".")
+ (major.to_i * 10000) + (minor.to_i * 100) + tiny.to_i
+ else
+ 0
+ end
rescue
0
end
View
2 activerecord/lib/active_record/locking/optimistic.rb
@@ -109,7 +109,7 @@ def update(attribute_names = @attributes.keys) #:nodoc:
def destroy #:nodoc:
return super unless locking_enabled?
- if persisted?
+ unless new_record?
lock_col = self.class.locking_column
previous_value = send(lock_col).to_i
View
2 activerecord/lib/active_record/locking/pessimistic.rb
@@ -47,7 +47,7 @@ module Pessimistic
# or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
# the locked record.
def lock!(lock = true)
- reload(:lock => lock) if persisted?
+ reload(:lock => lock) unless new_record?
self
end
end
View
30 activerecord/lib/active_record/observer.rb
@@ -89,51 +89,29 @@ module ActiveRecord
# singletons and that call instantiates and registers them.
#
class Observer < ActiveModel::Observer
- class_attribute :observed_methods
- self.observed_methods = [].freeze
def initialize
super
observed_descendants.each { |klass| add_observer!(klass) }
end
- def self.method_added(method)
- method = method.to_sym
-
- if ActiveRecord::Callbacks::CALLBACKS.include?(method)
- self.observed_methods += [method]
- self.observed_methods.freeze
- end
- end
-
protected
def observed_descendants
observed_classes.sum([]) { |klass| klass.descendants }
end
- def observe_callbacks?
- self.class.observed_methods.any?
- end
-
def add_observer!(klass)
super
- define_callbacks klass if observe_callbacks?
+ define_callbacks klass
end
def define_callbacks(klass)
- existing_methods = klass.instance_methods.map { |m| m.to_sym }
observer = self
- observer_name = observer.class.name.underscore.gsub('/', '__')
- self.class.observed_methods.each do |method|
- callback = :"_notify_#{observer_name}_for_#{method}"
- unless existing_methods.include? callback
- klass.send(:define_method, callback) do # def _notify_user_observer_for_before_save
- observer.update(method, self) # observer.update(:before_save, self)
- end # end
- klass.send(method, callback) # before_save :_notify_user_observer_for_before_save
- end
+ ActiveRecord::Callbacks::CALLBACKS.each do |callback|
+ next unless respond_to?(callback)
+ klass.send(callback){|record| observer.send(callback, record)}
end
end
end
View
10 activerecord/lib/active_record/persistence.rb
@@ -4,7 +4,7 @@ module Persistence
# Returns true if this object hasn't been saved yet -- that is, a record
# for the object doesn't exist in the data store yet; otherwise, returns false.
def new_record?
- !@persisted
+ @new_record
end
# Returns true if this object has been destroyed, otherwise returns false.
@@ -15,7 +15,7 @@ def destroyed?
# Returns if the record is persisted, i.e. it's not a new record and it was
# not destroyed.
def persisted?
- @persisted && !destroyed?
+ !(new_record? || destroyed?)
end
# :call-seq:
@@ -97,7 +97,7 @@ def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
became.instance_variable_set("@attributes_cache", @attributes_cache)
- became.instance_variable_set("@persisted", persisted?)
+ became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
became
end
@@ -243,7 +243,7 @@ def touch(name = nil)
private
def create_or_update
raise ReadOnlyRecord if readonly?
- result = persisted? ? update : create
+ result = new_record? ? create : update
result != false
end
@@ -272,7 +272,7 @@ def create
self.id ||= new_id
- @persisted = true
+ @new_record = false
id
end
View
2 activerecord/lib/active_record/relation.rb
@@ -376,7 +376,7 @@ def method_missing(method, *args, &block)
def references_eager_loaded_tables?
# always convert table names to downcase as in Oracle quoted table names are in uppercase
- joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
+ joined_tables = (tables_in_string(arel.join_sql) + [table.name, table.table_alias]).compact.map{ |t| t.downcase }.uniq
(tables_in_string(to_sql) - joined_tables).any?
end
View
2 activerecord/lib/active_record/relation/calculations.rb
@@ -166,7 +166,7 @@ def perform_calculation(operation, column_name, options = {})
if operation == "count"
column_name ||= (select_for_count || :all)
- if arel.joins(arel) =~ /LEFT OUTER/i
+ if arel.join_sql =~ /LEFT OUTER/i
distinct = true
column_name = @klass.primary_key if column_name == :all
end
View
2 activerecord/lib/active_record/relation/finder_methods.rb
@@ -194,7 +194,7 @@ def find_with_associations
def construct_relation_for_association_calculations
including = (@eager_load_values + @includes_values).uniq
- join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.joins(arel))
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, arel.join_sql)
relation = except(:includes, :eager_load, :preload)
apply_join_dependency(relation, join_dependency)
end
View
2 activerecord/lib/active_record/relation/predicate_builder.rb
@@ -31,7 +31,7 @@ def build_from_hash(attributes, default_table)
when Range, Arel::Relation
attribute.in(value)
when ActiveRecord::Base
- attribute.eq(value.quoted_id)
+ attribute.eq(Arel.sql(value.quoted_id))
when Class
# FIXME: I think we need to deprecate this behavior
attribute.eq(value.name)
View
2 activerecord/lib/active_record/relation/query_methods.rb
@@ -168,7 +168,7 @@ def custom_join_sql(*joins)
arel.join(join)
end
- arel.joins(arel)
+ arel.join_sql
end
def build_arel
View
8 activerecord/lib/active_record/session_store.rb
@@ -228,7 +228,7 @@ def initialize(attributes)
@session_id = attributes[:session_id]
@data = attributes[:data]
@marshaled_data = attributes[:marshaled_data]
- @persisted = !@marshaled_data.nil?
+ @new_record = @marshaled_data.nil?
end
# Lazy-unmarshal session state.
@@ -252,8 +252,8 @@ def save
marshaled_data = self.class.marshal(data)
connect = connection
- unless @persisted
- @persisted = true
+ if @new_record
+ @new_record = false
connect.update <<-end_sql, 'Create session'
INSERT INTO #{table_name} (
#{connect.quote_column_name(session_id_column)},
@@ -272,7 +272,7 @@ def save
end
def destroy
- return unless @persisted
+ return if @new_record
connect = connection
connect.delete <<-end_sql, 'Destroy session'
View
31 activerecord/lib/active_record/transactions.rb
@@ -130,7 +130,7 @@ class TransactionError < ActiveRecordError # :nodoc:
#
# +transaction+ calls can be nested. By default, this makes all database
# statements in the nested transaction block become part of the parent
- # transaction. For example:
+ # transaction. For example, the following behavior may be surprising:
#
# User.transaction do
# User.create(:username => 'Kotori')
@@ -140,12 +140,15 @@ class TransactionError < ActiveRecordError # :nodoc:
# end
# end
#
- # User.find(:all) # => empty
+ # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
+ # exception in the nested block does not issue a ROLLBACK. Since these exceptions
+ # are captured in transaction blocks, the parent block does not see it and the
+ # real transaction is committed.
#
- # It is also possible to requires a sub-transaction by passing
- # <tt>:requires_new => true</tt>. If anything goes wrong, the
- # database rolls back to the beginning of the sub-transaction
- # without rolling back the parent transaction. For example:
+ # In order to get a ROLLBACK for the nested transaction you may ask for a real
+ # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
+ # the database rolls back to the beginning of the sub-transaction without rolling
+ # back the parent transaction. If we add it to the previous example:
#
# User.transaction do
# User.create(:username => 'Kotori')
@@ -155,12 +158,12 @@ class TransactionError < ActiveRecordError # :nodoc:
# end
# end
#
- # User.find(:all) # => Returns only Kotori
+ # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
#
# Most databases don't support true nested transactions. At the time of
# writing, the only database that we're aware of that supports true nested
# transactions, is MS-SQL. Because of this, Active Record emulates nested
- # transactions by using savepoints. See
+ # transactions by using savepoints on MySQL and PostgreSQL. See
# http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
# for more information about savepoints.
#
@@ -242,7 +245,7 @@ def save!(*) #:nodoc:
with_transaction_returning_status { super }
end
- # Reset id and @persisted if the transaction rolls back.
+ # Reset id and @new_record if the transaction rolls back.
def rollback_active_record_state!
remember_transaction_record_state
yield
@@ -297,9 +300,9 @@ def with_transaction_returning_status
# Save the new record state and id of a record so it can be restored later if a transaction fails.
def remember_transaction_record_state #:nodoc
@_start_transaction_state ||= {}
- unless @_start_transaction_state.include?(:persisted)
+ unless @_start_transaction_state.include?(:new_record)
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
- @_start_transaction_state[:persisted] = @persisted
+ @_start_transaction_state[:new_record] = @new_record
end
unless @_start_transaction_state.include?(:destroyed)
@_start_transaction_state[:destroyed] = @destroyed
@@ -323,7 +326,7 @@ def restore_transaction_record_state(force = false) #:nodoc
restore_state = remove_instance_variable(:@_start_transaction_state)
if restore_state
@attributes = @attributes.dup if @attributes.frozen?
- @persisted = restore_state[:persisted]
+ @new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
if restore_state[:id]
self.id = restore_state[:id]
@@ -345,11 +348,11 @@ def transaction_record_state(state) #:nodoc
def transaction_include_action?(action) #:nodoc
case action
when :create
- transaction_record_state(:new_record) || !transaction_record_state(:persisted)
+ transaction_record_state(:new_record)
when :destroy
destroyed?
when :update
- !(transaction_record_state(:new_record) || !transaction_record_state(:persisted) || destroyed?)
+ !(transaction_record_state(:new_record) || destroyed?)
end
end
end
View
2 activerecord/lib/active_record/validations.rb
@@ -51,7 +51,7 @@ def save!(options={})
# Runs all the specified validations and returns true if no errors were added otherwise false.
def valid?(context = nil)
- context ||= (persisted? ? :update : :create)
+ context ||= (new_record? ? :create : :update)
output = super(context)
deprecated_callback_method(:validate)
View
2 activerecord/lib/active_record/validations/uniqueness.rb
@@ -31,7 +31,7 @@ def validate_each(record, attribute, value)
relation = relation.where(scope_item => scope_value)
end
- if record.persisted?
+ unless record.new_record?
# TODO : This should be in Arel
relation = relation.where("#{record.class.quoted_table_name}.#{record.class.primary_key} <> ?", record.send(:id))
end
View
12 activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -278,10 +278,10 @@ def test_assignment_before_child_saved
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
final_cut.firm = firm
- assert !final_cut.persisted?
+ assert final_cut.new_record?
assert final_cut.save
- assert final_cut.persisted?
- assert firm.persisted?
+ assert !final_cut.new_record?
+ assert !firm.new_record?
assert_equal firm, final_cut.firm
assert_equal firm, final_cut.firm(true)
end
@@ -290,10 +290,10 @@ def test_assignment_before_child_saved_with_primary_key
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
final_cut.firm_with_primary_key = firm
- assert !final_cut.persisted?
+ assert final_cut.new_record?
assert final_cut.save
- assert final_cut.persisted?
- assert firm.persisted?
+ assert !final_cut.new_record?
+ assert !firm.new_record?
assert_equal firm, final_cut.firm_with_primary_key
assert_equal firm, final_cut.firm_with_primary_key(true)
end
View
26 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
<
@@ -251,10 +251,10 @@ def test_habtm_adding_before_save
no_of_projects = Project.count
aredridel = Developer.new("name" => "Aredridel")
aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
- assert !aredridel.persisted?
- assert !p.persisted?
+ assert aredridel.new_record?
+ assert p.new_record?
assert aredridel.save
- assert aredridel.persisted?
+ assert !aredridel.new_record?
assert_equal no_of_devels+1, Developer.count
assert_equal no_of_projects+1, Project.count
assert_equal 2, aredridel.projects.size
@@ -288,9 +288,9 @@ def test_build
assert_equal devel.projects.last, proj
assert devel.projects.loaded?
- assert !proj.persisted?
+ assert proj.new_record?
devel.save
- assert proj.persisted?
+ assert !proj.new_record?
assert_equal devel.projects.last, proj
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
@@ -300,10 +300,10 @@ def test_build_by_new_record
proj1 = devel.projects.build(:name => "Make bed")
proj2 = devel.projects.build(:name => "Lie in it")
assert_equal devel.projects.last, proj2
- assert !proj2.persisted?
+ assert proj2.new_record?
devel.save
- assert devel.persisted?
- assert proj2.persisted?
+ assert !devel.new_record?
+ assert !proj2.new_record?
assert_equal devel.projects.last, proj2
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
@@ -316,7 +316,7 @@ def test_create
assert_equal devel.projects.last, proj
assert !devel.projects.loaded?