diff options
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 20 | ||||
-rw-r--r-- | actionpack/test/abstract_unit.rb | 1 | ||||
-rw-r--r-- | actionpack/test/template/form_helper_test.rb | 125 | ||||
-rw-r--r-- | actionpack/test/template/sprockets_helper_test.rb | 9 | ||||
-rw-r--r-- | activemodel/test/cases/helper.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/association_scope.rb | 20 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/join_dependency/join_association.rb | 10 | ||||
-rw-r--r-- | activerecord/lib/active_record/reflection.rb | 9 | ||||
-rw-r--r-- | activerecord/test/cases/associations/has_many_associations_test.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/reflection_test.rb | 4 | ||||
-rw-r--r-- | activeresource/test/abstract_unit.rb | 6 | ||||
-rw-r--r-- | activesupport/activesupport.gemspec | 7 | ||||
-rw-r--r-- | activesupport/lib/active_support/i18n.rb | 2 | ||||
-rw-r--r-- | activesupport/lib/active_support/values/time_zone.rb | 4 | ||||
-rw-r--r-- | activesupport/lib/active_support/xml_mini.rb | 6 | ||||
-rw-r--r-- | railties/test/isolation/abstract_unit.rb | 2 | ||||
-rw-r--r-- | railties/test/rails_info_controller_test.rb | 1 |
17 files changed, 197 insertions, 37 deletions
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 07e2c8d341..cb1c13912a 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -555,6 +555,19 @@ module ActionView # ... # <% end %> # + # In addition, you may want to have access to the current iteration index. + # In that case, you can use a similar method called fields_for_with_index + # which receives a block with an extra parameter: + # + # <%= form_for @person do |person_form| %> + # ... + # <%= person_form.fields_for_with_index :projects do |project_fields, index| %> + # Position: <%= index %> + # Name: <%= project_fields.text_field :name %> + # <% end %> + # ... + # <% end %> + # # When projects is already an association on Person you can use # +accepts_nested_attributes_for+ to define the writer method for you: # @@ -1216,6 +1229,13 @@ module ActionView RUBY_EVAL end + # Check +fields_for+ for docs and examples. + def fields_for_with_index(record_name, record_object = nil, fields_options = {}, &block) + index = fields_options[:index] || options[:child_index] || nested_child_index(@object_name) + block_with_index = Proc.new{ |obj| block.call(obj, index) } + fields_for(record_name, record_object, fields_options, &block_with_index) + end + def fields_for(record_name, record_object = nil, fields_options = {}, &block) fields_options, record_object = record_object, nil if record_object.is_a?(Hash) fields_options[:builder] ||= options[:builder] diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 60534a9746..ad0ad807db 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,4 +1,5 @@ require File.expand_path('../../../load_paths', __FILE__) +require File.expand_path('../../../version', __FILE__) lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 286bfb4d04..f2a49f7a68 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -974,6 +974,22 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_with_index_and_parent_fields + form_for(@post, :index => 1) do |c| + concat c.text_field(:title) + concat c.fields_for_with_index('comment', @comment, :index => 1) { |r, comment_index| + concat r.text_field(:name, "data-index" => comment_index) + } + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'put') do + "<input name='post[1][title]' size='30' type='text' id='post_1_title' value='Hello World' />" + + "<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' data-index='1' />" + end + + assert_dom_equal expected, output_buffer + end + def test_form_for_with_index_and_nested_fields_for output_buffer = form_for(@post, :index => 1) do |f| concat f.fields_for(:comment, @post) { |c| @@ -1030,6 +1046,20 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_with_index_radio_button + form_for(@post) do |f| + concat f.fields_for_with_index(:comment, @post, :index => 5) { |c, index| + concat c.radio_button(:title, "hello", "data-index" => index) + } + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'put') do + "<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' data-index='5' />" + end + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_with_auto_index_on_both form_for(@post, :as => "post[]") do |f| concat f.fields_for("comment[]", @post) { |c| @@ -1229,6 +1259,29 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_with_existing_records_on_a_nested_attributes_collection_association + @post.comments = Array.new(2) { |id| Comment.new(id + 1) } + + form_for(@post) do |f| + concat f.text_field(:title) + @post.comments.each do |comment| + concat f.fields_for_with_index(:comments, comment) { |cf, index| + concat cf.text_field(:name, "data-index" => index) + } + end + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do + '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" data-index="0" />' + + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" data-index="1" />' + + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' + end + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id @post.comments = Array.new(2) { |id| Comment.new(id + 1) } @post.author = Author.new(321) @@ -1256,6 +1309,33 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id + @post.comments = Array.new(2) { |id| Comment.new(id + 1) } + @post.author = Author.new(321) + + form_for(@post) do |f| + concat f.text_field(:title) + concat f.fields_for(:author) { |af| + concat af.text_field(:name) + } + @post.comments.each do |comment| + concat f.fields_for_with_index(:comments, comment, :include_id => false) { |cf, index| + concat cf.text_field(:name, 'data-index' => index) + } + end + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do + '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" data-index="0" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" data-index="1" />' + end + + assert_dom_equal expected, output_buffer + end + def test_nested_fields_for_with_existing_records_on_a_nested_attributes_collection_association_with_disabled_hidden_id_inherited @post.comments = Array.new(2) { |id| Comment.new(id + 1) } @post.author = Author.new(321) @@ -1377,6 +1457,28 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_with_new_records_on_a_nested_attributes_collection_association + @post.comments = [Comment.new, Comment.new] + + form_for(@post) do |f| + concat f.text_field(:title) + @post.comments.each do |comment| + concat f.fields_for_with_index(:comments, comment) { |cf, index| + concat cf.text_field(:name, "data-index" => index) + } + end + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do + '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="new comment" data-index="0" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" data-index="1" />' + end + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_existing_and_new_records_on_a_nested_attributes_collection_association @post.comments = [Comment.new(321), Comment.new] @@ -1399,6 +1501,29 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_index_with_existing_and_new_records_on_a_nested_attributes_collection_association + @post.comments = [Comment.new(321), Comment.new] + + form_for(@post) do |f| + concat f.text_field(:title) + @post.comments.each do |comment| + concat f.fields_for_with_index(:comments, comment) { |cf, index| + concat cf.text_field(:name, "data-index" => index) + } + end + end + + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'put') do + '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" data-index="0" />' + + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" data-index="1" />' + end + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_an_empty_supplied_attributes_collection form_for(@post) do |f| concat f.text_field(:title) diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb index 4ce8c672cd..23288a242c 100644 --- a/actionpack/test/template/sprockets_helper_test.rb +++ b/actionpack/test/template/sprockets_helper_test.rb @@ -1,9 +1,14 @@ require 'abstract_unit' + +module Rails + class Engine + def self.initializer(*args); end + end +end + require 'sprockets' require 'mocha' -module Rails; end - class SprocketsHelperTest < ActionView::TestCase tests ActionView::Helpers::SprocketsHelper diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index 01f0158678..2e860272a4 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -10,5 +10,4 @@ require 'active_support/core_ext/string/access' # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true -require 'rubygems' require 'test/unit' diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index ab102b2b8f..94847bc2ae 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -75,10 +75,16 @@ module ActiveRecord foreign_key = reflection.active_record_primary_key end + conditions = self.conditions[i] + if reflection == chain.last scope = scope.where(table[key].eq(owner[foreign_key])) - conditions[i].each do |condition| + if reflection.type + scope = scope.where(table[reflection.type].eq(owner.class.base_class.name)) + end + + conditions.each do |condition| if options[:through] && condition.is_a?(Hash) condition = { table.name => condition } end @@ -87,12 +93,16 @@ module ActiveRecord end else constraint = table[key].eq(foreign_table[foreign_key]) - join = join(foreign_table, constraint) - scope = scope.joins(join) + if reflection.type + type = chain[i + 1].klass.base_class.name + constraint = constraint.and(table[reflection.type].eq(type)) + end + + scope = scope.joins(join(foreign_table, constraint)) - unless conditions[i].empty? - scope = scope.where(sanitize(conditions[i], table)) + unless conditions.empty? + scope = scope.where(sanitize(conditions, table)) end end end diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index c32753782f..03963ab060 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -62,6 +62,7 @@ module ActiveRecord def join_to(relation) tables = @tables.dup foreign_table = parent_table + foreign_klass = parent.active_record # The chain starts with the target table, but we want to end with it here (makes # more sense in this context), so we reverse @@ -91,14 +92,17 @@ module ActiveRecord constraint = build_constraint(reflection, table, key, foreign_table, foreign_key) - unless conditions[i].empty? - constraint = constraint.and(sanitize(conditions[i], table)) + conditions = self.conditions[i].dup + conditions << { reflection.type => foreign_klass.base_class.name } if reflection.type + + unless conditions.empty? + constraint = constraint.and(sanitize(conditions, table)) end relation.from(join(table, constraint)) # The current table in this iteration becomes the foreign table in the next - foreign_table = table + foreign_table, foreign_klass = table, reflection.klass end relation diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index bcba85d7a4..b1bb1b0f7f 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -212,7 +212,7 @@ module ActiveRecord end def type - @type ||= "#{options[:as]}_type" + @type ||= options[:as] && "#{options[:as]}_type" end def primary_key_column @@ -280,9 +280,7 @@ module ActiveRecord # in the #chain. The inside arrays are simply conditions (and each condition may itself be # a hash, array, arel predicate, etc...) def conditions - conditions = [options[:conditions]].compact - conditions << { type => active_record.base_class.name } if options[:as] - [conditions] + [[options[:conditions]].compact] end alias :source_macro :macro @@ -378,7 +376,8 @@ module ActiveRecord # Holds all the meta-data about a :through association as it was specified # in the Active Record class. class ThroughReflection < AssociationReflection #:nodoc: - delegate :foreign_key, :foreign_type, :association_foreign_key, :active_record_primary_key, :to => :source_reflection + delegate :foreign_key, :foreign_type, :association_foreign_key, + :active_record_primary_key, :type, :to => :source_reflection # Gets the source of the through reflection. It checks both a singularized # and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>. diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 522ac56d82..43974fd895 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -11,6 +11,7 @@ require 'models/comment' require 'models/person' require 'models/reader' require 'models/tagging' +require 'models/tag' require 'models/invoice' require 'models/line_item' require 'models/car' @@ -1468,4 +1469,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase bulb = car.bulbs.build({ :bulb_type => :custom }, :as => :admin) assert_equal CustomBulb, bulb.class end + + def test_abstract_class_with_polymorphic_has_many + post = SubStiPost.create! :title => "fooo", :body => "baa" + tagging = Tagging.create! :taggable => post + assert_equal [tagging], post.taggings + end end diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 97d9669483..7e4ae1ea8d 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -216,7 +216,7 @@ class ReflectionTest < ActiveRecord::TestCase def test_conditions expected = [ [{ :tags => { :name => 'Blue' } }], - [{ :taggings => { :comment => 'first' } }, { "taggable_type" => "Post" }], + [{ :taggings => { :comment => 'first' } }], [{ :posts => { :title => ['misc post by bob', 'misc post by mary'] } }] ] actual = Author.reflect_on_association(:misc_post_first_blue_tags).conditions @@ -224,7 +224,7 @@ class ReflectionTest < ActiveRecord::TestCase expected = [ [{ :tags => { :name => 'Blue' } }, { :taggings => { :comment => 'first' } }, { :posts => { :title => ['misc post by bob', 'misc post by mary'] } }], - [{ "taggable_type" => "Post" }], + [], [] ] actual = Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 948dd94a1d..9c1e9a526d 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -3,7 +3,6 @@ require File.expand_path('../../../load_paths', __FILE__) lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) -require 'rubygems' require 'test/unit' require 'active_resource' require 'active_support' @@ -14,11 +13,6 @@ require 'setter_trap' require 'logger' ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log") -begin - require 'ruby-debug' -rescue LoadError -end - def setup_response matz_hash = { 'person' => { :id => 1, :name => 'Matz' } } diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index f6eb2be5ae..738a10a18f 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -9,12 +9,13 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 1.8.7' - s.author = 'David Heinemeier Hansson' - s.email = 'david@loudthinking.com' - s.homepage = 'http://www.rubyonrails.org' + s.author = 'David Heinemeier Hansson' + s.email = 'david@loudthinking.com' + s.homepage = 'http://www.rubyonrails.org' s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' + s.add_dependency('i18n', '~> 0.6') s.add_dependency('multi_json', '~> 1.0') end diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 00ea8813dd..f9c5e5e2f8 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -2,7 +2,7 @@ begin require 'i18n' require 'active_support/lazy_load_hooks' rescue LoadError => e - $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install" + $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install" raise e end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index abd585b64f..728921a069 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -315,10 +315,8 @@ module ActiveSupport tzinfo.period_for_local(time, dst) end - # TODO: Preload instead of lazy load for thread safety def self.find_tzinfo(name) - require 'active_support/tzinfo' unless defined?(::TZInfo) - ::TZInfo::TimezoneProxy.new(MAPPING[name] || name) + TZInfo::TimezoneProxy.new(MAPPING[name] || name) end class << self diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index 6e12404ad4..1ea9a9d7e1 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -1,3 +1,4 @@ +require 'time' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/string/inflections' @@ -51,13 +52,12 @@ module ActiveSupport "yaml" => Proc.new { |yaml| yaml.to_yaml } } unless defined?(FORMATTING) - # TODO: use Time.xmlschema instead of Time.parse; - # use regexp instead of Date.parse + # TODO use regexp instead of Date.parse unless defined?(PARSING) PARSING = { "symbol" => Proc.new { |symbol| symbol.to_sym }, "date" => Proc.new { |date| ::Date.parse(date) }, - "datetime" => Proc.new { |time| ::Time.parse(time).utc rescue ::DateTime.parse(time).utc }, + "datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc }, "integer" => Proc.new { |integer| integer.to_i }, "float" => Proc.new { |float| float.to_f }, "decimal" => Proc.new { |number| BigDecimal(number) }, diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb index f2261540ca..17a4131158 100644 --- a/railties/test/isolation/abstract_unit.rb +++ b/railties/test/isolation/abstract_unit.rb @@ -8,8 +8,6 @@ # Rails booted up. require 'fileutils' -# TODO: Remove rubygems when possible -require 'rubygems' require 'test/unit' # TODO: Remove setting this magic constant diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index 9d194f41a6..8a9363fb80 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -1,5 +1,4 @@ require 'abstract_unit' -require 'action_controller' module ActionController class Base |