diff options
Diffstat (limited to 'activerecord/lib/active_record')
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| |