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/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb73
-rw-r--r--activerecord/lib/active_record/base.rb3
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb35
-rw-r--r--activerecord/lib/active_record/core.rb2
-rw-r--r--activerecord/lib/active_record/dynamic_finder_match.rb26
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb4
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb2
10 files changed, 108 insertions, 43 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index b2136605e1..da4c311bce 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -544,7 +544,7 @@ module ActiveRecord
# If using a custom finder_sql, #find scans the entire collection.
def find_by_scan(*args)
expects_array = args.first.kind_of?(Array)
- ids = args.flatten.compact.uniq.map { |arg| arg.to_i }
+ ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq
if ids.size == 1
id = ids.first
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 93c243e7b1..bebf6a1a05 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -214,37 +214,64 @@ module ActiveRecord
value
end
- # Returns a copy of the attributes hash where all the values have been safely quoted for use in
- # an Arel insert/update method.
- def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
- attrs = {}
- klass = self.class
- arel_table = klass.arel_table
+ def arel_attributes_with_values_for_create(pk_attribute_allowed)
+ arel_attributes_with_values(attributes_for_create(pk_attribute_allowed))
+ end
- attribute_names.each do |name|
- if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
+ def arel_attributes_with_values_for_update(attribute_names)
+ arel_attributes_with_values(attributes_for_update(attribute_names))
+ end
- if include_readonly_attributes || !self.class.readonly_attributes.include?(name)
+ def attribute_method?(attr_name)
+ defined?(@attributes) && @attributes.include?(attr_name)
+ end
- value = if klass.serialized_attributes.include?(name)
- @attributes[name].serialized_value
- else
- # FIXME: we need @attributes to be used consistently.
- # If the values stored in @attributes were already type
- # casted, this code could be simplified
- read_attribute(name)
- end
+ private
- attrs[arel_table[name]] = value
- end
- end
- end
+ # Returns a Hash of the Arel::Attributes and attribute values that have been
+ # type casted for use in an Arel insert/update method.
+ def arel_attributes_with_values(attribute_names)
+ attrs = {}
+ arel_table = self.class.arel_table
+ attribute_names.each do |name|
+ attrs[arel_table[name]] = typecasted_attribute_value(name)
+ end
attrs
end
- def attribute_method?(attr_name)
- defined?(@attributes) && @attributes.include?(attr_name)
+ # Filters the primary keys and readonly attributes from the attribute names.
+ def attributes_for_update(attribute_names)
+ attribute_names.select do |name|
+ column_for_attribute(name) && !pk_attribute?(name) && !readonly_attribute?(name)
+ end
+ end
+
+ # Filters out the primary keys, from the attribute names, when the primary
+ # key is to be generated (e.g. the id attribute has no value).
+ def attributes_for_create(pk_attribute_allowed)
+ @attributes.keys.select do |name|
+ column_for_attribute(name) && (pk_attribute_allowed || !pk_attribute?(name))
+ end
+ end
+
+ def readonly_attribute?(name)
+ self.class.readonly_attributes.include?(name)
+ end
+
+ def pk_attribute?(name)
+ column_for_attribute(name).primary
+ end
+
+ def typecasted_attribute_value(name)
+ if self.class.serialized_attributes.include?(name)
+ @attributes[name].serialized_value
+ else
+ # FIXME: we need @attributes to be used consistently.
+ # If the values stored in @attributes were already typecasted, this code
+ # could be simplified
+ read_attribute(name)
+ end
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index d4d0220fb7..d25a821688 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -201,6 +201,9 @@ module ActiveRecord #:nodoc:
# # Now 'Bob' exist and is an 'admin'
# User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true }
#
+ # Adding an exclamation point (!) on to the end of <tt>find_or_create_by_</tt> will
+ # raise an <tt>ActiveRecord::RecordInvalid</tt> error if the new record is invalid.
+ #
# Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without
# saving it first. Protected attributes won't be set unless they are given in a block.
#
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 37a9d216df..561e48d52e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -128,10 +128,11 @@ module ActiveRecord
@reserved_connections[current_connection_id] ||= checkout
end
- # Check to see if there is an active connection in this connection
- # pool.
+ # Is there an open connection that is being used for the current thread?
def active_connection?
- active_connections.any?
+ @reserved_connections.fetch(current_connection_id) {
+ return false
+ }.in_use?
end
# Signal that the thread is finished with the current connection.
@@ -185,8 +186,9 @@ module ActiveRecord
end
def clear_stale_cached_connections! # :nodoc:
+ reap
end
- deprecate :clear_stale_cached_connections!
+ deprecate :clear_stale_cached_connections! => "Please use #reap instead"
# Check-out a database connection from the pool, indicating that you want
# to use it. You should call #checkin when you no longer need this.
@@ -233,6 +235,8 @@ module ActiveRecord
conn.run_callbacks :checkin do
conn.expire
end
+
+ release conn
end
end
@@ -244,10 +248,7 @@ module ActiveRecord
# FIXME: we might want to store the key on the connection so that removing
# from the reserved hash will be a little easier.
- thread_id = @reserved_connections.keys.find { |k|
- @reserved_connections[k] == conn
- }
- @reserved_connections.delete thread_id if thread_id
+ release conn
end
end
@@ -265,6 +266,20 @@ module ActiveRecord
private
+ def release(conn)
+ thread_id = nil
+
+ if @reserved_connections[current_connection_id] == conn
+ thread_id = current_connection_id
+ else
+ thread_id = @reserved_connections.keys.find { |k|
+ @reserved_connections[k] == conn
+ }
+ end
+
+ @reserved_connections.delete thread_id if thread_id
+ end
+
def new_connection
ActiveRecord::Base.send(spec.adapter_method, spec.config)
end
@@ -288,10 +303,6 @@ module ActiveRecord
end
c
end
-
- def active_connections
- @connections.find_all { |c| c.in_use? }
- end
end
# ConnectionHandler is a collection of ConnectionPool objects. It is used
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index e75a2a1cb4..46aababfb6 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -130,7 +130,7 @@ module ActiveRecord
end
def arel_engine
- @arel_engine ||= connection_handler.connection_pools[name] ? self : active_record_super.arel_engine
+ @arel_engine ||= connection_handler.retrieve_connection_pool(self) ? self : active_record_super.arel_engine
end
private
diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb
index 38dbbef5fc..0473d6aafc 100644
--- a/activerecord/lib/active_record/dynamic_finder_match.rb
+++ b/activerecord/lib/active_record/dynamic_finder_match.rb
@@ -7,7 +7,7 @@ module ActiveRecord
class DynamicFinderMatch
def self.match(method)
method = method.to_s
- klass = [FindBy, FindByBang, FindOrInitializeCreateBy].find do |_klass|
+ klass = klasses.find do |_klass|
_klass.matches?(method)
end
klass.new(method) if klass
@@ -17,6 +17,10 @@ module ActiveRecord
method =~ self::METHOD_PATTERN
end
+ def self.klasses
+ [FindBy, FindByBang, FindOrInitializeCreateBy, FindOrCreateByBang]
+ end
+
def initialize(method)
@finder = :first
@instantiator = nil
@@ -47,6 +51,14 @@ module ActiveRecord
arguments.size >= @attribute_names.size
end
+ def save_record?
+ @instantiator == :create
+ end
+
+ def save_method
+ bang? ? :save! : :save
+ end
+
private
def initialize_from_match_data(match_data)
@@ -81,4 +93,16 @@ module ActiveRecord
arguments.size == 1 && arguments.first.is_a?(Hash) || super
end
end
+
+ class FindOrCreateByBang < DynamicFinderMatch
+ METHOD_PATTERN = /^find_or_create_by_([_a-zA-Z]\w*)\!$/
+
+ def initialize_from_match_data(match_data)
+ @instantiator = :create
+ end
+
+ def bang?
+ true
+ end
+ end
end
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 9b2dc096a0..8266427b71 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -84,7 +84,7 @@ module ActiveRecord
relation.table[self.class.primary_key].eq(id).and(
relation.table[lock_col].eq(quote_value(previous_lock_value))
)
- ).arel.compile_update(arel_attributes_values(false, false, attribute_names))
+ ).arel.compile_update(arel_attributes_with_values_for_update(attribute_names))
affected_rows = connection.update stmt
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index c4bce87311..09502bb52c 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -350,7 +350,7 @@ module ActiveRecord
# Updates the associated record with values matching those of the instance attributes.
# Returns the number of affected rows.
def update(attribute_names = @attributes.keys)
- attributes_with_values = arel_attributes_values(false, false, attribute_names)
+ 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)
@@ -360,7 +360,7 @@ module ActiveRecord
# Creates a record with values matching those of the instance attributes
# and returns its id.
def create
- attributes_values = arel_attributes_values(!id.nil?)
+ attributes_values = arel_attributes_with_values_for_create(!id.nil?)
new_id = self.class.unscoped.insert attributes_values
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 4cd703e0a5..adfacf37ee 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -290,7 +290,7 @@ module ActiveRecord
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
end
yield(record) if block_given?
- record.save if match.instantiator == :create
+ record.send(match.save_method) if match.save_record?
end
record
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 87dd513880..d737b34115 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -329,7 +329,7 @@ module ActiveRecord
arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
arel.take(connection.sanitize_limit(@limit_value)) if @limit_value
- arel.skip(@offset_value) if @offset_value
+ arel.skip(@offset_value.to_i) if @offset_value
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?