mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge branch 'master' of git://github.com/rails/rails
This commit is contained in:
@@ -15,5 +15,6 @@ module AbstractController
|
||||
autoload :LocalizedCache
|
||||
autoload :Logger
|
||||
autoload :Rendering
|
||||
autoload :Translation
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module ActionController
|
||||
module AbstractController
|
||||
module Translation
|
||||
def translate(*args)
|
||||
I18n.translate(*args)
|
||||
@@ -8,7 +8,6 @@ module ActionController
|
||||
autoload :Base
|
||||
autoload :Caching
|
||||
autoload :PolymorphicRoutes
|
||||
autoload :Translation
|
||||
autoload :Metal
|
||||
autoload :Middleware
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,6 +60,7 @@ module ActionController
|
||||
# :api: private
|
||||
def dispatch(name, env)
|
||||
@_env = env
|
||||
@_env['action_controller.instance'] = self
|
||||
process(name)
|
||||
to_a
|
||||
end
|
||||
|
||||
@@ -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={})
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
def delete_all
|
||||
arel.delete.tap { reset }
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user