aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/abstract_controller.rb1
-rw-r--r--actionpack/lib/abstract_controller/localized_cache.rb2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb24
-rw-r--r--actionpack/lib/abstract_controller/translation.rb (renamed from actionpack/lib/action_controller/translation.rb)2
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb31
-rw-r--r--actionpack/lib/action_controller/metal.rb1
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb16
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb7
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb26
-rw-r--r--actionpack/lib/action_dispatch/middleware/notifications.rb17
-rw-r--r--actionpack/lib/action_dispatch/routing/route.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb38
-rw-r--r--actionpack/test/abstract/translation_test.rb (renamed from actionpack/test/controller/translation_test.rb)0
-rwxr-xr-xactiverecord/lib/active_record/base.rb232
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/named_scope.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb172
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb44
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb12
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb14
-rw-r--r--railties/lib/rails/application.rb7
22 files changed, 310 insertions, 347 deletions
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index efc35a7e56..725d8fb8fc 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -15,5 +15,6 @@ module AbstractController
autoload :LocalizedCache
autoload :Logger
autoload :Rendering
+ autoload :Translation
autoload :UrlFor
end
diff --git a/actionpack/lib/abstract_controller/localized_cache.rb b/actionpack/lib/abstract_controller/localized_cache.rb
index bf648af60a..5e3efa002c 100644
--- a/actionpack/lib/abstract_controller/localized_cache.rb
+++ b/actionpack/lib/abstract_controller/localized_cache.rb
@@ -34,7 +34,7 @@ module AbstractController
end
end
- def render(options)
+ def render(*args)
Thread.current[:format_locale_key] = HashKey.get(self.class, formats, I18n.locale)
super
end
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 0dab4a3cc0..826e82c8c6 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -40,12 +40,13 @@ module AbstractController
# Mostly abstracts the fact that calling render twice is a DoubleRenderError.
# Delegates render_to_body and sticks the result in self.response_body.
- def render(*args)
+ def render(*args, &block)
if response_body
- raise AbstractController::DoubleRenderError, "Can only render or redirect once per action"
+ raise AbstractController::DoubleRenderError
end
- self.response_body = render_to_body(*args)
+ options = _normalize_options(*args, &block)
+ self.response_body = render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
@@ -69,7 +70,8 @@ module AbstractController
# render_to_body into a String.
#
# :api: plugin
- def render_to_string(options = {})
+ def render_to_string(*args)
+ options = _normalize_options(*args)
AbstractController::Rendering.body_to_s(render_to_body(options))
end
@@ -96,6 +98,20 @@ module AbstractController
_view_paths
end
+ # Normalize options, by converting render "foo" to render :template => "foo"
+ # and render "/foo" to render :file => "/foo".
+ def _normalize_options(action=nil, options={})
+ case action
+ when Hash
+ options, action = action, nil
+ when String
+ key = (action.index("/") == 0 ? :file : :template)
+ options.merge!(key => action)
+ end
+
+ options
+ end
+
# Return a string representation of a Rack-compatible response body.
def self.body_to_s(body)
if body.respond_to?(:to_str)
diff --git a/actionpack/lib/action_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb
index 65e9eddb0a..6d68cf4944 100644
--- a/actionpack/lib/action_controller/translation.rb
+++ b/actionpack/lib/abstract_controller/translation.rb
@@ -1,4 +1,4 @@
-module ActionController
+module AbstractController
module Translation
def translate(*args)
I18n.translate(*args)
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 8bc2cc79d2..fa4a253ec1 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -8,7 +8,6 @@ module ActionController
autoload :Base
autoload :Caching
autoload :PolymorphicRoutes
- autoload :Translation
autoload :Metal
autoload :Middleware
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index f86a61d791..21a811c004 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -4,6 +4,7 @@ module ActionController
include AbstractController::Callbacks
include AbstractController::Layouts
+ include AbstractController::Translation
include ActionController::Helpers
helper :all # By default, all helpers should be included
@@ -33,7 +34,6 @@ module ActionController
include ActionController::Streaming
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Digest::ControllerMethods
- include ActionController::Translation
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
@@ -74,17 +74,14 @@ module ActionController
@subclasses ||= []
end
- def _normalize_options(action = nil, options = {}, &blk)
- if action.is_a?(Hash)
- options, action = action, nil
- elsif action.is_a?(String) || action.is_a?(Symbol)
- key = case action = action.to_s
- when %r{^/} then :file
- when %r{/} then :template
- else :action
- end
- options.merge! key => action
- elsif action
+ def _normalize_options(action=nil, options={}, &blk)
+ case action
+ when NilClass
+ when Hash, String
+ options = super
+ when Symbol
+ options.merge! :action => action
+ else
options.merge! :partial => action
end
@@ -99,15 +96,5 @@ module ActionController
options[:update] = blk if block_given?
options
end
-
- def render(action = nil, options = {}, &blk)
- options = _normalize_options(action, options, &blk)
- super(options)
- end
-
- def render_to_string(action = nil, options = {}, &blk)
- options = _normalize_options(action, options, &blk)
- super(options)
- end
end
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 1819c0f886..57627a6f0b 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -60,6 +60,7 @@ module ActionController
# :api: private
def dispatch(name, env)
@_env = env
+ @_env['action_controller.instance'] = self
process(name)
to_a
end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 7b2b037c67..19c962bafa 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -32,18 +32,12 @@ module ActionController
end
end
- def render(*args, &block)
- if logger
- render_output = nil
-
- self.view_runtime = cleanup_view_runtime do
- Benchmark.ms { render_output = super }
- end
-
- render_output
- else
- super
+ def render(*args)
+ render_output = nil
+ self.view_runtime = cleanup_view_runtime do
+ Benchmark.ms { render_output = super }
end
+ render_output
end
def send_file(path, options={})
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 74e50bb032..475ed54167 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -12,9 +12,10 @@ module ActionController
super
end
- def render(options)
- super
- self.content_type ||= options[:_template].mime_type.to_s
+ def render(*args)
+ args << {} unless args.last.is_a?(Hash)
+ super(*args)
+ self.content_type ||= args.last[:_template].mime_type.to_s
response_body
end
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index 97546d5f93..68ba3637bf 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -29,9 +29,31 @@ module ActionDispatch
def path_parameters
@env["action_dispatch.request.path_parameters"] ||= {}
end
-
- private
+ def filter_parameters
+ # TODO: Remove dependency on controller
+ if controller = @env['action_controller.instance']
+ controller.send(:filter_parameters, params)
+ else
+ params
+ end
+ end
+
+ def filter_env
+ if controller = @env['action_controller.instance']
+ @env.map do |key, value|
+ if (key =~ /RAW_POST_DATA/i)
+ '[FILTERED]'
+ else
+ controller.send(:filter_parameters, {key => value}).values[0]
+ end
+ end
+ else
+ env
+ end
+ end
+
+ private
# Convert nested Hashs to HashWithIndifferentAccess
def normalize_parameters(value)
case value
diff --git a/actionpack/lib/action_dispatch/middleware/notifications.rb b/actionpack/lib/action_dispatch/middleware/notifications.rb
index c3776d53a8..ce3732b740 100644
--- a/actionpack/lib/action_dispatch/middleware/notifications.rb
+++ b/actionpack/lib/action_dispatch/middleware/notifications.rb
@@ -9,7 +9,9 @@ module ActionDispatch
end
def call(env)
- payload = retrieve_payload_from_env(env)
+ request = Request.new(env)
+ payload = retrieve_payload_from_env(request.filter_env)
+
ActiveSupport::Notifications.instrument("action_dispatch.before_dispatch", payload)
ActiveSupport::Notifications.instrument!("action_dispatch.after_dispatch", payload) do
@@ -21,11 +23,10 @@ module ActionDispatch
raise exception
end
- protected
-
- # Remove any rack related constants from the env, like rack.input.
- def retrieve_payload_from_env(env)
- Hash[:env => env.except(*env.keys.select { |k| k.to_s.index("rack.") == 0 })]
- end
+ protected
+ # Remove any rack related constants from the env, like rack.input.
+ def retrieve_payload_from_env(env)
+ Hash[:env => env.except(*env.keys.select { |k| k.to_s.index("rack.") == 0 })]
+ end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb
index f1431e7a37..e6e44d3546 100644
--- a/actionpack/lib/action_dispatch/routing/route.rb
+++ b/actionpack/lib/action_dispatch/routing/route.rb
@@ -44,6 +44,12 @@ module ActionDispatch
def to_a
[@app, @conditions, @defaults, @name]
end
+
+ def to_s
+ @to_s ||= begin
+ "%-6s %-40s %s" % [(verb || :any).to_s.upcase, path, requirements.inspect]
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index d4c9df7ebd..2093bb3a0e 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -240,9 +240,9 @@ module ActionDispatch
path = location.query ? "#{location.path}?#{location.query}" : location.path
end
- [ControllerCapture, ActionController::Testing].each do |mod|
- unless ActionController::Base < mod
- ActionController::Base.class_eval { include mod }
+ unless ActionController::Base < ActionController::Testing
+ ActionController::Base.class_eval do
+ include ActionController::Testing
end
end
@@ -269,16 +269,15 @@ module ActionDispatch
end
session = Rack::Test::Session.new(@mock_session)
-
- @controller = ActionController::Base.capture_instantiation do
- session.request(path, env)
- end
+ session.request(path, env)
@request_count += 1
@request = ActionDispatch::Request.new(session.last_request.env)
@response = ActionDispatch::TestResponse.from_response(@mock_session.last_response)
@html_document = nil
+ @controller = session.last_request.env['action_controller.instance']
+
return response.status
end
@@ -296,31 +295,6 @@ module ActionDispatch
end
end
- # A module used to extend ActionController::Base, so that integration tests
- # can capture the controller used to satisfy a request.
- module ControllerCapture #:nodoc:
- extend ActiveSupport::Concern
-
- included do
- alias_method_chain :initialize, :capture
- end
-
- def initialize_with_capture(*args)
- initialize_without_capture
- self.class.last_instantiation ||= self
- end
-
- module ClassMethods #:nodoc:
- mattr_accessor :last_instantiation
-
- def capture_instantiation
- self.last_instantiation = nil
- yield
- return last_instantiation
- end
- end
- end
-
module Runner
def app
@app
diff --git a/actionpack/test/controller/translation_test.rb b/actionpack/test/abstract/translation_test.rb
index 0bf61a6556..0bf61a6556 100644
--- a/actionpack/test/controller/translation_test.rb
+++ b/actionpack/test/abstract/translation_test.rb
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 8f2ea10206..bc1b0bde31 100755
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -556,7 +556,7 @@ module ActiveRecord #:nodoc:
end
alias :colorize_logging= :colorize_logging
- delegate :find, :first, :last, :all, :to => :scoped
+ delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped
@@ -586,40 +586,6 @@ module ActiveRecord #:nodoc:
connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) }
end
- # Returns true if a record exists in the table that matches the +id+ or
- # conditions given, or false otherwise. The argument can take five forms:
- #
- # * Integer - Finds the record with this primary key.
- # * String - Finds the record with a primary key corresponding to this
- # string (such as <tt>'5'</tt>).
- # * Array - Finds the record that matches these +find+-style conditions
- # (such as <tt>['color = ?', 'red']</tt>).
- # * Hash - Finds the record that matches these +find+-style conditions
- # (such as <tt>{:color => 'red'}</tt>).
- # * No args - Returns false if the table is empty, true otherwise.
- #
- # For more information about specifying conditions as a Hash or Array,
- # see the Conditions section in the introduction to ActiveRecord::Base.
- #
- # Note: You can't pass in a condition as a string (like <tt>name =
- # 'Jamie'</tt>), since it would be sanitized and then queried against
- # the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
- #
- # ==== Examples
- # Person.exists?(5)
- # Person.exists?('5')
- # Person.exists?(:name => "David")
- # Person.exists?(['name LIKE ?', "%#{query}%"])
- # Person.exists?
- def exists?(id_or_conditions = nil)
- case id_or_conditions
- when Array, Hash
- where(id_or_conditions).exists?
- else
- scoped.exists?(id_or_conditions)
- end
- end
-
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
# The resulting object is returned whether the object was saved successfully to the database or not.
#
@@ -653,177 +619,6 @@ module ActiveRecord #:nodoc:
end
end
- # Updates an object (or multiple objects) and saves it to the database, if validations pass.
- # The resulting object is returned whether the object was saved successfully to the database or not.
- #
- # ==== Parameters
- #
- # * +id+ - This should be the id or an array of ids to be updated.
- # * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes.
- #
- # ==== Examples
- #
- # # Updating one record:
- # Person.update(15, :user_name => 'Samuel', :group => 'expert')
- #
- # # Updating multiple records:
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
- # Person.update(people.keys, people.values)
- def update(id, attributes)
- if id.is_a?(Array)
- idx = -1
- id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
- else
- object = find(id)
- object.update_attributes(attributes)
- object
- end
- end
-
- # Deletes the row with a primary key matching the +id+ argument, using a
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
- # Record objects are not instantiated, so the object's callbacks are not
- # executed, including any <tt>:dependent</tt> association options or
- # Observer methods.
- #
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
- #
- # Note: Although it is often much faster than the alternative,
- # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
- # your application that ensures referential integrity or performs other
- # essential jobs.
- #
- # ==== Examples
- #
- # # Delete a single row
- # Todo.delete(1)
- #
- # # Delete multiple rows
- # Todo.delete([2,3,4])
- def delete(id_or_array)
- scoped.delete(id_or_array)
- end
-
- # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
- # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
- #
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
- # from the attributes, and then calls destroy on it.
- #
- # ==== Parameters
- #
- # * +id+ - Can be either an Integer or an Array of Integers.
- #
- # ==== Examples
- #
- # # Destroy a single object
- # Todo.destroy(1)
- #
- # # Destroy multiple objects
- # todos = [1,2,3]
- # Todo.destroy(todos)
- def destroy(id)
- if id.is_a?(Array)
- id.map { |one_id| destroy(one_id) }
- else
- find(id).destroy
- end
- end
-
- # Updates all records with details given if they match a set of conditions supplied, limits and order can
- # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
- # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
- # or validations.
- #
- # ==== Parameters
- #
- # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
- # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement. See conditions in the intro.
- # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
- #
- # ==== Examples
- #
- # # Update all customers with the given attributes
- # Customer.update_all :wants_email => true
- #
- # # Update all books with 'Rails' in their title
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'"
- #
- # # Update all avatars migrated more than a week ago
- # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
- #
- # # Update all books that match our conditions, but limit it to 5 ordered by date
- # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
- def update_all(updates, conditions = nil, options = {})
- relation = unscoped
-
- relation = relation.where(conditions) if conditions
- relation = relation.limit(options[:limit]) if options[:limit].present?
- relation = relation.order(options[:order]) if options[:order].present?
-
- if current_scoped_methods && current_scoped_methods.limit_value.present? && current_scoped_methods.order_values.present?
- # Only take order from scope if limit is also provided by scope, this
- # is useful for updating a has_many association with a limit.
- relation = current_scoped_methods.merge(relation) if current_scoped_methods
- else
- relation = current_scoped_methods.except(:limit, :order).merge(relation) if current_scoped_methods
- end
-
- relation.update(sanitize_sql_for_assignment(updates))
- end
-
- # Destroys the records matching +conditions+ by instantiating each
- # record and calling its +destroy+ method. Each object's callbacks are
- # executed (including <tt>:dependent</tt> association options and
- # +before_destroy+/+after_destroy+ Observer methods). Returns the
- # collection of objects that were destroyed; each will be frozen, to
- # reflect that no changes should be made (since they can't be
- # persisted).
- #
- # Note: Instantiation, callback execution, and deletion of each
- # record can be time consuming when you're removing many records at
- # once. It generates at least one SQL +DELETE+ query per record (or
- # possibly more, to enforce your callbacks). If you want to delete many
- # rows quickly, without concern for their associations or callbacks, use
- # +delete_all+ instead.
- #
- # ==== Parameters
- #
- # * +conditions+ - A string, array, or hash that specifies which records
- # to destroy. If omitted, all records are destroyed. See the
- # Conditions section in the introduction to ActiveRecord::Base for
- # more information.
- #
- # ==== Examples
- #
- # Person.destroy_all("last_login < '2004-04-04'")
- # Person.destroy_all(:status => "inactive")
- def destroy_all(conditions = nil)
- where(conditions).destroy_all
- end
-
- # Deletes the records matching +conditions+ without instantiating the records first, and hence not
- # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
- # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
- # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
- # the number of rows affected.
- #
- # ==== Parameters
- #
- # * +conditions+ - Conditions are specified the same way as with +find+ method.
- #
- # ==== Example
- #
- # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
- # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
- #
- # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent
- # associations or call your <tt>before_*</tt> or +after_destroy+ callbacks, use the +destroy_all+ method instead.
- def delete_all(conditions = nil)
- where(conditions).delete_all
- end
-
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
# The use of this method should be restricted to complicated SQL queries that can't be executed
# using the ActiveRecord::Calculations class methods. Look into those before using this.
@@ -1111,6 +906,10 @@ module ActiveRecord #:nodoc:
reset_table_name
end
+ def quoted_table_name
+ @quoted_table_name ||= connection.quote_table_name(table_name)
+ end
+
def reset_table_name #:nodoc:
base = base_class
@@ -1128,6 +927,7 @@ module ActiveRecord #:nodoc:
name = "#{table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}"
end
+ @quoted_table_name = nil
set_table_name(name)
name
end
@@ -1374,20 +1174,6 @@ module ActiveRecord #:nodoc:
store_full_sti_class ? name : name.demodulize
end
- # Merges conditions so that the result is a valid +condition+
- def merge_conditions(*conditions)
- segments = []
-
- conditions.each do |condition|
- unless condition.blank?
- sql = sanitize_sql(condition)
- segments << sql unless sql.blank?
- end
- end
-
- "(#{segments.join(') AND (')})" unless segments.empty?
- end
-
def unscoped
@unscoped ||= Relation.new(self, arel_table)
finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped
@@ -2324,7 +2110,7 @@ module ActiveRecord #:nodoc:
def update(attribute_names = @attributes.keys)
attributes_with_values = arel_attributes_values(false, false, attribute_names)
return 0 if attributes_with_values.empty?
- self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values)
+ self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
end
# Creates a record with values matching those of the instance attributes
@@ -2548,10 +2334,6 @@ module ActiveRecord #:nodoc:
hash.inject([]) { |list, pair| list << "#{pair.first} = #{pair.last}" }.join(", ")
end
- def self.quoted_table_name
- self.connection.quote_table_name(self.table_name)
- end
-
def quote_columns(quoter, hash)
hash.inject({}) do |quoted, (name, value)|
quoted[quoter.quote_column_name(name)] = value
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index bf0683eb8f..9044ca418b 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -84,7 +84,7 @@ module ActiveRecord
relation.table[self.class.primary_key].eq(quoted_id).and(
relation.table[self.class.locking_column].eq(quote_value(previous_value))
)
- ).update(arel_attributes_values(false, false, attribute_names))
+ ).arel.update(arel_attributes_values(false, false, attribute_names))
unless affected_rows == 1
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index d606934dce..ff6c041ef4 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -165,7 +165,7 @@ module ActiveRecord
end
def ==(other)
- other.respond_to?(:to_a) ? to_a == other.to_a : false
+ other.respond_to?(:to_ary) ? to_a == other.to_a : false
end
private
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index decde50427..c9fff15199 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -8,7 +8,7 @@ module ActiveRecord
include FinderMethods, Calculations, SpawnMethods, QueryMethods
delegate :length, :collect, :map, :each, :all?, :include?, :to => :to_a
- delegate :insert, :update, :to => :arel
+ delegate :insert, :to => :arel
attr_reader :table, :klass
@@ -80,19 +80,177 @@ module ActiveRecord
if block_given?
to_a.many? { |*block_args| yield(*block_args) }
else
- arel.send(:taken).present? ? to_a.many? : size > 1
+ @limit_value.present? ? to_a.many? : size > 1
end
end
- def destroy_all
- to_a.each {|object| object.destroy}
- reset
+ # Updates all records with details given if they match a set of conditions supplied, limits and order can
+ # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
+ # database. It does not instantiate the involved models and it does not trigger Active Record callbacks
+ # or validations.
+ #
+ # ==== Parameters
+ #
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
+ # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement. See conditions in the intro.
+ # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage.
+ #
+ # ==== Examples
+ #
+ # # Update all customers with the given attributes
+ # Customer.update_all :wants_email => true
+ #
+ # # Update all books with 'Rails' in their title
+ # Book.update_all "author = 'David'", "title LIKE '%Rails%'"
+ #
+ # # Update all avatars migrated more than a week ago
+ # Avatar.update_all ['migrated_at = ?', Time.now.utc], ['migrated_at > ?', 1.week.ago]
+ #
+ # # Update all books that match our conditions, but limit it to 5 ordered by date
+ # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5
+ def update_all(updates, conditions = nil, options = {})
+ if conditions || options.present?
+ where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
+ else
+ # Apply limit and order only if they're both present
+ if @limit_value.present? == @order_values.present?
+ arel.update(@klass.send(:sanitize_sql_for_assignment, updates))
+ else
+ except(:limit, :order).update_all(updates)
+ end
+ end
+ end
+
+ # Updates an object (or multiple objects) and saves it to the database, if validations pass.
+ # The resulting object is returned whether the object was saved successfully to the database or not.
+ #
+ # ==== Parameters
+ #
+ # * +id+ - This should be the id or an array of ids to be updated.
+ # * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes.
+ #
+ # ==== Examples
+ #
+ # # Updating one record:
+ # Person.update(15, :user_name => 'Samuel', :group => 'expert')
+ #
+ # # Updating multiple records:
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
+ # Person.update(people.keys, people.values)
+ def update(id, attributes)
+ if id.is_a?(Array)
+ idx = -1
+ id.collect { |one_id| idx += 1; update(one_id, attributes[idx]) }
+ else
+ object = find(id)
+ object.update_attributes(attributes)
+ object
+ end
+ end
+
+ # Destroys the records matching +conditions+ by instantiating each
+ # record and calling its +destroy+ method. Each object's callbacks are
+ # executed (including <tt>:dependent</tt> association options and
+ # +before_destroy+/+after_destroy+ Observer methods). Returns the
+ # collection of objects that were destroyed; each will be frozen, to
+ # reflect that no changes should be made (since they can't be
+ # persisted).
+ #
+ # Note: Instantiation, callback execution, and deletion of each
+ # record can be time consuming when you're removing many records at
+ # once. It generates at least one SQL +DELETE+ query per record (or
+ # possibly more, to enforce your callbacks). If you want to delete many
+ # rows quickly, without concern for their associations or callbacks, use
+ # +delete_all+ instead.
+ #
+ # ==== Parameters
+ #
+ # * +conditions+ - A string, array, or hash that specifies which records
+ # to destroy. If omitted, all records are destroyed. See the
+ # Conditions section in the introduction to ActiveRecord::Base for
+ # more information.
+ #
+ # ==== Examples
+ #
+ # Person.destroy_all("last_login < '2004-04-04'")
+ # Person.destroy_all(:status => "inactive")
+ def destroy_all(conditions = nil)
+ if conditions
+ where(conditions).destroy_all
+ else
+ to_a.each {|object| object.destroy}
+ reset
+ end
+ end
+
+ # Destroy an object (or multiple objects) that has the given id, the object is instantiated first,
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
+ # less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
+ #
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
+ # from the attributes, and then calls destroy on it.
+ #
+ # ==== Parameters
+ #
+ # * +id+ - Can be either an Integer or an Array of Integers.
+ #
+ # ==== Examples
+ #
+ # # Destroy a single object
+ # Todo.destroy(1)
+ #
+ # # Destroy multiple objects
+ # todos = [1,2,3]
+ # Todo.destroy(todos)
+ def destroy(id)
+ if id.is_a?(Array)
+ id.map { |one_id| destroy(one_id) }
+ else
+ find(id).destroy
+ end
end
- def delete_all
- arel.delete.tap { reset }
+ # Deletes the records matching +conditions+ without instantiating the records first, and hence not
+ # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that
+ # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations
+ # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns
+ # the number of rows affected.
+ #
+ # ==== Parameters
+ #
+ # * +conditions+ - Conditions are specified the same way as with +find+ method.
+ #
+ # ==== Example
+ #
+ # Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
+ # Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
+ #
+ # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent
+ # associations or call your <tt>before_*</tt> or +after_destroy+ callbacks, use the +destroy_all+ method instead.
+ def delete_all(conditions = nil)
+ conditions ? where(conditions).delete_all : arel.delete.tap { reset }
end
+ # Deletes the row with a primary key matching the +id+ argument, using a
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
+ # Record objects are not instantiated, so the object's callbacks are not
+ # executed, including any <tt>:dependent</tt> association options or
+ # Observer methods.
+ #
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
+ #
+ # Note: Although it is often much faster than the alternative,
+ # <tt>#destroy</tt>, skipping callbacks might bypass business logic in
+ # your application that ensures referential integrity or performs other
+ # essential jobs.
+ #
+ # ==== Examples
+ #
+ # # Delete a single row
+ # Todo.delete(1)
+ #
+ # # Delete multiple rows
+ # Todo.delete([2,3,4])
def delete(id_or_array)
where(@klass.primary_key => id_or_array).delete_all
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 999309d2bd..d6d3d66642 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -119,10 +119,40 @@ module ActiveRecord
args.any? ? apply_finder_options(args.first).to_a : to_a
end
+ # Returns true if a record exists in the table that matches the +id+ or
+ # conditions given, or false otherwise. The argument can take five forms:
+ #
+ # * Integer - Finds the record with this primary key.
+ # * String - Finds the record with a primary key corresponding to this
+ # string (such as <tt>'5'</tt>).
+ # * Array - Finds the record that matches these +find+-style conditions
+ # (such as <tt>['color = ?', 'red']</tt>).
+ # * Hash - Finds the record that matches these +find+-style conditions
+ # (such as <tt>{:color => 'red'}</tt>).
+ # * No args - Returns false if the table is empty, true otherwise.
+ #
+ # For more information about specifying conditions as a Hash or Array,
+ # see the Conditions section in the introduction to ActiveRecord::Base.
+ #
+ # Note: You can't pass in a condition as a string (like <tt>name =
+ # 'Jamie'</tt>), since it would be sanitized and then queried against
+ # the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
+ #
+ # ==== Examples
+ # Person.exists?(5)
+ # Person.exists?('5')
+ # Person.exists?(:name => "David")
+ # Person.exists?(['name LIKE ?', "%#{query}%"])
+ # Person.exists?
def exists?(id = nil)
- relation = select(primary_key).limit(1)
- relation = relation.where(primary_key.eq(id)) if id
- relation.first ? true : false
+ case id
+ when Array, Hash
+ where(id).exists?
+ else
+ relation = select(primary_key).limit(1)
+ relation = relation.where(primary_key.eq(id)) if id
+ relation.first ? true : false
+ end
end
protected
@@ -242,15 +272,15 @@ module ActiveRecord
result = where(primary_key.in(ids)).all
expected_size =
- if arel.taken && ids.size > arel.taken
- arel.taken
+ if @limit_value && ids.size > @limit_value
+ @limit_value
else
ids.size
end
# 11 ids with limit 3, offset 9 should give 2 results.
- if arel.skipped && (ids.size - arel.skipped < expected_size)
- expected_size = ids.size - arel.skipped
+ if @offset_value && (ids.size - @offset_value < expected_size)
+ expected_size = ids.size - @offset_value
end
if result.size == expected_size
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 163d698b5c..d0689cd93e 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -140,16 +140,18 @@ module ActiveRecord
selects = @select_values.uniq
+ quoted_table_name = @klass.quoted_table_name
+
if selects.present?
selects.each do |s|
@implicit_readonly = false
arel = arel.project(s) if s.present?
end
- elsif joins.present?
- arel = arel.project(@klass.quoted_table_name + '.*')
+ else
+ arel = arel.project(quoted_table_name + '.*')
end
- arel = arel.from(@from_value) if @from_value.present?
+ arel = @from_value.present? ? arel.from(@from_value) : arel.from(quoted_table_name)
case @lock_value
when TrueClass
@@ -167,8 +169,8 @@ module ActiveRecord
builder = PredicateBuilder.new(table.engine)
conditions = if [String, Array].include?(args.first.class)
- merged = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first)
- Arel::SqlLiteral.new(merged) if merged
+ sql = @klass.send(:sanitize_sql, args.size > 1 ? args : args.first)
+ Arel::SqlLiteral.new("(#{sql})") if sql.present?
elsif args.first.is_a?(Hash)
attributes = @klass.send(:expand_hash_conditions_for_aggregates, args.first)
builder.build_from_hash(attributes, table)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 1577a9b116..cccf413e67 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -1,17 +1,7 @@
module ActiveRecord
module SpawnMethods
- def spawn(arel_table = self.table)
- relation = self.class.new(@klass, arel_table)
-
- (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |query_method|
- relation.send(:"#{query_method}_values=", send(:"#{query_method}_values"))
- end
-
- Relation::SINGLE_VALUE_METHODS.each do |query_method|
- relation.send(:"#{query_method}_value=", send(:"#{query_method}_value"))
- end
-
- relation
+ def spawn
+ clone.reset
end
def merge(r)
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index b92a7ff129..743681359c 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -93,13 +93,12 @@ module Rails
initializers
end
- # TODO: Fix this method
+ # TODO: Fix this method. It loads all railties independent if :all is given
+ # or not, otherwise frameworks are never loaded.
def plugins
@plugins ||= begin
plugin_names = (config.plugins || [:all]).map { |p| p.to_sym }
- Railtie.plugins.select { |p|
- plugin_names.include?(:all) || plugin_names.include?(p.plugin_name)
- }.map { |p| p.new } + Plugin.all(plugin_names, config.paths.vendor.plugins)
+ Railtie.plugins.map(&:new) + Plugin.all(plugin_names, config.paths.vendor.plugins)
end
end