diff options
author | wycats <wycats@gmail.com> | 2010-04-10 17:22:52 -0400 |
---|---|---|
committer | wycats <wycats@gmail.com> | 2010-04-10 17:22:52 -0400 |
commit | 87f7093ee3306f417e1136d947eba200d40ff8e7 (patch) | |
tree | ba70dbdaf67e12fc067bb5d8343d7681932452ef /activerecord/lib | |
parent | ee8e9d548472fb8cb8792a569e579c6513be77d6 (diff) | |
parent | 381f877bbbbf81d679f5be3b7ac7e961d41502bd (diff) | |
download | rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.gz rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.bz2 rails-87f7093ee3306f417e1136d947eba200d40ff8e7.zip |
Merge branch 'master' into docrails_master
Diffstat (limited to 'activerecord/lib')
8 files changed, 61 insertions, 77 deletions
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 5ecdf1ac8d..b808f8c306 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -105,6 +105,7 @@ module ActiveRecord def reset reset_target! + reset_named_scopes_cache! @loaded = false end @@ -146,7 +147,7 @@ module ActiveRecord # has_many :books # end # - # Author.find(:first).books.transaction do + # Author.first.books.transaction do # # same effect as calling Book.transaction # end def transaction(*args) @@ -162,6 +163,7 @@ module ActiveRecord load_target delete(@target) reset_target! + reset_named_scopes_cache! end # Calculate sum using SQL, not Enumerable @@ -250,6 +252,7 @@ module ActiveRecord load_target destroy(@target) reset_target! + reset_named_scopes_cache! end def create(attrs = {}) @@ -406,8 +409,8 @@ module ActiveRecord super end elsif @reflection.klass.scopes[method] - @_scopes ||= {} - @_scopes[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) } + @_named_scopes_cache ||= {} + @_named_scopes_cache[method] ||= with_scope(construct_scope) { @reflection.klass.send(method, *args) } else with_scope(construct_scope) do if block_given? @@ -428,6 +431,10 @@ module ActiveRecord @target = Array.new end + def reset_named_scopes_cache! + @_named_scopes_cache = {} + end + def find_target records = if @reflection.options[:finder_sql] diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 435aea9b09..36f2a9777c 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -52,6 +52,8 @@ module ActiveRecord @changed_attributes.delete(attr) unless field_changed?(attr, old, value) else old = clone_attribute_value(:read_attribute, attr) + # Save Time objects as TimeWithZone if time_zone_aware_attributes == true + old = old.in_time_zone if clone_with_time_zone_conversion_attribute?(attr, old) @changed_attributes[attr] = old if field_changed?(attr, old, value) end @@ -84,6 +86,10 @@ module ActiveRecord old != value end + + def clone_with_time_zone_conversion_attribute?(attr, old) + old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym) + end end end end diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index a8e3e28a7a..783d61383b 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -17,7 +17,7 @@ module ActiveRecord # This enhanced read method automatically converts the UTC time stored in the database to the time zone stored in Time.zone. def define_method_attribute(attr_name) if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) - method_body = <<-EOV + method_body, line = <<-EOV, __LINE__ + 1 def #{attr_name}(reload = false) cached = @attributes_cache['#{attr_name}'] return cached if cached && !reload @@ -25,7 +25,7 @@ module ActiveRecord @attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time end EOV - generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__) + generated_attribute_methods.module_eval(method_body, __FILE__, line) else super end @@ -35,7 +35,7 @@ module ActiveRecord # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone. def define_method_attribute=(attr_name) if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name]) - method_body = <<-EOV + method_body, line = <<-EOV, __LINE__ + 1 def #{attr_name}=(time) unless time.acts_like?(:time) time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time @@ -44,7 +44,7 @@ module ActiveRecord write_attribute(:#{attr_name}, time) end EOV - generated_attribute_methods.module_eval(method_body, __FILE__, __LINE__) + generated_attribute_methods.module_eval(method_body, __FILE__, line) else super end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f0b107255c..d544c48a4c 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -56,15 +56,15 @@ module ActiveRecord #:nodoc: # # class User < ActiveRecord::Base # def self.authenticate_unsafely(user_name, password) - # find(:first, :conditions => "user_name = '#{user_name}' AND password = '#{password}'") + # where("user_name = '#{user_name}' AND password = '#{password}'").first # end # # def self.authenticate_safely(user_name, password) - # find(:first, :conditions => [ "user_name = ? AND password = ?", user_name, password ]) + # where("user_name = ? AND password = ?", user_name, password).first # end # # def self.authenticate_safely_simply(user_name, password) - # find(:first, :conditions => { :user_name => user_name, :password => password }) + # where(:user_name => user_name, :password => password).first # end # end # @@ -77,30 +77,30 @@ module ActiveRecord #:nodoc: # question mark is supposed to represent. In those cases, you can resort to named bind variables instead. That's done by replacing # the question marks with symbols and supplying a hash with values for the matching symbol keys: # - # Company.find(:first, :conditions => [ + # Company.where( # "id = :id AND name = :name AND division = :division AND created_at > :accounting_date", # { :id => 3, :name => "37signals", :division => "First", :accounting_date => '2005-01-01' } - # ]) + # ).first # # Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND # operator. For instance: # - # Student.find(:all, :conditions => { :first_name => "Harvey", :status => 1 }) - # Student.find(:all, :conditions => params[:student]) + # Student.where(:first_name => "Harvey", :status => 1) + # Student.where(params[:student]) # # A range may be used in the hash to use the SQL BETWEEN operator: # - # Student.find(:all, :conditions => { :grade => 9..12 }) + # Student.where(:grade => 9..12) # # An array may be used in the hash to use the SQL IN operator: # - # Student.find(:all, :conditions => { :grade => [9,11,12] }) + # Student.where(:grade => [9,11,12]) # # When joining tables, nested hashes or keys written in the form 'table_name.column_name' can be used to qualify the table name of a # particular condition. For instance: # - # Student.find(:all, :conditions => { :schools => { :type => 'public' }}, :joins => :schools) - # Student.find(:all, :conditions => { 'schools.type' => 'public' }, :joins => :schools) + # Student.joins(:schools).where(:schools => { :type => 'public' }) + # Student.joins(:schools).where('schools.type' => 'public' ) # # == Overwriting default accessors # @@ -153,18 +153,18 @@ module ActiveRecord #:nodoc: # Dynamic attribute-based finders are a cleaner way of getting (and/or creating) objects by simple queries without turning to SQL. They work by # appending the name of an attribute to <tt>find_by_</tt>, <tt>find_last_by_</tt>, or <tt>find_all_by_</tt>, so you get finders like <tt>Person.find_by_user_name</tt>, # <tt>Person.find_all_by_last_name</tt>, and <tt>Payment.find_by_transaction_id</tt>. So instead of writing - # <tt>Person.find(:first, :conditions => ["user_name = ?", user_name])</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>. - # And instead of writing <tt>Person.find(:all, :conditions => ["last_name = ?", last_name])</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>. + # <tt>Person.where(:user_name => user_name).first</tt>, you just do <tt>Person.find_by_user_name(user_name)</tt>. + # And instead of writing <tt>Person.where(:last_name => last_name).all</tt>, you just do <tt>Person.find_all_by_last_name(last_name)</tt>. # # It's also possible to use multiple attributes in the same find by separating them with "_and_", so you get finders like # <tt>Person.find_by_user_name_and_password</tt> or even <tt>Payment.find_by_purchaser_and_state_and_country</tt>. So instead of writing - # <tt>Person.find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password])</tt>, you just do + # <tt>Person.where(:user_name => user_name, :password => password).first</tt>, you just do # <tt>Person.find_by_user_name_and_password(user_name, password)</tt>. # - # It's even possible to use all the additional parameters to find. For example, the full interface for <tt>Payment.find_all_by_amount</tt> - # is actually <tt>Payment.find_all_by_amount(amount, options)</tt>. And the full interface to <tt>Person.find_by_user_name</tt> is - # actually <tt>Person.find_by_user_name(user_name, options)</tt>. So you could call <tt>Payment.find_all_by_amount(50, :order => "created_on")</tt>. - # Also you may call <tt>Payment.find_last_by_amount(amount, options)</tt> returning the last record matching that amount and options. + # It's even possible to call these dynamic finder methods on relations and named scopes. For example : + # + # Payment.order("created_on").find_all_by_amount(50) + # Payment.pending.find_last_by_amount(100) # # The same dynamic finder style can be used to create the object if it doesn't already exist. This dynamic finder is called with # <tt>find_or_create_by_</tt> and will return the object if it already exists and otherwise creates it, then returns it. Protected attributes won't be set unless they are given in a block. For example: @@ -224,7 +224,7 @@ module ActiveRecord #:nodoc: # class PriorityClient < Client; end # # When you do <tt>Firm.create(:name => "37signals")</tt>, this record will be saved in the companies table with type = "Firm". You can then - # fetch this row again using <tt>Company.find(:first, "name = '37signals'")</tt> and it will return a Firm object. + # fetch this row again using <tt>Company.where(:name => '37signals').first</tt> and it will return a Firm object. # # If you don't have a type column defined in your table, single-table inheritance won't be triggered. In that case, it'll work just # like normal subclasses with no special magic for differentiating between them or reloading the right type with find. @@ -1117,10 +1117,6 @@ module ActiveRecord #:nodoc: # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+ # is actually <tt>find_all_by_amount(amount, options)</tt>. # - # 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) @@ -1138,7 +1134,7 @@ module ActiveRecord #:nodoc: attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) if match.scope? - self.class_eval %{ + self.class_eval <<-METHOD, __FILE__, __LINE__ + 1 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( @@ -1147,7 +1143,7 @@ module ActiveRecord #:nodoc: # scoped(:conditions => attributes) # scoped(:conditions => attributes) end # end - }, __FILE__, __LINE__ + METHOD send(method_id, *arguments) end else @@ -1196,12 +1192,12 @@ module ActiveRecord #:nodoc: protected # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash. - # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>, - # <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. <tt>:create</tt> parameters are an attributes hash. + # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while + # <tt>:create</tt> parameters are an attributes hash. # # class Article < ActiveRecord::Base # def self.create_with_scope - # with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do + # with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do # find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1 # a = create(1) # a.blog_id # => 1 @@ -1210,20 +1206,20 @@ module ActiveRecord #:nodoc: # end # # In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of - # <tt>:conditions</tt>, <tt>:include</tt>, and <tt>:joins</tt> options in <tt>:find</tt>, which are merged. + # <tt>where</tt>, <tt>includes</tt>, and <tt>joins</tt> operations in <tt>Relation</tt>, which are merged. # - # <tt>:joins</tt> options are uniqued so multiple scopes can join in the same table without table aliasing + # <tt>joins</tt> operations are uniqued so multiple scopes can join in the same table without table aliasing # problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the # array of strings format for your joins. # # class Article < ActiveRecord::Base # def self.find_with_scope - # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }, :create => { :blog_id => 1 }) do - # with_scope(:find => { :limit => 10 }) do - # find(:all) # => SELECT * from articles WHERE blog_id = 1 LIMIT 10 + # with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do + # with_scope(:find => limit(10)) do + # all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10 # end - # with_scope(:find => { :conditions => "author_id = 3" }) do - # find(:all) # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1 + # with_scope(:find => where(:author_id => 3)) do + # all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1 # end # end # end @@ -1233,9 +1229,9 @@ module ActiveRecord #:nodoc: # # class Article < ActiveRecord::Base # def self.find_with_exclusive_scope - # with_scope(:find => { :conditions => "blog_id = 1", :limit => 1 }) do - # with_exclusive_scope(:find => { :limit => 10 }) - # find(:all) # => SELECT * from articles LIMIT 10 + # with_scope(:find => where(:blog_id => 1).limit(1)) do + # with_exclusive_scope(:find => limit(10)) + # all # => SELECT * from articles LIMIT 10 # end # end # end @@ -1318,9 +1314,9 @@ module ActiveRecord #:nodoc: modularized_name = type_name_with_module(type_name) silence_warnings do begin - class_eval(modularized_name, __FILE__, __LINE__) + class_eval(modularized_name, __FILE__) rescue NameError - class_eval(type_name, __FILE__, __LINE__) + class_eval(type_name, __FILE__) end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 1e095110f2..e5d100b51b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -15,7 +15,7 @@ module ActiveRecord def dirties_query_cache(base, *method_names) method_names.each do |method_name| - base.class_eval <<-end_code, __FILE__, __LINE__ + base.class_eval <<-end_code, __FILE__, __LINE__ + 1 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) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 2395a744a3..68ee88bba4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -658,27 +658,6 @@ 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) query(<<-SQL, name).map { |row| row[0] } diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index a107befef3..2b53afc68b 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -84,15 +84,9 @@ namespace :db do end 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 + @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' 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 @@ -452,6 +446,8 @@ namespace :db do end end +task 'test:prepare' => 'db:test:prepare' + def drop_database(config) case config['adapter'] when 'mysql' diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index cf78ec6e12..bf6b995a37 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -3,7 +3,7 @@ module ActiveRecord MAJOR = 3 MINOR = 0 TINY = 0 - BUILD = "beta2" + BUILD = "beta3" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end |