diff options
Diffstat (limited to 'activerecord/lib')
15 files changed, 215 insertions, 123 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 33a184d48d..6cdec8c487 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -93,20 +93,20 @@ module ActiveRecord first_or_last(:last, *args) end - def build(attributes = {}, &block) - build_or_create(attributes, :build, &block) + def build(attributes = {}, options = {}, &block) + build_or_create(:build, attributes, options, &block) end - def create(attributes = {}, &block) + def create(attributes = {}, options = {}, &block) unless owner.persisted? raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved" end - build_or_create(attributes, :create, &block) + build_or_create(:create, attributes, options, &block) end - def create!(attrs = {}, &block) - record = create(attrs, &block) + def create!(attrs = {}, options = {}, &block) + record = create(attrs, options, &block) Array.wrap(record).each(&:save!) record end @@ -403,9 +403,9 @@ module ActiveRecord end + existing end - def build_or_create(attributes, method) + def build_or_create(method, attributes, options) records = Array.wrap(attributes).map do |attrs| - record = build_record(attrs) + record = build_record(attrs, options) add_to_target(record) do yield(record) if block_given? @@ -421,8 +421,8 @@ module ActiveRecord raise NotImplementedError end - def build_record(attributes) - reflection.build_association(scoped.scope_for_create.merge(attributes)) + def build_record(attributes, options) + reflection.build_association(scoped.scope_for_create.merge(attributes), options) end def delete_or_destroy(records, method) diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 9d2b29685b..7708228d23 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -60,10 +60,10 @@ module ActiveRecord through_record end - def build_record(attributes) + def build_record(attributes, options = {}) ensure_not_nested - record = super(attributes) + record = super(attributes, options) inverse = source_reflection.inverse_of if inverse diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index 4edbe216be..ea4d73d414 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -17,16 +17,16 @@ module ActiveRecord replace(record) end - def create(attributes = {}) - new_record(:create, attributes) + def create(attributes = {}, options = {}) + new_record(:create, attributes, options) end - def create!(attributes = {}) - build(attributes).tap { |record| record.save! } + def create!(attributes = {}, options = {}) + build(attributes, options).tap { |record| record.save! } end - def build(attributes = {}) - new_record(:build, attributes) + def build(attributes = {}, options = {}) + new_record(:build, attributes, options) end private @@ -44,9 +44,9 @@ module ActiveRecord replace(record) end - def new_record(method, attributes) + def new_record(method, attributes, options) attributes = scoped.scope_for_create.merge(attributes || {}) - record = reflection.send("#{method}_association", attributes) + record = reflection.send("#{method}_association", attributes, options) set_new_record(record) record end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 04c12f86b6..58a056bce9 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -83,7 +83,7 @@ module ActiveRecord #:nodoc: # # The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query # and is thus susceptible to SQL-injection attacks if the <tt>user_name</tt> and +password+ - # parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and + # parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and # <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+ # before inserting them in the query, which will ensure that an attacker can't escape the # query and fake the login (or worse). @@ -406,10 +406,10 @@ module ActiveRecord #:nodoc: ## # :singleton-method: # Specifies the format to use when dumping the database schema with Rails' - # Rakefile. If :sql, the schema is dumped as (potentially database- - # specific) SQL statements. If :ruby, the schema is dumped as an + # Rakefile. If :sql, the schema is dumped as (potentially database- + # specific) SQL statements. If :ruby, the schema is dumped as an # ActiveRecord::Schema file which can be loaded into any database that - # supports migrations. Use :ruby if you want to have different database + # supports migrations. Use :ruby if you want to have different database # adapters for, e.g., your development and test environments. cattr_accessor :schema_format , :instance_writer => false @@schema_format = :ruby @@ -443,17 +443,17 @@ module ActiveRecord #:nodoc: delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped - # Executes a custom SQL query against your database and returns all the results. The results will + # Executes a custom SQL query against your database and returns all the results. The results will # be returned as an array with columns requested encapsulated as attributes of the model you call - # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in + # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in # a Product object with the attributes you specified in the SQL query. # # If you call a complicated SQL query which spans multiple tables the columns specified by the # SELECT will be attributes of the model, whether or not they are columns of the corresponding # table. # - # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be - # no database agnostic conversions performed. This should be a last resort because using, for example, + # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be + # no database agnostic conversions performed. This should be a last resort because using, for example, # MySQL specific terms will lock you to using that particular database engine or require you to # change your call if you switch engines. # @@ -472,13 +472,22 @@ module ActiveRecord #:nodoc: # Creates an object (or multiple objects) and saves it to the database, if validations pass. # The resulting object is returned whether the object was saved successfully to the database or not. # - # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the + # The +attributes+ parameter can be either be a Hash or an Array of Hashes. These Hashes describe the # attributes on the objects that are to be created. # + # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options + # in the +options+ parameter. + # # ==== Examples # # Create a single new object # User.create(:first_name => 'Jamie') # + # # Create a single new object using the :admin mass-assignment security scope + # User.create({ :first_name => 'Jamie', :is_admin => true }, :as => :admin) + # + # # Create a single new object bypassing mass-assignment security + # User.create({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true) + # # # Create an Array of new objects # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) # @@ -491,11 +500,11 @@ module ActiveRecord #:nodoc: # User.create([{ :first_name => 'Jamie' }, { :first_name => 'Jeremy' }]) do |u| # u.is_admin = false # end - def create(attributes = nil, &block) + def create(attributes = nil, options = {}, &block) if attributes.is_a?(Array) - attributes.collect { |attr| create(attr, &block) } + attributes.collect { |attr| create(attr, options, &block) } else - object = new(attributes) + object = new(attributes, options) yield(object) if block_given? object.save object @@ -504,7 +513,7 @@ module ActiveRecord #:nodoc: # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. # The use of this method should be restricted to complicated SQL queries that can't be executed - # using the ActiveRecord::Calculations class methods. Look into those before using this. + # using the ActiveRecord::Calculations class methods. Look into those before using this. # # ==== Parameters # @@ -581,7 +590,7 @@ module ActiveRecord #:nodoc: # invoice/lineitem.rb Invoice::Lineitem lineitems # # Additionally, the class-level +table_name_prefix+ is prepended and the - # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix, + # +table_name_suffix+ is appended. So if you have "myapp_" as a prefix, # the table name guess for an Invoice class becomes "myapp_invoices". # Invoice::Lineitem becomes "myapp_invoice_lineitems". # @@ -615,7 +624,7 @@ module ActiveRecord #:nodoc: @inheritance_column ||= "type" end - # Lazy-set the sequence name to the connection's default. This method + # Lazy-set the sequence name to the connection's default. This method # is only ever called once since set_sequence_name overrides it. def sequence_name #:nodoc: reset_sequence_name @@ -627,7 +636,7 @@ module ActiveRecord #:nodoc: default end - # Sets the table name. If the value is nil or false then the value returned by the given + # Sets the table name. If the value is nil or false then the value returned by the given # block is used. # # class Project < ActiveRecord::Base @@ -1077,7 +1086,7 @@ module ActiveRecord #:nodoc: # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged. # # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing - # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the + # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the # array of strings format for your joins. # # class Article < ActiveRecord::Base @@ -1180,19 +1189,15 @@ MSG # Use this macro in your model to set a default scope for all operations on # the model. # - # class Person < ActiveRecord::Base - # default_scope order('last_name, first_name') + # class Article < ActiveRecord::Base + # default_scope where(:published => true) # end # - # Person.all # => SELECT * FROM people ORDER BY last_name, first_name + # Article.all # => SELECT * FROM articles WHERE published = true # # The <tt>default_scope</tt> is also applied while creating/building a record. It is not # applied while updating a record. # - # class Article < ActiveRecord::Base - # default_scope where(:published => true) - # end - # # Article.new.published # => true # Article.create.published # => true # @@ -1205,6 +1210,19 @@ MSG # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt> # macro, and it will be called when building the default scope.) # + # If you use multiple <tt>default_scope</tt> declarations in your model then they will + # be merged together: + # + # class Article < ActiveRecord::Base + # default_scope where(:published => true) + # default_scope where(:rating => 'G') + # end + # + # Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G' + # + # This is also the case with inheritance and module includes where the parent or module + # defines a <tt>default_scope</tt> and the child or including class defines a second one. + # # If you need to do more complex things with a default scope, you can alternatively # define it as a class method: # @@ -1214,36 +1232,8 @@ MSG # end # end def default_scope(scope = {}) - if default_scopes.length != 0 - ActiveSupport::Deprecation.warn <<-WARN -Calling 'default_scope' multiple times in a class (including when a superclass calls 'default_scope') is deprecated. The current behavior is that this will merge the default scopes together: - -class Post < ActiveRecord::Base # Rails 3.1 - default_scope where(:published => true) - default_scope where(:hidden => false) - # The default scope is now: where(:published => true, :hidden => false) -end - -In Rails 3.2, the behavior will be changed to overwrite previous scopes: - -class Post < ActiveRecord::Base # Rails 3.2 - default_scope where(:published => true) - default_scope where(:hidden => false) - # The default scope is now: where(:hidden => false) -end - -If you wish to merge default scopes in special ways, it is recommended to define your default scope as a class method and use the standard techniques for sharing code (inheritance, mixins, etc.): - -class Post < ActiveRecord::Base - def self.default_scope - where(:published => true).where(:hidden => false) - end -end - WARN - end - scope = Proc.new if block_given? - self.default_scopes = default_scopes.dup << scope + self.default_scopes = default_scopes + [scope] end def build_default_scope #:nodoc: @@ -1400,7 +1390,7 @@ end end.join(', ') end - # Accepts an array of conditions. The array has each value + # Accepts an array of conditions. The array has each value # sanitized and interpolated into the SQL statement. # ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'" def sanitize_sql_array(ary) @@ -1484,7 +1474,20 @@ end # attributes but not yet saved (pass a hash with key names matching the associated table column names). # In both instances, valid attribute keys are determined by the column names of the associated table -- # hence you can't have attributes that aren't part of the table columns. - def initialize(attributes = nil) + # + # +initialize+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options + # in the +options+ parameter. + # + # ==== Examples + # # Instantiates a single new object + # User.new(:first_name => 'Jamie') + # + # # Instantiates a single new object using the :admin mass-assignment security scope + # User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin) + # + # # 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 @association_cache = {} @aggregation_cache = {} @@ -1500,7 +1503,8 @@ end set_serialized_attributes populate_with_current_scope_attributes - self.attributes = attributes unless attributes.nil? + + assign_attributes(attributes, options) if attributes result = yield self if block_given? run_callbacks :initialize @@ -1508,7 +1512,7 @@ end end # Populate +coder+ with attributes about this record that should be - # serialized. The structure of +coder+ defined in this method is + # serialized. The structure of +coder+ defined in this method is # guaranteed to match the structure of +coder+ passed to the +init_with+ # method. # @@ -1523,8 +1527,8 @@ end coder['attributes'] = attributes end - # Initialize an empty model object from +coder+. +coder+ must contain - # the attributes necessary for initializing an empty model object. For + # Initialize an empty model object from +coder+. +coder+ must contain + # the attributes necessary for initializing an empty model object. For # example: # # class Post < ActiveRecord::Base 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 1db397f584..093c30aa42 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -29,6 +29,14 @@ module ActiveRecord @query_cache_enabled = old end + def enable_query_cache! + @query_cache_enabled = true + end + + def disable_query_cache! + @query_cache_enabled = false + end + # Disable the query cache within the block. def uncached old, @query_cache_enabled = @query_cache_enabled, false diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 7ac48c6646..70a8f6bb58 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -104,7 +104,7 @@ module ActiveRecord # Available options are (none of these exists by default): # * <tt>:limit</tt> - # Requests a maximum column length. This is number of characters for <tt>:string</tt> and - # <tt>:text</tt> columns and number of bytes for :binary and :integer columns. + # <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns. # * <tt>:default</tt> - # The column's default value. Use nil for NULL. # * <tt>:null</tt> - @@ -153,7 +153,7 @@ module ActiveRecord # This method returns <tt>self</tt>. # # == Examples - # # Assuming td is an instance of TableDefinition + # # Assuming +td+ is an instance of TableDefinition # td.column(:granted, :boolean) # # granted BOOLEAN # @@ -204,7 +204,7 @@ module ActiveRecord # end # # There's a short-hand method for each of the type values declared at the top. And then there's - # TableDefinition#timestamps that'll add created_at and +updated_at+ as datetimes. + # TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes. # # TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type # column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of @@ -351,7 +351,7 @@ module ActiveRecord @base.index_exists?(@table_name, column_name, options) end - # Adds timestamps (created_at and updated_at) columns to the table. See SchemaStatements#add_timestamps + # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps # ===== Example # t.timestamps def timestamps @@ -398,7 +398,7 @@ module ActiveRecord @base.remove_index(@table_name, options) end - # Removes the timestamp columns (created_at and updated_at) from the table. + # Removes the timestamp columns (+created_at+ and +updated_at+) from the table. # ===== Example # t.remove_timestamps def remove_timestamps @@ -412,7 +412,7 @@ module ActiveRecord @base.rename_column(@table_name, column_name, new_column_name) end - # Adds a reference. Optionally adds a +type+ column. + # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided. # <tt>references</tt> and <tt>belongs_to</tt> are acceptable. # ===== Examples # t.references(:goat) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 468a2b106b..65024d76f8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -46,34 +46,34 @@ module ActiveRecord @instrumenter = ActiveSupport::Notifications.instrumenter end - # Returns the human-readable name of the adapter. Use mixed case - one + # Returns the human-readable name of the adapter. Use mixed case - one # can always use downcase if needed. def adapter_name 'Abstract' end - # Does this adapter support migrations? Backend specific, as the + # Does this adapter support migrations? Backend specific, as the # abstract adapter always returns +false+. def supports_migrations? false end # Can this adapter determine the primary key for tables not attached - # to an Active Record class, such as join tables? Backend specific, as + # to an Active Record class, such as join tables? Backend specific, as # the abstract adapter always returns +false+. def supports_primary_key? false end - # Does this adapter support using DISTINCT within COUNT? This is +true+ + # Does this adapter support using DISTINCT within COUNT? This is +true+ # for all adapters except sqlite. def supports_count_distinct? true end - # Does this adapter support DDL rollbacks in transactions? That is, would - # CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL, - # SQL Server, and others support this. MySQL and others do not. + # Does this adapter support DDL rollbacks in transactions? That is, would + # CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL, + # SQL Server, and others support this. MySQL and others do not. def supports_ddl_transactions? false end @@ -89,7 +89,7 @@ module ActiveRecord end # Should primary key values be selected from their corresponding - # sequence before the insert statement? If true, next_sequence_value + # sequence before the insert statement? If true, next_sequence_value # is called before each insert to set the record's primary key. # This is false for all adapters but Firebird. def prefetch_primary_key?(table_name = nil) @@ -149,7 +149,7 @@ module ActiveRecord ### # Clear any caching the database adapter may be doing, for example - # clearing the prepared statement cache. This is database specific. + # clearing the prepared statement cache. This is database specific. def clear_cache! # this should be overridden by concrete adapters end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index d3a054c29b..8af22fe9f5 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -5,6 +5,7 @@ require 'mysql2' module ActiveRecord class Base + # Establishes a connection to the database that's used by all Active Record objects. def self.mysql2_connection(config) config[:username] = 'root' if config[:username].nil? @@ -213,6 +214,8 @@ module ActiveRecord false end + # Disconnects from the database if already connected. + # Otherwise, this method does nothing. def disconnect! unless @connection.nil? @connection.close @@ -368,6 +371,8 @@ module ActiveRecord end end + # Drops the database specified on the +name+ attribute + # and creates it again using the provided +options+. def recreate_database(name, options = {}) drop_database(name) create_database(name, options) @@ -410,12 +415,25 @@ module ActiveRecord show_variable 'collation_database' end - def tables(name = nil) - tables = [] - execute("SHOW TABLES", name).each do |field| - tables << field.first + def tables(name = nil, database = nil) #:nodoc: + sql = ["SHOW TABLES", database].compact.join(' IN ') + execute(sql, 'SCHEMA').collect do |field| + field.first end - tables + end + + def table_exists?(name) + return true if super + + name = name.to_s + schema, table = name.split('.', 2) + + unless table # A table was provided without a schema + table = schema + schema = nil + end + + tables(nil, schema).include? table end def drop_table(table_name, options = {}) @@ -426,7 +444,7 @@ module ActiveRecord def indexes(table_name, name = nil) indexes = [] current_index = nil - result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name) + result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", 'SCHEMA') result.each(:symbolize_keys => true, :as => :hash) do |row| if current_index != row[:Key_name] next if row[:Key_name] == PRIMARY # skip the primary key @@ -444,7 +462,7 @@ module ActiveRecord def columns(table_name, name = nil) sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" columns = [] - result = execute(sql) + result = execute(sql, 'SCHEMA') result.each(:symbolize_keys => true, :as => :hash) { |field| columns << Mysql2Column.new(field[:Field], field[:Default], field[:Type], field[:Null] == "YES") } @@ -538,6 +556,7 @@ module ActiveRecord end end + # SHOW VARIABLES LIKE 'name'. def show_variable(name) variables = select_all("SHOW VARIABLES LIKE '#{name}'") variables.first['Value'] unless variables.empty? @@ -546,7 +565,7 @@ module ActiveRecord # Returns a table's primary key and belonging sequence. def pk_and_sequence_for(table) keys = [] - result = execute("describe #{quote_table_name(table)}") + result = execute("DESCRIBE #{quote_table_name(table)}", 'SCHEMA') result.each(:symbolize_keys => true, :as => :hash) do |row| keys << row[:Field] if row[:Key] == "PRI" end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 862ce852e6..a9f4c08348 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -517,6 +517,8 @@ module ActiveRecord end.join("") end + # Drops the database specified on the +name+ attribute + # and creates it again using the provided +options+. def recreate_database(name, options = {}) #:nodoc: drop_database(name) create_database(name, options) @@ -560,9 +562,8 @@ module ActiveRecord end def tables(name = nil, database = nil) #:nodoc: - tables = [] result = execute(["SHOW TABLES", database].compact.join(' IN '), 'SCHEMA') - result.each { |field| tables << field[0] } + tables = result.collect { |field| field[0] } result.free tables end @@ -607,9 +608,8 @@ module ActiveRecord # Returns an array of +MysqlColumn+ objects for the table specified by +table_name+. def columns(table_name, name = nil)#:nodoc: sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" - columns = [] result = execute(sql, 'SCHEMA') - result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") } + columns = result.collect { |field| MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") } result.free columns end diff --git a/activerecord/lib/active_record/identity_map.rb b/activerecord/lib/active_record/identity_map.rb index 95a8e5cff7..9eb47ad99f 100644 --- a/activerecord/lib/active_record/identity_map.rb +++ b/activerecord/lib/active_record/identity_map.rb @@ -49,12 +49,15 @@ module ActiveRecord end def get(klass, primary_key) - obj = repository[klass.symbolized_base_class][primary_key] - if obj.is_a?(klass) - if ActiveRecord::Base.logger - ActiveRecord::Base.logger.debug "#{klass} with ID = #{primary_key} loaded from Identity Map" - end - obj + record = repository[klass.symbolized_base_class][primary_key] + + if record.is_a?(klass) + ActiveSupport::Notifications.instrument("identity.active_record", + :line => "From Identity Map (id: #{primary_key})", + :name => "#{klass} Loaded", + :connection_id => object_id) + + record else nil end @@ -95,14 +98,33 @@ module ActiveRecord end class Middleware + class Body #:nodoc: + def initialize(target, original) + @target = target + @original = original + end + + def each(&block) + @target.each(&block) + end + + def close + @target.close if @target.respond_to?(:close) + ensure + IdentityMap.enabled = @original + IdentityMap.clear + end + end + def initialize(app) @app = app end def call(env) - ActiveRecord::IdentityMap.use do - @app.call(env) - end + enabled = IdentityMap.enabled + IdentityMap.enabled = true + status, headers, body = @app.call(env) + [status, headers, Body.new(body, enabled)] end end end diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index d31e321440..3a015ee8c2 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -46,6 +46,15 @@ module ActiveRecord debug " #{name} #{sql}#{binds}" end + def identity(event) + return unless logger.debug? + + name = color(event.payload[:name], odd? ? CYAN : MAGENTA, true) + line = odd? ? color(event.payload[:line], nil, true) : event.payload[:line] + + debug " #{name} #{line}" + end + def odd? @odd_or_even = !@odd_or_even end diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index d9f85a4e5e..4e61671473 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -27,10 +27,32 @@ module ActiveRecord @app = app end - def call(env) - ActiveRecord::Base.cache do - @app.call(env) + class BodyProxy # :nodoc: + def initialize(original_cache_value, target) + @original_cache_value = original_cache_value + @target = target + end + + def each(&block) + @target.each(&block) + end + + def close + @target.close if @target.respond_to?(:close) + ensure + ActiveRecord::Base.connection.clear_query_cache + unless @original_cache_value + ActiveRecord::Base.connection.disable_query_cache! + end end end + + def call(env) + old = ActiveRecord::Base.connection.query_cache_enabled + ActiveRecord::Base.connection.enable_query_cache! + + status, headers, body = @app.call(env) + [status, headers, BodyProxy.new(old, body)] + end end end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index d38588519b..bae2ded244 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -26,10 +26,12 @@ module ActiveRecord load "active_record/railties/databases.rake" end - # When loading console, force ActiveRecord to be loaded to avoid cross - # references when loading a constant for the first time. - console do - ActiveRecord::Base + # When loading console, force ActiveRecord::Base to be loaded + # to avoid cross references when loading a constant for the + # first time. Also, make it output to STDERR. + console do |sandbox| + require "active_record/railties/console_sandbox" if sandbox + ActiveRecord::Base.logger = Logger.new(STDERR) end initializer "active_record.initialize_timezone" do diff --git a/activerecord/lib/active_record/railties/console_sandbox.rb b/activerecord/lib/active_record/railties/console_sandbox.rb new file mode 100644 index 0000000000..65a3d68619 --- /dev/null +++ b/activerecord/lib/active_record/railties/console_sandbox.rb @@ -0,0 +1,6 @@ +ActiveRecord::Base.connection.increment_open_transactions +ActiveRecord::Base.connection.begin_db_transaction +at_exit do + ActiveRecord::Base.connection.rollback_db_transaction + ActiveRecord::Base.connection.decrement_open_transactions +end diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 0667be7d23..2c20dd997f 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -3,7 +3,7 @@ module ActiveRecord MAJOR = 3 MINOR = 1 TINY = 0 - PRE = "beta" + PRE = "beta1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end |