aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
authorwycats <wycats@gmail.com>2010-04-10 17:22:52 -0400
committerwycats <wycats@gmail.com>2010-04-10 17:22:52 -0400
commit87f7093ee3306f417e1136d947eba200d40ff8e7 (patch)
treeba70dbdaf67e12fc067bb5d8343d7681932452ef /activerecord/lib
parentee8e9d548472fb8cb8792a569e579c6513be77d6 (diff)
parent381f877bbbbf81d679f5be3b7ac7e961d41502bd (diff)
downloadrails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.gz
rails-87f7093ee3306f417e1136d947eba200d40ff8e7.tar.bz2
rails-87f7093ee3306f417e1136d947eba200d40ff8e7.zip
Merge branch 'master' into docrails_master
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb13
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb8
-rwxr-xr-xactiverecord/lib/active_record/base.rb76
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb21
-rw-r--r--activerecord/lib/active_record/railties/databases.rake10
-rw-r--r--activerecord/lib/active_record/version.rb2
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