diff options
20 files changed, 104 insertions, 22 deletions
diff --git a/actionpack/lib/action_controller/base/http_authentication.rb b/actionpack/lib/action_controller/base/http_authentication.rb index fa8ecea408..2893290efb 100644 --- a/actionpack/lib/action_controller/base/http_authentication.rb +++ b/actionpack/lib/action_controller/base/http_authentication.rb @@ -194,9 +194,10 @@ module ActionController if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque] password = password_procedure.call(credentials[:username]) + method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD'] [true, false].any? do |password_is_ha1| - expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1) + expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1) expected == credentials[:response] end end diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 7c0dfdab10..b4b9f6e34b 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -1,5 +1,6 @@ require 'cgi' require 'action_view/helpers/form_helper' +require 'active_support/core_ext/class/attribute_accessors' module ActionView class Base diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 8136a1cb13..ad0733a7e1 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -34,12 +34,16 @@ module ActionView # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt> # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "..."). + # Pass a <tt>:separator</tt> to truncate +text+ at a natural break. # # ==== Examples # # truncate("Once upon a time in a world far far away") # # => Once upon a time in a world f... # + # truncate("Once upon a time in a world far far away", :separator => ' ') + # # => Once upon a time in a world... + # # truncate("Once upon a time in a world far far away", :length => 14) # # => Once upon a... # @@ -71,7 +75,8 @@ module ActionView if text l = options[:length] - options[:omission].mb_chars.length chars = text.mb_chars - (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s + stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l + (chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s end end diff --git a/actionpack/test/abstract_controller/layouts_test.rb b/actionpack/test/abstract_controller/layouts_test.rb index 6e1c2bf9e8..d3440c3de0 100644 --- a/actionpack/test/abstract_controller/layouts_test.rb +++ b/actionpack/test/abstract_controller/layouts_test.rb @@ -1,4 +1,5 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper") +require 'active_support/core_ext/class/removal' module AbstractControllerTests module Layouts diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index 7bebc8cd2a..b8a2205ce6 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -149,6 +149,16 @@ class HttpDigestAuthenticationTest < ActionController::TestCase assert_equal 'Definitely Maybe', @response.body end + test "authentication request with _method" do + @request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please', :method => :post) + @request.env['rack.methodoverride.original_method'] = 'POST' + put :display + + assert_response :success + assert assigns(:logged_in) + assert_equal 'Definitely Maybe', @response.body + end + private def encode_credentials(options) @@ -159,15 +169,22 @@ class HttpDigestAuthenticationTest < ActionController::TestCase # to prevent tampering of timestamp ActionController::Base.session_options[:secret] = "session_options_secret" - # Perform unauthenticated GET to retrieve digest parameters to use on subsequent request - get :index + # Perform unauthenticated request to retrieve digest parameters to use on subsequent request + method = options.delete(:method) || 'GET' + + case method.to_s.upcase + when 'GET' + get :index + when 'POST' + post :index + end assert_response :unauthorized credentials = decode_credentials(@response.headers['WWW-Authenticate']) credentials.merge!(options) credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}") - ActionController::HttpAuthentication::Digest.encode_credentials("GET", credentials, password, options[:password_is_ha1]) + ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1]) end def decode_credentials(header) diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index a780bfc606..706b5085f4 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -49,6 +49,9 @@ class TextHelperTest < ActionView::TestCase assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]") assert_equal "Hello W...", truncate("Hello World!", :length => 10) assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10) + assert_equal "Hello[...]", truncate("Hello Big World!", :omission => "[...]", :length => 13, :separator => ' ') + assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 14, :separator => ' ') + assert_equal "Hello Big[...]", truncate("Hello Big World!", :omission => "[...]", :length => 15, :separator => ' ') end if RUBY_VERSION < '1.9.0' diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 9448481884..05afd20f23 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -25,9 +25,6 @@ activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" $:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' -# TODO: Figure out what parts of AS are *actually* required and use those -require 'active_support/core_ext' - $:.unshift(File.dirname(__FILE__) + '/../../arel/lib') require 'arel' 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 2dca84b911..e8dbae9011 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -17,6 +17,13 @@ module ActiveRecord end end + def destroy(*records) + transaction do + delete_records(flatten_deeper(records)) + super + end + end + # Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been loaded and # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero, # and you need to fetch that collection afterwards, it'll take one fewer SELECT query if you use #length. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3b12b15a5f..f3f31affe1 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -9,6 +9,7 @@ require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' +require 'active_support/core_ext/symbol' require 'active_support/core/time' module ActiveRecord #:nodoc: @@ -1539,12 +1540,12 @@ module ActiveRecord #:nodoc: end def reverse_sql_order(order_query) - reversed_query = order_query.to_s.split(/,/).each { |s| + 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)$/) s.gsub!(/\s(desc|DESC)$/, ' ASC') - elsif !s.match(/\s(asc|ASC|desc|DESC)$/) + else s.concat(' DESC') end }.join(',') @@ -2244,7 +2245,7 @@ module ActiveRecord #:nodoc: # 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] : {} } + self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} } end # Test whether the given method and optional key are scoped. diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index e30fcf9a4f..721114d9d0 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -1,6 +1,7 @@ require 'erb' require 'yaml' require 'csv' +require 'zlib' require 'active_support/dependencies' require 'active_support/test_case' require 'active_support/core_ext/logger' @@ -434,6 +435,7 @@ end # Any fixture labeled "DEFAULTS" is safely ignored. class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) + MAX_ID = 2 ** 31 - 1 DEFAULT_FILTER_RE = /\.ya?ml$/ @@all_cached_fixtures = {} @@ -525,11 +527,10 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) cached_fixtures(connection, table_names) end - # Returns a consistent identifier for +label+. This will always - # be a positive integer, and will always be the same for a given - # label, assuming the same OS, platform, and version of Ruby. + # Returns a consistent, platform-independent identifier for +label+. + # Identifiers are positive integers less than 2^32. def self.identify(label) - label.to_s.hash.abs + Zlib.crc32(label.to_s) % MAX_ID end attr_reader :table_name, :name diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index b6e848fa79..a18fb3f426 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/integer/even_odd' + module ActiveRecord # Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the # +record+ method to retrieve the record which did not validate. diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index b075db24e7..c9baef5f7e 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -93,7 +93,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_destroy_association - assert_difference "Person.count", -1 do + assert_difference ["Person.count", "Reader.count"], -1 do posts(:welcome).people.destroy(people(:michael)) end @@ -102,7 +102,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_destroy_all - assert_difference "Person.count", -1 do + assert_difference ["Person.count", "Reader.count"], -1 do posts(:welcome).people.destroy_all end @@ -110,6 +110,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert posts(:welcome).people(true).empty? end + def test_should_raise_exception_for_destroying_mismatching_records + assert_no_difference ["Person.count", "Reader.count"] do + assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:welcome).people.destroy(posts(:thinking)) } + end + end + def test_replace_association assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)} diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 252bf4ff61..b07d4f3521 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -518,6 +518,11 @@ class FoxyFixturesTest < ActiveRecord::TestCase assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo)) end + def test_identifies_consistently + assert_equal 1281023246, Fixtures.identify(:ruby) + assert_equal 2140105598, Fixtures.identify(:sapphire_2) + end + TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on) def test_populates_timestamp_columns diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb index 015c6ac652..bea5c5fb76 100644 --- a/activerecord/test/cases/method_scoping_test.rb +++ b/activerecord/test/cases/method_scoping_test.rb @@ -591,6 +591,16 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end + def test_default_scope_with_conditions_string + assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort + assert_equal nil, DeveloperCalledDavid.create!.name + end + + def test_default_scope_with_conditions_hash + assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort + assert_equal 'Jamis', DeveloperCalledJamis.create!.name + end + def test_default_scoping_with_threads scope = [{ :create => {}, :find => { :order => 'salary DESC' } }] diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 92039a4f54..058970336b 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -89,3 +89,13 @@ class DeveloperOrderedBySalary < ActiveRecord::Base end end end + +class DeveloperCalledDavid < ActiveRecord::Base + self.table_name = 'developers' + default_scope :conditions => "name = 'David'" +end + +class DeveloperCalledJamis < ActiveRecord::Base + self.table_name = 'developers' + default_scope :conditions => { :name => 'Jamis' } +end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 4d8e1fdd67..90b7526fe1 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -43,6 +43,11 @@ module ActiveSupport extend Strategy::LocalCache end + # Reads multiple keys from the cache. + def read_multi(*keys) + @data.get_multi keys + end + def read(key, options = nil) # :nodoc: super @data.get(key, raw?(options)) diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 60f082bcc1..96ed35f0e0 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +require 'active_support/core_ext/string/behavior' module ActiveSupport #:nodoc: module Multibyte #:nodoc: diff --git a/activesupport/memcached_get_multi.diff b/activesupport/memcached_get_multi.diff new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/activesupport/memcached_get_multi.diff diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 8bb0c155cf..b845796fe5 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -251,6 +251,15 @@ uses_memcached 'memcached backed store' do end end + def test_multi_get + @cache.with_local_cache do + @cache.write('foo', 1) + @cache.write('goo', 2) + result = @cache.read_multi('foo', 'goo') + assert_equal({'foo' => 1, 'goo' => 2}, result) + end + end + def test_middleware app = lambda { |env| result = @cache.write('foo', 'bar') diff --git a/arel b/arel -Subproject a7dea38204f3c40e4d0c3f29ebe17af81865969 +Subproject 4b8526dddd6a906a1879ec786401070b3545d7f |