From e8c4939fb3366472021c1af1331bfdfe5d7a5d75 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 9 Dec 2008 11:17:11 -0800 Subject: Benchmark.ms --- activerecord/lib/active_record/base.rb | 4 ++-- .../lib/active_record/connection_adapters/abstract_adapter.rb | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5d614442c3..a23518b357 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1417,8 +1417,8 @@ module ActiveRecord #:nodoc: def benchmark(title, log_level = Logger::DEBUG, use_silence = true) if logger && logger.level <= log_level result = nil - seconds = Benchmark.realtime { result = use_silence ? silence { yield } : yield } - logger.add(log_level, "#{title} (#{'%.1f' % (seconds * 1000)}ms)") + ms = Benchmark.ms { result = use_silence ? silence { yield } : yield } + logger.add(log_level, '%s (%.1fms)' % [title, ms]) result else yield diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index cab77fc031..bfafcfb3ab 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -160,9 +160,9 @@ module ActiveRecord @open_transactions -= 1 end - def log_info(sql, name, seconds) + def log_info(sql, name, ms) if @logger && @logger.debug? - name = "#{name.nil? ? "SQL" : name} (#{sprintf("%.1f", seconds * 1000)}ms)" + name = '%s (%.1fms)' % [name || 'SQL', ms] @logger.debug(format_log_entry(name, sql.squeeze(' '))) end end @@ -171,9 +171,9 @@ module ActiveRecord def log(sql, name) if block_given? result = nil - seconds = Benchmark.realtime { result = yield } - @runtime += seconds - log_info(sql, name, seconds) + ms = Benchmark.ms { result = yield } + @runtime += ms + log_info(sql, name, ms) result else log_info(sql, name, 0) -- cgit v1.2.3 From 05f2183747c8e75c9e8bbaadb9573b4bdf41ecfc Mon Sep 17 00:00:00 2001 From: Emilio Tagua Date: Tue, 2 Dec 2008 14:12:09 -0300 Subject: Fix: counter_cache should decrement on deleting associated records. [#1195 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations/association_collection.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 0ff91fbdf8..0ad58010b2 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -202,6 +202,9 @@ module ActiveRecord records.each do |record| @target.delete(record) + if respond_to?(:cached_counter_attribute_name) && @owner[cached_counter_attribute_name] + @owner.class.decrement_counter(cached_counter_attribute_name, @owner.send(@owner.class.primary_key)) + end callback(:after_remove, record) end end -- cgit v1.2.3 From aa5cdb0d47fb5484bfdde8244df7efeb2175bf3a Mon Sep 17 00:00:00 2001 From: Bruce Krysiak Date: Mon, 8 Dec 2008 16:38:04 -0800 Subject: Added a :camelize option to ActiveRecord and Hash to_xml serialization and from_xml deserialization Signed-off-by: Michael Koziarski --- .../active_record/serializers/xml_serializer.rb | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index d171b742f5..4749823b94 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -23,11 +23,12 @@ module ActiveRecord #:nodoc: # # # This behavior can be controlled with :only, :except, - # :skip_instruct, :skip_types and :dasherize. + # :skip_instruct, :skip_types, :dasherize and :camelize . # The :only and :except options are the same as for the # +attributes+ method. The default is to dasherize all column names, but you - # can disable this setting :dasherize to +false+. To not have the - # column type included in the XML output set :skip_types to +true+. + # can disable this setting :dasherize to +false+. Setting :camelize + # to +true+ will camelize all column names - this also overrides :dasherize. + # To not have the column type included in the XML output set :skip_types to +true+. # # For instance: # @@ -178,13 +179,22 @@ module ActiveRecord #:nodoc: def root root = (options[:root] || @record.class.to_s.underscore).to_s - dasherize? ? root.dasherize : root + reformat_name(root) end def dasherize? !options.has_key?(:dasherize) || options[:dasherize] end + def camelize? + options.has_key?(:camelize) && options[:camelize] + end + + def reformat_name(name) + name = name.camelize if camelize? + dasherize? ? name.dasherize : name + end + def serializable_attributes serializable_attribute_names.collect { |name| Attribute.new(name, @record) } end @@ -212,7 +222,7 @@ module ActiveRecord #:nodoc: def add_tag(attribute) builder.tag!( - dasherize? ? attribute.name.dasherize : attribute.name, + reformat_name(attribute.name), attribute.value.to_s, attribute.decorations(!options[:skip_types]) ) @@ -220,8 +230,7 @@ module ActiveRecord #:nodoc: def add_associations(association, records, opts) if records.is_a?(Enumerable) - tag = association.to_s - tag = tag.dasherize if dasherize? + tag = reformat_name(association.to_s) if records.empty? builder.tag!(tag, :type => :array) else -- cgit v1.2.3 From b30ae1974851b20ef430df9de17e6e79e5b25ad2 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 10 Dec 2008 14:48:12 -0800 Subject: Revert "Fix: counter_cache should decrement on deleting associated records." [#1196 state:open] This reverts commit 05f2183747c8e75c9e8bbaadb9573b4bdf41ecfc. --- activerecord/lib/active_record/associations/association_collection.rb | 3 --- 1 file changed, 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 0ad58010b2..0ff91fbdf8 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -202,9 +202,6 @@ module ActiveRecord records.each do |record| @target.delete(record) - if respond_to?(:cached_counter_attribute_name) && @owner[cached_counter_attribute_name] - @owner.class.decrement_counter(cached_counter_attribute_name, @owner.send(@owner.class.primary_key)) - end callback(:after_remove, record) end end -- cgit v1.2.3 From 455c7f9e37fda2969e52698b766413fc735eb488 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Wed, 10 Dec 2008 20:57:19 +0000 Subject: Don't use the transaction instance method so that people with has_one/belongs_to :transaction aren't fubared [#1551 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/transactions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 27b5aca18f..0a27ea980e 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -147,7 +147,7 @@ module ActiveRecord end def save_with_transactions! #:nodoc: - rollback_active_record_state! { transaction { save_without_transactions! } } + rollback_active_record_state! { self.class.transaction { save_without_transactions! } } end # Reset id and @new_record if the transaction rolls back. @@ -175,7 +175,7 @@ module ActiveRecord # instance. def with_transaction_returning_status(method, *args) status = nil - transaction do + self.class.transaction do status = send(method, *args) raise ActiveRecord::Rollback unless status end -- cgit v1.2.3 From ed708307137c811d14e5fd2cb4ea550add381a82 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 15 Dec 2008 16:33:31 -0600 Subject: Switch to Rack based session stores. --- activerecord/lib/active_record/session_store.rb | 319 ++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 activerecord/lib/active_record/session_store.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb new file mode 100644 index 0000000000..bd198c03b2 --- /dev/null +++ b/activerecord/lib/active_record/session_store.rb @@ -0,0 +1,319 @@ +module ActiveRecord + # A session store backed by an Active Record class. A default class is + # provided, but any object duck-typing to an Active Record Session class + # with text +session_id+ and +data+ attributes is sufficient. + # + # The default assumes a +sessions+ tables with columns: + # +id+ (numeric primary key), + # +session_id+ (text, or longtext if your session data exceeds 65K), and + # +data+ (text or longtext; careful if your session data exceeds 65KB). + # The +session_id+ column should always be indexed for speedy lookups. + # Session data is marshaled to the +data+ column in Base64 format. + # If the data you write is larger than the column's size limit, + # ActionController::SessionOverflowError will be raised. + # + # You may configure the table name, primary key, and data column. + # For example, at the end of config/environment.rb: + # ActiveRecord::SessionStore::Session.table_name = 'legacy_session_table' + # ActiveRecord::SessionStore::Session.primary_key = 'session_id' + # ActiveRecord::SessionStore::Session.data_column_name = 'legacy_session_data' + # Note that setting the primary key to the +session_id+ frees you from + # having a separate +id+ column if you don't want it. However, you must + # set session.model.id = session.session_id by hand! A before filter + # on ApplicationController is a good place. + # + # Since the default class is a simple Active Record, you get timestamps + # for free if you add +created_at+ and +updated_at+ datetime columns to + # the +sessions+ table, making periodic session expiration a snap. + # + # You may provide your own session class implementation, whether a + # feature-packed Active Record or a bare-metal high-performance SQL + # store, by setting + # ActiveRecord::SessionStore.session_class = MySessionClass + # You must implement these methods: + # self.find_by_session_id(session_id) + # initialize(hash_of_session_id_and_data) + # attr_reader :session_id + # attr_accessor :data + # save + # destroy + # + # The example SqlBypass class is a generic SQL session store. You may + # use it as a basis for high-performance database-specific stores. + class SessionStore < ActionController::Session::AbstractStore + # The default Active Record class. + class Session < ActiveRecord::Base + ## + # :singleton-method: + # Customizable data column name. Defaults to 'data'. + cattr_accessor :data_column_name + self.data_column_name = 'data' + + before_save :marshal_data! + before_save :raise_on_session_data_overflow! + + class << self + # Don't try to reload ARStore::Session in dev mode. + def reloadable? #:nodoc: + false + end + + def data_column_size_limit + @data_column_size_limit ||= columns_hash[@@data_column_name].limit + end + + # Hook to set up sessid compatibility. + def find_by_session_id(session_id) + setup_sessid_compatibility! + find_by_session_id(session_id) + end + + def marshal(data) + ActiveSupport::Base64.encode64(Marshal.dump(data)) if data + end + + def unmarshal(data) + Marshal.load(ActiveSupport::Base64.decode64(data)) if data + end + + def create_table! + connection.execute <<-end_sql + CREATE TABLE #{table_name} ( + id INTEGER PRIMARY KEY, + #{connection.quote_column_name('session_id')} TEXT UNIQUE, + #{connection.quote_column_name(@@data_column_name)} TEXT(255) + ) + end_sql + end + + def drop_table! + connection.execute "DROP TABLE #{table_name}" + end + + private + # Compatibility with tables using sessid instead of session_id. + def setup_sessid_compatibility! + # Reset column info since it may be stale. + reset_column_information + if columns_hash['sessid'] + def self.find_by_session_id(*args) + find_by_sessid(*args) + end + + define_method(:session_id) { sessid } + define_method(:session_id=) { |session_id| self.sessid = session_id } + else + def self.find_by_session_id(session_id) + find :first, :conditions => ["session_id #{attribute_condition(session_id)}", session_id] + end + end + end + end + + # Lazy-unmarshal session state. + def data + @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {} + end + + attr_writer :data + + # Has the session been loaded yet? + def loaded? + !!@data + end + + private + def marshal_data! + return false if !loaded? + write_attribute(@@data_column_name, self.class.marshal(self.data)) + end + + # Ensures that the data about to be stored in the database is not + # larger than the data storage column. Raises + # ActionController::SessionOverflowError. + def raise_on_session_data_overflow! + return false if !loaded? + limit = self.class.data_column_size_limit + if loaded? and limit and read_attribute(@@data_column_name).size > limit + raise ActionController::SessionOverflowError + end + end + end + + # A barebones session store which duck-types with the default session + # store but bypasses Active Record and issues SQL directly. This is + # an example session model class meant as a basis for your own classes. + # + # The database connection, table name, and session id and data columns + # are configurable class attributes. Marshaling and unmarshaling + # are implemented as class methods that you may override. By default, + # marshaling data is + # + # ActiveSupport::Base64.encode64(Marshal.dump(data)) + # + # and unmarshaling data is + # + # Marshal.load(ActiveSupport::Base64.decode64(data)) + # + # This marshaling behavior is intended to store the widest range of + # binary session data in a +text+ column. For higher performance, + # store in a +blob+ column instead and forgo the Base64 encoding. + class SqlBypass + ## + # :singleton-method: + # Use the ActiveRecord::Base.connection by default. + cattr_accessor :connection + + ## + # :singleton-method: + # The table name defaults to 'sessions'. + cattr_accessor :table_name + @@table_name = 'sessions' + + ## + # :singleton-method: + # The session id field defaults to 'session_id'. + cattr_accessor :session_id_column + @@session_id_column = 'session_id' + + ## + # :singleton-method: + # The data field defaults to 'data'. + cattr_accessor :data_column + @@data_column = 'data' + + class << self + def connection + @@connection ||= ActiveRecord::Base.connection + end + + # Look up a session by id and unmarshal its data if found. + def find_by_session_id(session_id) + if record = @@connection.select_one("SELECT * FROM #{@@table_name} WHERE #{@@session_id_column}=#{@@connection.quote(session_id)}") + new(:session_id => session_id, :marshaled_data => record['data']) + end + end + + def marshal(data) + ActiveSupport::Base64.encode64(Marshal.dump(data)) if data + end + + def unmarshal(data) + Marshal.load(ActiveSupport::Base64.decode64(data)) if data + end + + def create_table! + @@connection.execute <<-end_sql + CREATE TABLE #{table_name} ( + id INTEGER PRIMARY KEY, + #{@@connection.quote_column_name(session_id_column)} TEXT UNIQUE, + #{@@connection.quote_column_name(data_column)} TEXT + ) + end_sql + end + + def drop_table! + @@connection.execute "DROP TABLE #{table_name}" + end + end + + attr_reader :session_id + attr_writer :data + + # Look for normal and marshaled data, self.find_by_session_id's way of + # telling us to postpone unmarshaling until the data is requested. + # We need to handle a normal data attribute in case of a new record. + def initialize(attributes) + @session_id, @data, @marshaled_data = attributes[:session_id], attributes[:data], attributes[:marshaled_data] + @new_record = @marshaled_data.nil? + end + + def new_record? + @new_record + end + + # Lazy-unmarshal session state. + def data + unless @data + if @marshaled_data + @data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil + else + @data = {} + end + end + @data + end + + def loaded? + !!@data + end + + def save + return false if !loaded? + marshaled_data = self.class.marshal(data) + + if @new_record + @new_record = false + @@connection.update <<-end_sql, 'Create session' + INSERT INTO #{@@table_name} ( + #{@@connection.quote_column_name(@@session_id_column)}, + #{@@connection.quote_column_name(@@data_column)} ) + VALUES ( + #{@@connection.quote(session_id)}, + #{@@connection.quote(marshaled_data)} ) + end_sql + else + @@connection.update <<-end_sql, 'Update session' + UPDATE #{@@table_name} + SET #{@@connection.quote_column_name(@@data_column)}=#{@@connection.quote(marshaled_data)} + WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)} + end_sql + end + end + + def destroy + unless @new_record + @@connection.delete <<-end_sql, 'Destroy session' + DELETE FROM #{@@table_name} + WHERE #{@@connection.quote_column_name(@@session_id_column)}=#{@@connection.quote(session_id)} + end_sql + end + end + end + + # The class used for session storage. Defaults to + # ActiveRecord::SessionStore::Session + cattr_accessor :session_class + self.session_class = Session + + SESSION_RECORD_KEY = 'rack.session.record'.freeze + + private + def get_session(env, sid) + Base.silence do + sid ||= generate_sid + session = @@session_class.find_by_session_id(sid) + session ||= @@session_class.new(:session_id => sid, :data => {}) + env[SESSION_RECORD_KEY] = session + [sid, session.data] + end + end + + def set_session(env, sid, session_data) + Base.silence do + record = env[SESSION_RECORD_KEY] + record.data = session_data + return false unless record.save + + session_data = record.data + if session_data && session_data.respond_to?(:each_value) + session_data.each_value do |obj| + obj.clear_association_cache if obj.respond_to?(:clear_association_cache) + end + end + end + + return true + end + end +end -- cgit v1.2.3 From c4023cbe206415cf3ca1ca92cd9980a4aa4aed00 Mon Sep 17 00:00:00 2001 From: Daniel Luz Date: Mon, 1 Dec 2008 02:58:39 -0200 Subject: Update documentation for default_scope Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a23518b357..87c0002463 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2052,10 +2052,10 @@ module ActiveRecord #:nodoc: end # Sets the default options for the model. The format of the - # method_scoping argument is the same as in with_scope. + # options argument is the same as in find. # # class Person < ActiveRecord::Base - # default_scope :find => { :order => 'last_name, first_name' } + # default_scope :order => 'last_name, first_name' # end def default_scope(options = {}) self.default_scoping << { :find => options, :create => (options.is_a?(Hash) && options.has_key?(:conditions)) ? options[:conditions] : {} } -- cgit v1.2.3 From 428e77bf0fcee4369cb8d94011141f791b8e2ba9 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Wed, 17 Dec 2008 23:37:55 +0000 Subject: Make exceptions raise from find_by_foo! have a more helpful message [#1567 state:resolved] --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 87c0002463..dfead0b94f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1829,7 +1829,7 @@ module ActiveRecord #:nodoc: else find(:#{finder}, options.merge(finder_options)) end - #{'result || raise(RecordNotFound)' if bang} + #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect {|pair| "#{pair.first} = #{pair.second}"}.join(\', \')}")' if bang} end }, __FILE__, __LINE__ send(method_id, *arguments) -- cgit v1.2.3 From 707d0dd3e1e8df7771073670e4257d933d2818f9 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Wed, 17 Dec 2008 23:39:09 +0000 Subject: Fix preloading of belongs_to with null foreign key generating useless query [#1027 state:resolved] --- activerecord/lib/active_record/association_preload.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 99c3ce5e62..d8aa1051bd 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -307,6 +307,7 @@ module ActiveRecord klasses_and_ids.each do |klass_and_id| klass_name, id_map = *klass_and_id + next if id_map.empty? klass = klass_name.constantize table_name = klass.quoted_table_name -- cgit v1.2.3 From c9ab7098be7bdd748c0f4a49c8ef015b4aad3108 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Thu, 18 Dec 2008 19:07:55 +0000 Subject: Ensure :include checks joins when determining if it can preload [#528 state:resolved] --- activerecord/lib/active_record/associations.rb | 43 ++++++++++++++++++++------ 1 file changed, 33 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 3fbbea43ed..bc2a88ef7e 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1731,6 +1731,11 @@ module ActiveRecord return sanitize_sql(sql) end + def tables_in_string(string) + return [] if string.blank? + string.scan(/([\.a-zA-Z_]+).?\./).flatten + end + def conditions_tables(options) # look in both sets of conditions conditions = [scope(:find, :conditions), options[:conditions]].inject([]) do |all, cond| @@ -1741,37 +1746,55 @@ module ActiveRecord else all << cond end end - conditions.join(' ').scan(/([\.a-zA-Z_]+).?\./).flatten + tables_in_string(conditions.join(' ')) end def order_tables(options) order = [options[:order], scope(:find, :order) ].join(", ") return [] unless order && order.is_a?(String) - order.scan(/([\.a-zA-Z_]+).?\./).flatten + tables_in_string(order) end def selects_tables(options) select = options[:select] return [] unless select && select.is_a?(String) - select.scan(/"?([\.a-zA-Z_]+)"?.?\./).flatten + tables_in_string(select) + end + + def joined_tables(options) + scope = scope(:find) + joins = options[:joins] + merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) + [table_name] + case merged_joins + when Symbol, Hash, Array + if array_of_strings?(merged_joins) + tables_in_string(merged_joins.join(' ')) + else + join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) + [table_name] + join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact + end + else + tables_in_string(merged_joins) + end end # Checks if the conditions reference a table other than the current model table - def include_eager_conditions?(options, tables = nil) - ((tables || conditions_tables(options)) - [table_name]).any? + def include_eager_conditions?(options, tables = nil, joined_tables = nil) + ((tables || conditions_tables(options)) - (joined_tables || joined_tables(options))).any? end # Checks if the query order references a table other than the current model's table. - def include_eager_order?(options, tables = nil) - ((tables || order_tables(options)) - [table_name]).any? + def include_eager_order?(options, tables = nil, joined_tables = nil) + ((tables || order_tables(options)) - (joined_tables || joined_tables(options))).any? end - def include_eager_select?(options) - (selects_tables(options) - [table_name]).any? + def include_eager_select?(options, joined_tables = nil) + (selects_tables(options) - (joined_tables || joined_tables(options))).any? end def references_eager_loaded_tables?(options) - include_eager_order?(options) || include_eager_conditions?(options) || include_eager_select?(options) + joined_tables = joined_tables(options) + include_eager_order?(options, nil, joined_tables) || include_eager_conditions?(options, nil, joined_tables) || include_eager_select?(options, joined_tables) end def using_limitable_reflections?(reflections) -- cgit v1.2.3 From 9c7fe7c6729bbdde4a22f58e7279463092390330 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Thu, 18 Dec 2008 10:41:45 +0000 Subject: Don't include table_name twice --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index bc2a88ef7e..3165015f3e 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1771,7 +1771,7 @@ module ActiveRecord tables_in_string(merged_joins.join(' ')) else join_dependency = ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, merged_joins, nil) - [table_name] + join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact + join_dependency.join_associations.collect {|join_association| [join_association.aliased_join_table_name, join_association.aliased_table_name]}.flatten.compact end else tables_in_string(merged_joins) -- cgit v1.2.3 From 8326b95169ae6af3b81f5596107fef9db4bcbbb0 Mon Sep 17 00:00:00 2001 From: Manfred Stienstra Date: Wed, 19 Nov 2008 15:17:40 +0100 Subject: Free MySQL::Result objects after a call to execute [#1416 state:resolved] No freeing Result objects causes the MySQL driver to free result sets at undefined times, this can lead to erratic performance in your application. Signed-off-by: Frederick Cheung --- .../active_record/connection_adapters/mysql_adapter.rb | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index c3cbdc8c2f..46d4b6c89c 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -308,6 +308,7 @@ module ActiveRecord rows end + # Executes a SQL query and returns a MySQL::Result object. Note that you have to free the Result object after you're done using it. def execute(sql, name = nil) #:nodoc: log(sql, name) { @connection.query(sql) } rescue ActiveRecord::StatementInvalid => exception @@ -414,7 +415,9 @@ module ActiveRecord def tables(name = nil) #:nodoc: tables = [] - execute("SHOW TABLES", name).each { |field| tables << field[0] } + result = execute("SHOW TABLES", name) + result.each { |field| tables << field[0] } + result.free tables end @@ -425,7 +428,8 @@ module ActiveRecord def indexes(table_name, name = nil)#:nodoc: indexes = [] current_index = nil - execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name).each do |row| + result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name) + result.each do |row| if current_index != row[2] next if row[2] == "PRIMARY" # skip the primary key current_index = row[2] @@ -434,13 +438,16 @@ module ActiveRecord indexes.last.columns << row[4] end + result.free indexes end def columns(table_name, name = nil)#:nodoc: sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}" columns = [] - execute(sql, name).each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") } + result = execute(sql, name) + result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") } + result.free columns end @@ -521,9 +528,11 @@ module ActiveRecord # Returns a table's primary key and belonging sequence. def pk_and_sequence_for(table) #:nodoc: keys = [] - execute("describe #{quote_table_name(table)}").each_hash do |h| + result = execute("describe #{quote_table_name(table)}") + result.each_hash do |h| keys << h["Field"]if h["Key"] == "PRI" end + result.free keys.length == 1 ? [keys.first, nil] : nil end -- cgit v1.2.3 From a9422cc1db9501a80ecf2c25a5d3b0c4f4f32763 Mon Sep 17 00:00:00 2001 From: Matt Jones Date: Tue, 2 Dec 2008 16:21:21 -0500 Subject: Fix preloading of has_one :through associations on belongs_to [#1507 state:resolved] Signed-off-by: Frederick Cheung --- activerecord/lib/active_record/association_preload.rb | 15 ++++++++++++--- .../active_record/associations/association_collection.rb | 6 +++++- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index d8aa1051bd..7b1b2d9ad9 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -204,9 +204,18 @@ module ActiveRecord unless through_records.empty? source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source) - through_records.each do |through_record| - add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s], - reflection.name, through_record.send(source)) + if through_reflection.macro == :belongs_to + rev_id_to_record_map, rev_ids = construct_id_map(records, through_primary_key) + rev_primary_key = through_reflection.klass.primary_key + through_records.each do |through_record| + add_preloaded_record_to_collection(rev_id_to_record_map[through_record[rev_primary_key].to_s], + reflection.name, through_record.send(source)) + end + else + through_records.each do |through_record| + add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s], + reflection.name, through_record.send(source)) + end end end else diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 0ff91fbdf8..0fefec1216 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -83,7 +83,11 @@ module ActiveRecord def to_ary load_target - @target.to_ary + if @target.is_a?(Array) + @target.to_ary + else + Array(@target) + end end def reset -- cgit v1.2.3 From c3f53f412cd170fc295b46e48aa81837ad15ec83 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 19 Dec 2008 14:27:43 +0000 Subject: Merge docrails --- activerecord/lib/active_record/associations.rb | 10 +++++----- activerecord/lib/active_record/base.rb | 3 +-- .../connection_adapters/abstract/connection_specification.rb | 1 + .../connection_adapters/abstract/schema_definitions.rb | 4 +++- activerecord/lib/active_record/validations.rb | 6 +++--- 5 files changed, 13 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 3165015f3e..3cee9c7af2 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -153,7 +153,7 @@ module ActiveRecord # #others.destroy_all | X | X | X # #others.find(*args) | X | X | X # #others.find_first | X | | - # #others.exist? | X | X | X + # #others.exists? | X | X | X # #others.uniq | X | X | X # #others.reset | X | X | X # @@ -652,7 +652,7 @@ module ActiveRecord # Returns the number of associated objects. # [collection.find(...)] # Finds an associated object according to the same rules as ActiveRecord::Base.find. - # [collection.exist?(...)] + # [collection.exists?(...)] # Checks whether an associated object with the given conditions exists. # Uses the same rules as ActiveRecord::Base.exists?. # [collection.build(attributes = {}, ...)] @@ -682,7 +682,7 @@ module ActiveRecord # * Firm#clients.empty? (similar to firm.clients.size == 0) # * Firm#clients.size (similar to Client.count "firm_id = #{id}") # * Firm#clients.find (similar to Client.find(id, :conditions => "firm_id = #{id}")) - # * Firm#clients.exist?(:name => 'ACME') (similar to Client.exist?(:name => 'ACME', :firm_id => firm.id)) + # * Firm#clients.exists?(:name => 'ACME') (similar to Client.exists?(:name => 'ACME', :firm_id => firm.id)) # * Firm#clients.build (similar to Client.new("firm_id" => id)) # * Firm#clients.create (similar to c = Client.new("firm_id" => id); c.save; c) # The declaration can also include an options hash to specialize the behavior of the association. @@ -1107,7 +1107,7 @@ module ActiveRecord # Finds an associated object responding to the +id+ and that # meets the condition that it has to be associated with this object. # Uses the same rules as ActiveRecord::Base.find. - # [collection.exist?(...)] + # [collection.exists?(...)] # Checks whether an associated object with the given conditions exists. # Uses the same rules as ActiveRecord::Base.exists?. # [collection.build(attributes = {})] @@ -1133,7 +1133,7 @@ module ActiveRecord # * Developer#projects.empty? # * Developer#projects.size # * Developer#projects.find(id) - # * Developer#clients.exist?(...) + # * Developer#clients.exists?(...) # * Developer#projects.build (similar to Project.new("project_id" => id)) # * Developer#projects.create (similar to c = Project.new("project_id" => id); c.save; c) # The declaration may include an options hash to specialize the behavior of the association. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index dfead0b94f..aa6013583d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -811,8 +811,7 @@ module ActiveRecord #:nodoc: # # ==== Parameters # - # * +updates+ - A string of column and value pairs that will be set on any records that match conditions. - # What goes into the SET clause. + # * +updates+ - A string of column and value pairs that will be set on any records that match conditions. This creates the SET clause of the generated SQL. # * +conditions+ - An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro for more info. # * +options+ - Additional options are :limit and :order, see the examples for usage. # diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index ccb79f547a..bbc290f721 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -123,6 +123,7 @@ module ActiveRecord connection_handler.retrieve_connection(self) end + # Returns true if +ActiveRecord+ is connected. def connected? connection_handler.connected?(self) end 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 58992f91da..fe9cbcf024 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -32,10 +32,12 @@ module ActiveRecord @primary = nil end + # Returns +true+ if the column is either of type string or text. def text? type == :string || type == :text end + # Returns +true+ if the column is either of type integer, float or decimal. def number? type == :integer || type == :float || type == :decimal end @@ -295,7 +297,7 @@ module ActiveRecord # puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition" # end # end - # + # # def self.down # ... # end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 4b275ddd70..617b3f440f 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1049,15 +1049,15 @@ module ActiveRecord protected # Overwrite this method for validation checks on all saves and use Errors.add(field, msg) for invalid attributes. - def validate #:doc: + def validate end # Overwrite this method for validation checks used only on creation. - def validate_on_create #:doc: + def validate_on_create end # Overwrite this method for validation checks used only on updates. - def validate_on_update # :doc: + def validate_on_update end end end -- cgit v1.2.3 From 75a133f92ff7e27b83032babf829d8a58803bb3c Mon Sep 17 00:00:00 2001 From: Karthik Krishnan Date: Sat, 20 Dec 2008 20:52:48 +0000 Subject: Fix has many through not quoting table names [#1163 state:resolved] Signed-off-by: Frederick Cheung --- activerecord/lib/active_record/associations.rb | 2 +- .../lib/active_record/associations/has_many_through_association.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 3cee9c7af2..07bc50c886 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2198,7 +2198,7 @@ module ActiveRecord protected def aliased_table_name_for(name, suffix = nil) - if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{name.downcase}\son} + if !parent.table_joins.blank? && parent.table_joins.to_s.downcase =~ %r{join(\s+\w+)?\s+#{active_record.connection.quote_table_name name.downcase}\son} @join_dependency.table_aliases[name] += 1 end 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 a0bb3a45b0..2eeeb28d1f 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -160,9 +160,9 @@ module ActiveRecord end "INNER JOIN %s ON %s.%s = %s.%s %s #{@reflection.options[:joins]} #{custom_joins}" % [ - @reflection.through_reflection.table_name, - @reflection.table_name, reflection_primary_key, - @reflection.through_reflection.table_name, source_primary_key, + @reflection.through_reflection.quoted_table_name, + @reflection.quoted_table_name, reflection_primary_key, + @reflection.through_reflection.quoted_table_name, source_primary_key, polymorphic_join ] end -- cgit v1.2.3 From 276ea48de96a0c4242139ec9323eefb6f254c2a1 Mon Sep 17 00:00:00 2001 From: Yaroslav Markin Date: Sun, 21 Dec 2008 11:35:50 +0000 Subject: Remove dead commented out code [#1467 state:resolved] Signed-off-by: Frederick Cheung --- activerecord/lib/active_record/validations.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 617b3f440f..6a9690ba85 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -203,7 +203,6 @@ module ActiveRecord if attr == "base" full_messages << message else - #key = :"activerecord.att.#{@base.class.name.underscore.to_sym}.#{attr}" attr_name = @base.class.human_attribute_name(attr) full_messages << attr_name + I18n.t('activerecord.errors.format.separator', :default => ' ') + message end -- cgit v1.2.3 From b17b9371c6a26484eb1984d45acffcdcd91b1ae1 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Sun, 21 Dec 2008 15:38:40 +0000 Subject: Fix configure_dependency_for_has_many not quoting conditions properly [#1461 state:resolved] --- activerecord/lib/active_record/associations.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 07bc50c886..5a60b13fd8 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1453,7 +1453,7 @@ module ActiveRecord dependent_conditions << sanitize_sql(reflection.options[:conditions]) if reflection.options[:conditions] dependent_conditions << extra_conditions if extra_conditions dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") - + dependent_conditions = dependent_conditions.gsub('@', '\@') case reflection.options[:dependent] when :destroy method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym @@ -1467,7 +1467,7 @@ module ActiveRecord delete_all_has_many_dependencies(record, "#{reflection.name}", #{reflection.class_name}, - "#{dependent_conditions}") + %@#{dependent_conditions}@) end } when :nullify @@ -1477,7 +1477,7 @@ module ActiveRecord "#{reflection.name}", #{reflection.class_name}, "#{reflection.primary_key_name}", - "#{dependent_conditions}") + %@#{dependent_conditions}@) end } else -- cgit v1.2.3 From f7bd0beb67c5d9d50e37aa596605b91e61197fbe Mon Sep 17 00:00:00 2001 From: Daniel Luz Date: Sun, 21 Dec 2008 22:38:12 +0000 Subject: Ensure Model#last doesn't affects order for another finders inside the same scope [#1499 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/base.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index aa6013583d..9db0366c46 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1494,11 +1494,16 @@ module ActiveRecord #:nodoc: end if scoped?(:find, :order) - scoped_order = reverse_sql_order(scope(:find, :order)) - scoped_methods.select { |s| s[:find].update(:order => scoped_order) } + scope = scope(:find) + original_scoped_order = scope[:order] + scope[:order] = reverse_sql_order(original_scoped_order) end - find_initial(options.merge({ :order => order })) + begin + find_initial(options.merge({ :order => order })) + ensure + scope[:order] = original_scoped_order if original_scoped_order + end end def reverse_sql_order(order_query) -- cgit v1.2.3 From 63aac338332a06d3c9e28dde7954679703ec7620 Mon Sep 17 00:00:00 2001 From: Luis Hurtado Date: Mon, 22 Dec 2008 15:18:43 +0000 Subject: Ensure of Model#create support custom updated_at and updated_on attributes [#1612 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/timestamp.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index a9e0efa6fe..8dbe80a01a 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -23,8 +23,8 @@ module ActiveRecord write_attribute('created_at', t) if respond_to?(:created_at) && created_at.nil? write_attribute('created_on', t) if respond_to?(:created_on) && created_on.nil? - write_attribute('updated_at', t) if respond_to?(:updated_at) - write_attribute('updated_on', t) if respond_to?(:updated_on) + write_attribute('updated_at', t) if respond_to?(:updated_at) && updated_at.nil? + write_attribute('updated_on', t) if respond_to?(:updated_on) && updated_on.nil? end create_without_timestamps end -- cgit v1.2.3 From aa002c0e86afdc83693f14667a710107843f0fbd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 22 Dec 2008 11:31:18 -0600 Subject: ActiveRecord::QueryCache middleware --- activerecord/lib/active_record/base.rb | 2 +- activerecord/lib/active_record/query_cache.rb | 38 ++++++++++++++++++--------- 2 files changed, 26 insertions(+), 14 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9db0366c46..9746a46d47 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -3015,7 +3015,7 @@ module ActiveRecord #:nodoc: end Base.class_eval do - extend QueryCache + extend QueryCache::ClassMethods include Validations include Locking::Optimistic, Locking::Pessimistic include AttributeMethods diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index a8af89fcb9..eb92bc2545 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -1,20 +1,32 @@ module ActiveRecord - module QueryCache - # Enable the query cache within the block if Active Record is configured. - def cache(&block) - if ActiveRecord::Base.configurations.blank? - yield - else - connection.cache(&block) + class QueryCache + module ClassMethods + # Enable the query cache within the block if Active Record is configured. + def cache(&block) + if ActiveRecord::Base.configurations.blank? + yield + else + connection.cache(&block) + end end + + # Disable the query cache within the block if Active Record is configured. + def uncached(&block) + if ActiveRecord::Base.configurations.blank? + yield + else + connection.uncached(&block) + end + end + end + + def initialize(app) + @app = app end - # Disable the query cache within the block if Active Record is configured. - def uncached(&block) - if ActiveRecord::Base.configurations.blank? - yield - else - connection.uncached(&block) + def call(env) + ActiveRecord::Base.cache do + @app.call(env) end end end -- cgit v1.2.3 From eb457ceee13779ade67e1bdebd2919d476148277 Mon Sep 17 00:00:00 2001 From: Pivotal Labs Date: Wed, 24 Dec 2008 14:57:09 -0800 Subject: Association preloading no longer stops if it hits a nil object [#1630 state:resolved] Signed-off-by: Frederick Cheung --- activerecord/lib/active_record/association_preload.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 7b1b2d9ad9..0bcf50c167 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -94,8 +94,8 @@ module ActiveRecord raise "parent must be an association name" unless parent.is_a?(String) || parent.is_a?(Symbol) preload_associations(records, parent, preload_options) reflection = reflections[parent] - parents = records.map {|record| record.send(reflection.name)}.flatten - unless parents.empty? || parents.first.nil? + parents = records.map {|record| record.send(reflection.name)}.flatten.compact + unless parents.empty? parents.first.class.preload_associations(parents, child) end end -- cgit v1.2.3 From 5cebe69e74d411c3c9e5f6ab9d4b2b16ee36177c Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Fri, 19 Dec 2008 01:02:21 +0000 Subject: Preload uses exclusive scope [#643 state:resolved] With self referential associations, the scope for the the top level should not affect fetching of associations, for example when doing Person.male.find :all, :include => :friends we should load all of the friends for each male, not just the male friends. --- .../lib/active_record/association_preload.rb | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 0bcf50c167..9d0bf3a308 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -43,7 +43,7 @@ module ActiveRecord # loading in a more high-level (application developer-friendly) manner. module ClassMethods protected - + # Eager loads the named associations for the given ActiveRecord record(s). # # In this description, 'association name' shall refer to the name passed @@ -113,7 +113,7 @@ module ActiveRecord # unnecessarily records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records| raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection - + # 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus, # the following could call 'preload_belongs_to_association', # 'preload_has_many_association', etc. @@ -128,7 +128,7 @@ module ActiveRecord association_proxy.target.push(*[associated_record].flatten) end end - + def add_preloaded_record_to_collection(parent_records, reflection_name, associated_record) parent_records.each do |parent_record| parent_record.send("set_#{reflection_name}_target", associated_record) @@ -183,18 +183,19 @@ module ActiveRecord conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}" conditions << append_conditions(reflection, preload_options) - associated_records = reflection.klass.find(:all, :conditions => [conditions, ids], - :include => options[:include], - :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", - :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id", - :order => options[:order]) - + associated_records = reflection.klass.with_exclusive_scope do + reflection.klass.find(:all, :conditions => [conditions, ids], + :include => options[:include], + :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", + :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id", + :order => options[:order]) + end set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id') end def preload_has_one_association(records, reflection, preload_options={}) return if records.first.send("loaded_#{reflection.name}?") - id_to_record_map, ids = construct_id_map(records) + id_to_record_map, ids = construct_id_map(records) options = reflection.options records.each {|record| record.send("set_#{reflection.name}_target", nil)} if options[:through] @@ -248,7 +249,7 @@ module ActiveRecord reflection.primary_key_name) end end - + def preload_through_records(records, reflection, through_association) through_reflection = reflections[through_association] through_primary_key = through_reflection.primary_key_name @@ -333,11 +334,13 @@ module ActiveRecord end conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}" conditions << append_conditions(reflection, preload_options) - associated_records = klass.find(:all, :conditions => [conditions, ids], + associated_records = klass.with_exclusive_scope do + klass.find(:all, :conditions => [conditions, ids], :include => options[:include], :select => options[:select], :joins => options[:joins], :order => options[:order]) + end set_association_single_records(id_map, reflection.name, associated_records, primary_key) end end @@ -355,13 +358,15 @@ module ActiveRecord conditions << append_conditions(reflection, preload_options) - reflection.klass.find(:all, + reflection.klass.with_exclusive_scope do + reflection.klass.find(:all, :select => (preload_options[:select] || options[:select] || "#{table_name}.*"), :include => preload_options[:include] || options[:include], :conditions => [conditions, ids], :joins => options[:joins], :group => preload_options[:group] || options[:group], :order => preload_options[:order] || options[:order]) + end end -- cgit v1.2.3 From 7db1704068b86fb2212388b14b4963526bacfa5d Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Fri, 26 Dec 2008 22:53:07 +0000 Subject: Fix :include of has_many associations with :primary_key option --- activerecord/lib/active_record/association_preload.rb | 2 +- activerecord/lib/active_record/associations.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 9d0bf3a308..195d2fb4f8 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -229,7 +229,7 @@ module ActiveRecord options = reflection.options primary_key_name = reflection.through_reflection_primary_key_name - id_to_record_map, ids = construct_id_map(records, primary_key_name) + id_to_record_map, ids = construct_id_map(records, primary_key_name || reflection.options[:primary_key]) records.each {|record| record.send(reflection.name).loaded} if options[:through] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 5a60b13fd8..237e5c0e9d 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2171,7 +2171,7 @@ module ActiveRecord aliased_table_name, foreign_key, parent.aliased_table_name, - parent.primary_key + reflection.options[:primary_key] || parent.primary_key ] end when :belongs_to -- cgit v1.2.3 From f9cab0e503a4721c9d0369f89bb85c6e658f778c Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Fri, 26 Dec 2008 23:26:37 +0000 Subject: Fix :include of has_one with :primary_key option --- activerecord/lib/active_record/association_preload.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 195d2fb4f8..e4ab69aa1b 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -195,7 +195,7 @@ module ActiveRecord def preload_has_one_association(records, reflection, preload_options={}) return if records.first.send("loaded_#{reflection.name}?") - id_to_record_map, ids = construct_id_map(records) + id_to_record_map, ids = construct_id_map(records, reflection.options[:primary_key]) options = reflection.options records.each {|record| record.send("set_#{reflection.name}_target", nil)} if options[:through] -- cgit v1.2.3 From 21efba464afa2ae6e5dfd938ac8a3ce446faf7e7 Mon Sep 17 00:00:00 2001 From: Roman Shterenzon Date: Sat, 27 Dec 2008 01:10:29 +0000 Subject: Fix HasManyAssociation#create ignoring the :primary_key option [#1633 state:resolved] Signed-off-by: Frederick Cheung --- activerecord/lib/active_record/associations/association_proxy.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 59f1d3b867..676c4ace61 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -180,7 +180,10 @@ module ActiveRecord record["#{@reflection.options[:as]}_id"] = @owner.id unless @owner.new_record? record["#{@reflection.options[:as]}_type"] = @owner.class.base_class.name.to_s else - record[@reflection.primary_key_name] = @owner.id unless @owner.new_record? + unless @owner.new_record? + primary_key = @reflection.options[:primary_key] || :id + record[@reflection.primary_key_name] = @owner.send(primary_key) + end end end -- cgit v1.2.3 From 6e98adfc8e19a39fa45d4acd94145d318d151964 Mon Sep 17 00:00:00 2001 From: Yaroslav Markin Date: Sat, 27 Dec 2008 16:26:13 +0300 Subject: ActiveRecord::Base#new_record? now returns false for existing records (was nil) [#1219 state:committed] Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9746a46d47..80c109aea7 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2406,9 +2406,9 @@ module ActiveRecord #:nodoc: write_attribute(self.class.primary_key, value) end - # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet. + # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false. def new_record? - defined?(@new_record) && @new_record + (defined?(@new_record) && @new_record) || false end # :call-seq: -- cgit v1.2.3 From afdec83ed543e904b495d3225b6401101ea7ba6c Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Sat, 27 Dec 2008 14:16:17 +0000 Subject: Fix to_sentence being used with options removed by 273c77 --- activerecord/lib/active_record/associations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 237e5c0e9d..eba10b505e 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -22,7 +22,7 @@ module ActiveRecord through_reflection = reflection.through_reflection source_reflection_names = reflection.source_reflection_names source_associations = reflection.through_reflection.klass.reflect_on_all_associations.collect { |a| a.name.inspect } - super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :connector => 'or'} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence :connector => 'or'}?") + super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence :two_words_connector => ' or ', :last_word_connector => ', or '}?") end end -- cgit v1.2.3 From 28347d889bb4304e681bf885fea734067fdd8ff6 Mon Sep 17 00:00:00 2001 From: Yaroslav Markin Date: Sat, 27 Dec 2008 17:02:07 +0300 Subject: Refactor ActiveRecord::Base#new_record? [#1647 state:committed] Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 80c109aea7..e5e94555eb 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2408,7 +2408,7 @@ module ActiveRecord #:nodoc: # Returns true if this object hasn't been saved yet -- that is, a record for the object doesn't exist yet; otherwise, returns false. def new_record? - (defined?(@new_record) && @new_record) || false + @new_record || false end # :call-seq: -- cgit v1.2.3 From a2270ef2594b97891994848138614657363f2806 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 28 Dec 2008 19:48:05 +0000 Subject: Inline code comments for class_eval/module_eval [#1657 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 36 ++++++++-------- activerecord/lib/active_record/base.rb | 48 +++++++++++++++++++++- .../connection_adapters/abstract/query_cache.rb | 12 +++--- .../abstract/schema_definitions.rb | 48 +++++++++++----------- .../connection_adapters/mysql_adapter.rb | 24 ++++++----- .../connection_adapters/postgresql_adapter.rb | 14 +++---- activerecord/lib/active_record/dirty.rb | 2 +- 7 files changed, 116 insertions(+), 68 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 5a60b13fd8..c154a5087c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1216,11 +1216,11 @@ module ActiveRecord # callbacks will be executed after the association is wiped out. old_method = "destroy_without_habtm_shim_for_#{reflection.name}" class_eval <<-end_eval unless method_defined?(old_method) - alias_method :#{old_method}, :destroy_without_callbacks - def destroy_without_callbacks - #{reflection.name}.clear - #{old_method} - end + alias_method :#{old_method}, :destroy_without_callbacks # alias_method :destroy_without_habtm_shim_for_posts, :destroy_without_callbacks + def destroy_without_callbacks # def destroy_without_callbacks + #{reflection.name}.clear # posts.clear + #{old_method} # destroy_without_habtm_shim_for_posts + end # end end_eval add_association_callbacks(reflection.name, options) @@ -1463,22 +1463,22 @@ module ActiveRecord before_destroy method_name when :delete_all module_eval %Q{ - before_destroy do |record| - delete_all_has_many_dependencies(record, - "#{reflection.name}", - #{reflection.class_name}, - %@#{dependent_conditions}@) - end + before_destroy do |record| # before_destroy do |record| + delete_all_has_many_dependencies(record, # delete_all_has_many_dependencies(record, + "#{reflection.name}", # "posts", + #{reflection.class_name}, # Post, + %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...) + end # end } when :nullify module_eval %Q{ - before_destroy do |record| - nullify_has_many_dependencies(record, - "#{reflection.name}", - #{reflection.class_name}, - "#{reflection.primary_key_name}", - %@#{dependent_conditions}@) - end + before_destroy do |record| # before_destroy do |record| + nullify_has_many_dependencies(record, # nullify_has_many_dependencies(record, + "#{reflection.name}", # "posts", + #{reflection.class_name}, # Post, + "#{reflection.primary_key_name}", # "user_id", + %@#{dependent_conditions}@) # %@...@) # this is a string literal like %(...) + end # end } else raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, or :nullify (#{reflection.options[:dependent].inspect})" diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e5e94555eb..4c9ad24ea3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1818,10 +1818,31 @@ module ActiveRecord #:nodoc: if match.finder? finder = match.finder bang = match.bang? + # def self.find_by_login_and_activated(*args) + # options = args.extract_options! + # attributes = construct_attributes_from_arguments( + # [:login,:activated], + # args + # ) + # finder_options = { :conditions => attributes } + # validate_find_options(options) + # set_readonly_option!(options) + # + # if options[:conditions] + # with_scope(:find => finder_options) do + # find(:first, options) + # end + # else + # find(:first, options.merge(finder_options)) + # end + # end self.class_eval %{ def self.#{method_id}(*args) options = args.extract_options! - attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) + attributes = construct_attributes_from_arguments( + [:#{attribute_names.join(',:')}], + args + ) finder_options = { :conditions => attributes } validate_find_options(options) set_readonly_option!(options) @@ -1839,6 +1860,31 @@ module ActiveRecord #:nodoc: send(method_id, *arguments) elsif match.instantiator? instantiator = match.instantiator + # def self.find_or_create_by_user_id(*args) + # guard_protected_attributes = false + # + # if args[0].is_a?(Hash) + # guard_protected_attributes = true + # attributes = args[0].with_indifferent_access + # find_attributes = attributes.slice(*[:user_id]) + # else + # find_attributes = attributes = construct_attributes_from_arguments([:user_id], args) + # end + # + # options = { :conditions => find_attributes } + # set_readonly_option!(options) + # + # record = find(:first, options) + # + # if record.nil? + # record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } + # yield(record) if block_given? + # record.save + # record + # else + # record + # end + # end self.class_eval %{ def self.#{method_id}(*args) guard_protected_attributes = false 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 950bd72101..00c71090f3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -14,12 +14,12 @@ module ActiveRecord def dirties_query_cache(base, *method_names) method_names.each do |method_name| base.class_eval <<-end_code, __FILE__, __LINE__ - def #{method_name}_with_query_dirty(*args) - clear_query_cache if @query_cache_enabled - #{method_name}_without_query_dirty(*args) - end - - alias_method_chain :#{method_name}, :query_dirty + def #{method_name}_with_query_dirty(*args) # def update_with_query_dirty(*args) + clear_query_cache if @query_cache_enabled # clear_query_cache if @query_cache_enabled + #{method_name}_without_query_dirty(*args) # update_without_query_dirty(*args) + end # end + # + alias_method_chain :#{method_name}, :query_dirty # alias_method_chain :update, :query_dirty end_code end end 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 fe9cbcf024..273f823e7f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -476,12 +476,12 @@ module ActiveRecord %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type| class_eval <<-EOV - def #{column_type}(*args) - options = args.extract_options! - column_names = args - - column_names.each { |name| column(name, '#{column_type}', options) } - end + def #{column_type}(*args) # def string(*args) + options = args.extract_options! # options = args.extract_options! + column_names = args # column_names = args + # + column_names.each { |name| column(name, '#{column_type}', options) } # column_names.each { |name| column(name, 'string', options) } + end # end EOV end @@ -676,24 +676,24 @@ module ActiveRecord # t.string(:goat, :sheep) %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |column_type| class_eval <<-EOV - def #{column_type}(*args) - options = args.extract_options! - column_names = args - - column_names.each do |name| - column = ColumnDefinition.new(@base, name, '#{column_type}') - if options[:limit] - column.limit = options[:limit] - elsif native['#{column_type}'.to_sym].is_a?(Hash) - column.limit = native['#{column_type}'.to_sym][:limit] - end - column.precision = options[:precision] - column.scale = options[:scale] - column.default = options[:default] - column.null = options[:null] - @base.add_column(@table_name, name, column.sql_type, options) - end - end + def #{column_type}(*args) # def string(*args) + options = args.extract_options! # options = args.extract_options! + column_names = args # column_names = args + # + column_names.each do |name| # column_names.each do |name| + column = ColumnDefinition.new(@base, name, '#{column_type}') # column = ColumnDefinition.new(@base, name, 'string') + if options[:limit] # if options[:limit] + column.limit = options[:limit] # column.limit = options[:limit] + elsif native['#{column_type}'.to_sym].is_a?(Hash) # elsif native['string'.to_sym].is_a?(Hash) + column.limit = native['#{column_type}'.to_sym][:limit] # column.limit = native['string'.to_sym][:limit] + end # end + column.precision = options[:precision] # column.precision = options[:precision] + column.scale = options[:scale] # column.scale = options[:scale] + column.default = options[:default] # column.default = options[:default] + column.null = options[:null] # column.null = options[:null] + @base.add_column(@table_name, name, column.sql_type, options) # @base.add_column(@table_name, name, column.sql_type, options) + end # end + end # end EOV end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 46d4b6c89c..60729c63db 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -13,23 +13,25 @@ module MysqlCompat #:nodoc: # C driver >= 2.7 returns null values in each_hash if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700) target.class_eval <<-'end_eval' - def all_hashes - rows = [] - each_hash { |row| rows << row } - rows - end + def all_hashes # def all_hashes + rows = [] # rows = [] + each_hash { |row| rows << row } # each_hash { |row| rows << row } + rows # rows + end # end end_eval # adapters before 2.7 don't have a version constant # and don't return null values in each_hash else target.class_eval <<-'end_eval' - def all_hashes - rows = [] - all_fields = fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields } - each_hash { |row| rows << all_fields.dup.update(row) } - rows - end + def all_hashes # def all_hashes + rows = [] # rows = [] + all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f| + fields[f.name] = nil; fields # fields[f.name] = nil; fields + } # } + each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) } + rows # rows + end # end end_eval end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 60ec01b95e..6685cb8663 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -950,13 +950,13 @@ module ActiveRecord # should know about this but can't detect it there, so deal with it here. money_precision = (postgresql_version >= 80300) ? 19 : 10 PostgreSQLColumn.module_eval(<<-end_eval) - def extract_precision(sql_type) - if sql_type =~ /^money$/ - #{money_precision} - else - super - end - end + def extract_precision(sql_type) # def extract_precision(sql_type) + if sql_type =~ /^money$/ # if sql_type =~ /^money$/ + #{money_precision} # 19 + else # else + super # super + end # end + end # end end_eval configure_connection diff --git a/activerecord/lib/active_record/dirty.rb b/activerecord/lib/active_record/dirty.rb index a1760875ba..4c899f58e5 100644 --- a/activerecord/lib/active_record/dirty.rb +++ b/activerecord/lib/active_record/dirty.rb @@ -174,7 +174,7 @@ module ActiveRecord alias_attribute_without_dirty(new_name, old_name) DIRTY_SUFFIXES.each do |suffix| module_eval <<-STR, __FILE__, __LINE__+1 - def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end + def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end # def subject_changed?; self.title_changed?; end STR end end -- cgit v1.2.3 From 66ee5890c5f21995b7fe0c486547f1287afe2b55 Mon Sep 17 00:00:00 2001 From: Yaroslav Markin Date: Sun, 28 Dec 2008 22:25:55 +0300 Subject: Introduce dynamic scopes for ActiveRecord: you can now use class methods like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that will use the scoped method with attributes you supply. [#1648 state:committed] Signed-off-by: David Heinemeier Hansson --- activerecord/lib/active_record/base.rb | 25 +++++++++++++++++++++- .../lib/active_record/dynamic_scope_match.rb | 25 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 activerecord/lib/active_record/dynamic_scope_match.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e5e94555eb..5198dbfa9b 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1456,7 +1456,10 @@ module ActiveRecord #:nodoc: def respond_to?(method_id, include_private = false) if match = DynamicFinderMatch.match(method_id) return true if all_attributes_exists?(match.attribute_names) + elsif match = DynamicScopeMatch.match(method_id) + return true if all_attributes_exists?(match.attribute_names) end + super end @@ -1809,7 +1812,11 @@ module ActiveRecord #:nodoc: # This also enables you to initialize a record if it is not found, such as find_or_initialize_by_amount(amount) # or find_or_create_by_user_and_password(user, password). # - # Each dynamic finder or initializer/creator is also defined in the class after it is first invoked, so that future + # Also enables dynamic scopes like scoped_by_user_name(user_name) and scoped_by_user_name_and_password(user_name, password) that + # are turned into scoped(:conditions => ["user_name = ?", user_name]) and scoped(:conditions => ["user_name = ? AND password = ?", user_name, password]) + # respectively. + # + # Each dynamic finder, scope or initializer/creator is also defined in the class after it is first invoked, so that future # attempts to use it do not run through method_missing. def method_missing(method_id, *arguments, &block) if match = DynamicFinderMatch.match(method_id) @@ -1868,6 +1875,22 @@ module ActiveRecord #:nodoc: }, __FILE__, __LINE__ send(method_id, *arguments, &block) end + elsif match = DynamicScopeMatch.match(method_id) + attribute_names = match.attribute_names + super unless all_attributes_exists?(attribute_names) + if match.scope? + self.class_eval %{ + def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args) + options = args.extract_options! # options = args.extract_options! + attributes = construct_attributes_from_arguments( # attributes = construct_attributes_from_arguments( + [:#{attribute_names.join(',:')}], args # [:user_name, :password], args + ) # ) + # + scoped(:conditions => attributes) # scoped(:conditions => attributes) + end # end + }, __FILE__, __LINE__ + send(method_id, *arguments) + end else super end diff --git a/activerecord/lib/active_record/dynamic_scope_match.rb b/activerecord/lib/active_record/dynamic_scope_match.rb new file mode 100644 index 0000000000..f796ba669a --- /dev/null +++ b/activerecord/lib/active_record/dynamic_scope_match.rb @@ -0,0 +1,25 @@ +module ActiveRecord + class DynamicScopeMatch + def self.match(method) + ds_match = self.new(method) + ds_match.scope ? ds_match : nil + end + + def initialize(method) + @scope = true + case method.to_s + when /^scoped_by_([_a-zA-Z]\w*)$/ + names = $1 + else + @scope = nil + end + @attribute_names = names && names.split('_and_') + end + + attr_reader :scope, :attribute_names + + def scope? + !@scope.nil? + end + end +end -- cgit v1.2.3 From 220dff4c3b58b7becb587ee6f2434b2ca720f7c3 Mon Sep 17 00:00:00 2001 From: Mike Gunderloy Date: Mon, 29 Dec 2008 20:46:20 -0600 Subject: Add transaction check to SQLite2 adapter to fix test_sqlite_add_column_in_transaction_raises_statement_invalid [#1669 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 84f8c0284e..9387cf8827 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -402,6 +402,10 @@ module ActiveRecord end def add_column(table_name, column_name, type, options = {}) #:nodoc: + if @connection.respond_to?(:transaction_active?) && @connection.transaction_active? + raise StatementInvalid, 'Cannot add columns to a SQLite database while inside a transaction' + end + alter_table(table_name) do |definition| definition.column(column_name, type, options) end -- cgit v1.2.3