diff options
author | David Heinemeier Hansson <david@loudthinking.com> | 2008-05-01 14:32:50 -0500 |
---|---|---|
committer | David Heinemeier Hansson <david@loudthinking.com> | 2008-05-01 14:32:50 -0500 |
commit | e2af713d1c71b4f319e5435a63011a7bc23f77c3 (patch) | |
tree | 0397b4aea75e5c77eb6b288996769fbe24f65b60 | |
parent | 9c20391bbe6ec1c56f8c8ed4aefb31a93576f76a (diff) | |
parent | 74436d2203eba186baebc1ddc82ff2202d0fc005 (diff) | |
download | rails-e2af713d1c71b4f319e5435a63011a7bc23f77c3.tar.gz rails-e2af713d1c71b4f319e5435a63011a7bc23f77c3.tar.bz2 rails-e2af713d1c71b4f319e5435a63011a7bc23f77c3.zip |
Merge branch 'master' of git@github.com:rails/rails
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 19 | ||||
-rw-r--r-- | actionpack/lib/action_view/template_finder.rb | 7 | ||||
-rw-r--r-- | actionpack/test/controller/render_test.rb | 20 | ||||
-rw-r--r-- | actionpack/test/fixtures/shared.html.erb | 1 | ||||
-rw-r--r-- | actionpack/test/template/form_helper_test.rb | 128 | ||||
-rw-r--r-- | actionpack/test/template/template_finder_test.rb | 5 | ||||
-rw-r--r-- | activerecord/CHANGELOG | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/association_preload.rb | 26 | ||||
-rwxr-xr-x | activerecord/lib/active_record/base.rb | 16 | ||||
-rwxr-xr-x | activerecord/lib/active_record/fixtures.rb | 6 | ||||
-rwxr-xr-x | activerecord/lib/active_record/validations.rb | 5 | ||||
-rw-r--r-- | activerecord/test/cases/associations/eager_test.rb | 23 | ||||
-rwxr-xr-x | activerecord/test/cases/base_test.rb | 21 | ||||
-rwxr-xr-x | activerecord/test/cases/fixtures_test.rb | 4 | ||||
-rwxr-xr-x | activerecord/test/cases/validations_test.rb | 16 | ||||
-rw-r--r-- | activerecord/test/models/subscriber.rb | 2 | ||||
-rw-r--r-- | activerecord/test/schema/schema.rb | 5 | ||||
-rw-r--r-- | activesupport/lib/active_support/dependencies.rb | 10 |
18 files changed, 228 insertions, 88 deletions
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 0e77a7e067..4459ccbce5 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1,6 +1,7 @@ require 'cgi' require 'action_view/helpers/date_helper' require 'action_view/helpers/tag_helper' +require 'action_view/helpers/form_tag_helper' module ActionView module Helpers @@ -51,7 +52,7 @@ module ActionView # # If the object name contains square brackets the id for the object will be inserted. For example: # - # <%= text_field "person[]", "name" %> + # <%= text_field "person[]", "name" %> # # ...will generate the following ERb. # @@ -91,7 +92,7 @@ module ActionView # # Even further, the form_for method allows you to more easily escape the instance variable convention. So while the stand-alone # approach would require <tt>text_field :person, :name, :object => person</tt> - # to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with + # to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with # <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>. # # Also note that form_for doesn't create an exclusive scope. It's still possible to use both the stand-alone FormHelper methods @@ -149,7 +150,7 @@ module ActionView # ... # <% end %> # - # And for namespaced routes, like admin_post_url: + # And for namespaced routes, like admin_post_url: # # <% form_for([:admin, @post]) do |f| %> # ... @@ -337,7 +338,7 @@ module ActionView # hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example # shown. # - # ==== Examples + # ==== Examples # hidden_field(:signup, :pass_confirm) # # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" /> # @@ -404,7 +405,7 @@ module ActionView # is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything, # we add a hidden value with the same name as the checkbox as a work around. # - # ==== Examples + # ==== Examples # # Let's say that @post.validated? is 1: # check_box("post", "validated") # # => <input type="checkbox" id="post_validated" name="post[validated]" value="1" /> @@ -445,7 +446,7 @@ module ActionView end class InstanceTag #:nodoc: - include Helpers::TagHelper + include Helpers::TagHelper, Helpers::FormTagHelper attr_reader :method_name, :object_name @@ -467,11 +468,13 @@ module ActionView end def to_label_tag(text = nil, options = {}) + options = options.stringify_keys name_and_id = options.dup add_default_name_and_id(name_and_id) - options["for"] = name_and_id["id"] + options.delete("index") + options["for"] ||= name_and_id["id"] content = (text.blank? ? nil : text.to_s) || method_name.humanize - content_tag("label", content, options) + label_tag(name_and_id["id"], content, options) end def to_input_field_tag(field_type, options = {}) diff --git a/actionpack/lib/action_view/template_finder.rb b/actionpack/lib/action_view/template_finder.rb index aaf34de538..83b7e27c09 100644 --- a/actionpack/lib/action_view/template_finder.rb +++ b/actionpack/lib/action_view/template_finder.rb @@ -24,7 +24,12 @@ module ActionView #:nodoc: view_paths.flatten.compact.each do |dir| next if @@processed_view_paths.has_key?(dir) @@processed_view_paths[dir] = [] - Dir.glob("#{dir}/**/*/**").each do |file| + + # + # Dir.glob("#{dir}/**/*/**") reads all the directories in view path and templates inside those directories + # Dir.glob("#{dir}/**") reads templates residing at top level of view path + # + (Dir.glob("#{dir}/**/*/**") | Dir.glob("#{dir}/**")).each do |file| unless File.directory?(file) @@processed_view_paths[dir] << file.split(dir).last.sub(/^\//, '') diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 7491f16a84..066fa6acd4 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -23,6 +23,14 @@ class TestController < ActionController::Base def render_hello_world_with_forward_slash render :template => "/test/hello_world" end + + def render_template_in_top_directory + render :template => 'shared' + end + + def render_template_in_top_directory_with_slash + render :template => '/shared' + end def render_hello_world_from_variable @person = "david" @@ -243,6 +251,18 @@ class RenderTest < Test::Unit::TestCase get :render_hello_world_with_forward_slash assert_template "test/hello_world" end + + def test_render_in_top_directory + get :render_template_in_top_directory + assert_template "shared" + assert_equal "Elastica", @response.body + end + + def test_render_in_top_directory_with_slash + get :render_template_in_top_directory_with_slash + assert_template "shared" + assert_equal "Elastica", @response.body + end def test_render_from_variable get :render_hello_world_from_variable diff --git a/actionpack/test/fixtures/shared.html.erb b/actionpack/test/fixtures/shared.html.erb new file mode 100644 index 0000000000..af262fc9f8 --- /dev/null +++ b/actionpack/test/fixtures/shared.html.erb @@ -0,0 +1 @@ +Elastica
\ No newline at end of file diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index c48d5dfd2d..b4857fcb62 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -6,11 +6,11 @@ silence_warnings do alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast) alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) - + def new_record=(boolean) @new_record = boolean end - + def new_record? @new_record end @@ -36,13 +36,13 @@ class FormHelperTest < ActionView::TestCase def setup @post = Post.new @comment = Comment.new - def @post.errors() - Class.new{ - def on(field); "can't be empty" if field == "author_name"; end - def empty?() false end + def @post.errors() + Class.new{ + def on(field); "can't be empty" if field == "author_name"; end + def empty?() false end def count() 1 end - def full_messages() [ "Author name can't be empty" ] end - }.new + def full_messages() [ "Author name can't be empty" ] end + }.new end def @post.id; 123; end def @post.id_before_type_cast; 123; end @@ -72,11 +72,19 @@ class FormHelperTest < ActionView::TestCase label("post", "title", nil, :class => 'title_label') ) end - + def test_label_with_symbols assert_dom_equal('<label for="post_title">Title</label>', label(:post, :title)) 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 + + def test_label_with_for_attribute_as_string + assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, "for" => "my_for")) + end + def test_text_field assert_dom_equal( '<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title") @@ -303,7 +311,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.submit('Create post') end - expected = + expected = "<form action='http://www.example.com' id='create-post' method='post'>" + "<label for='post_title'>Title</label>" + "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + @@ -325,7 +333,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<form action='http://www.example.com' id='create-post' method='post'>" + "<div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div>" + "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + @@ -346,7 +354,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<form action='http://www.example.com' id='create-post' method='post'>" + "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + @@ -367,7 +375,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<form action='http://www.example.com' method='post'>" + "<label for=\"post_123_title\">Title</label>" + "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" + @@ -423,7 +431,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + @@ -494,7 +502,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + @@ -511,7 +519,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + @@ -532,6 +540,18 @@ class FormHelperTest < ActionView::TestCase _erbout end + def test_fields_for_object_with_bracketed_name_and_index + _erbout = '' + fields_for("author[post]", @post, :index => 1) do |f| + _erbout.concat f.label(:title) + _erbout.concat f.text_field(:title) + end + + assert_dom_equal "<label for=\"author_post_1_title\">Title</label>" + + "<input name='author[post][1][title]' size='30' type='text' id='author_post_1_title' value='Hello World' />", + _erbout + end + def test_form_builder_does_not_have_form_for_method assert ! ActionView::Helpers::FormBuilder.instance_methods.include?('form_for') end @@ -548,7 +568,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = + expected = "<form action='http://www.example.com' id='create-post' method='post'>" + "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + @@ -571,7 +591,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = + expected = "<form action='http://www.example.com' id='create-post' method='post'>" + "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + "<textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea>" + @@ -601,7 +621,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<form action='http://www.example.com' method='post'>" + "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" + @@ -623,7 +643,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = "<form action='http://www.example.com' method='post'>" + "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" + @@ -637,39 +657,39 @@ class FormHelperTest < ActionView::TestCase end def test_default_form_builder_with_active_record_helpers - - _erbout = '' + + _erbout = '' form_for(:post, @post) do |f| _erbout.concat f.error_message_on('author_name') _erbout.concat f.error_messages - end - - expected = %(<form action='http://www.example.com' method='post'>) + - %(<div class='formError'>can't be empty</div>) + + end + + expected = %(<form action='http://www.example.com' method='post'>) + + %(<div class='formError'>can't be empty</div>) + %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) + %(</form>) - + assert_dom_equal expected, _erbout end - + def test_default_form_builder_no_instance_variable post = @post @post = nil - - _erbout = '' + + _erbout = '' form_for(:post, post) do |f| _erbout.concat f.error_message_on('author_name') _erbout.concat f.error_messages - end - - expected = %(<form action='http://www.example.com' method='post'>) + - %(<div class='formError'>can't be empty</div>) + + end + + expected = %(<form action='http://www.example.com' method='post'>) + + %(<div class='formError'>can't be empty</div>) + %(<div class="errorExplanation" id="errorExplanation"><h2>1 error prohibited this post from being saved</h2><p>There were problems with the following fields:</p><ul><li>Author name can't be empty</li></ul></div>) + %(</form>) - + assert_dom_equal expected, _erbout - + end # Perhaps this test should be moved to prototype helper tests. @@ -683,7 +703,7 @@ class FormHelperTest < ActionView::TestCase _erbout.concat f.check_box(:secret) end - expected = + expected = %(<form action="http://www.example.com" onsubmit="new Ajax.Request('http://www.example.com', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" method="post">) + "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" + @@ -693,31 +713,31 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, _erbout end - + def test_fields_for_with_labelled_builder _erbout = '' - + fields_for(:post, @post, :builder => LabelledFormBuilder) do |f| _erbout.concat f.text_field(:title) _erbout.concat f.text_area(:body) _erbout.concat f.check_box(:secret) end - - expected = + + expected = "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>Back to the hill and over it again!</textarea><br/>" + "<label for='secret'>Secret:</label> <input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + "<input name='post[secret]' type='hidden' value='0' /><br/>" - + assert_dom_equal expected, _erbout end def test_form_for_with_html_options_adds_options_to_form_tag _erbout = '' - + form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\"></form>" - + assert_dom_equal expected, _erbout end @@ -793,16 +813,16 @@ class FormHelperTest < ActionView::TestCase @comment.save _erbout = '' form_for([:admin, @post, @comment]) {} - + expected = %(<form action="#{admin_comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /></div></form>) assert_dom_equal expected, _erbout end - + def test_form_for_with_new_object_and_namespace_in_list @post.new_record = false _erbout = '' form_for([:admin, @post, @comment]) {} - + expected = %(<form action="#{admin_comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>) assert_dom_equal expected, _erbout end @@ -819,10 +839,10 @@ class FormHelperTest < ActionView::TestCase def test_remote_form_for_with_html_options_adds_options_to_form_tag self.extend ActionView::Helpers::PrototypeHelper _erbout = '' - + remote_form_for(:post, @post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end expected = "<form action=\"http://www.example.com\" class=\"some_class\" id=\"some_form\" method=\"post\" onsubmit=\"new Ajax.Request('http://www.example.com', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\"></form>" - + assert_dom_equal expected, _erbout end @@ -837,21 +857,21 @@ class FormHelperTest < ActionView::TestCase "/posts/#{post.id}/comments/#{comment.id}" end alias_method :post_comment_path, :comment_path - + def admin_comments_path(post) "/admin/posts/#{post.id}/comments" end alias_method :admin_post_comments_path, :admin_comments_path - + def admin_comment_path(post, comment) "/admin/posts/#{post.id}/comments/#{comment.id}" end alias_method :admin_post_comment_path, :admin_comment_path - + def posts_path "/posts" - end - + end + def post_path(post) "/posts/#{post.id}" end diff --git a/actionpack/test/template/template_finder_test.rb b/actionpack/test/template/template_finder_test.rb index a162640de3..3d6baff5fb 100644 --- a/actionpack/test/template/template_finder_test.rb +++ b/actionpack/test/template/template_finder_test.rb @@ -21,13 +21,14 @@ class TemplateFinderTest < Test::Unit::TestCase assert_equal ["builder", "erb", "rhtml", "rjs", "rxml", "mab"].sort, ActionView::TemplateFinder.file_extension_cache[LOAD_PATH_ROOT].values.flatten.uniq.sort - assert_equal Dir.glob("#{LOAD_PATH_ROOT}/**/*/*.{erb,rjs,rhtml,builder,rxml,mab}").size, + assert_equal (Dir.glob("#{LOAD_PATH_ROOT}/**/*/*.{erb,rjs,rhtml,builder,rxml,mab}") | + Dir.glob("#{LOAD_PATH_ROOT}/**.{erb,rjs,rhtml,builder,rxml,mab}")).size, ActionView::TemplateFinder.file_extension_cache[LOAD_PATH_ROOT].keys.size end def test_should_cache_dir_content_properly assert ActionView::TemplateFinder.processed_view_paths[LOAD_PATH_ROOT] - assert_equal Dir.glob("#{LOAD_PATH_ROOT}/**/*/**").find_all {|f| !File.directory?(f) }.size, + assert_equal (Dir.glob("#{LOAD_PATH_ROOT}/**/*/**") | Dir.glob("#{LOAD_PATH_ROOT}/**")).find_all {|f| !File.directory?(f) }.size, ActionView::TemplateFinder.processed_view_paths[LOAD_PATH_ROOT].size end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 154b282130..f72d8aceaa 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Added block-setting of attributes for Base.create like Base.new already has (Adam Meehan) [#39] + * Fixed that pessimistic locking you reference the quoted table name (Josh Susser) [#67] * Fixed that change_column should be able to use :null => true on a field that formerly had false [Nate Wiger] [#26] diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 55c0cdd808..3e7c787dee 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -59,14 +59,14 @@ module ActiveRecord def set_association_collection_records(id_to_record_map, reflection_name, associated_records, key) associated_records.each do |associated_record| - mapped_records = id_to_record_map[associated_record[key].to_i] + mapped_records = id_to_record_map[associated_record[key].to_s] add_preloaded_records_to_collection(mapped_records, reflection_name, associated_record) end end def set_association_single_records(id_to_record_map, reflection_name, associated_records, key) associated_records.each do |associated_record| - mapped_records = id_to_record_map[associated_record[key].to_i] + mapped_records = id_to_record_map[associated_record[key].to_s] mapped_records.each do |mapped_record| mapped_record.send("set_#{reflection_name}_target", associated_record) end @@ -78,7 +78,7 @@ module ActiveRecord ids = [] records.each do |record| ids << record.id - mapped_records = (id_to_record_map[record.id] ||= []) + mapped_records = (id_to_record_map[record.id.to_s] ||= []) mapped_records << record end ids.uniq! @@ -115,7 +115,7 @@ module ActiveRecord source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source) through_records.each do |through_record| - add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_i], + add_preloaded_record_to_collection(id_to_record_map[through_record[through_primary_key].to_s], reflection.name, through_record.send(source)) end end @@ -140,7 +140,7 @@ module ActiveRecord source = reflection.source_reflection.name through_records.first.class.preload_associations(through_records, source) through_records.each do |through_record| - add_preloaded_records_to_collection(id_to_record_map[through_record[through_primary_key].to_i], + add_preloaded_records_to_collection(id_to_record_map[through_record[through_primary_key].to_s], reflection.name, through_record.send(source)) end end @@ -195,18 +195,22 @@ module ActiveRecord records.each do |record| if klass = record.send(polymorph_type) klass_id = record.send(primary_key_name) - - id_map = klasses_and_ids[klass] ||= {} - id_list_for_klass_id = (id_map[klass_id] ||= []) - id_list_for_klass_id << record + if klass_id + id_map = klasses_and_ids[klass] ||= {} + id_list_for_klass_id = (id_map[klass_id.to_s] ||= []) + id_list_for_klass_id << record + end end end klasses_and_ids = klasses_and_ids.to_a else id_map = {} records.each do |record| - mapped_records = (id_map[record.send(primary_key_name)] ||= []) - mapped_records << record + key = record.send(primary_key_name) + if key + mapped_records = (id_map[key.to_s] ||= []) + mapped_records << record + end end klasses_and_ids = [[reflection.klass.name, id_map]] end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 63306644fb..4f4ba83a47 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -603,13 +603,25 @@ module ActiveRecord #:nodoc: # ==== Examples # # Create a single new object # User.create(:first_name => 'Jamie') + # # # Create an Array of new objects # User.create([{:first_name => 'Jamie'}, {:first_name => 'Jeremy'}]) - def create(attributes = nil) + # + # # Create a single object and pass it into a block to set other attributes. + # User.create(:first_name => 'Jamie') do |u| + # u.is_admin = false + # end + # + # # Creating an Array of new objects using a block, where the block is executed for each object: + # User.create([{:first_name => 'Jamie'}, {:first_name => 'Jeremy'}]) do |u| + # u.is_admin = false + # end + def create(attributes = nil, &block) if attributes.is_a?(Array) - attributes.collect { |attr| create(attr) } + attributes.collect { |attr| create(attr, &block) } else object = new(attributes) + yield(object) if block_given? object.save object end diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index f5b2e73da9..7d5fd35dae 100755 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -469,8 +469,8 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) fixtures.size > 1 ? fixtures : fixtures.first end - def self.cache_fixtures(connection, fixtures) - cache_for_connection(connection).update(fixtures.index_by { |f| f.table_name }) + def self.cache_fixtures(connection, fixtures_map) + cache_for_connection(connection).update(fixtures_map) end def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true) @@ -526,7 +526,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) end end - cache_fixtures(connection, fixtures) + cache_fixtures(connection, fixtures_map) end end end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 1d12ea8ad7..5ca51c014c 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -873,11 +873,12 @@ module ActiveRecord # Creates an object just like Base.create but calls save! instead of save # so an exception is raised if the record is invalid. - def create!(attributes = nil) + def create!(attributes = nil, &block) if attributes.is_a?(Array) - attributes.collect { |attr| create!(attr) } + attributes.collect { |attr| create!(attr, &block) } else object = new(attributes) + yield(object) if block_given? object.save! object end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 1a3017a22c..546ed80894 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -11,11 +11,14 @@ require 'models/owner' require 'models/pet' require 'models/reference' require 'models/job' +require 'models/subscriber' +require 'models/subscription' +require 'models/book' class EagerAssociationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors, :categories, :categories_posts, :companies, :accounts, :tags, :taggings, :people, :readers, - :owners, :pets, :author_favorites, :jobs, :references + :owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books def test_loading_with_one_association posts = Post.find(:all, :include => :comments) @@ -220,6 +223,24 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_no_queries{ assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) } end + def test_eager_load_has_many_with_string_keys + subscriptions = subscriptions(:webster_awdr, :webster_rfr) + subscriber =Subscriber.find(subscribers(:second).id, :include => :subscriptions) + assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id) + end + + def test_eager_load_has_many_through_with_string_keys + books = books(:awdr, :rfr) + subscriber = Subscriber.find(subscribers(:second).id, :include => :books) + assert_equal books, subscriber.books.sort_by(&:id) + end + + def test_eager_load_belongs_to_with_string_keys + subscriber = subscribers(:second) + subscription = Subscription.find(subscriptions(:webster_awdr).id, :include => :subscriber) + assert_equal subscriber, subscription.subscriber + end + def test_eager_association_loading_with_explicit_join posts = Post.find(:all, :include => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id') assert_equal 1, posts.length diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 93719c710a..e07ec50c46 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -251,6 +251,27 @@ class BasicsTest < ActiveRecord::TestCase topic = Topic.create("title" => "New Topic") topicReloaded = Topic.find(topic.id) assert_equal(topic, topicReloaded) + end + + def test_create_through_factory_with_block + topic = Topic.create("title" => "New Topic") do |t| + t.author_name = "David" + end + topicReloaded = Topic.find(topic.id) + assert_equal("New Topic", topic.title) + assert_equal("David", topic.author_name) + end + + def test_create_many_through_factory_with_block + topics = Topic.create([ { "title" => "first" }, { "title" => "second" }]) do |t| + t.author_name = "David" + end + assert_equal 2, topics.size + topic1, topic2 = Topic.find(topics[0].id), Topic.find(topics[1].id) + assert_equal "first", topic1.title + assert_equal "David", topic1.author_name + assert_equal "second", topic2.title + assert_equal "David", topic2.author_name end def test_update diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 182f4f0f0b..2787b9a39d 100755 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -96,6 +96,10 @@ class FixturesTest < ActiveRecord::TestCase second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'") assert_nil(second_row["author_email_address"]) + + # This checks for a caching problem which causes a bug in the fixtures + # class-level configuration helper. + assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create" ensure # Restore prefix/suffix to its previous values ActiveRecord::Base.table_name_prefix = old_prefix diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index ca36ad3581..e3ca8660ac 100755 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -133,6 +133,22 @@ class ValidationsTest < ActiveRecord::TestCase Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }]) end end + + def test_exception_on_create_bang_with_block + assert_raises(ActiveRecord::RecordInvalid) do + Reply.create!({ "title" => "OK" }) do |r| + r.content = nil + end + end + end + + def test_exception_on_create_bang_many_with_block + assert_raises(ActiveRecord::RecordInvalid) do + Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r| + r.content = nil + end + end + end def test_scoped_create_without_attributes Reply.with_scope(:create => {}) do diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb index 51335a8f20..5b78014e6f 100644 --- a/activerecord/test/models/subscriber.rb +++ b/activerecord/test/models/subscriber.rb @@ -1,5 +1,7 @@ class Subscriber < ActiveRecord::Base set_primary_key 'nick' + has_many :subscriptions + has_many :books, :through => :subscriptions end class SpecialSubscriber < Subscriber diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index e22b78749c..818237f076 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -349,6 +349,11 @@ ActiveRecord::Schema.define do end add_index :subscribers, :nick, :unique => true + create_table :subscriptions, :force => true do |t| + t.string :subscriber_id + t.integer :book_id + end + create_table :tasks, :force => true do |t| t.datetime :starting t.datetime :ending diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 64b2248e28..70b34c90e8 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -423,10 +423,12 @@ module Dependencies #:nodoc: protected def log_call(*args) - arg_str = args.collect(&:inspect) * ', ' - /in `([a-z_\?\!]+)'/ =~ caller(1).first - selector = $1 || '<unknown>' - log "called #{selector}(#{arg_str})" + if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity + arg_str = args.collect(&:inspect) * ', ' + /in `([a-z_\?\!]+)'/ =~ caller(1).first + selector = $1 || '<unknown>' + log "called #{selector}(#{arg_str})" + end end def log(msg) |