Permalink
Browse files

Merge branch 'master' into mysql2

* master:
  Bring returning back to ease migration.
  Remove duplicated logic.
  Eager loading an association should not change the count of children
  fix loading of different elements in array then int and string [#5036 state:resolved]
  Tidy up previous commit.
  test and fix collection_singular_ids= with string primary keys [#5125 state:resolved]
  Handle edge cases in the previous patch.
  Improved how AppGenerator generates the application name. It now detects the current app name whenever possible. This means that renaming the residing directory will not effect the app name generated by AppGenerator.
  ActiveModel::Errors json serialization to work as Rails 3b4 [#5254 state:resolved]
  Add missing require in ActiveSupport::HashWithIndifferentAccess [#5189 state:resolved]
  Add an internal (private API) after_touch callback. [#5271 state:resolved]
  added failing touch propagation test
  Makes rails destroy scaffold don't duplicate routes.draw do |map| |map| when using the deprecated syntax
  Failing test to check for route file corruption if legacy map parameter is used. [#5263 state:open]
  Corrected the rake test:units and test:functionals description [#5251 state:committed]
  Use AS::OrderedHash when trusting in the order of the hash
  Fix label form helper to use I18n and html options, without the need of 'nil' text param:
  • Loading branch information...
2 parents 2353e82 + 88b5f93 commit 954de9940b8f212cafdb9f5e97abc83d27b3e4ff @brianmario brianmario committed Aug 2, 2010
Showing with 291 additions and 56 deletions.
  1. +4 −3 actionpack/lib/action_view/helpers/form_helper.rb
  2. +3 −2 actionpack/test/controller/test_test.rb
  3. +9 −2 actionpack/test/template/form_helper_test.rb
  4. +2 −2 activemodel/lib/active_model/errors.rb
  5. +15 −1 activemodel/test/cases/serializeration/json_serialization_test.rb
  6. +5 −3 activemodel/test/cases/validations_test.rb
  7. +14 −5 activerecord/lib/active_record/associations.rb
  8. +6 −2 activerecord/lib/active_record/callbacks.rb
  9. +13 −0 activerecord/lib/active_record/persistence.rb
  10. +12 −14 activerecord/lib/active_record/reflection.rb
  11. +0 −13 activerecord/lib/active_record/timestamp.rb
  12. +39 −1 activerecord/test/cases/associations/has_many_through_associations_test.rb
  13. +13 −0 activerecord/test/cases/associations_test.rb
  14. +18 −1 activerecord/test/cases/timestamp_test.rb
  15. +1 −1 activerecord/test/fixtures/subscriptions.yml
  16. +3 −0 activerecord/test/models/book.rb
  17. +3 −0 activerecord/test/models/electron.rb
  18. +5 −0 activerecord/test/models/liquid.rb
  19. +4 −0 activerecord/test/models/molecule.rb
  20. +15 −0 activerecord/test/schema/schema.rb
  21. +3 −3 activeresource/lib/active_resource/base.rb
  22. +12 −0 activeresource/test/cases/base/load_test.rb
  23. +1 −0 activesupport/lib/active_support/core_ext/object.rb
  24. +43 −0 activesupport/lib/active_support/core_ext/object/returning.rb
  25. +1 −0 activesupport/lib/active_support/hash_with_indifferent_access.rb
  26. +1 −1 railties/lib/rails/generators/actions.rb
  27. +6 −1 railties/lib/rails/generators/rails/app/app_generator.rb
  28. +1 −1 railties/lib/rails/test_unit/testing.rake
  29. +24 −0 railties/test/generators/app_generator_test.rb
  30. +15 −0 railties/test/generators/scaffold_generator_test.rb
@@ -215,7 +215,7 @@ module FormHelper
# ...
# <% end %>
#
- # If your resource has associations defined, for example, you want to add comments
+ # If your resource has associations defined, for example, you want to add comments
# to the post given that the routes are set correctly:
#
# <%= form_for([@document, @comment]) do |f| %>
@@ -583,8 +583,9 @@ def fields_for(record_or_name_or_array, *args, &block)
# 'Accept <a href="/terms">Terms</a>.'
# end
def label(object_name, method, content_or_options = nil, options = nil, &block)
- if block_given?
- options = content_or_options if content_or_options.is_a?(Hash)
+ content_is_options = content_or_options.is_a?(Hash)
+ if content_is_options || block_given?
+ options = content_or_options if content_is_options
text = nil
else
text = content_or_options
@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'controller/fake_controllers'
+require 'active_support/ordered_hash'
class TestTest < ActionController::TestCase
class TestController < ActionController::Base
@@ -137,14 +138,14 @@ def setup
end
def test_raw_post_handling
- params = {:page => {:name => 'page name'}, 'some key' => 123}
+ params = ActiveSupport::OrderedHash[:page, {:name => 'page name'}, 'some key', 123]
post :render_raw_post, params.dup
assert_equal params.to_query, @response.body
end
def test_body_stream
- params = { :page => { :name => 'page name' }, 'some key' => 123 }
+ params = ActiveSupport::OrderedHash[:page, { :name => 'page name' }, 'some key', 123]
post :render_body, params.dup
@@ -120,6 +120,13 @@ def test_label_with_locales_symbols
I18n.locale = old_locale
end
+ def test_label_with_locales_and_options
+ old_locale, I18n.locale = I18n.locale, :label
+ assert_dom_equal('<label for="post_body" class="post_body">Write entire text here</label>', label(:post, :body, :class => 'post_body'))
+ ensure
+ I18n.locale = old_locale
+ end
+
def test_label_with_for_attribute_as_symbol
assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for"))
end
@@ -620,15 +627,15 @@ def test_form_for
def test_form_for_with_symbol_object_name
form_for(@post, :as => "other_name", :html => { :id => 'create-post' }) do |f|
- concat f.label(:title)
+ concat f.label(:title, :class => 'post_title')
concat f.text_field(:title)
concat f.text_area(:body)
concat f.check_box(:secret)
concat f.submit('Create post')
end
expected = whole_form("/posts/123", "create-post", "other_name_edit", :method => "put") do
- "<label for='other_name_title'>Title</label>" +
+ "<label for='other_name_title' class='post_title'>Title</label>" +
"<input name='other_name[title]' size='30' id='other_name_title' value='Hello World' type='text' />" +
"<textarea name='other_name[body]' id='other_name_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" +
"<input name='other_name[secret]' value='0' type='hidden' />" +
@@ -169,9 +169,9 @@ def to_xml(options={})
to_a.to_xml options.reverse_merge(:root => "errors", :skip_types => true)
end
- # Returns an array as JSON representation for this object.
+ # Returns an ActiveSupport::OrderedHash that can be used as the JSON representation for this object.
def as_json(options=nil)
- to_a
+ self
end
# Adds +message+ to the error messages on +attribute+, which will be returned on a call to
@@ -89,7 +89,7 @@ def setup
assert_match %r{"preferences":\{"shows":"anime"\}}, json
end
- test "methds are called on object" do
+ test "methods are called on object" do
# Define methods on fixture.
def @contact.label; "Has cheezburger"; end
def @contact.favorite_quote; "Constraints are liberating"; end
@@ -102,4 +102,18 @@ def @contact.favorite_quote; "Constraints are liberating"; end
assert_match %r{"label":"Has cheezburger"}, methods_json
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
end
+
+ test "should return OrderedHash for errors" do
+ car = Automobile.new
+
+ # run the validation
+ car.valid?
+
+ hash = ActiveSupport::OrderedHash.new
+ hash[:make] = "can't be blank"
+ hash[:model] = "is too short (minimum is 2 characters)"
+ assert_equal hash.to_json, car.errors.to_json
+ end
+
+
end
@@ -170,9 +170,11 @@ def test_errors_conversions
assert_match %r{<errors>}, xml
assert_match %r{<error>Title can't be blank</error>}, xml
assert_match %r{<error>Content can't be blank</error>}, xml
-
- json = t.errors.to_json
- assert_equal t.errors.to_a.to_json, json
+
+ hash = ActiveSupport::OrderedHash.new
+ hash[:title] = "can't be blank"
+ hash[:content] = "can't be blank"
+ assert_equal t.errors.to_json, hash.to_json
end
def test_validation_order
@@ -1437,9 +1437,11 @@ def collection_accessor_methods(reflection, association_proxy_class, writer = tr
association.replace(new_value)
association
end
-
+
redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
- ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
+ pk_column = reflection.primary_key_column
+ ids = (new_value || []).reject { |nid| nid.blank? }
+ ids.map!{ |i| pk_column.type_cast(i) }
send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
end
end
@@ -1498,6 +1500,7 @@ def add_touch_callbacks(reflection, touch_attribute)
end
end
after_save(method_name)
+ after_touch(method_name)
after_destroy(method_name)
end
@@ -1802,16 +1805,15 @@ def remove_duplicate_results!(base, records, associations)
case associations
when Symbol, String
reflection = base.reflections[associations]
- if reflection && reflection.collection?
- records.each { |record| record.send(reflection.name).target.uniq! }
- end
+ remove_uniq_by_reflection(reflection, records)
when Array
associations.each do |association|
remove_duplicate_results!(base, records, association)
end
when Hash
associations.keys.each do |name|
reflection = base.reflections[name]
+ remove_uniq_by_reflection(reflection, records)
parent_records = []
records.each do |record|
@@ -1830,6 +1832,7 @@ def remove_duplicate_results!(base, records, associations)
end
protected
+
def build(associations, parent = nil, join_class = Arel::InnerJoin)
parent ||= @joins.last
case associations
@@ -1852,6 +1855,12 @@ def build(associations, parent = nil, join_class = Arel::InnerJoin)
end
end
+ def remove_uniq_by_reflection(reflection, records)
+ if reflection && reflection.collection?
+ records.each { |record| record.send(reflection.name).target.uniq! }
+ end
+ end
+
def build_join_association(reflection, parent)
JoinAssociation.new(reflection, self, parent)
end
@@ -228,7 +228,7 @@ module Callbacks
extend ActiveSupport::Concern
CALLBACKS = [
- :after_initialize, :after_find, :before_validation, :after_validation,
+ :after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
:before_save, :around_save, :after_save, :before_create, :around_create,
:after_create, :before_update, :around_update, :after_update,
:before_destroy, :around_destroy, :after_destroy
@@ -238,7 +238,7 @@ module Callbacks
extend ActiveModel::Callbacks
include ActiveModel::Validations::Callbacks
- define_model_callbacks :initialize, :find, :only => :after
+ define_model_callbacks :initialize, :find, :touch, :only => :after
define_model_callbacks :save, :create, :update, :destroy
end
@@ -256,6 +256,10 @@ def destroy #:nodoc:
_run_destroy_callbacks { super }
end
+ def touch(*) #:nodoc:
+ _run_touch_callbacks { super }
+ end
+
def deprecated_callback_method(symbol) #:nodoc:
if respond_to?(symbol, true)
ActiveSupport::Deprecation.warn("Overwriting #{symbol} in your models has been deprecated, please use Base##{symbol} :method_name instead")
@@ -218,6 +218,19 @@ def reload(options = nil)
self
end
+ # Saves the record with the updated_at/on attributes set to the current time.
+ # Please note that no validation is performed and no callbacks are executed.
+ # If an attribute name is passed, that attribute is updated along with
+ # updated_at/on attributes.
+ #
+ # Examples:
+ #
+ # product.touch # updates updated_at/on
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
+ def touch(attribute = nil)
+ update_attribute(attribute, current_time_from_proper_timezone)
+ end
+
private
def create_or_update
raise ReadOnlyRecord if readonly?
@@ -91,25 +91,19 @@ def initialize(macro, name, options, active_record)
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
- def name
- @name
- end
+ attr_reader :name
# Returns the macro type.
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:composed_of</tt>
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
- def macro
- @macro
- end
+ attr_reader :macro
# Returns the hash of options used for the macro.
#
# <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>{ :class_name => "Money" }</tt>
# <tt>has_many :clients</tt> returns +{}+
- def options
- @options
- end
+ attr_reader :options
# Returns the class for the macro.
#
@@ -137,11 +131,6 @@ def sanitized_conditions #:nodoc:
@sanitized_conditions ||= klass.send(:sanitize_sql, options[:conditions]) if options[:conditions]
end
- # Returns +true+ if +self+ is a +belongs_to+ reflection.
- def belongs_to?
- macro == :belongs_to
- end
-
private
def derive_class_name
name.to_s.camelize
@@ -213,6 +202,10 @@ def primary_key_name
@primary_key_name ||= options[:foreign_key] || derive_primary_key_name
end
+ def primary_key_column
+ @primary_key_column ||= klass.columns.find { |c| c.name == klass.primary_key }
+ end
+
def association_foreign_key
@association_foreign_key ||= @options[:association_foreign_key] || class_name.foreign_key
end
@@ -307,6 +300,11 @@ def dependent_conditions(record, base_class, extra_conditions)
dependent_conditions
end
+ # Returns +true+ if +self+ is a +belongs_to+ reflection.
+ def belongs_to?
+ macro == :belongs_to
+ end
+
private
def derive_class_name
class_name = name.to_s.camelize
@@ -31,19 +31,6 @@ module Timestamp
class_inheritable_accessor :record_timestamps, :instance_writer => false
self.record_timestamps = true
end
-
- # Saves the record with the updated_at/on attributes set to the current time.
- # Please note that no validation is performed and no callbacks are executed.
- # If an attribute name is passed, that attribute is updated along with
- # updated_at/on attributes.
- #
- # Examples:
- #
- # product.touch # updates updated_at/on
- # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
- def touch(attribute = nil)
- update_attribute(attribute, current_time_from_proper_timezone)
- end
private
@@ -14,9 +14,14 @@
require 'models/contract'
require 'models/company'
require 'models/developer'
+require 'models/subscriber'
+require 'models/book'
+require 'models/subscription'
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
- fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys, :jobs, :references, :companies
+ fixtures :posts, :readers, :people, :comments, :authors,
+ :owners, :pets, :toys, :jobs, :references, :companies,
+ :subscribers, :books, :subscriptions, :developers
# Dummies to force column loads so query counts are clean.
def setup
@@ -383,4 +388,37 @@ def test_modifying_has_many_through_has_one_reflection_should_raise
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
end
+
+ def test_collection_singular_ids_getter_with_string_primary_keys
+ book = books(:awdr)
+ assert_equal 2, book.subscriber_ids.size
+ assert_equal [subscribers(:first).nick, subscribers(:second).nick].sort, book.subscriber_ids.sort
+ end
+
+ def test_collection_singular_ids_setter
+ company = companies(:rails_core)
+ dev = Developer.find(:first)
+
+ company.developer_ids = [dev.id]
+ assert_equal [dev], company.developers
+ end
+
+ def test_collection_singular_ids_setter_with_string_primary_keys
+ assert_nothing_raised do
+ book = books(:awdr)
+ book.subscriber_ids = [subscribers(:second).nick]
+ assert_equal [subscribers(:second)], book.subscribers(true)
+
+ book.subscriber_ids = []
+ assert_equal [], book.subscribers(true)
+ end
+
+ end
+
+ def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
+ company = companies(:rails_core)
+ ids = [Developer.find(:first).id, -9999]
+ assert_raises(ActiveRecord::RecordNotFound) {company.developer_ids= ids}
+ end
+
end
Oops, something went wrong.

0 comments on commit 954de99

Please sign in to comment.