mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Merge branch '3-2-stable' of github.com:rails/rails into 3-2-stable
* '3-2-stable' of github.com:rails/rails: Use sass-rails >= 3.2.2 for new apps Make ActiveRecord::Relation#pluck work with serialized attributes Make read_attribute code path accessible at the class level
This commit is contained in:
@@ -39,6 +39,26 @@ module ActiveRecord
|
||||
super
|
||||
end
|
||||
|
||||
def type_cast_attribute(attr_name, attributes, cache = {}) #:nodoc:
|
||||
return unless attr_name
|
||||
attr_name = attr_name.to_s
|
||||
|
||||
if generated_external_attribute_methods.method_defined?(attr_name)
|
||||
if attributes.has_key?(attr_name) || attr_name == 'id'
|
||||
generated_external_attribute_methods.send(attr_name, attributes[attr_name], attributes, cache, attr_name)
|
||||
end
|
||||
elsif !attribute_methods_generated?
|
||||
# If we haven't generated the caster methods yet, do that and
|
||||
# then try again
|
||||
define_attribute_methods
|
||||
type_cast_attribute(attr_name, attributes, cache)
|
||||
else
|
||||
# If we get here, the attribute has no associated DB column, so
|
||||
# just return it verbatim.
|
||||
attributes[attr_name]
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
# We want to generate the methods via module_eval rather than define_method,
|
||||
# because define_method is slower on dispatch and uses more memory (because it
|
||||
@@ -105,25 +125,7 @@ module ActiveRecord
|
||||
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
||||
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
|
||||
def read_attribute(attr_name)
|
||||
return unless attr_name
|
||||
|
||||
attr_name = attr_name.to_s
|
||||
methods = self.class.generated_external_attribute_methods
|
||||
|
||||
if methods.method_defined?(attr_name)
|
||||
if @attributes.has_key?(attr_name) || attr_name == 'id'
|
||||
methods.send(attr_name, @attributes[attr_name], @attributes, @attributes_cache, attr_name)
|
||||
end
|
||||
elsif !self.class.attribute_methods_generated?
|
||||
# If we haven't generated the caster methods yet, do that and
|
||||
# then try again
|
||||
self.class.define_attribute_methods
|
||||
read_attribute(attr_name)
|
||||
else
|
||||
# If we get here, the attribute has no associated DB column, so
|
||||
# just return it verbatim.
|
||||
@attributes[attr_name]
|
||||
end
|
||||
self.class.type_cast_attribute(attr_name, @attributes, @attributes_cache)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -58,6 +58,18 @@ module ActiveRecord
|
||||
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
|
||||
end
|
||||
|
||||
def initialize_attributes(attributes) #:nodoc:
|
||||
super
|
||||
|
||||
serialized_attributes.each do |key, coder|
|
||||
if attributes.key?(key)
|
||||
attributes[key] = Attribute.new(coder, attributes[key], :serialized)
|
||||
end
|
||||
end
|
||||
|
||||
attributes
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def attribute_cast_code(attr_name)
|
||||
@@ -69,14 +81,6 @@ module ActiveRecord
|
||||
end
|
||||
end
|
||||
|
||||
def set_serialized_attributes
|
||||
self.class.serialized_attributes.each do |key, coder|
|
||||
if @attributes.key?(key)
|
||||
@attributes[key] = Attribute.new(coder, @attributes[key], :serialized)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def type_cast_attribute_for_write(column, value)
|
||||
if column && coder = self.class.serialized_attributes[column.name]
|
||||
Attribute.new(coder, value, :unserialized)
|
||||
|
||||
@@ -469,7 +469,7 @@ module ActiveRecord #:nodoc:
|
||||
# # Instantiates a single new object bypassing mass-assignment security
|
||||
# User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
|
||||
def initialize(attributes = nil, options = {})
|
||||
@attributes = attributes_from_column_definition
|
||||
@attributes = self.class.initialize_attributes(self.class.column_defaults.dup)
|
||||
@association_cache = {}
|
||||
@aggregation_cache = {}
|
||||
@attributes_cache = {}
|
||||
@@ -482,7 +482,6 @@ module ActiveRecord #:nodoc:
|
||||
@relation = nil
|
||||
|
||||
ensure_proper_type
|
||||
set_serialized_attributes
|
||||
|
||||
populate_with_current_scope_attributes
|
||||
|
||||
@@ -503,11 +502,9 @@ module ActiveRecord #:nodoc:
|
||||
# post.init_with('attributes' => { 'title' => 'hello world' })
|
||||
# post.title # => 'hello world'
|
||||
def init_with(coder)
|
||||
@attributes = coder['attributes']
|
||||
@attributes = self.class.initialize_attributes(coder['attributes'])
|
||||
@relation = nil
|
||||
|
||||
set_serialized_attributes
|
||||
|
||||
@attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
|
||||
@association_cache = {}
|
||||
@aggregation_cache = {}
|
||||
@@ -534,7 +531,7 @@ module ActiveRecord #:nodoc:
|
||||
_run_after_initialize_callbacks if respond_to?(:_run_after_initialize_callbacks)
|
||||
|
||||
@changed_attributes = {}
|
||||
attributes_from_column_definition.each do |attr, orig_value|
|
||||
self.class.column_defaults.each do |attr, orig_value|
|
||||
@changed_attributes[attr] = orig_value if field_changed?(attr, orig_value, @attributes[attr])
|
||||
end
|
||||
|
||||
|
||||
@@ -64,21 +64,6 @@ module ActiveRecord
|
||||
send(lock_col + '=', previous_lock_value + 1)
|
||||
end
|
||||
|
||||
def attributes_from_column_definition
|
||||
result = self.class.column_defaults.dup
|
||||
|
||||
# If the locking column has no default value set,
|
||||
# start the lock version at zero. Note we can't use
|
||||
# <tt>locking_enabled?</tt> at this point as
|
||||
# <tt>@attributes</tt> may not have been initialized yet.
|
||||
|
||||
if result.key?(self.class.locking_column) && lock_optimistically
|
||||
result[self.class.locking_column] ||= 0
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
def update(attribute_names = @attributes.keys) #:nodoc:
|
||||
return super unless locking_enabled?
|
||||
return 0 if attribute_names.empty?
|
||||
@@ -180,6 +165,18 @@ module ActiveRecord
|
||||
counters = counters.merge(locking_column => 1) if locking_enabled?
|
||||
super
|
||||
end
|
||||
|
||||
# If the locking column has no default value set,
|
||||
# start the lock version at zero. Note we can't use
|
||||
# <tt>locking_enabled?</tt> at this point as
|
||||
# <tt>@attributes</tt> may not have been initialized yet.
|
||||
def initialize_attributes(attributes) #:nodoc:
|
||||
if attributes.key?(locking_column) && lock_optimistically
|
||||
attributes[locking_column] ||= 0
|
||||
end
|
||||
|
||||
attributes
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -368,13 +368,5 @@ module ActiveRecord
|
||||
@new_record = false
|
||||
id
|
||||
end
|
||||
|
||||
# Initializes the attributes array with keys matching the columns from the linked table and
|
||||
# the values matching the corresponding default value of that column, so
|
||||
# that a new instance, or one populated from a passed-in Hash, still has all the attributes
|
||||
# that instances loaded from the database would.
|
||||
def attributes_from_column_definition
|
||||
self.class.column_defaults.dup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -168,7 +168,7 @@ module ActiveRecord
|
||||
|
||||
# This method is designed to perform select by a single column as direct SQL query
|
||||
# Returns <tt>Array</tt> with values of the specified column name
|
||||
# The values has same data type as column.
|
||||
# The values has same data type as column.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
@@ -177,9 +177,9 @@ module ActiveRecord
|
||||
# Person.where(:confirmed => true).limit(5).pluck(:id)
|
||||
#
|
||||
def pluck(column_name)
|
||||
scope = self.select(column_name)
|
||||
self.connection.select_values(scope.to_sql).map! do |value|
|
||||
type_cast_using_column(value, column_for(column_name))
|
||||
column_name = column_name.to_s
|
||||
klass.connection.select_all(select(column_name).arel).map! do |attributes|
|
||||
klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -458,7 +458,6 @@ class CalculationsTest < ActiveRecord::TestCase
|
||||
assert_equal [ topic.approved ], relation.pluck(:approved)
|
||||
assert_equal [ topic.last_read ], relation.pluck(:last_read)
|
||||
assert_equal [ topic.written_on ], relation.pluck(:written_on)
|
||||
|
||||
end
|
||||
|
||||
def test_pluck_and_uniq
|
||||
@@ -471,4 +470,12 @@ class CalculationsTest < ActiveRecord::TestCase
|
||||
assert_equal [contract.id], company.contracts.pluck(:id)
|
||||
end
|
||||
|
||||
def test_pluck_with_serialization
|
||||
t = Topic.create!(:content => { :foo => :bar })
|
||||
assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
|
||||
end
|
||||
|
||||
def test_pluck_with_qualified_column_name
|
||||
assert_equal [1,2,3,4], Topic.order(:id).pluck("topics.id")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -211,7 +211,7 @@ module Rails
|
||||
# Gems used only for assets and not required
|
||||
# in production environments by default.
|
||||
group :assets do
|
||||
gem 'sass-rails', '~> 3.2.0'
|
||||
gem 'sass-rails', '~> 3.2.2'
|
||||
gem 'coffee-rails', '~> 3.2.0'
|
||||
#{"gem 'therubyrhino'\n" if defined?(JRUBY_VERSION)}
|
||||
gem 'uglifier', '>= 1.0.3'
|
||||
|
||||
Reference in New Issue
Block a user