aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/association.rb3
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb9
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb32
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/cast.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb30
-rw-r--r--activerecord/lib/active_record/core.rb7
-rw-r--r--activerecord/lib/active_record/migration/join_table.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb18
-rw-r--r--activerecord/lib/active_record/relation.rb16
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb23
15 files changed, 98 insertions, 80 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 651b17920c..d8b6d7a86b 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -234,7 +234,7 @@ module ActiveRecord
# others.size | X | X | X
# others.length | X | X | X
# others.count | X | X | X
- # others.sum(args*,&block) | X | X | X
+ # others.sum(*args) | X | X | X
# others.empty? | X | X | X
# others.clear | X | X | X
# others.delete(other,other,...) | X | X | X
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 99e7383d42..3f0e4ca999 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -232,7 +232,8 @@ module ActiveRecord
def build_record(attributes)
reflection.build_association(attributes) do |record|
- attributes = create_scope.except(*(record.changed - [reflection.foreign_key]))
+ skip_assign = [reflection.foreign_key, reflection.type].compact
+ attributes = create_scope.except(*(record.changed - skip_assign))
record.assign_attributes(attributes)
end
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 862ff201de..1548e68cea 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -161,15 +161,6 @@ module ActiveRecord
end
end
- # Calculate sum using SQL, not Enumerable.
- def sum(*args)
- if block_given?
- scope.sum(*args) { |*block_args| yield(*block_args) }
- else
- scope.sum(*args)
- end
- end
-
# Count all records using SQL. If the +:counter_sql+ or +:finder_sql+ option is set for the
# association, it will be used for the query. Otherwise, construct options and pass them with
# scope to the target class's +count+.
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 90701938e5..3c03cce838 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -32,21 +32,29 @@ module ActiveRecord
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
- # creates a closure).
+ # 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 creates a closure).
#
- # But sometimes the database might return columns with characters that are not
- # allowed in normal method names (like 'my_column(omg)'. So to work around this
- # we first define with the __temp__ identifier, and then use alias method to
- # rename it to what we want.
- def define_method_attribute(attr_name)
+ # But sometimes the database might return columns with
+ # characters that are not allowed in normal method names (like
+ # 'my_column(omg)'. So to work around this we first define with
+ # the __temp__ identifier, and then use alias method to rename
+ # it to what we want.
+ #
+ # We are also defining a constant to hold the frozen string of
+ # the attribute name. Using a constant means that we do not have
+ # to allocate an object on each call to the attribute method.
+ # Making it frozen means that it doesn't get duped when used to
+ # key the @attributes_cache in read_attribute.
+ def define_method_attribute(name)
+ safe_name = name.unpack('h*').first
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
- def __temp__
- read_attribute('#{attr_name}') { |n| missing_attribute(n, caller) }
+ def __temp__#{safe_name}
+ read_attribute(AttrNames::ATTR_#{safe_name}) { |n| missing_attribute(n, caller) }
end
- alias_method '#{attr_name}', :__temp__
- undef_method :__temp__
+ alias_method #{name.inspect}, :__temp__#{safe_name}
+ undef_method :__temp__#{safe_name}
STR
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index fa9097db1f..cd33494cc3 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -9,15 +9,19 @@ module ActiveRecord
module ClassMethods
protected
- def define_method_attribute=(attr_name)
- if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
- generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
- else
- generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
- write_attribute(attr_name, new_value)
- end
+
+ # See define_method_attribute in read.rb for an explanation of
+ # this code.
+ def define_method_attribute=(name)
+ safe_name = name.unpack('h*').first
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ def __temp__#{safe_name}=(value)
+ write_attribute(AttrNames::ATTR_#{safe_name}, value)
end
- end
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
+ undef_method :__temp__#{safe_name}=
+ STR
+ end
end
# Updates the attribute identified by <tt>attr_name</tt> with the
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 17dd71e898..f1e42dfbbe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -540,7 +540,7 @@ module ActiveRecord
column_type_sql << "(#{precision})"
end
elsif scale
- raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale if specified"
+ raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
end
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 9627aaae3a..e9677415cc 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -2,7 +2,7 @@ require 'active_record/connection_adapters/abstract_mysql_adapter'
require 'active_record/connection_adapters/statement_pool'
require 'active_support/core_ext/hash/keys'
-gem 'mysql', '~> 2.9.0'
+gem 'mysql', '~> 2.9'
require 'mysql'
class Mysql
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
index 62d091357d..c04a799b8d 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb
@@ -8,6 +8,8 @@ module ActiveRecord
case string
when 'infinity'; 1.0 / 0.0
when '-infinity'; -1.0 / 0.0
+ when / BC$/
+ super("-" + string.sub(/ BC$/, ""))
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index 9d3fa18e3a..62a4d76928 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -129,11 +129,15 @@ module ActiveRecord
# Quote date/time values for use in SQL input. Includes microseconds
# if the value is a Time responding to usec.
def quoted_date(value) #:nodoc:
+ result = super
if value.acts_like?(:time) && value.respond_to?(:usec)
- "#{super}.#{sprintf("%06d", value.usec)}"
- else
- super
+ result = "#{result}.#{sprintf("%06d", value.usec)}"
+ end
+
+ if value.year < 0
+ result = result.sub(/^-/, "") + " BC"
end
+ result
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 82a0b662f4..7c561b6f82 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -396,6 +396,13 @@ module ActiveRecord
when nil, 0..0x3fffffff; super(type)
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
end
+ when 'text'
+ # PostgreSQL doesn't support limits on text columns.
+ # The hard limit is 1Gb, according to section 8.3 in the manual.
+ case limit
+ when nil, 0..0x3fffffff; super(type)
+ else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
+ end
when 'integer'
return 'integer' unless limit
@@ -422,20 +429,17 @@ module ActiveRecord
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
# requires that the ORDER BY include the distinct column.
#
- # distinct("posts.id", "posts.created_at desc")
+ # distinct("posts.id", ["posts.created_at desc"])
+ # # => "DISTINCT posts.id, posts.created_at AS alias_0"
def distinct(columns, orders) #:nodoc:
- return "DISTINCT #{columns}" if orders.empty?
-
- # Construct a clean list of column names from the ORDER BY clause, removing
- # any ASC/DESC modifiers
- order_columns = orders.collect do |s|
- s = s.to_sql unless s.is_a?(String)
- s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
- end
- order_columns.delete_if { |c| c.blank? }
- order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
-
- "DISTINCT #{columns}, #{order_columns * ', '}"
+ order_columns = orders.map{ |s|
+ # Convert Arel node to string
+ s = s.to_sql unless s.is_a?(String)
+ # Remove any ASC/DESC modifiers
+ s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '')
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
+
+ [super].concat(order_columns).join(', ')
end
end
end
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 957027c1ee..94c6684700 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -83,7 +83,12 @@ module ActiveRecord
@attribute_methods_mutex = Mutex.new
# force attribute methods to be higher in inheritance hierarchy than other generated methods
- generated_attribute_methods
+ generated_attribute_methods.const_set(:AttrNames, Module.new {
+ def self.const_missing(name)
+ const_set(name, [name.to_s.sub(/ATTR_/, '')].pack('h*').freeze)
+ end
+ })
+
generated_feature_methods
end
diff --git a/activerecord/lib/active_record/migration/join_table.rb b/activerecord/lib/active_record/migration/join_table.rb
index e880ae97bb..ebf64cbcdc 100644
--- a/activerecord/lib/active_record/migration/join_table.rb
+++ b/activerecord/lib/active_record/migration/join_table.rb
@@ -8,7 +8,7 @@ module ActiveRecord
end
def join_table_name(table_1, table_2)
- [table_1, table_2].sort.join("_").to_sym
+ [table_1.to_s, table_2.to_s].sort.join("_").to_sym
end
end
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 1c17c578e9..eed49e17b1 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -180,7 +180,7 @@ module ActiveRecord
name = name.to_s
verify_readonly_attribute(name)
send("#{name}=", value)
- save(:validate => false)
+ save(validate: false)
end
# Updates the attributes of the model from the passed-in hash and saves the
@@ -235,8 +235,8 @@ module ActiveRecord
updated_count = self.class.where(self.class.primary_key => id).update_all(attributes)
- attributes.each do |k,v|
- raw_write_attribute(k,v)
+ attributes.each do |k, v|
+ raw_write_attribute(k, v)
end
updated_count == 1
@@ -388,10 +388,14 @@ module ActiveRecord
# Returns the number of affected rows.
def update(attribute_names = @attributes.keys)
attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
- return 0 if attributes_with_values.empty?
- klass = self.class
- stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
- klass.connection.update stmt
+
+ if attributes_with_values.empty?
+ 0
+ else
+ klass = self.class
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values)
+ klass.connection.update stmt
+ end
end
# Creates a record with values matching those of the instance attributes
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 3ee55c580e..f0f170b684 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -31,6 +31,14 @@ module ActiveRecord
@default_scoped = false
end
+ def initialize_copy(other)
+ # This method is a hot spot, so for now, use Hash[] to dup the hash.
+ # https://bugs.ruby-lang.org/issues/7166
+ @values = Hash[@values]
+ @values[:bind] = @values[:bind].dup if @values.key? :bind
+ reset
+ end
+
def insert(values)
primary_key_value = nil
@@ -90,14 +98,6 @@ module ActiveRecord
scoping { @klass.new(*args, &block) }
end
- def initialize_copy(other)
- # This method is a hot spot, so for now, use Hash[] to dup the hash.
- # https://bugs.ruby-lang.org/issues/7166
- @values = Hash[@values]
- @values[:bind] = @values[:bind].dup if @values.key? :bind
- reset
- end
-
alias build new
# Tries to create a new record with the same scoped attributes
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 741f94f777..72b035a023 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -1,3 +1,5 @@
+require 'active_support/deprecation'
+
module ActiveRecord
module Calculations
# Count the records.
@@ -13,16 +15,9 @@ module ActiveRecord
#
# Person.count(:age, distinct: true)
# # => counts the number of different age values
- #
- # Person.where("age > 26").count { |person| person.gender == 'female' }
- # # => queries people where "age > 26" then count the loaded results filtering by gender
def count(column_name = nil, options = {})
- if block_given?
- self.to_a.count { |item| yield item }
- else
- column_name, options = nil, column_name if column_name.is_a?(Hash)
- calculate(:count, column_name, options)
- end
+ column_name, options = nil, column_name if column_name.is_a?(Hash)
+ calculate(:count, column_name, options)
end
# Calculates the average value on a given column. Returns +nil+ if there's
@@ -56,13 +51,13 @@ module ActiveRecord
# +calculate+ for examples with options.
#
# Person.sum('age') # => 4562
- # # => returns the total sum of all people's age
- #
- # Person.where('age > 100').sum { |person| person.age - 100 }
- # # queries people where "age > 100" then perform a sum calculation with the block returns
def sum(*args)
if block_given?
- self.to_a.sum(*args) { |item| yield item }
+ ActiveSupport::Deprecation.warn(
+ "Calling #sum with a block is deprecated and will be removed in Rails 4.1. " \
+ "If you want to perform sum calculation over the array of elements, use `to_a.sum(&block)`."
+ )
+ self.to_a.sum(*args) {|*block_args| yield(*block_args)}
else
calculate(:sum, *args)
end