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/builder/association.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb24
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb2
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb10
-rw-r--r--activerecord/lib/active_record/null_relation.rb10
-rw-r--r--activerecord/lib/active_record/querying.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb33
-rw-r--r--activerecord/lib/active_record/result.rb25
11 files changed, 100 insertions, 23 deletions
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index ba2fcf5ed7..c3fa4a05fd 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -58,9 +58,9 @@ module ActiveRecord::Associations::Builder
def dependent_restrict_deprecation_warning
if dependent_restrict_raises?
- msg = "In the next release, `:dependent => :restrict` will not raise a `DeleteRestrictionError`."\
- "Instead, it will add an error on the model. To fix this warning, make sure your code" \
- "isn't relying on a `DeleteRestrictionError` and then add" \
+ msg = "In the next release, `:dependent => :restrict` will not raise a `DeleteRestrictionError`. "\
+ "Instead, it will add an error on the model. To fix this warning, make sure your code " \
+ "isn't relying on a `DeleteRestrictionError` and then add " \
"`config.active_record.dependent_restrict_raises = false` to your application config."
ActiveSupport::Deprecation.warn msg
end
@@ -80,5 +80,5 @@ module ActiveRecord::Associations::Builder
end
end
end
- end
+ end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
index 52f09efd53..6ba64bb88f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb
@@ -65,18 +65,24 @@ module ActiveRecord
end
private
- def cache_sql(sql, binds)
- result =
- if @query_cache[sql].key?(binds)
- ActiveSupport::Notifications.instrument("sql.active_record",
- :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
- @query_cache[sql][binds]
- else
- @query_cache[sql][binds] = yield
- end
+ def cache_sql(sql, binds)
+ result =
+ if @query_cache[sql].key?(binds)
+ ActiveSupport::Notifications.instrument("sql.active_record",
+ :sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
+ @query_cache[sql][binds]
+ else
+ @query_cache[sql][binds] = yield
+ end
+ # FIXME: we should guarantee that all cached items are Result
+ # objects. Then we can avoid this conditional
+ if ActiveRecord::Result === result
+ result.dup
+ else
result.collect { |row| row.dup }
end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 6086c32dbe..c1332fde1a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -225,7 +225,7 @@ module ActiveRecord
# column values as values.
def select(sql, name = nil, binds = [])
binds = binds.dup
- exec_query(sql.gsub("\0") { quote(*binds.shift.reverse) }, name).to_a
+ exec_query(sql.gsub("\0") { quote(*binds.shift.reverse) }, name)
end
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index e432c5af32..5905242747 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -408,7 +408,7 @@ module ActiveRecord
def select(sql, name = nil, binds = [])
@connection.query_with_result = true
- rows = exec_query(sql, name, binds).to_a
+ rows = exec_query(sql, name, binds)
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
rows
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index dbfb375ba8..1d8e5d813a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -1249,7 +1249,7 @@ module ActiveRecord
# Executes a SELECT query and returns the results, performing any data type
# conversions that are required to be performed here instead of in PostgreSQLColumn.
def select(sql, name = nil, binds = [])
- exec_query(sql, name, binds).to_a
+ exec_query(sql, name, binds)
end
def select_raw(sql, name = nil)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 69750a911d..0520fc8b62 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -454,7 +454,7 @@ module ActiveRecord
protected
def select(sql, name = nil, binds = []) #:nodoc:
- exec_query(sql, name, binds).to_a
+ exec_query(sql, name, binds)
end
def table_structure(table_name)
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index e643c0d437..4d73cdd37a 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -40,11 +40,13 @@ module ActiveRecord
# This locking mechanism will function inside a single Ruby process. To make it work across all
# web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
#
- # You must ensure that your database schema defaults the +lock_version+ column to 0.
- #
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
- # To override the name of the +lock_version+ column, invoke the <tt>set_locking_column</tt> method.
- # This method uses the same syntax as <tt>set_table_name</tt>
+ # To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
+ #
+ # class Person < ActiveRecord::Base
+ # self.locking_column = :lock_person
+ # end
+ #
module Optimistic
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
new file mode 100644
index 0000000000..60c37ac2b7
--- /dev/null
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+module ActiveRecord
+ # = Active Record Null Relation
+ class NullRelation < Relation
+ def exec_queries
+ @records = []
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 94e34e1bd4..5945b05190 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -8,7 +8,7 @@ module ActiveRecord
delegate :find_each, :find_in_batches, :to => :scoped
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
:where, :preload, :eager_load, :includes, :from, :lock, :readonly,
- :having, :create_with, :uniq, :references, :to => :scoped
+ :having, :create_with, :uniq, :references, :none, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :to => :scoped
# Executes a custom SQL query against your database and returns all the results. The results will
@@ -35,7 +35,8 @@ module ActiveRecord
# > [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
def find_by_sql(sql, binds = [])
logging_query_plan do
- connection.select_all(sanitize_sql(sql), "#{name} Load", binds).collect! { |record| instantiate(record) }
+ result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
+ result_set.map { |record| instantiate(record) }
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index a8ae7208fc..b6d762c2e2 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -196,6 +196,39 @@ module ActiveRecord
relation
end
+ # Returns a chainable relation with zero records, specifically an
+ # instance of the NullRelation class.
+ #
+ # The returned NullRelation inherits from Relation and implements the
+ # Null Object pattern so it is an object with defined null behavior:
+ # it always returns an empty array of records and does not query the database.
+ #
+ # Any subsequent condition chained to the returned relation will continue
+ # generating an empty relation and will not fire any query to the database.
+ #
+ # This is useful in scenarios where you need a chainable response to a method
+ # or a scope that could return zero results.
+ #
+ # For example:
+ #
+ # @posts = current_user.visible_posts.where(:name => params[:name])
+ # # => the visible_posts method is expected to return a chainable Relation
+ #
+ # def visible_posts
+ # case role
+ # when 'Country Manager'
+ # Post.where(:country => country)
+ # when 'Reviewer'
+ # Post.published
+ # when 'Bad User'
+ # Post.none # => returning [] instead breaks the previous code
+ # end
+ # end
+ #
+ def none
+ NullRelation.new(@klass, @table)
+ end
+
def readonly(value = true)
relation = clone
relation.readonly_value = value
diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb
index 9ceab2eabc..60a2e90e23 100644
--- a/activerecord/lib/active_record/result.rb
+++ b/activerecord/lib/active_record/result.rb
@@ -24,6 +24,31 @@ module ActiveRecord
hash_rows
end
+ alias :map! :map
+ alias :collect! :map
+
+ def empty?
+ rows.empty?
+ end
+
+ def to_ary
+ hash_rows
+ end
+
+ def [](idx)
+ hash_rows[idx]
+ end
+
+ def last
+ hash_rows.last
+ end
+
+ def initialize_copy(other)
+ @columns = columns.dup
+ @rows = rows.dup
+ @hash_rows = nil
+ end
+
private
def hash_rows
@hash_rows ||= @rows.map { |row|