Browse files

Merge commit 'mainstream/master'

  • Loading branch information...
2 parents 25fd841 + 9605541 commit f6c0f688680778a7bf693a69ffee021e1122b01c @lifo lifo committed Sep 13, 2008
Showing with 379 additions and 102 deletions.
  1. +17 −15 actionpack/lib/action_view/helpers/asset_tag_helper.rb
  2. +8 −5 activerecord/lib/active_record/association_preload.rb
  3. +8 −1 activerecord/lib/active_record/associations.rb
  4. +1 −1 activerecord/lib/active_record/base.rb
  5. +19 −1 activerecord/lib/active_record/dirty.rb
  6. +6 −2 activerecord/test/cases/aggregations_test.rb
  7. +13 −0 activerecord/test/cases/associations/belongs_to_associations_test.rb
  8. +12 −0 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
  9. +5 −0 activerecord/test/cases/associations/has_many_associations_test.rb
  10. +12 −0 activerecord/test/cases/associations_test.rb
  11. +13 −0 activerecord/test/cases/dirty_test.rb
  12. +16 −2 activerecord/test/cases/finder_test.rb
  13. +1 −1 activerecord/test/cases/inheritance_test.rb
  14. +2 −2 activerecord/test/cases/reflection_test.rb
  15. +25 −0 activerecord/test/cases/sanitize_test.rb
  16. +26 −0 activerecord/test/cases/validations_i18n_test.rb
  17. +36 −29 activerecord/test/cases/validations_test.rb
  18. +3 −0 activerecord/test/models/category.rb
  19. +2 −0 activerecord/test/models/company.rb
  20. +1 −0 activerecord/test/models/parrot.rb
  21. +2 −0 activesupport/CHANGELOG
  22. +11 −39 activesupport/lib/active_support/cache/memory_store.rb
  23. +46 −0 activesupport/lib/active_support/cache/synchronized_memory_store.rb
  24. +1 −1 activesupport/lib/active_support/core_ext/hash/except.rb
  25. +2 −0 activesupport/lib/active_support/core_ext/string.rb
  26. +13 −0 activesupport/lib/active_support/core_ext/string/behavior.rb
  27. +1 −1 activesupport/lib/active_support/inflector.rb
  28. +5 −0 activesupport/lib/active_support/multibyte/chars.rb
  29. +1 −1 activesupport/lib/active_support/values/time_zone.rb
  30. +30 −0 activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/definitions/Asia/Colombo.rb
  31. +14 −0 activesupport/test/core_ext/hash_ext_test.rb
  32. +6 −0 activesupport/test/core_ext/string_ext_test.rb
  33. +4 −1 activesupport/test/inflector_test_cases.rb
  34. +4 −0 activesupport/test/multibyte_chars_test.rb
  35. +13 −0 railties/lib/commands/console.rb
View
32 actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -463,7 +463,8 @@ def image_tag(source, options = {})
end
private
- COMPUTED_PUBLIC_PATHS = ActiveSupport::Cache::MemoryStore.new.silence!
+ COMPUTED_PUBLIC_PATHS = {}
+ COMPUTED_PUBLIC_PATHS_GUARD = Mutex.new
# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
@@ -483,23 +484,24 @@ def compute_public_path(source, dir, ext = nil, include_host = true)
dir, source, ext, include_host ].join
end
- source = COMPUTED_PUBLIC_PATHS.fetch(cache_key) do
- begin
- source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))
-
- if source =~ %r{^[-a-z]+://}
- source
- else
- source = "/#{dir}/#{source}" unless source[0] == ?/
- if has_request
- unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
- source = "#{ActionController::Base.relative_url_root}#{source}"
+ COMPUTED_PUBLIC_PATHS_GUARD.synchronize do
+ source = COMPUTED_PUBLIC_PATHS[cache_key] ||=
+ begin
+ source += ".#{ext}" if ext && File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}"))
+
+ if source =~ %r{^[-a-z]+://}
+ source
+ else
+ source = "/#{dir}/#{source}" unless source[0] == ?/
+ if has_request
+ unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
+ source = "#{ActionController::Base.relative_url_root}#{source}"
+ end
end
- end
- rewrite_asset_path(source)
+ rewrite_asset_path(source)
+ end
end
- end
end
if include_host && source !~ %r{^[-a-z]+://}
View
13 activerecord/lib/active_record/association_preload.rb
@@ -95,7 +95,7 @@ def preload_has_and_belongs_to_many_association(records, reflection, preload_opt
records.each {|record| record.send(reflection.name).loaded}
options = reflection.options
- conditions = "t0.#{reflection.primary_key_name} IN (?)"
+ conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
conditions << append_conditions(options, preload_options)
associated_records = reflection.klass.find(:all, :conditions => [conditions, ids],
@@ -222,8 +222,6 @@ def preload_belongs_to_association(records, reflection, preload_options={})
table_name = klass.quoted_table_name
primary_key = klass.primary_key
- conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} IN (?)"
- conditions << append_conditions(options, preload_options)
column_type = klass.columns.detect{|c| c.name == primary_key}.type
ids = id_map.keys.uniq.map do |id|
if column_type == :integer
@@ -234,6 +232,8 @@ def preload_belongs_to_association(records, reflection, preload_options={})
id
end
end
+ conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}"
+ conditions << append_conditions(options, preload_options)
associated_records = klass.find(:all, :conditions => [conditions, ids],
:include => options[:include],
:select => options[:select],
@@ -248,10 +248,10 @@ def find_associated_records(ids, reflection, preload_options)
table_name = reflection.klass.quoted_table_name
if interface = reflection.options[:as]
- conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
+ conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids(ids)} and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'"
else
foreign_key = reflection.primary_key_name
- conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)"
+ conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} #{in_or_equals_for_ids(ids)}"
end
conditions << append_conditions(options, preload_options)
@@ -277,6 +277,9 @@ def append_conditions(options, preload_options)
sql
end
+ def in_or_equals_for_ids(ids)
+ ids.size > 1 ? "IN (?)" : "= ?"
+ end
end
end
end
View
9 activerecord/lib/active_record/associations.rb
@@ -1237,7 +1237,7 @@ def association_accessor_methods(reflection, association_proxy_class)
association = instance_variable_get(ivar) if instance_variable_defined?(ivar)
- if association.nil? || force_reload
+ if association.nil? || !association.loaded? || force_reload
association = association_proxy_class.new(self, reflection)
retval = association.reload
if retval.nil? and association_proxy_class == BelongsToAssociation
@@ -1266,6 +1266,13 @@ def association_accessor_methods(reflection, association_proxy_class)
end
end
+ if association_proxy_class == BelongsToAssociation
+ define_method("#{reflection.primary_key_name}=") do |target_id|
+ instance_variable_get(ivar).reset if instance_variable_defined?(ivar)
+ write_attribute(reflection.primary_key_name, target_id)
+ end
+ end
+
define_method("set_#{reflection.name}_target") do |target|
return if target.nil? and association_proxy_class == BelongsToAssociation
association = association_proxy_class.new(self, reflection)
View
2 activerecord/lib/active_record/base.rb
@@ -2152,7 +2152,7 @@ def expand_range_bind_variables(bind_vars) #:nodoc:
end
def quote_bound_value(value) #:nodoc:
- if value.respond_to?(:map) && !value.is_a?(String)
+ if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
connection.quote(nil)
else
View
20 activerecord/lib/active_record/dirty.rb
@@ -34,8 +34,10 @@ module ActiveRecord
# person.name << 'by'
# person.name_change # => ['uncle bob', 'uncle bobby']
module Dirty
+ DIRTY_SUFFIXES = ['_changed?', '_change', '_will_change!', '_was']
+
def self.included(base)
- base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
+ base.attribute_method_suffix *DIRTY_SUFFIXES
base.alias_method_chain :write_attribute, :dirty
base.alias_method_chain :save, :dirty
base.alias_method_chain :save!, :dirty
@@ -44,6 +46,8 @@ def self.included(base)
base.superclass_delegating_accessor :partial_updates
base.partial_updates = true
+
+ base.send(:extend, ClassMethods)
end
# Do any attributes have unsaved changes?
@@ -161,5 +165,19 @@ def field_changed?(attr, old, value)
old != value
end
+ module ClassMethods
+ def self.extended(base)
+ base.metaclass.alias_method_chain(:alias_attribute, :dirty)
+ end
+
+ def alias_attribute_with_dirty(new_name, old_name)
+ alias_attribute_without_dirty(new_name, old_name)
+ DIRTY_SUFFIXES.each do |suffix|
+ module_eval <<-STR, __FILE__, __LINE__+1
+ def #{new_name}#{suffix}; self.#{old_name}#{suffix}; end
+ STR
+ end
+ end
+ end
end
end
View
8 activerecord/test/cases/aggregations_test.rb
@@ -130,12 +130,16 @@ def test_conversion_block_is_deprecated
end
def test_conversion_block_used_when_converter_option_is_nil
- Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
+ assert_deprecated 'conversion block has been deprecated' do
+ Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
+ end
assert_raise(NoMethodError) { Person.new.balance = 5 }
end
def test_converter_option_overrides_conversion_block
- Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
+ assert_deprecated 'conversion block has been deprecated' do
+ Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
+ end
person = Person.new
assert_nothing_raised { person.balance = 5 }
View
13 activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -47,6 +47,19 @@ def test_natural_assignment
assert_equal apple.id, citibank.firm_id
end
+ def test_foreign_key_assignment
+ # Test using an existing record
+ signals37 = accounts(:signals37)
+ assert_equal companies(:first_firm), signals37.firm
+ signals37.firm_id = companies(:another_firm).id
+ assert_equal companies(:another_firm), signals37.firm
+
+ # Test using a new record
+ account = Account.new
+ account.firm_id = companies(:another_firm).id
+ assert_equal companies(:another_firm), account.firm
+ end
+
def test_no_unexpected_aliasing
first_firm = companies(:first_firm)
another_firm = companies(:another_firm)
View
12 activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -636,6 +636,18 @@ def test_join_with_group
assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
end
+ def test_find_grouped
+ all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
+ grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
+ assert_equal 4, all_posts_from_category1.size
+ assert_equal 1, grouped_posts_of_category1.size
+ end
+
+ def test_find_scoped_grouped
+ assert_equal 4, categories(:general).posts_gruoped_by_title.size
+ assert_equal 1, categories(:technology).posts_gruoped_by_title.size
+ end
+
def test_get_ids
assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
assert_equal [projects(:active_record).id], developers(:jamis).project_ids
View
5 activerecord/test/cases/associations/has_many_associations_test.rb
@@ -248,6 +248,11 @@ def test_find_grouped
assert_equal 1, grouped_clients_of_firm1.size
end
+ def test_find_scoped_grouped
+ assert_equal 1, companies(:first_firm).clients_grouped_by_firm_id.length
+ assert_equal 2, companies(:first_firm).clients_grouped_by_name.length
+ end
+
def test_adding
force_signal37_to_load_all_clients_of_firm
natural = Client.new("name" => "Natural Company")
View
12 activerecord/test/cases/associations_test.rb
@@ -182,6 +182,18 @@ def test_failed_reset_returns_nil
assert_nil p.author.reset
end
+ def test_reset_loads_association_next_time
+ welcome = posts(:welcome)
+ david = authors(:david)
+ author_assoc = welcome.author
+
+ assert_equal david, welcome.author # So we can be sure the test works correctly
+ author_assoc.reset
+ assert !author_assoc.loaded?
+ assert_nil author_assoc.target
+ assert_equal david, welcome.author
+ end
+
def test_reload_returns_assocition
david = developers(:david)
assert_nothing_raised do
View
13 activerecord/test/cases/dirty_test.rb
@@ -45,6 +45,19 @@ def test_attribute_changes
assert_nil pirate.catchphrase_change
end
+ def test_aliased_attribute_changes
+ # the actual attribute here is name, title is an
+ # alias setup via alias_attribute
+ parrot = Parrot.new
+ assert !parrot.title_changed?
+ assert_nil parrot.title_change
+
+ parrot.name = 'Sam'
+ assert parrot.title_changed?
+ assert_nil parrot.title_was
+ assert_equal parrot.name_change, parrot.title_change
+ end
+
def test_nullable_integer_not_marked_as_changed_if_new_value_is_blank
pirate = Pirate.new
View
18 activerecord/test/cases/finder_test.rb
@@ -169,6 +169,12 @@ def test_find_with_limit_and_condition
assert_equal("fixture_3", developers.first.name)
end
+ def test_find_with_group
+ developers = Developer.find(:all, :group => "salary", :select => "salary")
+ assert_equal 4, developers.size
+ assert_equal 4, developers.map(&:salary).uniq.size
+ end
+
def test_find_with_entire_select_statement
topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
@@ -297,7 +303,6 @@ def test_find_on_multiple_hash_conditions
assert_raises(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
end
-
def test_condition_interpolation
assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
@@ -392,7 +397,7 @@ def test_bind_variables
Company.find(:first, :conditions => ["id=? AND name = ?", 2])
}
assert_raises(ActiveRecord::PreparedStatementInvalid) {
- Company.find(:first, :conditions => ["id=?", 2, 3, 4])
+ Company.find(:first, :conditions => ["id=?", 2, 3, 4])
}
end
@@ -455,6 +460,15 @@ def test_bind_string
assert_equal ActiveRecord::Base.connection.quote(''), bind('?', '')
end
+ def test_bind_chars
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".chars)
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".chars)
+ end
+
def test_bind_record
o = Struct.new(:quoted_id).new(1)
assert_equal '1', bind('?', o)
View
2 activerecord/test/cases/inheritance_test.rb
@@ -193,7 +193,7 @@ def test_eager_load_belongs_to_something_inherited
def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
- assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} IN \(1\)\)/) do
+ assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} = 1\)/) do
Account.find(1, :include => :firm)
end
end
View
4 activerecord/test/cases/reflection_test.rb
@@ -160,8 +160,8 @@ def test_association_reflection_in_modules
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
- assert_equal 24, Firm.reflect_on_all_associations.size
- assert_equal 18, Firm.reflect_on_all_associations(:has_many).size
+ assert_equal 26, Firm.reflect_on_all_associations.size
+ assert_equal 20, Firm.reflect_on_all_associations(:has_many).size
assert_equal 6, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
View
25 activerecord/test/cases/sanitize_test.rb
@@ -0,0 +1,25 @@
+require "cases/helper"
+require 'models/binary'
+
+class SanitizeTest < ActiveRecord::TestCase
+ def setup
+ end
+
+ def test_sanitize_sql_array_handles_string_interpolation
+ quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"])
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".chars])
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"])
+ assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".chars])
+ end
+
+ def test_sanitize_sql_array_handles_bind_variables
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi"])
+ assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi".chars])
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".chars])
+ end
+end
View
26 activerecord/test/cases/validations_i18n_test.rb
@@ -40,6 +40,32 @@ def test_default_error_messages_is_deprecated
end
end
+ def test_percent_s_interpolation_syntax_in_error_messages_still_works
+ ActiveSupport::Deprecation.silence do
+ result = I18n.t :does_not_exist, :default => "%s interpolation syntax is deprecated", :value => 'this'
+ assert_equal result, "this interpolation syntax is deprecated"
+ end
+ end
+
+ def test_percent_s_interpolation_syntax_in_error_messages_is_deprecated
+ assert_deprecated('using %s in messages') do
+ I18n.t :does_not_exist, :default => "%s interpolation syntax is deprected", :value => 'this'
+ end
+ end
+
+ def test_percent_d_interpolation_syntax_in_error_messages_still_works
+ ActiveSupport::Deprecation.silence do
+ result = I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprecated", :count => 2
+ assert_equal result, "2 interpolation syntaxes are deprecated"
+ end
+ end
+
+ def test_percent_d_interpolation_syntax_in_error_messages_is_deprecated
+ assert_deprecated('using %d in messages') do
+ I18n.t :does_not_exist, :default => "%d interpolation syntaxes are deprected", :count => 2
+ end
+ end
+
# ActiveRecord::Errors
uses_mocha 'ActiveRecord::Errors' do
View
65 activerecord/test/cases/validations_test.rb
@@ -364,6 +364,13 @@ def test_validate_uniqueness
assert t2.save, "Should now save t2 as unique"
end
+ def test_validates_uniquness_with_newline_chars
+ Topic.validates_uniqueness_of(:title, :case_sensitive => false)
+
+ t = Topic.new("title" => "new\nline")
+ assert t.save, "Should save t as unique"
+ end
+
def test_validate_uniqueness_with_scope
Reply.validates_uniqueness_of(:content, :scope => "parent_id")
@@ -605,7 +612,7 @@ def test_validate_format_numeric
end
def test_validate_format_with_formatted_message
- Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be %s")
+ Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}")
t = Topic.create(:title => 'Invalid title')
assert_equal "can't be Invalid title", t.errors.on(:title)
end
@@ -666,7 +673,7 @@ def test_validates_length_of_with_allow_blank
end
def test_validates_inclusion_of_with_formatted_message
- Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" )
assert Topic.create("title" => "a", "content" => "abc").valid?
@@ -691,7 +698,7 @@ def test_validates_exclusion_of
end
def test_validates_exclusion_of_with_formatted_message
- Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %s is restricted" )
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" )
assert Topic.create("title" => "something", "content" => "abc")
@@ -791,7 +798,7 @@ def test_optionally_validates_length_of_using_within
end
def test_optionally_validates_length_of_using_within_on_create
- Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: %d"
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}"
t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
assert !t.save
@@ -812,7 +819,7 @@ def test_optionally_validates_length_of_using_within_on_create
end
def test_optionally_validates_length_of_using_within_on_update
- Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: %d"
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}"
t = Topic.create("title" => "vali", "content" => "whatever")
assert !t.save
@@ -875,7 +882,7 @@ def test_validates_length_of_using_bignum
def test_validates_length_with_globally_modified_error_message
ActiveSupport::Deprecation.silence do
- ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d'
+ ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}'
end
Topic.validates_length_of :title, :minimum => 10
t = Topic.create(:title => 'too short')
@@ -919,47 +926,47 @@ def test_validates_length_of_nasty_params
end
def test_validates_length_of_custom_errors_for_minimum_with_message
- Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %d" )
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" )
t = Topic.create("title" => "uhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "boo 5", t.errors["title"]
end
def test_validates_length_of_custom_errors_for_minimum_with_too_short
- Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %d" )
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" )
t = Topic.create("title" => "uhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "hoo 5", t.errors["title"]
end
def test_validates_length_of_custom_errors_for_maximum_with_message
- Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %d" )
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "boo 5", t.errors["title"]
end
def test_validates_length_of_custom_errors_for_maximum_with_too_long
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d" )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "hoo 5", t.errors["title"]
end
def test_validates_length_of_custom_errors_for_is_with_message
- Topic.validates_length_of( :title, :is=>5, :message=>"boo %d" )
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
assert_equal "boo 5", t.errors["title"]
end
def test_validates_length_of_custom_errors_for_is_with_wrong_length
- Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %d" )
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
@@ -1025,7 +1032,7 @@ def test_optionally_validates_length_of_using_within_utf8
def test_optionally_validates_length_of_using_within_on_create_utf8
with_kcode('UTF8') do
- Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: %d"
+ Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}"
t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
assert !t.save
@@ -1048,7 +1055,7 @@ def test_optionally_validates_length_of_using_within_on_create_utf8
def test_optionally_validates_length_of_using_within_on_update_utf8
with_kcode('UTF8') do
- Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: %d"
+ Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}"
t = Topic.create("title" => "一二三4", "content" => "whatever")
assert !t.save
@@ -1083,7 +1090,7 @@ def test_validates_length_of_using_is_utf8
end
def test_validates_length_of_with_block
- Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least %d words.",
+ Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.",
:tokenizer => lambda {|str| str.scan(/\w+/) }
t = Topic.create!(:content => "this content should be long enough")
assert t.valid?
@@ -1234,7 +1241,7 @@ def test_validates_associated_with_custom_message_using_quotes
def test_if_validation_using_method_true
# When the method returns true
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
@@ -1243,23 +1250,23 @@ def test_if_validation_using_method_true
def test_unless_validation_using_method_true
# When the method returns true
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_if_validation_using_method_false
# When the method returns false
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_unless_validation_using_method_false
# When the method returns false
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true_but_its_not )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
@@ -1268,7 +1275,7 @@ def test_unless_validation_using_method_false
def test_if_validation_using_string_true
# When the evaluated string returns true
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
@@ -1277,23 +1284,23 @@ def test_if_validation_using_string_true
def test_unless_validation_using_string_true
# When the evaluated string returns true
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "a = 1; a == 1" )
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_if_validation_using_string_false
# When the evaluated string returns false
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
assert !t.errors.on(:title)
end
def test_unless_validation_using_string_false
# When the evaluated string returns false
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "false")
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
assert t.errors.on(:title)
@@ -1302,7 +1309,7 @@ def test_unless_validation_using_string_false
def test_if_validation_using_block_true
# When the block returns true
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
:if => Proc.new { |r| r.content.size > 4 } )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
@@ -1312,7 +1319,7 @@ def test_if_validation_using_block_true
def test_unless_validation_using_block_true
# When the block returns true
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
:unless => Proc.new { |r| r.content.size > 4 } )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
@@ -1321,7 +1328,7 @@ def test_unless_validation_using_block_true
def test_if_validation_using_block_false
# When the block returns false
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
:if => Proc.new { |r| r.title != "uhohuhoh"} )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert t.valid?
@@ -1330,7 +1337,7 @@ def test_if_validation_using_block_false
def test_unless_validation_using_block_false
# When the block returns false
- Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
:unless => Proc.new { |r| r.title != "uhohuhoh"} )
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
assert !t.valid?
@@ -1507,13 +1514,13 @@ def test_validates_numericality_with_greater_than_less_than_and_even
end
def test_validates_numericality_with_numeric_message
- Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than %d"
+ Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}"
topic = Topic.new("title" => "numeric test", "approved" => 10)
assert !topic.valid?
assert_equal "smaller than 4", topic.errors.on(:approved)
- Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than %d"
+ Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}"
topic = Topic.new("title" => "numeric test", "approved" => 1)
assert !topic.valid?
View
3 activerecord/test/models/category.rb
@@ -13,6 +13,9 @@ class Category < ActiveRecord::Base
has_and_belongs_to_many :post_with_conditions,
:class_name => 'Post',
:conditions => { :title => 'Yet Another Testing Title' }
+
+ has_and_belongs_to_many :posts_gruoped_by_title, :class_name => "Post", :group => "title", :select => "title"
+
def self.what_are_you
'a category...'
end
View
2 activerecord/test/models/company.rb
@@ -55,6 +55,8 @@ class Firm < Company
has_many :readonly_clients, :class_name => 'Client', :readonly => true
has_many :clients_using_primary_key, :class_name => 'Client',
:primary_key => 'name', :foreign_key => 'firm_name'
+ has_many :clients_grouped_by_firm_id, :class_name => "Client", :group => "firm_id", :select => "firm_id"
+ has_many :clients_grouped_by_name, :class_name => "Client", :group => "name", :select => "name"
has_one :account, :foreign_key => "firm_id", :dependent => :destroy, :validate => true
has_one :unvalidated_account, :foreign_key => "firm_id", :class_name => 'Account', :validate => false
View
1 activerecord/test/models/parrot.rb
@@ -3,6 +3,7 @@ class Parrot < ActiveRecord::Base
has_and_belongs_to_many :pirates
has_and_belongs_to_many :treasures
has_many :loots, :as => :looter
+ alias_attribute :title, :name
end
class LiveParrot < Parrot
View
2 activesupport/CHANGELOG
@@ -1,5 +1,7 @@
*Edge*
+* Fixed Sri Jayawardenepura time zone to map to Asia/Colombo [Jamis Buck]
+
* Added Inflector#parameterize for easy slug generation ("Donald E. Knuth".parameterize => "donald-e-knuth") #713 [Matt Darby]
* Changed cache benchmarking to be reported in milliseconds [DHH]
View
50 activesupport/lib/active_support/cache/memory_store.rb
@@ -3,63 +3,35 @@ module Cache
class MemoryStore < Store
def initialize
@data = {}
- @guard = Monitor.new
- end
-
- def fetch(key, options = {})
- @guard.synchronize do
- super
- end
end
def read(name, options = nil)
- @guard.synchronize do
- super
- @data[name]
- end
+ super
+ @data[name]
end
def write(name, value, options = nil)
- @guard.synchronize do
- super
- @data[name] = value.freeze
- end
+ super
+ @data[name] = value.freeze
end
def delete(name, options = nil)
- @guard.synchronize do
- @data.delete(name)
- end
+ super
+ @data.delete(name)
end
def delete_matched(matcher, options = nil)
- @guard.synchronize do
- @data.delete_if { |k,v| k =~ matcher }
- end
+ super
+ @data.delete_if { |k,v| k =~ matcher }
end
def exist?(name,options = nil)
- @guard.synchronize do
- @data.has_key?(name)
- end
- end
-
- def increment(key, amount = 1)
- @guard.synchronize do
- super
- end
- end
-
- def decrement(key, amount = 1)
- @guard.synchronize do
- super
- end
+ super
+ @data.has_key?(name)
end
def clear
- @guard.synchronize do
- @data.clear
- end
+ @data.clear
end
end
end
View
46 activesupport/lib/active_support/cache/synchronized_memory_store.rb
@@ -0,0 +1,46 @@
+module ActiveSupport
+ module Cache
+ class SynchronizedMemoryStore < MemoryStore
+ def initialize
+ super
+ @guard = Monitor.new
+ end
+
+ def fetch(key, options = {})
+ @guard.synchronize { super }
+ end
+
+ def read(name, options = nil)
+ @guard.synchronize { super }
+ end
+
+ def write(name, value, options = nil)
+ @guard.synchronize { super }
+ end
+
+ def delete(name, options = nil)
+ @guard.synchronize { super }
+ end
+
+ def delete_matched(matcher, options = nil)
+ @guard.synchronize { super }
+ end
+
+ def exist?(name,options = nil)
+ @guard.synchronize { super }
+ end
+
+ def increment(key, amount = 1)
+ @guard.synchronize { super }
+ end
+
+ def decrement(key, amount = 1)
+ @guard.synchronize { super }
+ end
+
+ def clear
+ @guard.synchronize { super }
+ end
+ end
+ end
+end
View
2 activesupport/lib/active_support/core_ext/hash/except.rb
@@ -10,7 +10,7 @@ module Hash #:nodoc:
module Except
# Returns a new hash without the given keys.
def except(*keys)
- clone.except!(*keys)
+ dup.except!(*keys)
end
# Replaces the hash without the given keys.
View
2 activesupport/lib/active_support/core_ext/string.rb
@@ -6,6 +6,7 @@
require 'active_support/core_ext/string/unicode'
require 'active_support/core_ext/string/xchar'
require 'active_support/core_ext/string/filters'
+require 'active_support/core_ext/string/behavior'
class String #:nodoc:
include ActiveSupport::CoreExtensions::String::Access
@@ -15,4 +16,5 @@ class String #:nodoc:
include ActiveSupport::CoreExtensions::String::StartsEndsWith
include ActiveSupport::CoreExtensions::String::Iterators
include ActiveSupport::CoreExtensions::String::Unicode
+ include ActiveSupport::CoreExtensions::String::Behavior
end
View
13 activesupport/lib/active_support/core_ext/string/behavior.rb
@@ -0,0 +1,13 @@
+module ActiveSupport #:nodoc:
+ module CoreExtensions #:nodoc:
+ module String #:nodoc:
+ module Behavior
+ # Enable more predictable duck-typing on String-like classes. See
+ # Object#acts_like?.
+ def acts_like_string?
+ true
+ end
+ end
+ end
+ end
+end
View
2 activesupport/lib/active_support/inflector.rb
@@ -257,7 +257,7 @@ def demodulize(class_name_in_module)
# <%= link_to(@person.name, person_path %>
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
def parameterize(string, sep = '-')
- string.gsub(/[^a-z0-9]+/i, sep).downcase
+ string.chars.normalize(:kd).to_s.gsub(/[^\x00-\x7F]+/, '').gsub(/[^a-z0-9_\-]+/i, sep).downcase
end
# Create the name of a table like Rails does for models to table names. This method
View
5 activesupport/lib/active_support/multibyte/chars.rb
@@ -49,6 +49,11 @@ def respond_to?(method, include_priv = false)
false
end
+ # Enable more predictable duck-typing on String-like classes. See Object#acts_like?.
+ def acts_like_string?
+ true
+ end
+
# Create a new Chars instance.
def initialize(str)
@string = str.respond_to?(:string) ? str.string : str
View
2 activesupport/lib/active_support/values/time_zone.rb
@@ -124,7 +124,7 @@ class TimeZone
"Kathmandu" => "Asia/Katmandu",
"Astana" => "Asia/Dhaka",
"Dhaka" => "Asia/Dhaka",
- "Sri Jayawardenepura" => "Asia/Dhaka",
+ "Sri Jayawardenepura" => "Asia/Colombo",
"Almaty" => "Asia/Almaty",
"Novosibirsk" => "Asia/Novosibirsk",
"Rangoon" => "Asia/Rangoon",
View
30 activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/definitions/Asia/Colombo.rb
@@ -0,0 +1,30 @@
+require 'tzinfo/timezone_definition'
+
+module TZInfo
+ module Definitions
+ module Asia
+ module Colombo
+ include TimezoneDefinition
+
+ timezone 'Asia/Colombo' do |tz|
+ tz.offset :o0, 19164, 0, :LMT
+ tz.offset :o1, 19172, 0, :MMT
+ tz.offset :o2, 19800, 0, :IST
+ tz.offset :o3, 19800, 1800, :IHST
+ tz.offset :o4, 19800, 3600, :IST
+ tz.offset :o5, 23400, 0, :LKT
+ tz.offset :o6, 21600, 0, :LKT
+
+ tz.transition 1879, 12, :o1, 17335550003, 7200
+ tz.transition 1905, 12, :o2, 52211763607, 21600
+ tz.transition 1942, 1, :o3, 116657485, 48
+ tz.transition 1942, 8, :o4, 9722413, 4
+ tz.transition 1945, 10, :o2, 38907909, 16
+ tz.transition 1996, 5, :o5, 832962600
+ tz.transition 1996, 10, :o6, 846266400
+ tz.transition 2006, 4, :o2, 1145039400
+ end
+ end
+ end
+ end
+end
View
14 activesupport/test/core_ext/hash_ext_test.rb
@@ -341,6 +341,20 @@ def test_except
assert_equal expected, original.except!(:c)
assert_equal expected, original
end
+
+ def test_except_with_original_frozen
+ original = { :a => 'x', :b => 'y' }
+ original.freeze
+ assert_nothing_raised { original.except(:a) }
+ end
+
+ uses_mocha 'except with expectation' do
+ def test_except_with_mocha_expectation_on_original
+ original = { :a => 'x', :b => 'y' }
+ original.expects(:delete).never
+ original.except(:a)
+ end
+ end
end
class IWriteMyOwnXML
View
6 activesupport/test/core_ext/string_ext_test.rb
@@ -201,3 +201,9 @@ def test_each_char_with_utf8_string_when_kcode_is_utf8
end
end
end
+
+class StringBehaviourTest < Test::Unit::TestCase
+ def test_acts_like_string
+ assert 'Bambi'.acts_like_string?
+ end
+end
View
5 activesupport/test/inflector_test_cases.rb
@@ -144,7 +144,10 @@ module InflectorTestCases
StringToParameterized = {
"Donald E. Knuth" => "donald-e-knuth",
- "Random text with *(bad)* characters" => "random-text-with-bad-characters"
+ "Random text with *(bad)* characters" => "random-text-with-bad-characters",
+ "Malmö" => "malmo",
+ "Garçons" => "garcons",
+ "Allow_Under_Scores" => "allow_under_scores"
}
UnderscoreToHuman = {
View
4 activesupport/test/multibyte_chars_test.rb
@@ -167,6 +167,10 @@ def test_duck_typing
assert_equal false, 'test'.chars.respond_to?(:a_method_that_doesnt_exist)
end
+ def test_acts_like_string
+ assert 'Bambi'.chars.acts_like_string?
+ end
+
protected
def with_kcode(kcode)
View
13 railties/lib/commands/console.rb
@@ -1,11 +1,13 @@
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
require 'optparse'
+
options = { :sandbox => false, :irb => irb }
OptionParser.new do |opt|
opt.banner = "Usage: console [environment] [options]"
opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
+ opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
opt.parse!(ARGV)
end
@@ -15,6 +17,17 @@
libs << " -r console_sandbox" if options[:sandbox]
libs << " -r console_with_helpers"
+if options[:debugger]
+ begin
+ require 'ruby-debug'
+ libs << " -r ruby-debug"
+ puts "=> Debugger enabled"
+ rescue Exception
+ puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
+ exit
+ end
+end
+
ENV['RAILS_ENV'] = case ARGV.first
when "p"; "production"
when "d"; "development"

0 comments on commit f6c0f68

Please sign in to comment.