Merge branch 'master' of git://github.com/rails/rails

This commit is contained in:
José Valim and Mikel Lindsaar
2010-01-21 11:42:22 +11:00
22 changed files with 310 additions and 347 deletions

View File

@@ -15,5 +15,6 @@ module AbstractController
autoload :LocalizedCache
autoload :Logger
autoload :Rendering
autoload :Translation
autoload :UrlFor
end

View File

@@ -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

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
module ActionController
module AbstractController
module Translation
def translate(*args)
I18n.translate(*args)

View File

@@ -8,7 +8,6 @@ module ActionController
autoload :Base
autoload :Caching
autoload :PolymorphicRoutes
autoload :Translation
autoload :Metal
autoload :Middleware

View File

@@ -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

View File

@@ -60,6 +60,7 @@ module ActionController
# :api: private
def dispatch(name, env)
@_env = env
@_env['action_controller.instance'] = self
process(name)
to_a
end

View File

@@ -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={})

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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