diff options
author | Rizwan Reza <rizwanreza@gmail.com> | 2010-03-28 12:04:00 +0430 |
---|---|---|
committer | Rizwan Reza <rizwanreza@gmail.com> | 2010-03-28 12:04:00 +0430 |
commit | b4c91de9462f59d6395c7c871aafa3301afdc383 (patch) | |
tree | fadcaf603a6f473e8ccd52070a1f3b4f957c34cb /activerecord/lib/active_record | |
parent | 4b4f69b9bfbd33556e23262f57cf6d944dfd9f63 (diff) | |
parent | 66d57d7ba83df52ff1e0485c89f6192c2f84c6b8 (diff) | |
download | rails-b4c91de9462f59d6395c7c871aafa3301afdc383.tar.gz rails-b4c91de9462f59d6395c7c871aafa3301afdc383.tar.bz2 rails-b4c91de9462f59d6395c7c871aafa3301afdc383.zip |
Merge remote branch 'rails/master'
Diffstat (limited to 'activerecord/lib/active_record')
18 files changed, 151 insertions, 104 deletions
diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index 9ecf231a66..08389907ef 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -213,6 +213,11 @@ module ActiveRecord module_eval do define_method(name) do |*args| force_reload = args.first || false + + unless instance_variable_defined?("@#{name}") + instance_variable_set("@#{name}", nil) + end + if (instance_variable_get("@#{name}").nil? || force_reload) && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? }) attrs = mapping.collect {|pair| read_attribute(pair.first)} object = case constructor diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c47e0c37b8..d623ddb915 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1545,15 +1545,19 @@ module ActiveRecord case name when :destroy, :delete - define_method(method_name) do - association = send(reflection.name) - association.send(name) if association - end + class_eval <<-eoruby, __FILE__, __LINE__ + 1 + def #{method_name} + association = #{reflection.name} + association.#{name} if association + end + eoruby when :nullify - define_method(method_name) do - association = send(reflection.name) - association.update_attribute(reflection.primary_key_name, nil) if association - end + class_eval <<-eoruby, __FILE__, __LINE__ + 1 + def #{method_name} + association = #{reflection.name} + association.update_attribute(#{reflection.primary_key_name.inspect}, nil) if association + end + eoruby else raise ArgumentError, "The :dependent option expects either :destroy, :delete or :nullify (#{reflection.options[:dependent].inspect})" end @@ -1571,10 +1575,12 @@ module ActiveRecord end method_name = :"belongs_to_dependent_#{name}_for_#{reflection.name}" - define_method(method_name) do - association = send(reflection.name) - association.send(name) if association - end + class_eval <<-eoruby, __FILE__, __LINE__ + 1 + def #{method_name} + association = #{reflection.name} + association.#{name} if association + end + eoruby after_destroy method_name end end @@ -1673,15 +1679,6 @@ module ActiveRecord reflection end - def using_limitable_reflections?(reflections) - reflections.collect(&:collection?).length.zero? - end - - def column_aliases(join_dependency) - join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name| - "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ") - end - def add_association_callbacks(association_name, options) callbacks = %w(before_add after_add before_remove after_remove) callbacks.each do |callback_name| diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 0ff89df1e3..4fb1df3ab9 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -53,6 +53,7 @@ module ActiveRecord def initialize(owner, reflection) @owner, @reflection = owner, reflection + @updated = false reflection.check_validity! Array(reflection.options[:extend]).each { |ext| proxy_extend(ext) } reset diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 146a6ca55f..0464e8ceea 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -5,6 +5,10 @@ module ActiveRecord # If the association has a <tt>:through</tt> option further specialization # is provided by its child HasManyThroughAssociation. class HasManyAssociation < AssociationCollection #:nodoc: + def initialize(owner, reflection) + @finder_sql = nil + super + end protected def owner_quoted_id if @reflection.options[:primary_key] diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 4a3ab9ea82..a8698a2f5a 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -18,7 +18,7 @@ module ActiveRecord def save_with_dirty(*args) #:nodoc: if status = save_without_dirty(*args) @previously_changed = changes - changed_attributes.clear + @changed_attributes.clear end status end @@ -26,16 +26,16 @@ module ActiveRecord # Attempts to <tt>save!</tt> the record and clears changed attributes if successful. def save_with_dirty!(*args) #:nodoc: save_without_dirty!(*args).tap do - @previously_changed = changes - changed_attributes.clear + @previously_changed = changes + @changed_attributes.clear end end # <tt>reload</tt> the record and clears changed attributes. def reload_with_dirty(*args) #:nodoc: reload_without_dirty(*args).tap do - previously_changed_attributes.clear - changed_attributes.clear + @previously_changed.clear + @changed_attributes.clear end end @@ -45,12 +45,12 @@ module ActiveRecord attr = attr.to_s # The attribute already has an unsaved change. - if changed_attributes.include?(attr) - old = changed_attributes[attr] - changed_attributes.delete(attr) unless field_changed?(attr, old, value) + if attribute_changed?(attr) + old = @changed_attributes[attr] + @changed_attributes.delete(attr) unless field_changed?(attr, old, value) else old = clone_attribute_value(:read_attribute, attr) - changed_attributes[attr] = old if field_changed?(attr, old, value) + @changed_attributes[attr] = old if field_changed?(attr, old, value) end # Carry on. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 1488d9f967..367b4ce217 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1045,6 +1045,8 @@ module ActiveRecord #:nodoc: object.instance_variable_set(:@readonly, false) object.instance_variable_set(:@destroyed, false) object.instance_variable_set(:@marked_for_destruction, false) + object.instance_variable_set(:@previously_changed, {}) + object.instance_variable_set(:@changed_attributes, {}) object.send(:_run_find_callbacks) object.send(:_run_initialize_callbacks) @@ -1513,6 +1515,8 @@ module ActiveRecord #:nodoc: @readonly = false @destroyed = false @marked_for_destruction = false + @previously_changed = {} + @changed_attributes = {} ensure_proper_type @@ -2050,26 +2054,6 @@ module ActiveRecord #:nodoc: end # Returns a copy of the attributes hash where all the values have been safely quoted for use in - # an SQL statement. - def attributes_with_quotes(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) - quoted = {} - connection = self.class.connection - attribute_names.each do |name| - if (column = column_for_attribute(name)) && (include_primary_key || !column.primary) - value = read_attribute(name) - - # We need explicit to_yaml because quote() does not properly convert Time/Date fields to YAML. - if value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time)) - value = value.to_yaml - end - - quoted[name] = connection.quote(value, column) - end - end - include_readonly_attributes ? quoted : remove_readonly_attributes(quoted) - end - - # Returns a copy of the attributes hash where all the values have been safely quoted for use in # an Arel insert/update method. def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) attrs = {} diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index abb695264e..0c87e052c4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -181,6 +181,29 @@ module ActiveRecord # done if the transaction block raises an exception or returns false. def rollback_db_transaction() end + # Appends +LIMIT+ and +OFFSET+ options to an SQL statement, or some SQL + # fragment that has the same semantics as LIMIT and OFFSET. + # + # +options+ must be a Hash which contains a +:limit+ option + # and an +:offset+ option. + # + # This method *modifies* the +sql+ parameter. + # + # ===== Examples + # add_limit_offset!('SELECT * FROM suppliers', {:limit => 10, :offset => 50}) + # generates + # SELECT * FROM suppliers LIMIT 10 OFFSET 50 + + def add_limit_offset!(sql, options) + if limit = options[:limit] + sql << " LIMIT #{sanitize_limit(limit)}" + end + if offset = options[:offset] + sql << " OFFSET #{offset.to_i}" + end + sql + end + def default_sequence_name(table, column) nil end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 1f1df7e8c3..521bd810d0 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -61,7 +61,7 @@ module ActiveRecord begin require_library_or_gem('mysql') rescue LoadError - $stderr.puts '!!! The bundled mysql.rb driver has been removed from Rails 2.2. Please install the mysql gem and try again: gem install mysql.' + $stderr.puts '!!! Please install the mysql gem and try again: gem install mysql.' raise end end @@ -375,6 +375,18 @@ module ActiveRecord execute("RELEASE SAVEPOINT #{current_savepoint_name}") end + def add_limit_offset!(sql, options) #:nodoc: + limit, offset = options[:limit], options[:offset] + if limit && offset + sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}" + elsif limit + sql << " LIMIT #{sanitize_limit(limit)}" + elsif offset + sql << " OFFSET #{offset.to_i}" + end + sql + end + # SCHEMA STATEMENTS ======================================== def structure_dump #:nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index b3ce8c79dd..31d5266da8 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -651,14 +651,33 @@ module ActiveRecord end end + # Creates a schema for the given user + # + # Example: + # create_schema('products', 'postgres') + def create_schema(schema_name, pg_username) + execute("CREATE SCHEMA \"#{schema_name}\" AUTHORIZATION \"#{pg_username}\"") + end + + # Drops a schema + # + # Example: + # drop_schema('products') + def drop_schema(schema_name) + execute("DROP SCHEMA \"#{schema_name}\"") + end + + # Returns an array of all schemas in the database + def all_schemas + query('SELECT schema_name FROM information_schema.schemata').flatten + end # Returns the list of all tables in the schema search path or a specified schema. def tables(name = nil) - schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',') query(<<-SQL, name).map { |row| row[0] } SELECT tablename FROM pg_tables - WHERE schemaname IN (#{schemas}) + WHERE schemaname = ANY (current_schemas(false)) SQL end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 5825482db7..d4cda927ad 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -374,7 +374,7 @@ module ActiveRecord end def load_migration - load(filename) + require(File.expand_path(filename)) name.constantize end diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index d8fae495e7..76ec7eb681 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -243,11 +243,14 @@ module ActiveRecord # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes) # end - class_eval %{ + class_eval <<-eoruby, __FILE__, __LINE__ + 1 + if method_defined?(:#{association_name}_attributes=) + remove_method(:#{association_name}_attributes=) + end def #{association_name}_attributes=(attributes) assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes) end - }, __FILE__, __LINE__ + eoruby else raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 60addd46c6..de47461c73 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -10,7 +10,7 @@ require "action_controller/railtie" module ActiveRecord class Railtie < Rails::Railtie - railtie_name :active_record + config.active_record = ActiveSupport::OrderedOptions.new config.generators.orm :active_record, :migration => true, :timestamps => true @@ -19,9 +19,8 @@ module ActiveRecord load "active_record/railties/databases.rake" end - # TODO If we require the wrong file, the error never comes up. require "active_record/railties/log_subscriber" - log_subscriber ActiveRecord::Railties::LogSubscriber.new + log_subscriber :active_record, ActiveRecord::Railties::LogSubscriber.new initializer "active_record.initialize_timezone" do ActiveRecord.base_hook do diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index fa6caa4910..06485b9033 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -85,8 +85,14 @@ namespace :db do end when 'postgresql' @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' + schema_search_path = config['schema_search_path'] || 'public' + first_in_schema_search_path = schema_search_path.split(',').first.strip begin ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) + unless ActiveRecord::Base.connection.all_schemas.include?(first_in_schema_search_path) + ActiveRecord::Base.connection.create_schema(first_in_schema_search_path, config['username']) + $stderr.puts "Schema #{first_in_schema_search_path} has been created." + end ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding)) ActiveRecord::Base.establish_connection(config) rescue @@ -435,7 +441,7 @@ namespace :db do task :create => :environment do raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations? require 'rails/generators' - require 'generators/rails/session_migration/session_migration_generator' + require 'rails/generators/rails/session_migration/session_migration_generator' Rails::Generators::SessionMigrationGenerator.start [ ENV["MIGRATION"] || "add_sessions_table" ] end diff --git a/activerecord/lib/active_record/railties/log_subscriber.rb b/activerecord/lib/active_record/railties/log_subscriber.rb index 48b25032ce..31b98bb6ed 100644 --- a/activerecord/lib/active_record/railties/log_subscriber.rb +++ b/activerecord/lib/active_record/railties/log_subscriber.rb @@ -1,6 +1,11 @@ module ActiveRecord module Railties class LogSubscriber < Rails::LogSubscriber + def initialize + super + @odd_or_even = false + end + def sql(event) name = '%s (%.1fms)' % [event.payload[:name], event.duration] sql = event.payload[:sql].squeeze(' ') @@ -24,4 +29,4 @@ module ActiveRecord end end end -end
\ No newline at end of file +end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 1a84f70a8e..a20c152eeb 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -15,13 +15,10 @@ module ActiveRecord def initialize(klass, table) @klass, @table = klass, table - @readonly_value = nil - @create_with_value = nil @implicit_readonly = nil - @limit_value = nil - @offset_value = nil @loaded = nil + SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)} (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])} end @@ -62,7 +59,7 @@ module ActiveRecord preload = @preload_values preload += @includes_values unless eager_loading? - preload.each {|associations| @klass.send(:preload_associations, @records, associations) } + preload.each {|associations| @klass.send(:preload_associations, @records, associations) } # @readonly_value is true only if set explicity. @implicit_readonly is true if there are JOINS and no explicit SELECT. readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index d6d3d66642..c1cce679b6 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -175,7 +175,7 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:includes, :eager_load, :preload, :select).select(@klass.send(:column_aliases, join_dependency)) + relation = except(:includes, :eager_load, :preload, :select).select(column_aliases(join_dependency)) apply_join_dependency(relation, join_dependency) end @@ -184,7 +184,7 @@ module ActiveRecord relation = association.join_relation(relation) end - limitable_reflections = @klass.send(:using_limitable_reflections?, join_dependency.reflections) + limitable_reflections = using_limitable_reflections?(join_dependency.reflections) if !limitable_reflections && relation.limit_value limited_id_condition = construct_limited_ids_condition(relation.except(:select)) @@ -311,5 +311,14 @@ module ActiveRecord end end + def column_aliases(join_dependency) + join_dependency.joins.collect{|join| join.column_names_with_alias.collect{|column_name, aliased_name| + "#{connection.quote_table_name join.aliased_table_name}.#{connection.quote_column_name column_name} AS #{aliased_name}"}}.flatten.join(", ") + end + + def using_limitable_reflections?(reflections) + reflections.collect(&:collection?).length.zero? + end + end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e00d9cdf27..0250e739b8 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -6,6 +6,7 @@ module ActiveRecord (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method| attr_accessor :"#{query_method}_values" + next if [:where, :having].include?(query_method) class_eval <<-CEVAL def #{query_method}(*args) new_relation = clone @@ -133,13 +134,8 @@ module ActiveRecord arel = h.is_a?(String) ? arel.having(h) : arel.having(*h) end - if defined?(@limit_value) && @limit_value.present? - arel = arel.take(@limit_value) - end - - if defined?(@offset_value) && @offset_value.present? - arel = arel.skip(@offset_value) - end + arel = arel.take(@limit_value) if @limit_value.present? + arel = arel.skip(@offset_value) if @offset_value.present? @group_values.uniq.each do |g| arel = arel.group(g) if g.present? @@ -162,19 +158,14 @@ module ActiveRecord arel = arel.project(quoted_table_name + '.*') end - arel = - if defined?(@from_value) && @from_value.present? - arel.from(@from_value) - else - arel.from(quoted_table_name) - end + arel = @from_value.present? ? arel.from(@from_value) : arel.from(quoted_table_name) case @lock_value when TrueClass arel = arel.lock when String arel = arel.lock(@lock_value) - end if defined?(@lock_value) + end if @lock_value.present? arel end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a18380f01c..2841ff1239 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -4,30 +4,14 @@ module ActiveRecord merged_relation = clone return merged_relation unless r - merged_relation = merged_relation.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values) - - merged_relation.readonly_value = r.readonly_value unless r.readonly_value.nil? - merged_relation.limit_value = r.limit_value if r.limit_value.present? - merged_relation.lock_value = r.lock_value unless merged_relation.lock_value - merged_relation.offset_value = r.offset_value if r.offset_value.present? - - merged_relation = merged_relation. - joins(r.joins_values). - group(r.group_values). - select(r.select_values). - from(r.from_value). - having(r.having_values) - - merged_relation.order_values = r.order_values if r.order_values.present? - - merged_relation.create_with_value = @create_with_value - - if @create_with_value && r.create_with_value - merged_relation.create_with_value = @create_with_value.merge(r.create_with_value) - else - merged_relation.create_with_value = r.create_with_value || @create_with_value + (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).reject {|m| [:joins, :where].include?(m)}.each do |method| + unless (value = r.send(:"#{method}_values")).blank? + merged_relation.send(:"#{method}_values=", value) + end end + merged_relation = merged_relation.joins(r.joins_values) + merged_wheres = @where_values r.where_values.each do |w| @@ -40,6 +24,14 @@ module ActiveRecord merged_relation.where_values = merged_wheres + ActiveRecord::Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method| + unless (value = r.send(:"#{method}_value")).nil? + merged_relation.send(:"#{method}_value=", value) + end + end + + merged_relation.lock_value = r.lock_value unless merged_relation.lock_value + merged_relation end |