diff options
author | Carl Lerche & Yehuda Katz <wycats@gmail.com> | 2009-04-13 15:18:45 -0700 |
---|---|---|
committer | Carl Lerche & Yehuda Katz <wycats@gmail.com> | 2009-04-13 15:18:45 -0700 |
commit | 906aebceedb95d8caa6db6314bc90f605bdfaf2b (patch) | |
tree | 5abc86bb6709b20df7cb5f4d1750b27c641dca4b /activerecord/lib/active_record/base.rb | |
parent | 2036d3ba75da1a0f3061bf5a33c89e2b2eaff420 (diff) | |
parent | c877857d59554d78dbf45f5f9fcaafb8badec4e2 (diff) | |
download | rails-906aebceedb95d8caa6db6314bc90f605bdfaf2b.tar.gz rails-906aebceedb95d8caa6db6314bc90f605bdfaf2b.tar.bz2 rails-906aebceedb95d8caa6db6314bc90f605bdfaf2b.zip |
Bring abstract_controller up to date with rails/master
Resolved all the conflicts since 2.3.0 -> HEAD. Following is a list
of commits that could not be applied cleanly or are obviated with the
abstract_controller refactor. They all need to be revisited to ensure
that fixes made in 2.3 do not reappear in 3.0:
2259ecf368e6a6715966f69216e3ee86bf1a82a7
AR not available
* This will be reimplemented with ActionORM or equivalent
06182ea02e92afad579998aa80144588e8865ac3
implicitly rendering a js response should not use the default layout
[#1844 state:resolved]
* This will be handled generically
893e9eb99504705419ad6edac14d00e71cef5f12
Improve view rendering performance in development mode and reinstate
template recompiling in production [#1909 state:resolved]
* We will need to reimplement rails-dev-boost on top of the refactor;
the changes here are very implementation specific and cannot be
cleanly applied. The following commits are implicated:
199e750d46c04970b5e7684998d09405648ecbd4
3942cb406e1d5db0ac00e03153809cc8dc4cc4db
f8ea9f85d4f1e3e6f3b5d895bef6b013aa4b0690
e3b166aab37ddc2fbab030b146eb61713b91bf55
ae9f258e03c9fd5088da12c1c6cd216cc89a01f7
44423126c6f6133a1d9cf1d0832b527e8711d40f
0cb020b4d6d838025859bd60fb8151c8e21b8e84
workaround for picking layouts based on wrong view_paths
[#1974 state:resolved]
* The specifics of this commit no longer apply. Since it is a two-line
commit, we will reimplement this change.
8c5cc66a831aadb159f3daaffa4208064c30af0e
make action_controller/layouts pick templates from the current instance's
view_paths instead of the class view_paths [#1974 state:resolved]
* This does not apply at all. It should be trivial to apply the feature
to the reimplemented ActionController::Base.
87e8b162463f13bd50d27398f020769460a770e3
fix HTML fallback for explicit templates [#2052 state:resolved]
* There were a number of patches related to this that simply compounded
each other. Basically none of them apply cleanly, and the underlying
issue needs to be revisited. After discussing the underlying problem
with Koz, we will defer these fixes for further discussion.
Diffstat (limited to 'activerecord/lib/active_record/base.rb')
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 98 |
1 files changed, 56 insertions, 42 deletions
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 0efccb66ee..9943a7014a 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -416,7 +416,7 @@ module ActiveRecord #:nodoc: end @@subclasses = {} - + ## # :singleton-method: # Contains the database configuration - as is typically stored in config/database.yml - @@ -661,9 +661,8 @@ module ActiveRecord #:nodoc: connection.select_all(sanitize_sql(sql), "#{name} Load").collect! { |record| instantiate(record) } end - # Returns true if a record exists in the table that matches the +id+ or - # conditions given, or false otherwise. The argument can take four forms: + # conditions given, or false otherwise. The argument can take five forms: # # * Integer - Finds the record with this primary key. # * String - Finds the record with a primary key corresponding to this @@ -672,6 +671,7 @@ module ActiveRecord #:nodoc: # (such as <tt>['color = ?', 'red']</tt>). # * Hash - Finds the record that matches these +find+-style conditions # (such as <tt>{:color => 'red'}</tt>). + # * No args - Returns false if the table is empty, true otherwise. # # For more information about specifying conditions as a Hash or Array, # see the Conditions section in the introduction to ActiveRecord::Base. @@ -685,7 +685,8 @@ module ActiveRecord #:nodoc: # Person.exists?('5') # Person.exists?(:name => "David") # Person.exists?(['name LIKE ?', "%#{query}%"]) - def exists?(id_or_conditions) + # Person.exists? + def exists?(id_or_conditions = {}) connection.select_all( construct_finder_sql( :select => "#{quoted_table_name}.#{primary_key}", @@ -735,12 +736,12 @@ module ActiveRecord #:nodoc: # ==== Parameters # # * +id+ - This should be the id or an array of ids to be updated. - # * +attributes+ - This should be a Hash of attributes to be set on the object, or an array of Hashes. + # * +attributes+ - This should be a hash of attributes to be set on the object, or an array of hashes. # # ==== Examples # # # Updating one record: - # Person.update(15, { :user_name => 'Samuel', :group => 'expert' }) + # Person.update(15, :user_name => 'Samuel', :group => 'expert') # # # Updating multiple records: # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } } @@ -809,25 +810,28 @@ module ActiveRecord #:nodoc: # Updates all records with details given if they match a set of conditions supplied, limits and order can # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the - # database. It does not instantiate the involved models and it does not trigger Active Record callbacks. + # database. It does not instantiate the involved models and it does not trigger Active Record callbacks + # or validations. # # ==== Parameters # - # * +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. + # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. + # * +conditions+ - A string, array, or hash representing the WHERE part of an SQL statement. See conditions in the intro. # * +options+ - Additional options are <tt>:limit</tt> and <tt>:order</tt>, see the examples for usage. # # ==== Examples # - # # Update all billing objects with the 3 different attributes given - # Billing.update_all( "category = 'authorized', approved = 1, author = 'David'" ) + # # Update all customers with the given attributes + # Customer.update_all :wants_email => true # - # # Update records that match our conditions - # Billing.update_all( "author = 'David'", "title LIKE '%Rails%'" ) + # # Update all books with 'Rails' in their title + # Book.update_all "author = 'David'", "title LIKE '%Rails%'" # - # # Update records that match our conditions but limit it to 5 ordered by date - # Billing.update_all( "author = 'David'", "title LIKE '%Rails%'", - # :order => 'created_at', :limit => 5 ) + # # Update all avatars migrated more than a week ago + # Avatar.update_all ['migrated_at = ?, Time.now.utc], ['migrated_at > ?', 1.week.ago] + # + # # Update all books that match our conditions, but limit it to 5 ordered by date + # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) sql = "UPDATE #{quoted_table_name} SET #{sanitize_sql_for_assignment(updates)} " @@ -884,7 +888,8 @@ module ActiveRecord #:nodoc: # Deletes the records matching +conditions+ without instantiating the records first, and hence not # calling the +destroy+ method nor invoking callbacks. This is a single SQL DELETE statement that # goes straight to the database, much more efficient than +destroy_all+. Be careful with relations - # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. + # though, in particular <tt>:dependent</tt> rules defined on associations are not honored. Returns + # the number of rows affected. # # ==== Parameters # @@ -1000,7 +1005,6 @@ module ActiveRecord #:nodoc: update_counters(id, counter_name => -1) end - # Attributes named in this macro are protected from mass-assignment, # such as <tt>new(attributes)</tt>, # <tt>update_attributes(attributes)</tt>, or @@ -1101,7 +1105,6 @@ module ActiveRecord #:nodoc: read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {}) end - # Guesses the table name (in forced lower-case) based on the name of the class in the inheritance hierarchy descending # directly from ActiveRecord::Base. So if the hierarchy looks like: Reply < Message < ActiveRecord::Base, then Message is used # to guess the table name even when called on Reply. The rules used to do the guess are handled by the Inflector class @@ -1344,7 +1347,7 @@ module ActiveRecord #:nodoc: subclasses.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information } end - def self_and_descendents_from_active_record#nodoc: + def self_and_descendants_from_active_record#nodoc: klass = self classes = [klass] while klass != klass.base_class @@ -1364,7 +1367,7 @@ module ActiveRecord #:nodoc: # module now. # Specify +options+ with additional translating options. def human_attribute_name(attribute_key_name, options = {}) - defaults = self_and_descendents_from_active_record.map do |klass| + defaults = self_and_descendants_from_active_record.map do |klass| :"#{klass.name.underscore}.#{attribute_key_name}" end defaults << options[:default] if options[:default] @@ -1379,7 +1382,7 @@ module ActiveRecord #:nodoc: # Default scope of the translation is activerecord.models # Specify +options+ with additional translating options. def human_name(options = {}) - defaults = self_and_descendents_from_active_record.map do |klass| + defaults = self_and_descendants_from_active_record.map do |klass| :"#{klass.name.underscore}" end defaults << self.name.humanize @@ -1414,7 +1417,6 @@ module ActiveRecord #:nodoc: end end - def quote_value(value, column = nil) #:nodoc: connection.quote(value,column) end @@ -1483,7 +1485,7 @@ module ActiveRecord #:nodoc: elsif match = DynamicScopeMatch.match(method_id) return true if all_attributes_exists?(match.attribute_names) end - + super end @@ -1534,7 +1536,7 @@ module ActiveRecord #:nodoc: end def reverse_sql_order(order_query) - reversed_query = order_query.split(/,/).each { |s| + reversed_query = order_query.to_s.split(/,/).each { |s| if s.match(/\s(asc|ASC)$/) s.gsub!(/\s(asc|ASC)$/, ' DESC') elsif s.match(/\s(desc|DESC)$/) @@ -1687,7 +1689,7 @@ module ActiveRecord #:nodoc: def construct_finder_sql(options) scope = scope(:find) sql = "SELECT #{options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} " - sql << "FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} " + sql << "FROM #{options[:from] || (scope && scope[:from]) || quoted_table_name} " add_joins!(sql, options[:joins], scope) add_conditions!(sql, options[:conditions], scope) @@ -1742,7 +1744,9 @@ module ActiveRecord #:nodoc: scoped_order = scope[:order] if scope if order sql << " ORDER BY #{order}" - sql << ", #{scoped_order}" if scoped_order + if scoped_order && scoped_order != order + sql << ", #{scoped_order}" + end else sql << " ORDER BY #{scoped_order}" if scoped_order end @@ -1751,12 +1755,12 @@ module ActiveRecord #:nodoc: def add_group!(sql, group, having, scope = :auto) if group sql << " GROUP BY #{group}" - sql << " HAVING #{having}" if having + sql << " HAVING #{sanitize_sql_for_conditions(having)}" if having else scope = scope(:find) if :auto == scope if scope && (scoped_group = scope[:group]) sql << " GROUP BY #{scoped_group}" - sql << " HAVING #{scoped_having}" if (scoped_having = scope[:having]) + sql << " HAVING #{sanitize_sql_for_conditions(scope[:having])}" if scope[:having] end end end @@ -1990,12 +1994,16 @@ module ActiveRecord #:nodoc: attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) } end - def attribute_condition(argument) + def attribute_condition(quoted_column_name, argument) case argument - when nil then "IS ?" - when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "IN (?)" - when Range then "BETWEEN ? AND ?" - else "= ?" + when nil then "#{quoted_column_name} IS ?" + when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "#{quoted_column_name} IN (?)" + when Range then if argument.exclude_end? + "#{quoted_column_name} >= ? AND #{quoted_column_name} < ?" + else + "#{quoted_column_name} BETWEEN ? AND ?" + end + else "#{quoted_column_name} = ?" end end @@ -2007,7 +2015,6 @@ module ActiveRecord #:nodoc: end end - # Defines an "attribute" method (like +inheritance_column+ or # +table_name+). A new (class) method will be created with the # given name. If a value is specified, the new method will @@ -2104,7 +2111,7 @@ module ActiveRecord #:nodoc: end # Merge scopings - if action == :merge && current_scoped_methods + if [:merge, :reverse_merge].include?(action) && current_scoped_methods method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)| case hash[method] when Hash @@ -2126,7 +2133,11 @@ module ActiveRecord #:nodoc: end end else - hash[method] = hash[method].merge(params) + if action == :reverse_merge + hash[method] = hash[method].merge(params) + else + hash[method] = params.merge(hash[method]) + end end else hash[method] = params @@ -2136,7 +2147,6 @@ module ActiveRecord #:nodoc: end self.scoped_methods << method_scoping - begin yield ensure @@ -2167,7 +2177,7 @@ module ActiveRecord #:nodoc: # Test whether the given method and optional key are scoped. def scoped?(method, key = nil) #:nodoc: if current_scoped_methods && (scope = current_scoped_methods[method]) - !key || scope.has_key?(key) + !key || !scope[key].nil? end end @@ -2305,7 +2315,7 @@ module ActiveRecord #:nodoc: table_name = connection.quote_table_name(table_name) end - "#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}" + attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value) else sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s)) end @@ -2742,7 +2752,6 @@ module ActiveRecord #:nodoc: assign_multiparameter_attributes(multi_parameter_attributes) end - # Returns a hash of all the attributes with their names as keys and the values of the attributes as values. def attributes self.attribute_names.inject({}) do |attrs, name| @@ -3136,7 +3145,12 @@ module ActiveRecord #:nodoc: include Dirty include Callbacks, Observing, Timestamp include Associations, AssociationPreload, NamedScope - include Aggregations, Transactions, Reflection, Calculations, Serialization + + # AutosaveAssociation needs to be included before Transactions, because we want + # #save_with_autosave_associations to be wrapped inside a transaction. + include AutosaveAssociation, NestedAttributes + + include Aggregations, Transactions, Reflection, Batches, Calculations, Serialization end end |