From b0429f5e66b4e33f9d0ba7b066a3dbfdab68534c Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 11 Jul 2009 23:39:45 -0400 Subject: Rename method to a more obvious name --- actionpack/lib/action_controller/abstract/layouts.rb | 4 ++-- actionpack/lib/action_controller/base/compatibility.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 2ac4e6068a..d8276cfa83 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -99,7 +99,7 @@ module AbstractController # the lookup to. By default, layout lookup is limited to the # formats specified for the current request. def _layout_for_name(name, details = {:formats => formats}) - name && _find_by_parts(name, details) + name && _find_layout(name, details) end # Take in the name and details and find a Template. @@ -111,7 +111,7 @@ module AbstractController # # ==== Returns # Template:: A template object matching the name and details - def _find_by_parts(name, details) + def _find_layout(name, details) # TODO: Make prefix actually part of details in ViewPath#find_by_parts prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts" view_paths.find_by_parts(name, details, prefix) diff --git a/actionpack/lib/action_controller/base/compatibility.rb b/actionpack/lib/action_controller/base/compatibility.rb index cd4b72b1c6..13813ffd17 100644 --- a/actionpack/lib/action_controller/base/compatibility.rb +++ b/actionpack/lib/action_controller/base/compatibility.rb @@ -114,7 +114,7 @@ module ActionController super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _find_by_parts(name, details) + def _find_layout(name, details) details[:prefix] = nil if name =~ /\blayouts/ super end @@ -123,7 +123,7 @@ module ActionController def _default_layout(details, require_layout = false) super rescue ActionView::MissingTemplate - _find_by_parts(_layout({}), {}) + _find_layout(_layout({}), {}) nil end -- cgit v1.2.3 From 28508d444e36dc8b5819f011f0a2398f44d8d3e3 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 15 Jul 2009 14:15:52 -0700 Subject: Centralize commonly used parts of AS in ActionController into action_controller.rb --- actionpack/lib/action_controller.rb | 16 +++++++++++++--- actionpack/lib/action_controller/abstract/base.rb | 2 -- actionpack/lib/action_controller/abstract/callbacks.rb | 2 ++ actionpack/lib/action_controller/abstract/logger.rb | 1 - actionpack/lib/action_controller/base/helpers.rb | 2 -- actionpack/lib/action_controller/base/http.rb | 1 - 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e822a11d14..32572c93c0 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -52,11 +52,21 @@ module ActionController autoload :SessionOverflowError, 'action_controller/base/exceptions' autoload :UnknownHttpMethod, 'action_controller/base/exceptions' - require 'action_controller/routing' + autoload :Routing, 'action_controller/routing' end autoload :HTML, 'action_controller/vendor/html-scanner' autoload :AbstractController, 'action_controller/abstract' -require 'action_dispatch' -require 'action_view' +autoload :Rack, 'action_dispatch' +autoload :ActionDispatch, 'action_dispatch' +autoload :ActionView, 'action_view' + +# Common ActiveSupport usage in ActionController +require "active_support/concern" +require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/load_error' +require 'active_support/core_ext/module/attr_internal' +require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/name_error' +require 'active_support/inflector' \ No newline at end of file diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index a19a236ef7..ca00e66349 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/module/attr_internal' - module AbstractController class Base diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index 0d5161c80e..ea4b59466e 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -1,3 +1,5 @@ +require "active_support/new_callbacks" + module AbstractController module Callbacks extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb index b960e152e3..fd33bd2ddd 100644 --- a/actionpack/lib/action_controller/abstract/logger.rb +++ b/actionpack/lib/action_controller/abstract/logger.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/logger' module AbstractController diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb index 2fa5ea6519..7c52779064 100644 --- a/actionpack/lib/action_controller/base/helpers.rb +++ b/actionpack/lib/action_controller/base/helpers.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/load_error' -require 'active_support/core_ext/name_error' require 'active_support/dependencies' module ActionController diff --git a/actionpack/lib/action_controller/base/http.rb b/actionpack/lib/action_controller/base/http.rb index ec78bc15a8..3efd1b656f 100644 --- a/actionpack/lib/action_controller/base/http.rb +++ b/actionpack/lib/action_controller/base/http.rb @@ -1,5 +1,4 @@ require 'action_controller/abstract' -require 'active_support/core_ext/module/delegation' module ActionController # ActionController::Http provides a way to get a valid Rack application from a controller. -- cgit v1.2.3 From 1273ffa1861d15eb7518b7bfab6003bb3d9ca347 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 15 Jul 2009 14:16:12 -0700 Subject: Fix AbstractController::Layouts to work when included directly on a controller --- actionpack/lib/action_controller/abstract/layouts.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index d8276cfa83..f021dd8b62 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -6,6 +6,7 @@ module AbstractController included do extlib_inheritable_accessor(:_layout_conditions) { Hash.new } + _write_layout_method end module ClassMethods -- cgit v1.2.3 From 1a2946a6d9b1dbcf3a4c77654693d73f9b11bde1 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 15 Jul 2009 14:16:30 -0700 Subject: Add some missing dependencies --- actionpack/lib/action_controller/testing/process.rb | 1 + actionpack/lib/action_view.rb | 1 + activemodel/lib/active_model/validations.rb | 2 ++ 3 files changed, 4 insertions(+) diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index 7634290ea1..e7c64d0942 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -1,3 +1,4 @@ +require 'action_dispatch' require 'rack/session/abstract/id' require 'active_support/core_ext/object/conversions' diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 27a06db5bb..2a17faf99a 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -36,6 +36,7 @@ module ActionView autoload :Base, 'action_view/base' autoload :Helpers, 'action_view/helpers' autoload :InlineTemplate, 'action_view/template/inline' + autoload :MissingTemplate, 'action_view/base' autoload :Partials, 'action_view/render/partials' autoload :Resolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 5223cea135..54a869396d 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -1,5 +1,7 @@ require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/keys' +require 'active_support/concern' +require 'active_support/callbacks' module ActiveModel module Validations -- cgit v1.2.3 From 1c11437a32a973fa9b521c32caa7256f9772acd7 Mon Sep 17 00:00:00 2001 From: Szymon Nowak Date: Wed, 15 Jul 2009 22:22:25 +0200 Subject: Add primary_key option to belongs_to association [#765 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 4 + activerecord/lib/active_record/associations.rb | 9 +- .../associations/belongs_to_association.rb | 26 +++++- .../belongs_to_polymorphic_association.rb | 6 +- .../lib/active_record/autosave_association.rb | 3 +- .../associations/belongs_to_associations_test.rb | 98 ++++++++++++++++++++++ activerecord/test/cases/base_test.rb | 2 +- activerecord/test/cases/reflection_test.rb | 10 +-- activerecord/test/models/author.rb | 2 + activerecord/test/models/company.rb | 1 + activerecord/test/models/essay.rb | 3 + activerecord/test/models/reply.rb | 3 +- activerecord/test/models/topic.rb | 1 + activerecord/test/schema/schema.rb | 7 ++ 14 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 activerecord/test/models/essay.rb diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 411b640c9e..659de99873 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,9 @@ *Edge* +* Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha] + # employees.company_name references companies.name + Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name' + * Implement #many? for NamedScope and AssociationCollection using #size. #1500 [Chris Kampmeier] * Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH] diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 419967b833..934beb7d39 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -963,6 +963,8 @@ module ActiveRecord # of the association with an "_id" suffix. So a class that defines a belongs_to :person association will use # "person_id" as the default :foreign_key. Similarly, belongs_to :favorite_person, :class_name => "Person" # will use a foreign key of "favorite_person_id". + # [:primary_key] + # Specify the method that returns the primary key of associated object used for the association. By default this is id. # [:dependent] # If set to :destroy, the associated object is destroyed when this object is. If set to # :delete, the associated object is deleted *without* calling its destroy method. This option should not be specified when @@ -993,6 +995,7 @@ module ActiveRecord # # Option examples: # belongs_to :firm, :foreign_key => "client_of" + # belongs_to :person, :primary_key => "name", :foreign_key => "person_name" # belongs_to :author, :class_name => "Person", :foreign_key => "author_id" # belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id", # :conditions => 'discounts > #{payments_count}' @@ -1328,14 +1331,14 @@ module ActiveRecord method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? + association.class.increment_counter(cache_column, association.id) unless association.nil? end after_create(method_name) method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil? + association.class.decrement_counter(cache_column, association.id) unless association.nil? end before_destroy(method_name) @@ -1527,7 +1530,7 @@ module ActiveRecord mattr_accessor :valid_keys_for_belongs_to_association @@valid_keys_for_belongs_to_association = [ - :class_name, :foreign_key, :foreign_type, :remote, :select, :conditions, + :class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions, :include, :dependent, :counter_cache, :extend, :polymorphic, :readonly, :validate, :touch, :inverse_of ] diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index c88575048a..628033c87a 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -14,7 +14,7 @@ module ActiveRecord if record.nil? if counter_cache_name && !@owner.new_record? - @reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name] + @reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name] end @target = @owner[@reflection.primary_key_name] = nil @@ -27,7 +27,7 @@ module ActiveRecord end @target = (AssociationProxy === record ? record.target : record) - @owner[@reflection.primary_key_name] = record.id unless record.new_record? + @owner[@reflection.primary_key_name] = record_id(record) unless record.new_record? @updated = true end @@ -43,13 +43,18 @@ module ActiveRecord private def find_target - the_target = @reflection.klass.find( + find_method = if @reflection.options[:primary_key] + "find_by_#{@reflection.options[:primary_key]}" + else + "find" + end + the_target = @reflection.klass.send(find_method, @owner[@reflection.primary_key_name], :select => @reflection.options[:select], :conditions => conditions, :include => @reflection.options[:include], :readonly => @reflection.options[:readonly] - ) + ) if @owner[@reflection.primary_key_name] set_inverse_instance(the_target, @owner) the_target end @@ -63,6 +68,19 @@ module ActiveRecord def we_can_set_the_inverse_on_this?(record) @reflection.has_inverse? && @reflection.inverse_of.macro == :has_one end + + def record_id(record) + record.send(@reflection.options[:primary_key] || :id) + end + + def previous_record_id + @previous_record_id ||= if @reflection.options[:primary_key] + previous_record = @owner.send(@reflection.name) + previous_record.nil? ? nil : previous_record.id + else + @owner[@reflection.primary_key_name] + end + end end end end diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index d8146daa54..67e18d692d 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -7,7 +7,7 @@ module ActiveRecord else @target = (AssociationProxy === record ? record.target : record) - @owner[@reflection.primary_key_name] = record.id + @owner[@reflection.primary_key_name] = record_id(record) @owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s @updated = true @@ -41,6 +41,10 @@ module ActiveRecord !@owner[@reflection.primary_key_name].nil? end + def record_id(record) + record.send(@reflection.options[:primary_key] || :id) + end + def association_class @owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index a540570f42..c1bc8423a9 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -339,7 +339,8 @@ module ActiveRecord association.save(!autosave) if association.new_record? || autosave if association.updated? - self[reflection.primary_key_name] = association.id + association_id = association.send(reflection.options[:primary_key] || :id) + self[reflection.primary_key_name] = association_id # TODO: Removing this code doesn't seem to matter… if reflection.options[:polymorphic] self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 13a78a1890..ab6f752243 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -14,6 +14,7 @@ require 'models/tagging' require 'models/comment' require 'models/sponsor' require 'models/member' +require 'models/essay' class BelongsToAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :developers, :projects, :topics, @@ -25,6 +26,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert !Client.find(3).firm.nil?, "Microsoft should have a firm" end + def test_belongs_to_with_primary_key + client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name) + assert_equal companies(:first_firm).name, client.firm_with_primary_key.name + end + def test_proxy_assignment account = Account.find(1) assert_nothing_raised { account.firm = account.firm } @@ -47,6 +53,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.id, citibank.firm_id end + def test_natural_assignment_with_primary_key + apple = Firm.create("name" => "Apple") + citibank = Client.create("name" => "Primary key client") + citibank.firm_with_primary_key = apple + assert_equal apple.name, citibank.firm_name + end + def test_no_unexpected_aliasing first_firm = companies(:first_firm) another_firm = companies(:another_firm) @@ -69,6 +82,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple, citibank.firm end + def test_creating_the_belonging_object_with_primary_key + client = Client.create(:name => "Primary key client") + apple = client.create_firm_with_primary_key("name" => "Apple") + assert_equal apple, client.firm_with_primary_key + client.save + client.reload + assert_equal apple, client.firm_with_primary_key + end + def test_building_the_belonging_object citibank = Account.create("credit_limit" => 10) apple = citibank.build_firm("name" => "Apple") @@ -76,6 +98,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal apple.id, citibank.firm_id end + def test_building_the_belonging_object_with_primary_key + client = Client.create(:name => "Primary key client") + apple = client.build_firm_with_primary_key("name" => "Apple") + client.save + assert_equal apple.name, client.firm_name + end + def test_natural_assignment_to_nil client = Client.find(3) client.firm = nil @@ -84,6 +113,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_nil client.client_of end + def test_natural_assignment_to_nil_with_primary_key + client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name) + client.firm_with_primary_key = nil + client.save + assert_nil client.firm_with_primary_key(true) + assert_nil client.client_of + end + def test_with_different_class_name assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm" @@ -110,6 +147,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted" end + def test_belongs_to_with_primary_key_counter + debate = Topic.create("title" => "debate") + assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet" + + trash = debate.replies_with_primary_key.create("title" => "blah!", "content" => "world around!") + assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created" + + trash.destroy + assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted" + end + def test_belongs_to_counter_with_assigning_nil p = Post.find(1) c = Comment.find(1) @@ -122,6 +170,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 1, Post.find(p.id).comments.size end + def test_belongs_to_with_primary_key_counter_with_assigning_nil + debate = Topic.create("title" => "debate") + reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate") + + assert_equal debate.title, reply.parent_title + assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count") + + reply.topic_with_primary_key = nil + + assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count") + end + def test_belongs_to_counter_with_reassigning t1 = Topic.create("title" => "t1") t2 = Topic.create("title" => "t2") @@ -219,6 +279,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal firm, final_cut.firm(true) end + def test_assignment_before_child_saved_with_primary_key + final_cut = Client.new("name" => "Final Cut") + firm = Firm.find(1) + final_cut.firm_with_primary_key = firm + assert final_cut.new_record? + assert final_cut.save + assert !final_cut.new_record? + assert !firm.new_record? + assert_equal firm, final_cut.firm_with_primary_key + assert_equal firm, final_cut.firm_with_primary_key(true) + end + def test_new_record_with_foreign_key_but_no_object c = Client.new("firm_id" => 1) assert_equal Firm.find(:first), c.firm_with_basic_id @@ -304,6 +376,20 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase sponsor.sponsorable = member assert_equal "Member", sponsor.sponsorable_type end + + def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating + # should update when assigning a saved record + essay = Essay.new + writer = Author.create(:name => "David") + essay.writer = writer + assert_equal "Author", essay.writer_type + + # should update when assigning a new record + essay = Essay.new + writer = Author.new + essay.writer = writer + assert_equal "Author", essay.writer_type + end def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records sponsor = Sponsor.new @@ -317,6 +403,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal nil, sponsor.sponsorable_id end + def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records + essay = Essay.new + saved_writer = Author.create(:name => "David") + new_writer = Author.new + + essay.writer = saved_writer + assert_equal saved_writer.name, essay.writer_id + + essay.writer = new_writer + assert_equal nil, essay.writer_id + end + def test_belongs_to_proxy_should_not_respond_to_private_methods assert_raise(NoMethodError) { companies(:first_firm).private_method } assert_raise(NoMethodError) { companies(:second_client).firm.private_method } diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index f9ac37cc87..e47f898485 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -2026,7 +2026,7 @@ class BasicsTest < ActiveRecord::TestCase def test_inspect_instance topic = topics(:first) - assert_equal %(#), topic.inspect + assert_equal %(#), topic.inspect end def test_inspect_new_instance diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 194d5e9dff..4083b990d9 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -27,25 +27,25 @@ class ReflectionTest < ActiveRecord::TestCase def test_read_attribute_names assert_equal( - %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id type ).sort, + %w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort, @first.attribute_names ) end def test_columns - assert_equal 12, Topic.columns.length + assert_equal 13, Topic.columns.length end def test_columns_are_returned_in_the_order_they_were_declared column_names = Topic.columns.map { |column| column.name } - assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id type), column_names + assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names end def test_content_columns content_columns = Topic.content_columns content_column_names = content_columns.map {|column| column.name} - assert_equal 8, content_columns.length - assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved).sort, content_column_names.sort + assert_equal 9, content_columns.length + assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort end def test_column_string_type_and_limit diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 0d9ee36b20..b844c7cce0 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -87,6 +87,8 @@ class Author < ActiveRecord::Base has_many :tags, :through => :posts # through has_many :through has_many :post_categories, :through => :posts, :source => :categories + has_one :essay, :primary_key => :name, :as => :writer + belongs_to :author_address, :dependent => :destroy belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress" diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index 840527ddeb..22168468a6 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -88,6 +88,7 @@ class Client < Company belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id" belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of" belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1] + belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name" belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true # Record destruction so we can test whether firm.clients.clear has diff --git a/activerecord/test/models/essay.rb b/activerecord/test/models/essay.rb new file mode 100644 index 0000000000..6c28f5e49b --- /dev/null +++ b/activerecord/test/models/essay.rb @@ -0,0 +1,3 @@ +class Essay < ActiveRecord::Base + belongs_to :writer, :primary_key => :name, :polymorphic => true +end diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb index 616c07687c..f5906dedd1 100644 --- a/activerecord/test/models/reply.rb +++ b/activerecord/test/models/reply.rb @@ -4,12 +4,13 @@ class Reply < Topic named_scope :base belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true + belongs_to :topic_with_primary_key, :class_name => "Topic", :primary_key => "title", :foreign_key => "parent_title", :counter_cache => "replies_count" has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id" validate :errors_on_empty_content validate_on_create :title_is_wrong_create - attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read + attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read, :parent_title validate :check_empty_title validate_on_create :check_content_mismatch diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 51012d22ed..201d96dcd7 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -39,6 +39,7 @@ class Topic < ActiveRecord::Base named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }} has_many :replies, :dependent => :destroy, :foreign_key => "parent_id" + has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title" serialize :content before_create :default_written_on diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index d2d6d1f4b3..2b7d3856b7 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -161,6 +161,12 @@ ActiveRecord::Schema.define do t.integer :course_id, :null => false end + create_table :essays, :force => true do |t| + t.string :name + t.string :writer_id + t.string :writer_type + end + create_table :events, :force => true do |t| t.string :title, :limit => 5 end @@ -421,6 +427,7 @@ ActiveRecord::Schema.define do t.boolean :approved, :default => true t.integer :replies_count, :default => 0 t.integer :parent_id + t.string :parent_title t.string :type end -- cgit v1.2.3 From 17d5cc12b9f8d0d78a081d231e7e0c5ec9df1104 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 8 Jul 2009 18:17:42 +0200 Subject: * don't include String#% for Ruby 1.9 * raise a KeyError exception for missing named interpolation args (like Ruby 1.9 does) * raise an ArgumentError when mixing named and unnamed placeholders (like Ruby 1.9 does) * improve docs and comply a bit more w/ Rails names/conventions [#2870 state:committed] Signed-off-by: Jeremy Kemper --- .../core_ext/string/interpolation.rb | 152 +++++++++++---------- activesupport/test/core_ext/string_ext_test.rb | 13 +- 2 files changed, 87 insertions(+), 78 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb index b21977ecc1..d459c03d39 100644 --- a/activesupport/lib/active_support/core_ext/string/interpolation.rb +++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb @@ -1,87 +1,93 @@ -if RUBY_VERSION < '1.9' - =begin - string.rb - Extension for String. - + heavily based on Masao Mutoh's gettext String interpolation extension + http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb Copyright (C) 2005-2009 Masao Mutoh - - You may redistribute it and/or modify it under the same - license terms as Ruby. + You may redistribute it and/or modify it under the same license terms as Ruby. =end -# This feature is included in Ruby 1.9 or later but not occur TypeError. -# -# String#% method which accepts named arguments. Particularly useful if the -# string is to be used by a translator because named arguments mean more -# than %s/%d style. -class String +if RUBY_VERSION < '1.9' - unless instance_methods.find {|m| m.to_s == 'bytesize'} - # For older ruby (such as ruby-1.8.5) - alias :bytesize :size - end + # KeyError is raised by String#% when the string contains a named placeholder + # that is not contained in the given arguments hash. Ruby 1.9 includes and + # raises this exception natively. We define it to mimic Ruby 1.9's behaviour + # in Ruby 1.8.x - alias :_old_format_m :% # :nodoc: + class KeyError < IndexError + def initialize(message = nil) + super(message || "key not found") + end + end unless defined?(KeyError) - PERCENT_MATCH_RE = Regexp.union( + # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError. + # + # String#% method which accept "named argument". The translator can know + # the meaning of the msgids using "named argument" instead of %s/%d style. + + class String + # For older ruby versions, such as ruby-1.8.5 + alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'} + alias :interpolate_without_ruby_19_syntax :% # :nodoc: + + INTERPOLATION_PATTERN = Regexp.union( /%%/, - /%\{(\w+)\}/, - /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ - ) + /%\{(\w+)\}/, # matches placeholders like "%{foo}" + /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%.d" + ) - # call-seq: - # %(arg) - # %(hash) - # - # Format - Uses str as a format specification, and returns the result of applying it to arg. - # If the format specification contains more than one substitution, then arg must be - # an Array containing the values to be substituted. See Kernel::sprintf for details of the - # format string. This is the default behavior of the String class. - # * arg: an Array or other class except Hash. - # * Returns: formatted String - # Example: - # "%s, %s" % ["Masao", "Mutoh"] - # - # Also you can use a Hash as the "named argument". This is recommended way so translators - # can understand the meanings of the msgids easily. - # * hash: {:key1 => value1, :key2 => value2, ... } - # * Returns: formatted String - # Example: - # For strings. - # "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"} - # - # With field type to specify format such as d(decimal), f(float),... - # "%d, %.1f" % {:age => 10, :weight => 43.4} - def %(args) - if args.kind_of?(Hash) - ret = dup - ret.gsub!(PERCENT_MATCH_RE) {|match| - if match == '%%' - '%' - elsif $1 - key = $1.to_sym - args.has_key?(key) ? args[key] : match - elsif $2 - key = $2.to_sym - args.has_key?(key) ? sprintf("%#{$3}", args[key]) : match - end - } - ret - else - ret = gsub(/%([{<])/, '%%\1') - begin - ret._old_format_m(args) - rescue ArgumentError => e - if $DEBUG - $stderr.puts " The string:#{ret}" - $stderr.puts " args:#{args.inspect}" - puts e.backtrace - else - raise ArgumentError, e.message + # % uses self (i.e. the String) as a format specification and returns the + # result of applying it to the given arguments. In other words it interpolates + # the given arguments to the string according to the formats the string + # defines. + # + # There are three ways to use it: + # + # * Using a single argument or Array of arguments. + # + # This is the default behaviour of the String class. See Kernel#sprintf for + # more details about the format string. + # + # Example: + # + # "%d %s" % [1, "message"] + # # => "1 message" + # + # * Using a Hash as an argument and unformatted, named placeholders. + # + # When you pass a Hash as an argument and specify placeholders with %{foo} + # it will interpret the hash values as named arguments. + # + # Example: + # + # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"} + # # => "Masao Mutoh" + # + # * Using a Hash as an argument and formatted, named placeholders. + # + # When you pass a Hash as an argument and specify placeholders with %d + # it will interpret the hash values as named arguments and format the value + # according to the formatting instruction appended to the closing >. + # + # Example: + # + # "%d, %.1f" % { :integer => 10, :float => 43.4 } + # # => "10, 43.3" + def %(args) + if args.kind_of?(Hash) + dup.gsub(INTERPOLATION_PATTERN) do |match| + if match == '%%' + '%' + else + key = ($1 || $2).to_sym + raise KeyError unless args.has_key?(key) + $3 ? sprintf("%#{$3}", args[key]) : args[key] + end end + elsif self =~ INTERPOLATION_PATTERN + raise ArgumentError.new('one hash required') + else + result = gsub(/%([{<])/, '%%\1') + result.send :'interpolate_without_ruby_19_syntax', args end end end -end - end \ No newline at end of file diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index f77ad5236e..a23d3f6fef 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -311,8 +311,8 @@ class TestGetTextString < Test::Unit::TestCase end def test_sprintf_lack_argument - assert_equal("%{num}, test", "%{num}, %{record}" % {:record => "test"}) - assert_equal("%{record}", "%{record}" % {:num => 1}) + assert_raises(KeyError) { "%{num}, %{record}" % {:record => "test"} } + assert_raises(KeyError) { "%{record}" % {:num => 1} } end def test_no_placeholder @@ -336,9 +336,12 @@ class TestGetTextString < Test::Unit::TestCase assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0]) end - def test_sprintf_mix + def test_sprintf_mix_unformatted_and_formatted_named_placeholders assert_equal("foo 1.000000", "%{name} %f" % {:name => "foo", :num => 1.0}) - assert_equal("%{name} 1.000000", "%{name} %f" % [1.0]) - assert_equal("%{name} 1.000000", "%{name} %f" % [1.0, 2.0]) + end + + def test_string_interpolation_raises_an_argument_error_when_mixing_named_and_unnamed_placeholders + assert_raises(ArgumentError) { "%{name} %f" % [1.0] } + assert_raises(ArgumentError) { "%{name} %f" % [1.0, 2.0] } end end -- cgit v1.2.3 From bb530923bcd5c643f9bfca8e36cd3fa36365032d Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 19 Jul 2009 21:53:02 +0900 Subject: Simplify required "ActionView compliant" API --- actionpack/lib/action_controller/abstract/helpers.rb | 6 ------ actionpack/lib/action_controller/abstract/renderer.rb | 19 ++++++++----------- actionpack/lib/action_view/base.rb | 6 ++++++ actionpack/lib/action_view/render/partials.rb | 6 ++---- actionpack/test/abstract_unit.rb | 2 +- 5 files changed, 17 insertions(+), 22 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb index 6b73f887c1..5efa37fde3 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/action_controller/abstract/helpers.rb @@ -8,12 +8,6 @@ module AbstractController extlib_inheritable_accessor(:_helpers) { Module.new } end - # Override AbstractController::Renderer's _action_view to include the - # helper module for this class into its helpers module. - def _action_view - @_action_view ||= super.tap { |av| av.helpers.include(_helpers) } - end - module ClassMethods # When a class is inherited, wrap its helper module in a new module. # This ensures that the parent class's module can be changed diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index 611d3a16ce..41b7d47458 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -17,25 +17,22 @@ module AbstractController # An instance of a view class. The default view class is ActionView::Base # # The view class must have the following methods: - # initialize[paths, assigns_for_first_render, controller] - # paths:: A list of resolvers to look for templates in - # controller A controller - # _render_partial_from_controller[options] + # View.for_controller[controller] Create a new ActionView instance for a + # controller + # View#_render_partial_from_controller[options] + # - responsible for setting options[:_template] + # - Returns String with the rendered partial # options:: see _render_partial in ActionView::Base - # _render_template_from_controller[template, layout, options, partial] + # View#_render_template_from_controller[template, layout, options, partial] + # - Returns String with the rendered template # template:: The template to render # layout:: The layout to render around the template # options:: See _render_template_with_layout in ActionView::Base # partial:: Whether or not the template to render is a partial - # _partial:: If a partial, rather than a template, was rendered, return - # the partial. - # helpers:: A module containing the helpers to be used in the view. This - # module should respond_to include. - # controller:: The controller that initialized the ActionView # # Override this method in a to change the default behavior. def _action_view - @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self) + @_action_view ||= ActionView::Base.for_controller(self) end # Mostly abstracts the fact that calling render twice is a DoubleRenderError. diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 45184f58fb..5915337dd2 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -228,6 +228,12 @@ module ActionView #:nodoc: end end + def self.for_controller(controller) + new(controller.class.view_paths, {}, controller).tap do |view| + view.helpers.include(controller._helpers) if controller.respond_to?(:_helpers) + end + end + def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: @formats = formats || [:html] @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index a80ffe3c20..ccb14d513a 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -259,7 +259,7 @@ module ActionView _set_locals(object, locals, template, options) - self._partial = template + options[:_template] = template _render_template(template, locals) end @@ -278,7 +278,7 @@ module ActionView locals = (options[:locals] ||= {}) index, @_partial_path = 0, nil collection.map do |object| - template = passed_template || begin + options[:_template] = template = passed_template || begin _partial_path = ActionController::RecordIdentifier.partial_path(object, controller_path) template = _pick_partial_template(_partial_path) @@ -289,8 +289,6 @@ module ActionView index += 1 - self._partial = template - _render_template(template, locals) end.join(spacer) end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 30e795a7a2..292d138fbd 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -97,7 +97,7 @@ module ActionController partials = hax[:partials] if expected_count = options[:count] found = partials.detect { |p, _| p.identifier.match(expected_partial) } - actual_count = found.nil? ? 0 : found.second + actual_count = found.nil? ? 0 : found[1] msg = build_message(message, "expecting ? to be rendered ? time(s) but rendered ? time(s)", expected_partial, expected_count, actual_count) -- cgit v1.2.3 From 0f2914be405410e824b40dcff28e5dfa541bdb2a Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 19 Jul 2009 22:12:15 +0900 Subject: Separate ActionView::Context so something else can easily be made into an AV context --- actionpack/lib/action_view.rb | 3 +- actionpack/lib/action_view/base.rb | 7 +--- actionpack/lib/action_view/context.rb | 40 ++++++++++++++++++++++ actionpack/lib/action_view/template/renderable.rb | 8 ++--- actionpack/lib/action_view/template/template.rb | 2 +- .../test/template/compiled_templates_test.rb | 2 +- 6 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 actionpack/lib/action_view/context.rb diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 2a17faf99a..70176a0ea4 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -30,10 +30,11 @@ require File.join(File.dirname(__FILE__), "action_pack") module ActionView def self.load_all! - [Base, InlineTemplate, TemplateError] + [Context, Base, InlineTemplate, TemplateError] end autoload :Base, 'action_view/base' + autoload :Context, 'action_view/context' autoload :Helpers, 'action_view/helpers' autoload :InlineTemplate, 'action_view/template/inline' autoload :MissingTemplate, 'action_view/base' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 5915337dd2..9e696af83b 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -172,8 +172,6 @@ module ActionView #:nodoc: attr_accessor :controller attr_internal :captures - attr_accessor :output_buffer - class << self delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true @@ -206,10 +204,7 @@ module ActionView #:nodoc: delegate :find_by_parts, :to => :view_paths - module CompiledTemplates #:nodoc: - # holds compiled template code - end - include CompiledTemplates + include Context def self.process_view_paths(value) ActionView::PathSet.new(Array(value)) diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb new file mode 100644 index 0000000000..63651fa3f1 --- /dev/null +++ b/actionpack/lib/action_view/context.rb @@ -0,0 +1,40 @@ +module ActionView + module CompiledTemplates #:nodoc: + # holds compiled template code + end + + # ActionView contexts are supplied to ActionController + # to render template. The default ActionView context + # is ActionView::Base. + # + # In order to work with ActionController, a Context + # must implement: + # + # Context.for_controller[controller] Create a new ActionView instance for a + # controller + # Context#_render_partial_from_controller[options] + # - responsible for setting options[:_template] + # - Returns String with the rendered partial + # options:: see _render_partial in ActionView::Base + # Context#_render_template_from_controller[template, layout, options, partial] + # - Returns String with the rendered template + # template:: The template to render + # layout:: The layout to render around the template + # options:: See _render_template_with_layout in ActionView::Base + # partial:: Whether or not the template to render is a partial + # + # An ActionView context can also mix in ActionView's + # helpers. In order to mix in helpers, a context must + # implement: + # + # Context#controller + # - Returns an instance of AbstractController + # + # In any case, a context must mix in ActionView::Context, + # which stores compiled template and provides the output + # buffer. + module Context + include CompiledTemplates + attr_accessor :output_buffer + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/template/renderable.rb b/actionpack/lib/action_view/template/renderable.rb index 54857516ab..7687578165 100644 --- a/actionpack/lib/action_view/template/renderable.rb +++ b/actionpack/lib/action_view/template/renderable.rb @@ -12,9 +12,9 @@ module ActionView end def load! - names = Base::CompiledTemplates.instance_methods.grep(/#{method_name_without_locals}/) + names = CompiledTemplates.instance_methods.grep(/#{method_name_without_locals}/) names.each do |name| - Base::CompiledTemplates.class_eval do + CompiledTemplates.class_eval do remove_method(name) end end @@ -56,7 +56,7 @@ module ActionView def compile(local_assigns) render_symbol = method_name(local_assigns) - if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile? + if !CompiledTemplates.method_defined?(render_symbol) || recompile? compile!(render_symbol, local_assigns) end end @@ -74,7 +74,7 @@ module ActionView end_src begin - ActionView::Base::CompiledTemplates.module_eval(source, filename.to_s, 0) + ActionView::CompiledTemplates.module_eval(source, filename.to_s, 0) rescue Exception => e # errors from template code if logger = defined?(ActionController) && Base.logger logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index fac50cd692..4145045e2d 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -75,7 +75,7 @@ module ActionView end begin - ActionView::Base::CompiledTemplates.module_eval(source, identifier, line) + ActionView::CompiledTemplates.module_eval(source, identifier, line) method_name rescue Exception => e # errors from template code if logger = (view && view.logger) diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb index 9c268aef27..7734e6da73 100644 --- a/actionpack/test/template/compiled_templates_test.rb +++ b/actionpack/test/template/compiled_templates_test.rb @@ -3,7 +3,7 @@ require 'controller/fake_models' class CompiledTemplatesTest < Test::Unit::TestCase def setup - @compiled_templates = ActionView::Base::CompiledTemplates + @compiled_templates = ActionView::CompiledTemplates @compiled_templates.instance_methods.each do |m| @compiled_templates.send(:remove_method, m) if m =~ /^_render_template_/ end -- cgit v1.2.3 From b20d68446d2fe98d129385a17a3a4cdacd4b5025 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 19 Jul 2009 22:30:00 +0900 Subject: Move default_form_builder to ActionView so it'll work in environments not using ActionView::Base --- actionpack/lib/action_view/helpers/form_helper.rb | 18 +++++++++++++++--- actionpack/test/template/form_helper_test.rb | 6 +++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 6d6d623938..a74709e98a 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -488,7 +488,7 @@ module ActionView object_name = ActionController::RecordIdentifier.singular_class_name(object) end - builder = options[:builder] || ActionView::Base.default_form_builder + builder = options[:builder] || ActionView.default_form_builder yield builder.new(object_name, object, self, options, block) end @@ -1054,9 +1054,21 @@ module ActionView end end - class << Base + class << ActionView attr_accessor :default_form_builder end - Base.default_form_builder = ::ActionView::Helpers::FormBuilder + self.default_form_builder = ::ActionView::Helpers::FormBuilder + + # 2.3 compatibility + class << Base + def default_form_builder=(builder) + ActionView.default_form_builder = builder + end + + def default_form_builder + ActionView.default_form_builder + end + end + end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 515f73c339..bc24ea3556 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1024,8 +1024,8 @@ class FormHelperTest < ActionView::TestCase end def test_default_form_builder - old_default_form_builder, ActionView::Base.default_form_builder = - ActionView::Base.default_form_builder, LabelledFormBuilder + old_default_form_builder, ActionView.default_form_builder = + ActionView.default_form_builder, LabelledFormBuilder form_for(:post, @post) do |f| concat f.text_field(:title) @@ -1042,7 +1042,7 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer ensure - ActionView::Base.default_form_builder = old_default_form_builder + ActionView.default_form_builder = old_default_form_builder end def test_default_form_builder_with_active_record_helpers -- cgit v1.2.3 From 45d41d8012c605f21de51e7018fa31e1d07776eb Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 19 Jul 2009 23:24:19 +0900 Subject: Add some simple examples for unconventional AMo and AP use --- actionpack/examples/very_simple.rb | 50 ++++++++++++++++++++++++ actionpack/examples/views/layouts/alt.html.erb | 1 + actionpack/examples/views/layouts/kaigi.html.erb | 1 + actionpack/examples/views/template.html.erb | 1 + activemodel/examples/amo_ap_example.rb | 36 +++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 actionpack/examples/very_simple.rb create mode 100644 actionpack/examples/views/layouts/alt.html.erb create mode 100644 actionpack/examples/views/layouts/kaigi.html.erb create mode 100644 actionpack/examples/views/template.html.erb create mode 100644 activemodel/examples/amo_ap_example.rb diff --git a/actionpack/examples/very_simple.rb b/actionpack/examples/very_simple.rb new file mode 100644 index 0000000000..8d01cb39bd --- /dev/null +++ b/actionpack/examples/very_simple.rb @@ -0,0 +1,50 @@ +$:.push "rails/activesupport/lib" +$:.push "rails/actionpack/lib" + +require "action_controller" + +class Kaigi < ActionController::Http + include AbstractController::Callbacks + include ActionController::RackConvenience + include ActionController::Renderer + include ActionController::Layouts + include ActionView::Context + + before_filter :set_name + append_view_path "views" + + def _action_view + self + end + + def controller + self + end + + DEFAULT_LAYOUT = Object.new.tap {|l| def l.render(*) yield end } + + def _render_template_from_controller(template, layout = DEFAULT_LAYOUT, options = {}, partial = false) + ret = template.render(self, {}) + layout.render(self, {}) { ret } + end + + def index + render :template => "template" + end + + def alt + render :template => "template", :layout => "alt" + end + + private + def set_name + @name = params[:name] + end +end + +app = Rack::Builder.new do + map("/kaigi") { run Kaigi.action(:index) } + map("/kaigi/alt") { run Kaigi.action(:alt) } +end.to_app + +Rack::Handler::Mongrel.run app, :Port => 3000 \ No newline at end of file diff --git a/actionpack/examples/views/layouts/alt.html.erb b/actionpack/examples/views/layouts/alt.html.erb new file mode 100644 index 0000000000..c4816337a6 --- /dev/null +++ b/actionpack/examples/views/layouts/alt.html.erb @@ -0,0 +1 @@ ++ <%= yield %> + \ No newline at end of file diff --git a/actionpack/examples/views/layouts/kaigi.html.erb b/actionpack/examples/views/layouts/kaigi.html.erb new file mode 100644 index 0000000000..274607a96a --- /dev/null +++ b/actionpack/examples/views/layouts/kaigi.html.erb @@ -0,0 +1 @@ +Hello <%= yield %> Goodbye \ No newline at end of file diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb new file mode 100644 index 0000000000..3108e9ad70 --- /dev/null +++ b/actionpack/examples/views/template.html.erb @@ -0,0 +1 @@ +Hello <%= @name %> \ No newline at end of file diff --git a/activemodel/examples/amo_ap_example.rb b/activemodel/examples/amo_ap_example.rb new file mode 100644 index 0000000000..cef718d0d4 --- /dev/null +++ b/activemodel/examples/amo_ap_example.rb @@ -0,0 +1,36 @@ +$:.push "activesupport/lib" +$:.push "activemodel/lib" + +require "active_model/validations" +require "active_model/deprecated_error_methods" +require "active_model/errors" +require "active_model/naming" + +class Person + include ActiveModel::Validations + extend ActiveModel::Naming + + validates_presence_of :name + + attr_accessor :name + def initialize(attributes = {}) + @name = attributes[:name] + end + + def persist + @persisted = true + end + + def new_record? + @persisted + end + + def to_model() self end +end + +person1 = Person.new +p person1.valid? +person1.errors + +person2 = Person.new(:name => "matz") +p person2.valid? \ No newline at end of file -- cgit v1.2.3 From 5ffaaa71d149c9807260c950c9a61d01fe734827 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 20 Jul 2009 00:27:04 +0900 Subject: Define ActiveModel API Compliance - Define to_model on AR - Define to_model on ActiveModel::APICompliant - Update test fixtures to be API Compliant - Start using to_model in AP --- .../routing/generation/polymorphic_routes.rb | 1 + .../action_view/helpers/active_record_helper.rb | 24 ++++++++++++++++----- actionpack/lib/action_view/helpers/form_helper.rb | 13 +++++++---- actionpack/test/abstract_unit.rb | 1 + ...nder_partial_with_record_identification_test.rb | 2 +- .../test/controller/record_identifier_test.rb | 2 +- actionpack/test/controller/redirect_test.rb | 2 +- actionpack/test/lib/controller/fake_models.rb | 4 ++-- .../test/template/active_record_helper_test.rb | 16 ++++++-------- actionpack/test/template/atom_feed_helper_test.rb | 9 ++++++-- actionpack/test/template/prototype_helper_test.rb | 8 ++++--- actionpack/test/template/record_tag_helper_test.rb | 2 +- actionpack/test/template/test_test.rb | 2 +- actionpack/test/template/url_helper_test.rb | 4 ++-- activemodel/lib/active_model.rb | 1 + activemodel/lib/active_model/api_compliant.rb | 25 ++++++++++++++++++++++ activerecord/lib/active_record/base.rb | 7 ++++++ 17 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 activemodel/lib/active_model/api_compliant.rb diff --git a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb index c6f7de17bd..159d869a54 100644 --- a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb @@ -77,6 +77,7 @@ module ActionController end record = extract_record(record_or_hash_or_array) + record = record.to_model if record.respond_to?(:to_model) namespace = extract_namespace(record_or_hash_or_array) args = case record_or_hash_or_array diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 75cc651968..8dffd3bc91 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -77,6 +77,7 @@ module ActionView # * :submit_value - The text of the submit button (default: "Create" if a new record, otherwise "Update"). def form(record_name, options = {}) record = instance_variable_get("@#{record_name}") + record = record.to_model if record.respond_to?(:to_model) options = options.symbolize_keys options[:action] ||= record.new_record? ? "create" : "update" @@ -121,6 +122,8 @@ module ActionView end options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') + object = object.to_model if object.respond_to?(:to_model) + if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && (errors = obj.errors[method]) content_tag("div", @@ -179,6 +182,8 @@ module ActionView objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact end + objects.map! {|o| o.respond_to?(:to_model) ? o.to_model : o } + count = objects.inject(0) {|sum, object| sum + object.errors.count } unless count.zero? html = {} @@ -226,7 +231,14 @@ module ActionView end end - class InstanceTag #:nodoc: + module ActiveRecordInstanceTag + def object + @active_model_object ||= begin + object = super + object.respond_to?(:to_model) ? object.to_model : object + end + end + def to_tag(options = {}) case column_type when :string @@ -248,11 +260,9 @@ module ActionView end %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| - without = "#{meth}_without_error_wrapping" - define_method "#{meth}_with_error_wrapping" do |*args| - error_wrapping(send(without, *args)) + define_method meth do |*| + error_wrapping(super) end - alias_method_chain meth, :error_wrapping end def error_wrapping(html_tag) @@ -267,5 +277,9 @@ module ActionView object.send(:column_for_attribute, @method_name).type end end + + class InstanceTag + include ActiveRecordInstanceTag + end end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index a74709e98a..ecbdcd8e7a 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -626,8 +626,8 @@ module ActionView # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object. - # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked. - # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1 + # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked. + # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1 # while the default +unchecked_value+ is set to 0 which is convenient for boolean values. # # ==== Gotcha @@ -709,7 +709,8 @@ module ActionView end end - class InstanceTag #:nodoc: + module InstanceTagMethods #:nodoc: + extend ActiveSupport::Concern include Helpers::TagHelper, Helpers::FormTagHelper attr_reader :method_name, :object_name @@ -832,7 +833,7 @@ module ActionView self.class.value_before_type_cast(object, @method_name) end - class << self + module ClassMethods def value(object, method_name) object.send method_name unless object.nil? end @@ -918,6 +919,10 @@ module ActionView end end + class InstanceTag + include InstanceTagMethods + end + class FormBuilder #:nodoc: # The methods which wrap a form helper call. class_inheritable_accessor :field_helpers diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 292d138fbd..6e71b85645 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -20,6 +20,7 @@ require 'action_controller/testing/process' require 'action_view/test_case' require 'action_controller/testing/integration' require 'active_support/dependencies' +require 'active_model' $tags[:new_base] = true diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index 2a31f3be44..0122bc7d8f 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -126,7 +126,7 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base end class Game < Struct.new(:name, :id) - extend ActiveModel::Naming + extend ActiveModel::APICompliant def to_param id.to_s end diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 28bc608d47..10f51639cf 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' class Comment - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_reader :id def save; @id = 1 end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 453a77e7bc..a71c39e504 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -4,7 +4,7 @@ class WorkshopsController < ActionController::Base end class Workshop - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_accessor :id, :new_record def initialize(id, new_record) diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 9e6f14d373..07f01e1c47 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -1,7 +1,7 @@ require "active_model" class Customer < Struct.new(:name, :id) - extend ActiveModel::Naming + extend ActiveModel::APICompliant def to_param id.to_s @@ -16,7 +16,7 @@ end module Quiz class Question < Struct.new(:name, :id) - extend ActiveModel::Naming + extend ActiveModel::APICompliant def to_param id.to_s diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index e1be048838..950072e45b 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -4,19 +4,17 @@ class ActiveRecordHelperTest < ActionView::TestCase tests ActionView::Helpers::ActiveRecordHelper silence_warnings do - Post = Struct.new("Post", :title, :author_name, :body, :secret, :written_on) - Post.class_eval 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) + class Post < Struct.new(:title, :author_name, :body, :secret, :written_on) + extend ActiveModel::APICompliant end - User = Struct.new("User", :email) - User.class_eval do - alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast) + class User < Struct.new(:email) + extend ActiveModel::APICompliant end - Column = Struct.new("Column", :type, :name, :human_name) + class Column < Struct.new(:type, :name, :human_name) + extend ActiveModel::APICompliant + end end class DirtyPost diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 6f1179f359..89ff01ab0e 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -1,7 +1,12 @@ require 'abstract_unit' -Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) -Scroll.extend ActiveModel::Naming +class Scroll < Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) + extend ActiveModel::APICompliant + + def new_record? + true + end +end class ScrollsController < ActionController::Base FEEDS = {} diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index a7a1bc99f3..301a110076 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -1,10 +1,12 @@ require 'abstract_unit' +require 'active_model' Bunny = Struct.new(:Bunny, :id) -Bunny.extend ActiveModel::Naming +Bunny.extend ActiveModel::APICompliant class Author - extend ActiveModel::Naming + extend ActiveModel::APICompliant + attr_reader :id def save; @id = 1 end def new_record?; @id.nil? end @@ -14,7 +16,7 @@ class Author end class Article - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_reader :id attr_reader :author_id def save; @id = 1; @author_id = 1 end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 5b840d123b..861958ffa9 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' class Post - extend ActiveModel::Naming + extend ActiveModel::APICompliant def id 45 end diff --git a/actionpack/test/template/test_test.rb b/actionpack/test/template/test_test.rb index f32d0b3d42..98307fbae4 100644 --- a/actionpack/test/template/test_test.rb +++ b/actionpack/test/template/test_test.rb @@ -41,7 +41,7 @@ class PeopleHelperTest < ActionView::TestCase def test_link_to_person person = mock(:name => "David") - person.class.extend ActiveModel::Naming + person.class.extend ActiveModel::APICompliant expects(:mocha_mock_path).with(person).returns("/people/1") assert_equal 'David', link_to_person(person) end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index f0364fd660..4569689534 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -494,7 +494,7 @@ class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase end class Workshop - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_accessor :id, :new_record def initialize(id, new_record) @@ -511,7 +511,7 @@ class Workshop end class Session - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_accessor :id, :workshop_id, :new_record def initialize(id, new_record) diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index f988cd71b8..c6f63d2fdc 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -26,6 +26,7 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' module ActiveModel + autoload :APICompliant, 'active_model/api_compliant' autoload :Attributes, 'active_model/attributes' autoload :Base, 'active_model/base' autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' diff --git a/activemodel/lib/active_model/api_compliant.rb b/activemodel/lib/active_model/api_compliant.rb new file mode 100644 index 0000000000..26f83feb6b --- /dev/null +++ b/activemodel/lib/active_model/api_compliant.rb @@ -0,0 +1,25 @@ +module ActiveModel + module APICompliant + include Naming + + def self.extended(klass) + klass.class_eval do + include Validations + include InstanceMethods + end + end + + module InstanceMethods + def to_model + if respond_to?(:new_record?) + self.class.class_eval { def to_model() self end } + to_model + else + raise "In order to be ActiveModel API compliant, you need to define " \ + "a new_record? method, which should return true if it has not " \ + "yet been persisted." + end + end + end + end +end \ No newline at end of file diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index deab56e219..f32857afa3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2511,6 +2511,13 @@ module ActiveRecord #:nodoc: (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes end + # Returns the ActiveRecord object when asked for its + # ActiveModel-compliant representation, because ActiveRecord is + # ActiveModel-compliant. + def to_model + self + end + # Returns a cache key that can be used to identify this record. # # ==== Examples -- cgit v1.2.3 From 13e18dd94000cef2b2058b96d62de16b7d3a2200 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 20 Jul 2009 00:58:59 +0900 Subject: Update some tests and add a to_model to form helpers --- actionpack/lib/action_view/context.rb | 8 ++++++-- actionpack/lib/action_view/helpers/active_record_helper.rb | 6 +++--- actionpack/lib/action_view/helpers/form_helper.rb | 2 ++ actionpack/lib/action_view/test_case.rb | 1 + actionpack/test/template/active_record_helper_i18n_test.rb | 1 + actionpack/test/template/form_helper_test.rb | 10 +++++----- actionpack/test/template/record_tag_helper_test.rb | 2 +- 7 files changed, 19 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index 63651fa3f1..f212fe25eb 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -2,7 +2,7 @@ module ActionView module CompiledTemplates #:nodoc: # holds compiled template code end - + # ActionView contexts are supplied to ActionController # to render template. The default ActionView context # is ActionView::Base. @@ -10,7 +10,7 @@ module ActionView # In order to work with ActionController, a Context # must implement: # - # Context.for_controller[controller] Create a new ActionView instance for a + # Context.for_controller[controller] Create a new ActionView instance for a # controller # Context#_render_partial_from_controller[options] # - responsible for setting options[:_template] @@ -36,5 +36,9 @@ module ActionView module Context include CompiledTemplates attr_accessor :output_buffer + + def convert_to_model(object) + object.respond_to?(:to_model) ? object.to_model : object + end end end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 8dffd3bc91..409b27de73 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -77,7 +77,7 @@ module ActionView # * :submit_value - The text of the submit button (default: "Create" if a new record, otherwise "Update"). def form(record_name, options = {}) record = instance_variable_get("@#{record_name}") - record = record.to_model if record.respond_to?(:to_model) + record = convert_to_model(record) options = options.symbolize_keys options[:action] ||= record.new_record? ? "create" : "update" @@ -122,7 +122,7 @@ module ActionView end options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') - object = object.to_model if object.respond_to?(:to_model) + object = convert_to_model(object) if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && (errors = obj.errors[method]) @@ -182,7 +182,7 @@ module ActionView objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact end - objects.map! {|o| o.respond_to?(:to_model) ? o.to_model : o } + objects.map! {|o| convert_to_model(o) } count = objects.inject(0) {|sum, object| sum + object.errors.count } unless count.zero? diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index ecbdcd8e7a..56ee43496c 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -288,6 +288,8 @@ module ActionView def apply_form_for_options!(object_or_array, options) #:nodoc: object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array + object = convert_to_model(object) + html_options = if object.respond_to?(:new_record?) && object.new_record? { :class => dom_class(object, :new), :id => dom_id(object), :method => :post } diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 7355af4192..3f3951509a 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -23,6 +23,7 @@ module ActionView class TestCase < ActiveSupport::TestCase include ActionDispatch::Assertions include ActionController::TestProcess + include ActionView::Context class_inheritable_accessor :helper_class @@helper_class = nil diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index 9d04c882c8..7dda9fe275 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' class ActiveRecordHelperI18nTest < Test::Unit::TestCase + include ActionView::Context include ActionView::Helpers::ActiveRecordHelper attr_reader :request diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index bc24ea3556..431ac35e54 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1,11 +1,9 @@ require 'abstract_unit' silence_warnings do - Post = Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) - Post.class_eval 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) + class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) + extend ActiveModel::APICompliant + alias_method :secret?, :secret def new_record=(boolean) @@ -27,6 +25,8 @@ silence_warnings do end class Comment + extend ActiveModel::APICompliant + attr_reader :id attr_reader :post_id def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 861958ffa9..bae26f555d 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -6,7 +6,7 @@ class Post 45 end def body - "What a wonderful world!" + super || "What a wonderful world!" end end -- cgit v1.2.3 From b00cac4adc0413418ffd2c59b52c8f64acff406b Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 20 Jul 2009 01:22:24 +0900 Subject: Finish convert_to_object updates --- actionpack/lib/action_view/helpers/form_helper.rb | 4 +++- actionpack/test/template/form_helper_test.rb | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 56ee43496c..2b39d08035 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1029,7 +1029,7 @@ module ActionView def fields_for_with_nested_attributes(association_name, args, block) name = "#{object_name}[#{association_name}_attributes]" association = @object.send(association_name) - explicit_object = args.first if args.first.respond_to?(:new_record?) + explicit_object = args.first.to_model if args.first.respond_to?(:to_model) if association.is_a?(Array) children = explicit_object ? [explicit_object] : association @@ -1044,6 +1044,8 @@ module ActionView end def fields_for_nested_model(name, object, args, block) + object = convert_to_model(object) + if object.new_record? @template.fields_for(name, object, *args, &block) else diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 431ac35e54..883afd985b 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -43,6 +43,8 @@ silence_warnings do end class Tag + extend ActiveModel::APICompliant + attr_reader :id attr_reader :post_id def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end @@ -59,6 +61,8 @@ silence_warnings do end class CommentRelevance + extend ActiveModel::APICompliant + attr_reader :id attr_reader :comment_id def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end @@ -71,6 +75,8 @@ silence_warnings do end class TagRelevance + extend ActiveModel::APICompliant + attr_reader :id attr_reader :tag_id def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end -- cgit v1.2.3 From f2f5cdc8bcd9df3a297ea02b80dbdb21790de732 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 20 Jul 2009 01:28:15 +0900 Subject: Rename ActiveRecordHelper to ActiveModelHelper --- actionpack/lib/action_view/helpers.rb | 4 +- .../lib/action_view/helpers/active_model_helper.rb | 285 +++++++++++++++++++++ .../action_view/helpers/active_record_helper.rb | 285 --------------------- actionpack/lib/action_view/helpers/form_helper.rb | 2 - .../template/active_record_helper_i18n_test.rb | 2 +- .../test/template/active_record_helper_test.rb | 2 +- 6 files changed, 289 insertions(+), 291 deletions(-) create mode 100644 actionpack/lib/action_view/helpers/active_model_helper.rb delete mode 100644 actionpack/lib/action_view/helpers/active_record_helper.rb diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 97fa2d80e9..c1c0eb59ae 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -1,6 +1,6 @@ module ActionView #:nodoc: module Helpers #:nodoc: - autoload :ActiveRecordHelper, 'action_view/helpers/active_record_helper' + autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper' autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper' autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper' autoload :BenchmarkHelper, 'action_view/helpers/benchmark_helper' @@ -31,7 +31,7 @@ module ActionView #:nodoc: include SanitizeHelper::ClassMethods end - include ActiveRecordHelper + include ActiveModelHelper include AssetTagHelper include AtomFeedHelper include BenchmarkHelper diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb new file mode 100644 index 0000000000..0f122d9232 --- /dev/null +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -0,0 +1,285 @@ +require 'cgi' +require 'action_view/helpers/form_helper' +require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/kernel/reporting' + +module ActionView + class Base + @@field_error_proc = Proc.new{ |html_tag, instance| "
#{html_tag}
" } + cattr_accessor :field_error_proc + end + + module Helpers + # The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+ + # method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This + # is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form. + # In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html + module ActiveModelHelper + # Returns a default input tag for the type of object returned by the method. For example, if @post + # has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World": + # + # input("post", "title") + # # => + def input(record_name, method, options = {}) + InstanceTag.new(record_name, method, self).to_tag(options) + end + + # Returns an entire form with all needed input tags for a specified Active Record object. For example, if @post + # has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then + # + # form("post") + # + # would yield a form like the following (modulus formatting): + # + #
+ #

+ #
+ # + #

+ #

+ #
+ # + #

+ # + #
+ # + # It's possible to specialize the form builder by using a different action name and by supplying another + # block renderer. For example, if @entry has an attribute +message+ of type +VARCHAR+ then + # + # form("entry", + # :action => "sign", + # :input_block => Proc.new { |record, column| + # "#{column.human_name}: #{input(record, column.name)}
" + # }) + # + # would yield a form like the following (modulus formatting): + # + #
+ # Message: + #
+ # + #
+ # + # It's also possible to add additional content to the form by giving it a block, such as: + # + # form("entry", :action => "sign") do |form| + # form << content_tag("b", "Department") + # form << collection_select("department", "id", @departments, "id", "name") + # end + # + # The following options are available: + # + # * :action - The action used when submitting the form (default: +create+ if a new record, otherwise +update+). + # * :input_block - Specialize the output using a different block, see above. + # * :method - The method used when submitting the form (default: +post+). + # * :multipart - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+). + # * :submit_value - The text of the submit button (default: "Create" if a new record, otherwise "Update"). + def form(record_name, options = {}) + record = instance_variable_get("@#{record_name}") + record = convert_to_model(record) + + options = options.symbolize_keys + options[:action] ||= record.new_record? ? "create" : "update" + action = url_for(:action => options[:action], :id => record) + + submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize + + contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil) + contents << hidden_field(record_name, :id) unless record.new_record? + contents << all_input_tags(record, record_name, options) + yield contents if block_given? + contents << submit_tag(submit_value) + contents << '' + end + + # Returns a string containing the error message attached to the +method+ on the +object+ if one exists. + # This error message is wrapped in a DIV tag, which can be extended to include a :prepend_text + # and/or :append_text (to properly explain the error), and a :css_class to style it + # accordingly. +object+ should either be the name of an instance variable or the actual object. The method can be + # passed in either as a string or a symbol. + # As an example, let's say you have a model @post that has an error message on the +title+ attribute: + # + # <%= error_message_on "post", "title" %> + # # =>
can't be empty
+ # + # <%= error_message_on @post, :title %> + # # =>
can't be empty
+ # + # <%= error_message_on "post", "title", + # :prepend_text => "Title simply ", + # :append_text => " (or it won't work).", + # :css_class => "inputError" %> + def error_message_on(object, method, *args) + options = args.extract_options! + unless args.empty? + ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' + + 'prepend_text, append_text, and css_class arguments', caller) + + options[:prepend_text] = args[0] || '' + options[:append_text] = args[1] || '' + options[:css_class] = args[2] || 'formError' + end + options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') + + object = convert_to_model(object) + + if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && + (errors = obj.errors[method]) + content_tag("div", + "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.first)}#{options[:append_text]}", + :class => options[:css_class] + ) + else + '' + end + end + + # Returns a string with a DIV containing all of the error messages for the objects located as instance variables by the names + # given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are + # provided. + # + # This DIV can be tailored by the following options: + # + # * :header_tag - Used for the header of the error div (default: "h2"). + # * :id - The id of the error div (default: "errorExplanation"). + # * :class - The class of the error div (default: "errorExplanation"). + # * :object - The object (or array of objects) for which to display errors, + # if you need to escape the instance variable convention. + # * :object_name - The object name to use in the header, or any text that you prefer. + # If :object_name is not set, the name of the first object will be used. + # * :header_message - The message in the header of the error div. Pass +nil+ + # or an empty string to avoid the header message altogether. (Default: "X errors + # prohibited this object from being saved"). + # * :message - The explanation message after the header message and before + # the error list. Pass +nil+ or an empty string to avoid the explanation message + # altogether. (Default: "There were problems with the following fields:"). + # + # To specify the display for one object, you simply provide its name as a parameter. + # For example, for the @user model: + # + # error_messages_for 'user' + # + # To specify more than one object, you simply list them; optionally, you can add an extra :object_name parameter, which + # will be the name used in the header message: + # + # error_messages_for 'user_common', 'user', :object_name => 'user' + # + # If the objects cannot be located as instance variables, you can add an extra :object parameter which gives the actual + # object (or array of objects to use): + # + # error_messages_for 'user', :object => @question.user + # + # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what + # you need is significantly different from the default presentation, it makes plenty of sense to access the object.errors + # instance yourself and set it up. View the source of this method to see how easy it is. + def error_messages_for(*params) + options = params.extract_options!.symbolize_keys + + if object = options.delete(:object) + objects = [object].flatten + else + objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact + end + + objects.map! {|o| convert_to_model(o) } + + count = objects.inject(0) {|sum, object| sum + object.errors.count } + unless count.zero? + html = {} + [:id, :class].each do |key| + if options.include?(key) + value = options[key] + html[key] = value unless value.blank? + else + html[key] = 'errorExplanation' + end + end + options[:object_name] ||= params.first + + I18n.with_options :locale => options[:locale], :scope => [:activerecord, :errors, :template] do |locale| + header_message = if options.include?(:header_message) + options[:header_message] + else + object_name = options[:object_name].to_s.gsub('_', ' ') + object_name = I18n.t(options[:object_name].to_s, :default => object_name, :scope => [:activerecord, :models], :count => 1) + locale.t :header, :count => count, :model => object_name + end + message = options.include?(:message) ? options[:message] : locale.t(:body) + error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join + + contents = '' + contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? + contents << content_tag(:p, message) unless message.blank? + contents << content_tag(:ul, error_messages) + + content_tag(:div, contents, html) + end + else + '' + end + end + + private + def all_input_tags(record, record_name, options) + input_block = options[:input_block] || default_input_block + record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n") + end + + def default_input_block + Proc.new { |record, column| %(


#{input(record, column.name)}

) } + end + end + + module ActiveRecordInstanceTag + def object + @active_model_object ||= begin + object = super + object.respond_to?(:to_model) ? object.to_model : object + end + end + + def to_tag(options = {}) + case column_type + when :string + field_type = @method_name.include?("password") ? "password" : "text" + to_input_field_tag(field_type, options) + when :text + to_text_area_tag(options) + when :integer, :float, :decimal + to_input_field_tag("text", options) + when :date + to_date_select_tag(options) + when :datetime, :timestamp + to_datetime_select_tag(options) + when :time + to_time_select_tag(options) + when :boolean + to_boolean_select_tag(options) + end + end + + %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| + define_method meth do |*| + error_wrapping(super) + end + end + + def error_wrapping(html_tag) + if object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && object.errors[@method_name].any? + Base.field_error_proc.call(html_tag, self) + else + html_tag + end + end + + def column_type + object.send(:column_for_attribute, @method_name).type + end + end + + class InstanceTag + include ActiveRecordInstanceTag + end + end +end diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb deleted file mode 100644 index 409b27de73..0000000000 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ /dev/null @@ -1,285 +0,0 @@ -require 'cgi' -require 'action_view/helpers/form_helper' -require 'active_support/core_ext/class/attribute_accessors' -require 'active_support/core_ext/enumerable' -require 'active_support/core_ext/kernel/reporting' - -module ActionView - class Base - @@field_error_proc = Proc.new{ |html_tag, instance| "
#{html_tag}
" } - cattr_accessor :field_error_proc - end - - module Helpers - # The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the +form+ - # method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This - # is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form. - # In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html - module ActiveRecordHelper - # Returns a default input tag for the type of object returned by the method. For example, if @post - # has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World": - # - # input("post", "title") - # # => - def input(record_name, method, options = {}) - InstanceTag.new(record_name, method, self).to_tag(options) - end - - # Returns an entire form with all needed input tags for a specified Active Record object. For example, if @post - # has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then - # - # form("post") - # - # would yield a form like the following (modulus formatting): - # - #
- #

- #
- # - #

- #

- #
- # - #

- # - #
- # - # It's possible to specialize the form builder by using a different action name and by supplying another - # block renderer. For example, if @entry has an attribute +message+ of type +VARCHAR+ then - # - # form("entry", - # :action => "sign", - # :input_block => Proc.new { |record, column| - # "#{column.human_name}: #{input(record, column.name)}
" - # }) - # - # would yield a form like the following (modulus formatting): - # - #
- # Message: - #
- # - #
- # - # It's also possible to add additional content to the form by giving it a block, such as: - # - # form("entry", :action => "sign") do |form| - # form << content_tag("b", "Department") - # form << collection_select("department", "id", @departments, "id", "name") - # end - # - # The following options are available: - # - # * :action - The action used when submitting the form (default: +create+ if a new record, otherwise +update+). - # * :input_block - Specialize the output using a different block, see above. - # * :method - The method used when submitting the form (default: +post+). - # * :multipart - Whether to change the enctype of the form to "multipart/form-data", used when uploading a file (default: +false+). - # * :submit_value - The text of the submit button (default: "Create" if a new record, otherwise "Update"). - def form(record_name, options = {}) - record = instance_variable_get("@#{record_name}") - record = convert_to_model(record) - - options = options.symbolize_keys - options[:action] ||= record.new_record? ? "create" : "update" - action = url_for(:action => options[:action], :id => record) - - submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize - - contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil) - contents << hidden_field(record_name, :id) unless record.new_record? - contents << all_input_tags(record, record_name, options) - yield contents if block_given? - contents << submit_tag(submit_value) - contents << '' - end - - # Returns a string containing the error message attached to the +method+ on the +object+ if one exists. - # This error message is wrapped in a DIV tag, which can be extended to include a :prepend_text - # and/or :append_text (to properly explain the error), and a :css_class to style it - # accordingly. +object+ should either be the name of an instance variable or the actual object. The method can be - # passed in either as a string or a symbol. - # As an example, let's say you have a model @post that has an error message on the +title+ attribute: - # - # <%= error_message_on "post", "title" %> - # # =>
can't be empty
- # - # <%= error_message_on @post, :title %> - # # =>
can't be empty
- # - # <%= error_message_on "post", "title", - # :prepend_text => "Title simply ", - # :append_text => " (or it won't work).", - # :css_class => "inputError" %> - def error_message_on(object, method, *args) - options = args.extract_options! - unless args.empty? - ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' + - 'prepend_text, append_text, and css_class arguments', caller) - - options[:prepend_text] = args[0] || '' - options[:append_text] = args[1] || '' - options[:css_class] = args[2] || 'formError' - end - options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') - - object = convert_to_model(object) - - if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && - (errors = obj.errors[method]) - content_tag("div", - "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.first)}#{options[:append_text]}", - :class => options[:css_class] - ) - else - '' - end - end - - # Returns a string with a DIV containing all of the error messages for the objects located as instance variables by the names - # given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are - # provided. - # - # This DIV can be tailored by the following options: - # - # * :header_tag - Used for the header of the error div (default: "h2"). - # * :id - The id of the error div (default: "errorExplanation"). - # * :class - The class of the error div (default: "errorExplanation"). - # * :object - The object (or array of objects) for which to display errors, - # if you need to escape the instance variable convention. - # * :object_name - The object name to use in the header, or any text that you prefer. - # If :object_name is not set, the name of the first object will be used. - # * :header_message - The message in the header of the error div. Pass +nil+ - # or an empty string to avoid the header message altogether. (Default: "X errors - # prohibited this object from being saved"). - # * :message - The explanation message after the header message and before - # the error list. Pass +nil+ or an empty string to avoid the explanation message - # altogether. (Default: "There were problems with the following fields:"). - # - # To specify the display for one object, you simply provide its name as a parameter. - # For example, for the @user model: - # - # error_messages_for 'user' - # - # To specify more than one object, you simply list them; optionally, you can add an extra :object_name parameter, which - # will be the name used in the header message: - # - # error_messages_for 'user_common', 'user', :object_name => 'user' - # - # If the objects cannot be located as instance variables, you can add an extra :object parameter which gives the actual - # object (or array of objects to use): - # - # error_messages_for 'user', :object => @question.user - # - # NOTE: This is a pre-packaged presentation of the errors with embedded strings and a certain HTML structure. If what - # you need is significantly different from the default presentation, it makes plenty of sense to access the object.errors - # instance yourself and set it up. View the source of this method to see how easy it is. - def error_messages_for(*params) - options = params.extract_options!.symbolize_keys - - if object = options.delete(:object) - objects = [object].flatten - else - objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact - end - - objects.map! {|o| convert_to_model(o) } - - count = objects.inject(0) {|sum, object| sum + object.errors.count } - unless count.zero? - html = {} - [:id, :class].each do |key| - if options.include?(key) - value = options[key] - html[key] = value unless value.blank? - else - html[key] = 'errorExplanation' - end - end - options[:object_name] ||= params.first - - I18n.with_options :locale => options[:locale], :scope => [:activerecord, :errors, :template] do |locale| - header_message = if options.include?(:header_message) - options[:header_message] - else - object_name = options[:object_name].to_s.gsub('_', ' ') - object_name = I18n.t(options[:object_name].to_s, :default => object_name, :scope => [:activerecord, :models], :count => 1) - locale.t :header, :count => count, :model => object_name - end - message = options.include?(:message) ? options[:message] : locale.t(:body) - error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join - - contents = '' - contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? - contents << content_tag(:p, message) unless message.blank? - contents << content_tag(:ul, error_messages) - - content_tag(:div, contents, html) - end - else - '' - end - end - - private - def all_input_tags(record, record_name, options) - input_block = options[:input_block] || default_input_block - record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n") - end - - def default_input_block - Proc.new { |record, column| %(


#{input(record, column.name)}

) } - end - end - - module ActiveRecordInstanceTag - def object - @active_model_object ||= begin - object = super - object.respond_to?(:to_model) ? object.to_model : object - end - end - - def to_tag(options = {}) - case column_type - when :string - field_type = @method_name.include?("password") ? "password" : "text" - to_input_field_tag(field_type, options) - when :text - to_text_area_tag(options) - when :integer, :float, :decimal - to_input_field_tag("text", options) - when :date - to_date_select_tag(options) - when :datetime, :timestamp - to_datetime_select_tag(options) - when :time - to_time_select_tag(options) - when :boolean - to_boolean_select_tag(options) - end - end - - %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| - define_method meth do |*| - error_wrapping(super) - end - end - - def error_wrapping(html_tag) - if object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && object.errors[@method_name].any? - Base.field_error_proc.call(html_tag, self) - else - html_tag - end - end - - def column_type - object.send(:column_for_attribute, @method_name).type - end - end - - class InstanceTag - include ActiveRecordInstanceTag - end - end -end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 2b39d08035..2d1d19d5f3 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1044,8 +1044,6 @@ module ActionView end def fields_for_nested_model(name, object, args, block) - object = convert_to_model(object) - if object.new_record? @template.fields_for(name, object, *args, &block) else diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index 7dda9fe275..63032e4e5c 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -2,7 +2,7 @@ require 'abstract_unit' class ActiveRecordHelperI18nTest < Test::Unit::TestCase include ActionView::Context - include ActionView::Helpers::ActiveRecordHelper + include ActionView::Helpers::ActiveModelHelper attr_reader :request diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index 950072e45b..c1bbdae8fb 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' class ActiveRecordHelperTest < ActionView::TestCase - tests ActionView::Helpers::ActiveRecordHelper + tests ActionView::Helpers::ActiveModelHelper silence_warnings do class Post < Struct.new(:title, :author_name, :body, :secret, :written_on) -- cgit v1.2.3 From d80316ad3269e11c2be9be0727ae518f5e427d24 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 20 Jul 2009 16:13:21 -0700 Subject: First effort at new Ajax helpers --- actionpack/lib/action_view/helpers.rb | 1 + actionpack/lib/action_view/helpers/ajax_helper.rb | 68 +++++++++++++ actionpack/test/javascript/ajax_test.rb | 115 ++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 actionpack/lib/action_view/helpers/ajax_helper.rb create mode 100644 actionpack/test/javascript/ajax_test.rb diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index c1c0eb59ae..652561f7f8 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -1,6 +1,7 @@ module ActionView #:nodoc: module Helpers #:nodoc: autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper' + autoload :AjaxHelper, 'action_view/helpers/ajax_helper' autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper' autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper' autoload :BenchmarkHelper, 'action_view/helpers/benchmark_helper' diff --git a/actionpack/lib/action_view/helpers/ajax_helper.rb b/actionpack/lib/action_view/helpers/ajax_helper.rb new file mode 100644 index 0000000000..9cc2acc239 --- /dev/null +++ b/actionpack/lib/action_view/helpers/ajax_helper.rb @@ -0,0 +1,68 @@ +module ActionView + module Helpers + module AjaxHelper + include UrlHelper + + def link_to_remote(name, url, options = {}) + html = options.delete(:html) || {} + + update = options.delete(:update) + if update.is_a?(Hash) + html["data-update-success"] = update[:success] + html["data-update-failure"] = update[:failure] + else + html["data-update-success"] = update + end + + html["data-update-position"] = options.delete(:position) + html["data-method"] = options.delete(:method) + html["data-remote"] = "true" + + html.merge!(options) + + url = url_for(url) if url.is_a?(Hash) + link_to(name, url, html) + end + + def button_to_remote(name, options = {}, html_options = {}) + url = options.delete(:url) + url = url_for(url) if url.is_a?(Hash) + + html_options.merge!(:type => "button", :value => name, + :"data-url" => url) + + tag(:input, html_options) + end + + module Rails2Compatibility + def set_callbacks(options, html) + [:complete, :failure, :success, :interactive, :loaded, :loading].each do |type| + html["data-#{type}-code"] = options.delete(type.to_sym) + end + + options.each do |option, value| + if option.is_a?(Integer) + html["data-#{option}-code"] = options.delete(option) + end + end + end + + def link_to_remote(name, url, options = nil) + if !options && url.is_a?(Hash) && url.key?(:url) + url, options = url.delete(:url), url + end + + set_callbacks(options, options[:html] ||= {}) + + super + end + + def button_to_remote(name, options = {}, html_options = {}) + set_callbacks(options, html_options) + super + end + end + + end + end +end \ No newline at end of file diff --git a/actionpack/test/javascript/ajax_test.rb b/actionpack/test/javascript/ajax_test.rb new file mode 100644 index 0000000000..b67a91dad3 --- /dev/null +++ b/actionpack/test/javascript/ajax_test.rb @@ -0,0 +1,115 @@ +require "abstract_unit" + +class AjaxTestCase < ActiveSupport::TestCase + include ActionView::Helpers::AjaxHelper + include ActionView::Helpers::TagHelper + + def assert_html(html, matches) + matches.each do |match| + assert_match Regexp.new(Regexp.escape(match)), html + end + end + + def self.assert_callbacks_work(&blk) + define_method(:assert_callbacks_work, &blk) + + [:complete, :failure, :success, :interactive, :loaded, :loading, 404].each do |callback| + test "#{callback} callback" do + markup = assert_callbacks_work(callback) + assert_html markup, %W(data-#{callback}-code="undoRequestCompleted\(request\)") + end + end + end +end + +class LinkToRemoteTest < AjaxTestCase + def url_for(hash) + "/blog/destroy/4" + end + + def link(options = {}) + link_to_remote("Delete this post", "/blog/destroy/3", options) + end + + test "with no update" do + assert_html link, %w(href="/blog/destroy/3" Delete\ this\ post data-remote="true") + end + + test "basic" do + assert_html link(:update => "#posts"), + %w(data-update-success="#posts") + end + + test "using a url hash" do + link = link_to_remote("Delete this post", {:controller => :blog}, :update => "#posts") + assert_html link, %w(href="/blog/destroy/4" data-update-success="#posts") + end + + test "with :html options" do + expected = %{Delete this post} + assert_equal expected, link(:update => "#posts", :html => {"data-custom" => "me"}) + end + + test "with a hash for :update" do + link = link(:update => {:success => "#posts", :failure => "#error"}) + assert_match /data-update-success="#posts"/, link + assert_match /data-update-failure="#error"/, link + end + + test "with positional parameters" do + link = link(:position => :top, :update => "#posts") + assert_match /data\-update\-position="top"/, link + end + + test "with an optional method" do + link = link(:method => "delete") + assert_match /data-method="delete"/, link + end + + class LegacyLinkToRemoteTest < AjaxTestCase + include ActionView::Helpers::AjaxHelper::Rails2Compatibility + + def link(options) + link_to_remote("Delete this post", "/blog/destroy/3", options) + end + + test "basic link_to_remote with :url =>" do + expected = %{Delete this post} + assert_equal expected, + link_to_remote("Delete this post", :url => "/blog/destroy/3", :update => "#posts") + end + + assert_callbacks_work do |callback| + link(callback => "undoRequestCompleted(request)") + end + end +end + +class ButtonToRemoteTest < AjaxTestCase + def button(options, html = {}) + button_to_remote("Remote outpost", options, html) + end + + def url_for(*) + "/whatnot" + end + + class StandardTest < ButtonToRemoteTest + test "basic" do + button = button({:url => {:action => "whatnot"}}, {:class => "fine"}) + [/input/, /class="fine"/, /type="button"/, /value="Remote outpost"/, + /data-url="\/whatnot"/].each do |match| + assert_match match, button + end + end + end + + class LegacyButtonToRemoteTest < ButtonToRemoteTest + include ActionView::Helpers::AjaxHelper::Rails2Compatibility + + assert_callbacks_work do |callback| + button(callback => "undoRequestCompleted(request)") + end + end + +end \ No newline at end of file -- cgit v1.2.3 From e7a2496fda92ff4e39c127b065bf51ed6301e772 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 20 Jul 2009 18:02:24 -0700 Subject: Vendor the bundler project --- railties/lib/vendor/bundler/LICENSE | 20 +++ railties/lib/vendor/bundler/Rakefile | 52 +++++++ railties/lib/vendor/bundler/bin/gem_bundler | 40 +++++ railties/lib/vendor/bundler/lib/bundler.rb | 24 +++ railties/lib/vendor/bundler/lib/bundler/cli.rb | 24 +++ .../lib/vendor/bundler/lib/bundler/dependency.rb | 35 +++++ railties/lib/vendor/bundler/lib/bundler/finder.rb | 42 +++++ .../lib/vendor/bundler/lib/bundler/gem_bundle.rb | 23 +++ .../bundler/lib/bundler/gem_specification.rb | 10 ++ .../lib/vendor/bundler/lib/bundler/installer.rb | 44 ++++++ .../lib/vendor/bundler/lib/bundler/manifest.rb | 130 ++++++++++++++++ .../lib/vendor/bundler/lib/bundler/resolver.rb | 19 +++ .../bundler/lib/bundler/resolver/builders.rb | 61 ++++++++ .../vendor/bundler/lib/bundler/resolver/engine.rb | 38 +++++ .../vendor/bundler/lib/bundler/resolver/inspect.rb | 24 +++ .../vendor/bundler/lib/bundler/resolver/search.rb | 71 +++++++++ .../vendor/bundler/lib/bundler/resolver/stack.rb | 72 +++++++++ .../vendor/bundler/lib/bundler/resolver/state.rb | 172 +++++++++++++++++++++ railties/lib/vendor/bundler/lib/bundler/runtime.rb | 39 +++++ 19 files changed, 940 insertions(+) create mode 100644 railties/lib/vendor/bundler/LICENSE create mode 100644 railties/lib/vendor/bundler/Rakefile create mode 100755 railties/lib/vendor/bundler/bin/gem_bundler create mode 100644 railties/lib/vendor/bundler/lib/bundler.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/cli.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/dependency.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/finder.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/gem_specification.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/installer.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/manifest.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/search.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/state.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/runtime.rb diff --git a/railties/lib/vendor/bundler/LICENSE b/railties/lib/vendor/bundler/LICENSE new file mode 100644 index 0000000000..41decca113 --- /dev/null +++ b/railties/lib/vendor/bundler/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2009 Engine Yard + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/railties/lib/vendor/bundler/Rakefile b/railties/lib/vendor/bundler/Rakefile new file mode 100644 index 0000000000..fd39fbff77 --- /dev/null +++ b/railties/lib/vendor/bundler/Rakefile @@ -0,0 +1,52 @@ +require 'rubygems' unless ENV['NO_RUBYGEMS'] +require 'rake/gempackagetask' +require 'rubygems/specification' +require 'date' +require 'spec/rake/spectask' + +spec = Gem::Specification.new do |s| + s.name = "bundler" + s.version = "0.0.1" + s.author = "Your Name" + s.email = "Your Email" + s.homepage = "http://example.com" + s.description = s.summary = "A gem that provides..." + + s.platform = Gem::Platform::RUBY + s.has_rdoc = true + s.extra_rdoc_files = ["README", "LICENSE"] + s.summary = "" + + # Uncomment this to add a dependency + # s.add_dependency "foo" + + s.bindir = "bin" + s.executables = %w( gem_bundler ) + s.require_path = 'lib' + s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*") +end + +task :default => :spec + +desc "Run specs" +Spec::Rake::SpecTask.new do |t| + t.spec_files = FileList['spec/**/*_spec.rb'] + t.spec_opts = %w(-fs --color) +end + + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + +desc "install the gem locally" +task :install => [:package] do + sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}} +end + +desc "create a gemspec file" +task :make_spec do + File.open("#{GEM}.gemspec", "w") do |file| + file.puts spec.to_ruby + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/bin/gem_bundler b/railties/lib/vendor/bundler/bin/gem_bundler new file mode 100755 index 0000000000..7a8771efee --- /dev/null +++ b/railties/lib/vendor/bundler/bin/gem_bundler @@ -0,0 +1,40 @@ +#!/usr/bin/env ruby +require "optparse" +require "bundler" + +options = {} + +parser = OptionParser.new do |op| + op.banner = "Usage: gem_bundler [OPTIONS] [PATH]" + + op.on("-m", "--manifest MANIFEST") do |manifest| + options[:manifest] = manifest + end + + op.on_tail("-h", "--help", "Show this message") do + puts op + exit + end +end +parser.parse! + +options[:path] = ARGV.shift + +unless options[:path] + puts parser + puts %( + [PATH] must be specified + ) + exit +end + +unless options[:manifest] && File.exist?(options[:manifest]) + puts parser + puts %( + MANIFEST must be a valid manifest file + ) + exit +end + + +Bundler.run(options) \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler.rb b/railties/lib/vendor/bundler/lib/bundler.rb new file mode 100644 index 0000000000..4dcab20da9 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler.rb @@ -0,0 +1,24 @@ +require 'logger' +require 'set' +# Required elements of rubygems +require "rubygems/remote_fetcher" +require "rubygems/installer" + +require "bundler/gem_bundle" +require "bundler/installer" +require "bundler/finder" +require "bundler/gem_specification" +require "bundler/resolver" +require "bundler/manifest" +require "bundler/dependency" +require "bundler/runtime" +require "bundler/cli" + +module Bundler + VERSION = "0.5.0" + + def self.run(options = {}) + manifest = ManifestBuilder.load(options[:path], options[:manifest]) + manifest.install + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/cli.rb b/railties/lib/vendor/bundler/lib/bundler/cli.rb new file mode 100644 index 0000000000..ed49ef88f0 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/cli.rb @@ -0,0 +1,24 @@ +module Bundler + module CLI + + def default_manifest + current = Pathname.new(Dir.pwd) + + begin + manifest = current.join("Gemfile") + return manifest.to_s if File.exist?(manifest) + current = current.parent + end until current.root? + nil + end + + module_function :default_manifest + + def default_path + Pathname.new(File.dirname(default_manifest)).join("vendor").join("gems").to_s + end + + module_function :default_path + + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/dependency.rb b/railties/lib/vendor/bundler/lib/bundler/dependency.rb new file mode 100644 index 0000000000..739a7a117d --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/dependency.rb @@ -0,0 +1,35 @@ +module Bundler + class Dependency + + attr_reader :name, :version, :require_as, :only, :except + + def initialize(name, options = {}) + options.each do |k, v| + options[k.to_s] = v + end + + @name = name + @version = options["version"] || ">= 0" + @require_as = Array(options["require_as"] || name) + @only = Array(options["only"]).map {|e| e.to_s } if options["only"] + @except = Array(options["except"]).map {|e| e.to_s } if options["except"] + end + + def in?(environment) + environment = environment.to_s + + return false unless !@only || @only.include?(environment) + return false if @except && @except.include?(environment) + true + end + + def to_s + to_gem_dependency.to_s + end + + def to_gem_dependency + @gem_dep ||= Gem::Dependency.new(name, version) + end + + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/finder.rb b/railties/lib/vendor/bundler/lib/bundler/finder.rb new file mode 100644 index 0000000000..43ff370ae4 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/finder.rb @@ -0,0 +1,42 @@ +module Bundler + class Finder + def initialize(*sources) + @results = {} + @index = Hash.new { |h,k| h[k] = {} } + + sources.each { |source| fetch(source) } + end + + def resolve(*dependencies) + resolved = Resolver.resolve(dependencies, self) + resolved && GemBundle.new(resolved.all_specs) + end + + def fetch(source) + deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{source}/Marshal.4.8.Z") + inflated = Gem.inflate deflated + + append(Marshal.load(inflated), source) + rescue Gem::RemoteFetcher::FetchError => e + raise ArgumentError, "#{source} is not a valid source: #{e.message}" + end + + def append(index, source) + index.gems.values.each do |spec| + next unless Gem::Platform.match(spec.platform) + spec.source = source + @index[spec.name][spec.version] ||= spec + end + self + end + + def search(dependency) + @results[dependency.hash] ||= begin + possibilities = @index[dependency.name].values + possibilities.select do |spec| + dependency =~ spec + end.sort_by {|s| s.version } + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb b/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb new file mode 100644 index 0000000000..b749720fd9 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb @@ -0,0 +1,23 @@ +module Bundler + class GemBundle < Array + def download(directory) + FileUtils.mkdir_p(directory) + + current = Dir[File.join(directory, "cache", "*.gem*")] + + each do |spec| + cached = File.join(directory, "cache", "#{spec.full_name}.gem") + + unless File.file?(cached) + Gem::RemoteFetcher.fetcher.download(spec, spec.source, directory) + end + + current.delete(cached) + end + + current.each { |file| File.delete(file) } + + self + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb b/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb new file mode 100644 index 0000000000..680b61fd69 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb @@ -0,0 +1,10 @@ +module Gem + class Specification + attribute :source + + def source=(source) + @source = source.is_a?(URI) ? source : URI.parse(source) + raise ArgumentError, "The source must be an absolute URI" unless @source.absolute? + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/installer.rb b/railties/lib/vendor/bundler/lib/bundler/installer.rb new file mode 100644 index 0000000000..581d9d3c8e --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/installer.rb @@ -0,0 +1,44 @@ +module Bundler + class Installer + def initialize(path) + if !File.directory?(path) + raise ArgumentError, "#{path} is not a directory" + elsif !File.directory?(File.join(path, "cache")) + raise ArgumentError, "#{path} is not a valid environment (it does not contain a cache directory)" + end + + @path = path + @gems = Dir[(File.join(path, "cache", "*.gem"))] + end + + def install(options = {}) + bin_dir = options[:bin_dir] ||= File.join(@path, "bin") + + specs = Dir[File.join(@path, "specifications", "*.gemspec")] + gems = Dir[File.join(@path, "gems", "*")] + + @gems.each do |gem| + name = File.basename(gem).gsub(/\.gem$/, '') + installed = specs.any? { |g| File.basename(g) == "#{name}.gemspec" } && + gems.any? { |g| File.basename(g) == name } + + unless installed + installer = Gem::Installer.new(gem, :install_dir => @path, + :ignore_dependencies => true, + :env_shebang => true, + :wrappers => true, + :bin_dir => bin_dir) + installer.install + end + + # remove this spec + specs.delete_if { |g| File.basename(g) == "#{name}.gemspec"} + gems.delete_if { |g| File.basename(g) == name } + end + + (specs + gems).each do |path| + FileUtils.rm_rf(path) + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/manifest.rb b/railties/lib/vendor/bundler/lib/bundler/manifest.rb new file mode 100644 index 0000000000..847a41940d --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/manifest.rb @@ -0,0 +1,130 @@ +require "rubygems/source_index" +require "pathname" + +module Bundler + class VersionConflict < StandardError; end + + class Manifest + attr_reader :sources, :dependencies, :path + + def initialize(sources, dependencies, path) + sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) } + @sources, @dependencies, @path = sources, dependencies, Pathname.new(path) + end + + def fetch + return if all_gems_installed? + + finder = Finder.new(*sources) + unless bundle = finder.resolve(*gem_dependencies) + gems = @dependencies.map {|d| " #{d.to_s}" }.join("\n") + raise VersionConflict, "No compatible versions could be found for:\n#{gems}" + end + + bundle.download(@path) + end + + def install(options = {}) + fetch + installer = Installer.new(@path) + installer.install # options come here + create_load_paths_files(File.join(@path, "environments")) + create_fake_rubygems(File.join(@path, "environments")) + end + + def activate(environment = "default") + require File.join(@path, "environments", "#{environment}.rb") + end + + def require_all + dependencies.each do |dep| + dep.require_as.each {|file| require file } + end + end + + def gems_for(environment) + deps = dependencies.select { |d| d.in?(environment) } + deps.map! { |d| d.to_gem_dependency } + index = Gem::SourceIndex.from_gems_in(File.join(@path, "specifications")) + Resolver.resolve(deps, index).all_specs + end + + def environments + envs = dependencies.map {|dep| Array(dep.only) + Array(dep.except) }.flatten + envs << "default" + end + + private + + def gem_dependencies + @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency } + end + + def all_gems_installed? + gem_versions = {} + + Dir[File.join(@path, "cache", "*.gem")].each do |file| + file =~ /\/([^\/]+)-([\d\.]+)\.gem$/ + name, version = $1, $2 + gem_versions[name] = Gem::Version.new(version) + end + + gem_dependencies.all? do |dep| + gem_versions[dep.name] && + dep.version_requirements.satisfied_by?(gem_versions[dep.name]) + end + end + + def create_load_paths_files(path) + FileUtils.mkdir_p(path) + environments.each do |environment| + gem_specs = gems_for(environment) + File.open(File.join(path, "#{environment}.rb"), "w") do |file| + file.puts <<-RUBY_EVAL + module Bundler + def self.rubygems_required + #{create_gem_stubs(path, gem_specs)} + end + end + RUBY_EVAL + file.puts "$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))" + load_paths_for_specs(gem_specs).each do |load_path| + file.puts "$LOAD_PATH.unshift #{load_path.inspect}" + end + end + end + end + + def create_gem_stubs(path, gem_specs) + gem_specs.map do |spec| + path = File.expand_path(File.join(path, '..', 'specifications', "#{spec.full_name}.gemspec")) + %{ + Gem.loaded_specs["#{spec.name}"] = eval(File.read("#{path}")) + } + end.join("\n") + end + + def create_fake_rubygems(path) + File.open(File.join(path, "rubygems.rb"), "w") do |file| + file.puts <<-RUBY_EVAL + $:.delete File.expand_path(File.dirname(__FILE__)) + load "rubygems.rb" + if defined?(Bundler) && Bundler.respond_to?(:rubygems_required) + Bundler.rubygems_required + end + RUBY_EVAL + end + end + + def load_paths_for_specs(specs) + load_paths = [] + specs.each do |spec| + load_paths << File.join(spec.full_gem_path, spec.bindir) if spec.bindir + spec.require_paths.each do |path| + load_paths << File.join(spec.full_gem_path, path) + end + end + load_paths + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver.rb b/railties/lib/vendor/bundler/lib/bundler/resolver.rb new file mode 100644 index 0000000000..1ec89e53c4 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver.rb @@ -0,0 +1,19 @@ +require 'bundler/resolver/inspect' +require 'bundler/resolver/search' +require 'bundler/resolver/engine' +require 'bundler/resolver/stack' +require 'bundler/resolver/state' + +module Bundler + module Resolver + def self.resolve(deps, source_index = Gem.source_index, logger = nil) + unless logger + logger = Logger.new($stderr) + logger.datetime_format = "" + logger.level = ENV["GEM_RESOLVER_DEBUG"] ? Logger::DEBUG : Logger::ERROR + end + + Engine.resolve(deps, source_index, logger) + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb new file mode 100644 index 0000000000..2b7b48211c --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb @@ -0,0 +1,61 @@ +module Bundler + module Resolver + module Builders + def build_index(&block) + index = Gem::SourceIndex.new + IndexBuilder.run(index, &block) if block_given? + index + end + + def build_spec(name, version, &block) + spec = Gem::Specification.new + spec.instance_variable_set(:@name, name) + spec.instance_variable_set(:@version, Gem::Version.new(version)) + DepBuilder.run(spec, &block) if block_given? + spec + end + + def build_dep(name, requirements, type = :runtime) + Gem::Dependency.new(name, requirements, type) + end + + class IndexBuilder + include Builders + + def self.run(index, &block) + new(index).run(&block) + end + + def initialize(index) + @index = index + end + + def run(&block) + instance_eval(&block) + end + + def add_spec(*args, &block) + @index.add_spec(build_spec(*args, &block)) + end + end + + class DepBuilder + def self.run(spec, &block) + new(spec).run(&block) + end + + def initialize(spec) + @spec = spec + end + + def run(&block) + instance_eval(&block) + end + + def runtime(name, requirements) + @spec.add_runtime_dependency(name, requirements) + end + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb new file mode 100644 index 0000000000..475ba516ff --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb @@ -0,0 +1,38 @@ +module Bundler + module Resolver + class ClosedSet < Set + end + + class Engine + include Search, Inspect + + def self.resolve(deps, source_index, logger) + new(deps, source_index, logger).resolve + end + + def initialize(deps, source_index, logger) + @deps, @source_index, @logger = deps, source_index, logger + logger.debug "searching for #{gem_resolver_inspect(@deps)}" + end + attr_reader :deps, :source_index, :logger, :solution + + def resolve + state = State.initial(self, [], Stack.new, Stack.new([[[], @deps.dup]])) + if solution = search(state) + logger.info "got the solution with #{solution.all_specs.size} specs" + solution.dump(Logger::INFO) + solution + end + end + + def open + @open ||= [] + end + + def closed + @closed ||= ClosedSet.new + end + end + end + +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb new file mode 100644 index 0000000000..59640aa5f3 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb @@ -0,0 +1,24 @@ +module Bundler + module Resolver + module Inspect + def gem_resolver_inspect(o) + case o + when Gem::Specification + "#" + when Array + '[' + o.map {|x| gem_resolver_inspect(x)}.join(", ") + ']' + when Set + gem_resolver_inspect(o.to_a) + when Hash + '{' + o.map {|k,v| "#{gem_resolver_inspect(k)} => #{gem_resolver_inspect(v)}"}.join(", ") + '}' + when Stack + o.gem_resolver_inspect + else + o.inspect + end + end + + module_function :gem_resolver_inspect + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb new file mode 100644 index 0000000000..34102ff04c --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb @@ -0,0 +1,71 @@ +module Bundler + module Resolver + module Search + def search(initial, max_depth = (1.0 / 0.0)) + if initial.goal_met? + return initial + end + + open << initial + + while open.any? + current = open.pop + closed << current + + new = [] + current.each_possibility do |attempt| + unless closed.include?(attempt) + if attempt.goal_met? + return attempt + elsif attempt.depth < max_depth + new << attempt + end + end + end + new.reverse.each do |state| + open << state + end + end + + nil + end + + def open + raise "implement #open in #{self.class}" + end + + def closed + raise "implement #closed in #{self.class}" + end + + module Node + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + def initial(*data) + new(0, *data) + end + end + + def initialize(depth) + @depth = depth + end + attr_reader :depth + + def child(*data) + self.class.new(@depth + 1, *data) + end + + def each_possibility + raise "implement #each_possibility on #{self.class}" + end + + def goal_met? + raise "implement #goal_met? on #{self.class}" + end + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb new file mode 100644 index 0000000000..6e1ac67e1f --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb @@ -0,0 +1,72 @@ +module Bundler + module Resolver + class Stack + def initialize(initial = []) + @data = [] + initial.each do |(path,value)| + self[path] = value + end + end + + def last + @data.last + end + + def []=(path, value) + raise ArgumentError, "#{path.inspect} already has a value" if key?(path) + @data << [path.dup, value] + end + + def [](path) + if key?(path) + _, value = @data.find do |(k,v)| + k == path + end + value + else + raise "No value for #{path.inspect}" + end + end + + def key?(path) + @data.any? do |(k,v)| + k == path + end + end + + def each + @data.each do |(k,v)| + yield k, v + end + end + + def map + @data.map do |(k,v)| + yield k, v + end + end + + def each_value + @data.each do |(k,v)| + yield v + end + end + + def dup + self.class.new(@data.dup) + end + + def to_s + @data.to_s + end + + def inspect + @data.inspect + end + + def gem_resolver_inspect + Inspect.gem_resolver_inspect(@data) + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb new file mode 100644 index 0000000000..f13ecbbee7 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb @@ -0,0 +1,172 @@ +module Bundler + module Resolver + class State + include Search::Node, Inspect + + def initialize(depth, engine, path, spec_stack, dep_stack) + super(depth) + @engine, @path, @spec_stack, @dep_stack = engine, path, spec_stack, dep_stack + end + attr_reader :path + + def logger + @engine.logger + end + + def goal_met? + logger.info "checking if goal is met" + dump + no_duplicates? + all_deps.all? do |dep| + dependency_satisfied?(dep) + end + end + + def no_duplicates? + names = [] + all_specs.each do |s| + if names.include?(s.name) + raise "somehow got duplicates for #{s.name}" + end + names << s.name + end + end + + def dependency_satisfied?(dep) + all_specs.any? do |spec| + spec.satisfies_requirement?(dep) + end + end + + def each_possibility(&block) + index, dep = remaining_deps.first + if dep + logger.warn "working on #{dep} for #{spec_name}" + handle_dep(index, dep, &block) + else + logger.warn "no dependencies left for #{spec_name}" + jump_to_parent(&block) + end + end + + def handle_dep(index, dep) + specs = @engine.source_index.search(dep) + + specs.reverse.each do |s| + logger.info "attempting with spec: #{s.full_name}" + new_path = @path + [index] + new_spec_stack = @spec_stack.dup + new_dep_stack = @dep_stack.dup + + new_spec_stack[new_path] = s + new_dep_stack[new_path] = s.runtime_dependencies.sort_by do |dep| + @engine.source_index.search(dep).size + end + yield child(@engine, new_path, new_spec_stack, new_dep_stack) + end + end + + def jump_to_parent + if @path.empty? + dump + logger.warn "at the end" + return + end + + logger.info "jumping to parent for #{spec_name}" + new_path = @path[0..-2] + new_spec_stack = @spec_stack.dup + new_dep_stack = @dep_stack.dup + + yield child(@engine, new_path, new_spec_stack, new_dep_stack) + end + + def remaining_deps + remaining_deps_for(@path) + end + + def remaining_deps_for(path) + no_duplicates? + remaining = [] + @dep_stack[path].each_with_index do |dep,i| + remaining << [i, dep] unless all_specs.find {|s| s.name == dep.name} + end + remaining + end + + def deps + @dep_stack[@path] + end + + def spec + @spec_stack[@path] + end + + def spec_name + @path.empty? ? "" : spec.full_name + end + + def all_deps + all_deps = Set.new + @dep_stack.each_value do |deps| + all_deps.merge(deps) + end + all_deps.to_a + end + + def all_specs + @spec_stack.map do |path,spec| + spec + end + end + + def dump(level = Logger::DEBUG) + logger.add level, "v" * 80 + logger.add level, "path: #{@path.inspect}" + logger.add level, "deps: (#{deps.size})" + deps.map do |dep| + logger.add level, gem_resolver_inspect(dep) + end + logger.add level, "remaining_deps: (#{remaining_deps.size})" + remaining_deps.each do |dep| + logger.add level, gem_resolver_inspect(dep) + end + logger.add level, "dep_stack: " + @dep_stack.each do |path,deps| + logger.add level, "#{path.inspect} (#{deps.size})" + deps.each do |dep| + logger.add level, "-> #{gem_resolver_inspect(dep)}" + end + end + logger.add level, "spec_stack: " + @spec_stack.each do |path,spec| + logger.add level, "#{path.inspect}: #{gem_resolver_inspect(spec)}" + end + logger.add level, "^" * 80 + end + + def to_dot + io = StringIO.new + io.puts 'digraph deps {' + io.puts ' fontname = "Courier";' + io.puts ' mincross = 4.0;' + io.puts ' ratio = "auto";' + dump_to_dot(io, "", []) + io.puts '}' + io.string + end + + def dump_to_dot(io, name, path) + @dep_stack[path].each_with_index do |dep,i| + new_path = path + [i] + spec_name = all_specs.find {|x| x.name == dep.name}.full_name + io.puts ' "%s" -> "%s";' % [name, dep.to_s] + io.puts ' "%s" -> "%s";' % [dep.to_s, spec_name] + if @spec_stack.key?(new_path) + dump_to_dot(io, spec_name, new_path) + end + end + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/runtime.rb b/railties/lib/vendor/bundler/lib/bundler/runtime.rb new file mode 100644 index 0000000000..2376deef28 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/runtime.rb @@ -0,0 +1,39 @@ +module Bundler + class ManifestBuilder + + attr_reader :sources + + def self.build(path, string) + builder = new(path) + builder.instance_eval(string) + builder.to_manifest + end + + def self.load(path, file) + string = File.read(file) + build(path, string) + end + + def initialize(path) + @path = path + @sources = %w(http://gems.rubyforge.org) + @dependencies = [] + end + + def to_manifest + Manifest.new(@sources, @dependencies, @path) + end + + def source(source) + @sources << source + end + + def gem(name, *args) + options = args.last.is_a?(Hash) ? args.pop : {} + version = args.last + + @dependencies << Dependency.new(name, options.merge(:version => version)) + end + + end +end \ No newline at end of file -- cgit v1.2.3 From 2685d93b0728b647b6f49f3e1802c779d5fb9867 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 20 Jul 2009 23:28:58 -0500 Subject: Kill AMo ivar attributes helper --- activemodel/lib/active_model.rb | 1 - activemodel/lib/active_model/attributes.rb | 25 ------------------ activemodel/lib/active_model/serializers/json.rb | 1 - activemodel/lib/active_model/serializers/xml.rb | 1 - activemodel/test/cases/attributes_test.rb | 30 ---------------------- .../serializeration/json_serialization_test.rb | 4 +++ .../serializeration/xml_serialization_test.rb | 4 +++ 7 files changed, 8 insertions(+), 58 deletions(-) delete mode 100644 activemodel/lib/active_model/attributes.rb delete mode 100644 activemodel/test/cases/attributes_test.rb diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index c6f63d2fdc..eed74731fa 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -27,7 +27,6 @@ require 'active_support' module ActiveModel autoload :APICompliant, 'active_model/api_compliant' - autoload :Attributes, 'active_model/attributes' autoload :Base, 'active_model/base' autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' autoload :Errors, 'active_model/errors' diff --git a/activemodel/lib/active_model/attributes.rb b/activemodel/lib/active_model/attributes.rb deleted file mode 100644 index ea8c8d5f72..0000000000 --- a/activemodel/lib/active_model/attributes.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'active_support/core_ext/object/instance_variables' - -module ActiveModel - module Attributes - def self.append_features(base) - unless base.instance_methods.include?('attributes') - super - else - false - end - end - - def attributes - instance_values - end - - def read_attribute(attr_name) - instance_variable_get(:"@#{attr_name}") - end - - def write_attribute(attr_name, value) - instance_variable_set(:"@#{attr_name}", value) - end - end -end diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb index adf200597d..e94512fd64 100644 --- a/activemodel/lib/active_model/serializers/json.rb +++ b/activemodel/lib/active_model/serializers/json.rb @@ -5,7 +5,6 @@ module ActiveModel module Serializers module JSON extend ActiveSupport::Concern - include ActiveModel::Attributes included do extend ActiveModel::Naming diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index 7cdd281223..76a0e54a56 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -5,7 +5,6 @@ module ActiveModel module Serializers module Xml extend ActiveSupport::Concern - include ActiveModel::Attributes class Serializer < ActiveModel::Serializer #:nodoc: class Attribute #:nodoc: diff --git a/activemodel/test/cases/attributes_test.rb b/activemodel/test/cases/attributes_test.rb deleted file mode 100644 index 5f3ea839a4..0000000000 --- a/activemodel/test/cases/attributes_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'cases/helper' - -class AttributesTest < ActiveModel::TestCase - class Person - include ActiveModel::Attributes - attr_accessor :name - end - - test "reads attribute" do - p = Person.new - assert_equal nil, p.read_attribute(:name) - - p.name = "Josh" - assert_equal "Josh", p.read_attribute(:name) - end - - test "writes attribute" do - p = Person.new - assert_equal nil, p.name - - p.write_attribute(:name, "Josh") - assert_equal "Josh", p.name - end - - test "returns all attributes" do - p = Person.new - p.name = "Josh" - assert_equal({"name" => "Josh"}, p.attributes) - end -end diff --git a/activemodel/test/cases/serializeration/json_serialization_test.rb b/activemodel/test/cases/serializeration/json_serialization_test.rb index 3e69db110e..6227aedc39 100644 --- a/activemodel/test/cases/serializeration/json_serialization_test.rb +++ b/activemodel/test/cases/serializeration/json_serialization_test.rb @@ -3,6 +3,10 @@ require 'models/contact' class Contact include ActiveModel::Serializers::JSON + + def attributes + instance_values + end end class JsonSerializationTest < ActiveModel::TestCase diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb index 57792e900e..e459f6433a 100644 --- a/activemodel/test/cases/serializeration/xml_serialization_test.rb +++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb @@ -3,6 +3,10 @@ require 'models/contact' class Contact include ActiveModel::Serializers::Xml + + def attributes + instance_values + end end class XmlSerializationTest < ActiveModel::TestCase -- cgit v1.2.3 From 7c84bbf1607bf4059de04cc4c8ec84df2334574b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 20 Jul 2009 23:57:01 -0500 Subject: Add wrap_with_notifications helper to AMo observing --- activemodel/lib/active_model/observing.rb | 22 ++++++++++++++++++++-- activerecord/lib/active_record/callbacks.rb | 2 +- activeresource/lib/active_resource.rb | 1 + activeresource/lib/active_resource/base.rb | 13 +++---------- activeresource/lib/active_resource/observing.rb | 10 ++++++++++ 5 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 activeresource/lib/active_resource/observing.rb diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 7bad2397ae..707b1a0da6 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -1,7 +1,8 @@ require 'observer' require 'singleton' -require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/module/aliasing' +require 'active_support/core_ext/string/inflections' module ActiveModel module Observing @@ -39,6 +40,23 @@ module ActiveModel observers.each { |o| instantiate_observer(o) } end + # Wraps methods with before and after notifications. + # + # wrap_with_notifications :create, :save, :update, :destroy + def wrap_with_notifications(*methods) + methods.each do |method| + class_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{method}_with_notifications(*args, &block) + notify_observers(:before_#{method}) + result = #{method}_without_notifications(*args, &block) + notify_observers(:after_#{method}) + result + end + EOS + alias_method_chain(method, :notifications) + end + end + protected def instantiate_observer(observer) # string/symbol @@ -60,7 +78,7 @@ module ActiveModel end private - def notify(method) #:nodoc: + def notify_observers(method) self.class.changed self.class.notify_observers(method, self) end diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 01e41c04df..3aa0b8f1b5 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -349,7 +349,7 @@ module ActiveRecord result = send(method) end - notify(method) + notify_observers(method) return result end diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index 1dcb795a7d..fd4c199b48 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -34,6 +34,7 @@ module ActiveResource autoload :Connection, 'active_resource/connection' autoload :CustomMethods, 'active_resource/custom_methods' autoload :Formats, 'active_resource/formats' + autoload :Observing, 'active_resource/observing' autoload :Validations, 'active_resource/validations' autoload :HttpMock, 'active_resource/http_mock' end diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 88a431a6d9..bc82139dac 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -804,8 +804,7 @@ module ActiveResource # my_company.size = 10 # my_company.save # sends PUT /companies/1 (update) def save - notify(:before_save) - (new? ? create : update).tap { notify(:after_save) } + new? ? create : update end # Deletes the resource from the remote service. @@ -821,8 +820,7 @@ module ActiveResource # new_person.destroy # Person.find(new_id) # 404 (Resource Not Found) def destroy - notify(:before_destroy) - connection.delete(element_path, self.class.headers).tap { notify(:after_destroy) } + connection.delete(element_path, self.class.headers) end # Evaluates to true if this resource is not new? and is @@ -997,20 +995,16 @@ module ActiveResource # Update the resource on the remote service. def update - notify(:before_update) connection.put(element_path(prefix_options), encode, self.class.headers).tap do |response| load_attributes_from_response(response) - notify(:after_update) end end # Create (i.e., \save to the remote service) the \new resource. def create - notify(:before_create) connection.post(collection_path, encode, self.class.headers).tap do |response| self.id = id_from_response(response) load_attributes_from_response(response) - notify(:after_create) end end @@ -1093,7 +1087,6 @@ module ActiveResource class Base extend ActiveModel::Naming - include CustomMethods, Validations - include ActiveModel::Observing + include CustomMethods, Observing, Validations end end diff --git a/activeresource/lib/active_resource/observing.rb b/activeresource/lib/active_resource/observing.rb new file mode 100644 index 0000000000..94836f4bb1 --- /dev/null +++ b/activeresource/lib/active_resource/observing.rb @@ -0,0 +1,10 @@ +module ActiveResource + module Observing + extend ActiveSupport::Concern + include ActiveModel::Observing + + included do + wrap_with_notifications :create, :save, :update, :destroy + end + end +end -- cgit v1.2.3 From 48bc39e03a077ed9a0684a181c1180c7f53b0cad Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 21 Jul 2009 00:11:26 -0500 Subject: Improve AMo observing docs --- activemodel/lib/active_model/observing.rb | 77 +++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 707b1a0da6..3b230c43b9 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -42,7 +42,7 @@ module ActiveModel # Wraps methods with before and after notifications. # - # wrap_with_notifications :create, :save, :update, :destroy + # wrap_with_notifications :create, :save, :update, :destroy def wrap_with_notifications(*methods) methods.each do |method| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -58,7 +58,7 @@ module ActiveModel end protected - def instantiate_observer(observer) + def instantiate_observer(observer) #:nodoc: # string/symbol if observer.respond_to?(:to_sym) observer = observer.to_s.camelize.constantize.instance @@ -78,12 +78,72 @@ module ActiveModel end private + # Fires notifications to model's observers + # + # def save + # notify_observers(:before_save) + # ... + # notify_observers(:after_save) + # end def notify_observers(method) self.class.changed self.class.notify_observers(method, self) end end + # Observer classes respond to lifecycle callbacks to implement trigger-like + # behavior outside the original class. This is a great way to reduce the + # clutter that normally comes when the model class is burdened with + # functionality that doesn't pertain to the core responsibility of the + # class. Example: + # + # class CommentObserver < ActiveModel::Observer + # def after_save(comment) + # Notifications.deliver_comment("admin@do.com", "New comment was posted", comment) + # end + # end + # + # This Observer sends an email when a Comment#save is finished. + # + # class ContactObserver < ActiveModel::Observer + # def after_create(contact) + # contact.logger.info('New contact added!') + # end + # + # def after_destroy(contact) + # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!") + # end + # end + # + # This Observer uses logger to log when specific callbacks are triggered. + # + # == Observing a class that can't be inferred + # + # Observers will by default be mapped to the class with which they share a name. So CommentObserver will + # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer + # differently than the class you're interested in observing, you can use the Observer.observe class method which takes + # either the concrete class (Product) or a symbol for that class (:product): + # + # class AuditObserver < ActiveModel::Observer + # observe :account + # + # def after_update(account) + # AuditTrail.new(account, "UPDATED") + # end + # end + # + # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments: + # + # class AuditObserver < ActiveModel::Observer + # observe :account, :balance + # + # def after_update(record) + # AuditTrail.new(record, "UPDATED") + # end + # end + # + # The AuditObserver will now act on both updates to Account and Balance by treating them both as records. + # class Observer include Singleton @@ -95,6 +155,15 @@ module ActiveModel define_method(:observed_classes) { models } end + # Returns an array of Classes to observe. + # + # You can override this instead of using the +observe+ helper. + # + # class AuditObserver < ActiveModel::Observer + # def self.observed_classes + # [AccountObserver, BalanceObserver] + # end + # end def observed_classes Array.wrap(observed_class) end @@ -115,7 +184,7 @@ module ActiveModel observed_classes.each { |klass| add_observer!(klass) } end - def observed_classes + def observed_classes #:nodoc: self.class.observed_classes end @@ -132,7 +201,7 @@ module ActiveModel end protected - def add_observer!(klass) + def add_observer!(klass) #:nodoc: klass.add_observer(self) end end -- cgit v1.2.3 From 6944b391cddbf1a3ffb3ac4ac588fa4b3d50f430 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 21 Jul 2009 00:13:26 -0500 Subject: Kill AMo Base --- activemodel/lib/active_model.rb | 1 - activemodel/lib/active_model/base.rb | 8 -------- activemodel/test/cases/observing_test.rb | 7 +++++-- 3 files changed, 5 insertions(+), 11 deletions(-) delete mode 100644 activemodel/lib/active_model/base.rb diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index eed74731fa..0665cfbae5 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -27,7 +27,6 @@ require 'active_support' module ActiveModel autoload :APICompliant, 'active_model/api_compliant' - autoload :Base, 'active_model/base' autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' autoload :Errors, 'active_model/errors' autoload :Name, 'active_model/naming' diff --git a/activemodel/lib/active_model/base.rb b/activemodel/lib/active_model/base.rb deleted file mode 100644 index a500adfdf1..0000000000 --- a/activemodel/lib/active_model/base.rb +++ /dev/null @@ -1,8 +0,0 @@ -module ActiveModel - class Base - include Observing - # disabled, until they're tested - # include Callbacks - # include Validations - end -end \ No newline at end of file diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb index 564451fa2f..fbf93c19ef 100644 --- a/activemodel/test/cases/observing_test.rb +++ b/activemodel/test/cases/observing_test.rb @@ -1,6 +1,8 @@ require 'cases/helper' -class ObservedModel < ActiveModel::Base +class ObservedModel + include ActiveModel::Observing + class Observer end end @@ -17,7 +19,8 @@ class FooObserver < ActiveModel::Observer end end -class Foo < ActiveModel::Base +class Foo + include ActiveModel::Observing end class ObservingTest < ActiveModel::TestCase -- cgit v1.2.3 From 92c00d75869b173f44ff9d68f219a99e6dc3bd82 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 21 Jul 2009 00:51:57 -0500 Subject: AMo conversion helper --- ...nder_partial_with_record_identification_test.rb | 3 ++- .../test/controller/record_identifier_test.rb | 3 ++- actionpack/test/controller/redirect_test.rb | 3 ++- actionpack/test/lib/controller/fake_models.rb | 6 ++++-- .../test/template/active_record_helper_test.rb | 9 +++++--- actionpack/test/template/atom_feed_helper_test.rb | 3 ++- actionpack/test/template/form_helper_test.rb | 15 ++++++++----- actionpack/test/template/prototype_helper_test.rb | 12 +++++++---- actionpack/test/template/record_tag_helper_test.rb | 3 ++- actionpack/test/template/test_test.rb | 2 +- actionpack/test/template/url_helper_test.rb | 6 ++++-- activemodel/lib/active_model.rb | 2 +- activemodel/lib/active_model/api_compliant.rb | 25 ---------------------- activemodel/lib/active_model/conversion.rb | 8 +++++++ activerecord/lib/active_record/base.rb | 8 +------ 15 files changed, 53 insertions(+), 55 deletions(-) delete mode 100644 activemodel/lib/active_model/api_compliant.rb create mode 100644 activemodel/lib/active_model/conversion.rb diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index 0122bc7d8f..df50c3dc6f 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -126,7 +126,8 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base end class Game < Struct.new(:name, :id) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion def to_param id.to_s end diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 10f51639cf..44e49ed3f8 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -1,7 +1,8 @@ require 'abstract_unit' class Comment - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id def save; @id = 1 end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index a71c39e504..b3321303c0 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -4,7 +4,8 @@ class WorkshopsController < ActionController::Base end class Workshop - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_accessor :id, :new_record def initialize(id, new_record) diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 07f01e1c47..c6726432ec 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -1,7 +1,8 @@ require "active_model" class Customer < Struct.new(:name, :id) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion def to_param id.to_s @@ -16,7 +17,8 @@ end module Quiz class Question < Struct.new(:name, :id) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion def to_param id.to_s diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index c1bbdae8fb..b07ce6cf5d 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -5,15 +5,18 @@ class ActiveRecordHelperTest < ActionView::TestCase silence_warnings do class Post < Struct.new(:title, :author_name, :body, :secret, :written_on) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion end class User < Struct.new(:email) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion end class Column < Struct.new(:type, :name, :human_name) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion end end diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 89ff01ab0e..3acaecd142 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -1,7 +1,8 @@ require 'abstract_unit' class Scroll < Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion def new_record? true diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 883afd985b..99160dd8b1 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -2,7 +2,8 @@ require 'abstract_unit' silence_warnings do class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion alias_method :secret?, :secret @@ -25,7 +26,8 @@ silence_warnings do end class Comment - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id attr_reader :post_id @@ -43,7 +45,8 @@ silence_warnings do end class Tag - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id attr_reader :post_id @@ -61,7 +64,8 @@ silence_warnings do end class CommentRelevance - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id attr_reader :comment_id @@ -75,7 +79,8 @@ silence_warnings do end class TagRelevance - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id attr_reader :tag_id diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 301a110076..acbf311212 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -1,11 +1,14 @@ require 'abstract_unit' require 'active_model' -Bunny = Struct.new(:Bunny, :id) -Bunny.extend ActiveModel::APICompliant +class Bunny < Struct.new(:Bunny, :id) + extend ActiveModel::Naming + include ActiveModel::Conversion +end class Author - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id def save; @id = 1 end @@ -16,7 +19,8 @@ class Author end class Article - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_reader :id attr_reader :author_id def save; @id = 1; @author_id = 1 end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index bae26f555d..4144fea678 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -1,7 +1,8 @@ require 'abstract_unit' class Post - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion def id 45 end diff --git a/actionpack/test/template/test_test.rb b/actionpack/test/template/test_test.rb index 98307fbae4..f32d0b3d42 100644 --- a/actionpack/test/template/test_test.rb +++ b/actionpack/test/template/test_test.rb @@ -41,7 +41,7 @@ class PeopleHelperTest < ActionView::TestCase def test_link_to_person person = mock(:name => "David") - person.class.extend ActiveModel::APICompliant + person.class.extend ActiveModel::Naming expects(:mocha_mock_path).with(person).returns("/people/1") assert_equal 'David', link_to_person(person) end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 4569689534..9eeb26831c 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -494,7 +494,8 @@ class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase end class Workshop - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_accessor :id, :new_record def initialize(id, new_record) @@ -511,7 +512,8 @@ class Workshop end class Session - extend ActiveModel::APICompliant + extend ActiveModel::Naming + include ActiveModel::Conversion attr_accessor :id, :workshop_id, :new_record def initialize(id, new_record) diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 0665cfbae5..2de19597b1 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -26,7 +26,7 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' module ActiveModel - autoload :APICompliant, 'active_model/api_compliant' + autoload :Conversion, 'active_model/conversion' autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' autoload :Errors, 'active_model/errors' autoload :Name, 'active_model/naming' diff --git a/activemodel/lib/active_model/api_compliant.rb b/activemodel/lib/active_model/api_compliant.rb deleted file mode 100644 index 26f83feb6b..0000000000 --- a/activemodel/lib/active_model/api_compliant.rb +++ /dev/null @@ -1,25 +0,0 @@ -module ActiveModel - module APICompliant - include Naming - - def self.extended(klass) - klass.class_eval do - include Validations - include InstanceMethods - end - end - - module InstanceMethods - def to_model - if respond_to?(:new_record?) - self.class.class_eval { def to_model() self end } - to_model - else - raise "In order to be ActiveModel API compliant, you need to define " \ - "a new_record? method, which should return true if it has not " \ - "yet been persisted." - end - end - end - end -end \ No newline at end of file diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb new file mode 100644 index 0000000000..d5c65920f6 --- /dev/null +++ b/activemodel/lib/active_model/conversion.rb @@ -0,0 +1,8 @@ +module ActiveModel + # Include ActiveModel::Conversion if your object "acts like an ActiveModel model". + module Conversion + def to_model + self + end + end +end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index f32857afa3..62e97158ec 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2511,13 +2511,6 @@ module ActiveRecord #:nodoc: (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes end - # Returns the ActiveRecord object when asked for its - # ActiveModel-compliant representation, because ActiveRecord is - # ActiveModel-compliant. - def to_model - self - end - # Returns a cache key that can be used to identify this record. # # ==== Examples @@ -3186,6 +3179,7 @@ module ActiveRecord #:nodoc: include Dirty include Callbacks, ActiveModel::Observing, Timestamp include Associations, AssociationPreload, NamedScope + include ActiveModel::Conversion # AutosaveAssociation needs to be included before Transactions, because we want # #save_with_autosave_associations to be wrapped inside a transaction. -- cgit v1.2.3 From 574323df62ef6ddc666e55487733f14d787f2419 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 21 Jul 2009 00:52:50 -0500 Subject: So you can require 'activemodel' --- activemodel/lib/activemodel.rb | 1 + 1 file changed, 1 insertion(+) create mode 100644 activemodel/lib/activemodel.rb diff --git a/activemodel/lib/activemodel.rb b/activemodel/lib/activemodel.rb new file mode 100644 index 0000000000..da3133103b --- /dev/null +++ b/activemodel/lib/activemodel.rb @@ -0,0 +1 @@ +require 'active_model' -- cgit v1.2.3 From 108545a9d69215aa0205f7e6b52eb1573443d404 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 21 Jul 2009 00:56:27 -0500 Subject: Clean up validation example --- activemodel/examples/amo_ap_example.rb | 36 ---------------------------------- activemodel/examples/validations.rb | 29 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 36 deletions(-) delete mode 100644 activemodel/examples/amo_ap_example.rb create mode 100644 activemodel/examples/validations.rb diff --git a/activemodel/examples/amo_ap_example.rb b/activemodel/examples/amo_ap_example.rb deleted file mode 100644 index cef718d0d4..0000000000 --- a/activemodel/examples/amo_ap_example.rb +++ /dev/null @@ -1,36 +0,0 @@ -$:.push "activesupport/lib" -$:.push "activemodel/lib" - -require "active_model/validations" -require "active_model/deprecated_error_methods" -require "active_model/errors" -require "active_model/naming" - -class Person - include ActiveModel::Validations - extend ActiveModel::Naming - - validates_presence_of :name - - attr_accessor :name - def initialize(attributes = {}) - @name = attributes[:name] - end - - def persist - @persisted = true - end - - def new_record? - @persisted - end - - def to_model() self end -end - -person1 = Person.new -p person1.valid? -person1.errors - -person2 = Person.new(:name => "matz") -p person2.valid? \ No newline at end of file diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb new file mode 100644 index 0000000000..3f8311ff96 --- /dev/null +++ b/activemodel/examples/validations.rb @@ -0,0 +1,29 @@ +require 'activemodel' + +class Person + include ActiveModel::Conversion + include ActiveModel::Validations + + validates_presence_of :name + + attr_accessor :name + + def initialize(attributes = {}) + @name = attributes[:name] + end + + def persist + @persisted = true + end + + def new_record? + @persisted + end +end + +person1 = Person.new +p person1.valid? +person1.errors + +person2 = Person.new(:name => "matz") +p person2.valid? -- cgit v1.2.3 From 9163b9ac9d0b986cb5de2af63a8ed50ffc8ece71 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 19 Jul 2009 14:13:28 +0900 Subject: Ruby 1.9.2 compat: Use File#expand_path for require path because "." will not be included in LOAD_PATH since Ruby 1.9.2 [#2921 state:resolved] Signed-off-by: Yehuda Katz --- railties/bin/about | 4 ++-- railties/bin/console | 2 +- railties/bin/dbconsole | 2 +- railties/bin/destroy | 2 +- railties/bin/generate | 2 +- railties/bin/performance/benchmarker | 2 +- railties/bin/performance/profiler | 2 +- railties/bin/plugin | 2 +- railties/bin/runner | 2 +- railties/bin/server | 2 +- 10 files changed, 11 insertions(+), 11 deletions(-) diff --git a/railties/bin/about b/railties/bin/about index ed8deb0dfc..1eeb6eb915 100755 --- a/railties/bin/about +++ b/railties/bin/about @@ -1,4 +1,4 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) $LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info" -require 'commands/about' \ No newline at end of file +require 'commands/about' diff --git a/railties/bin/console b/railties/bin/console index 498077ab33..235a1f2780 100755 --- a/railties/bin/console +++ b/railties/bin/console @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/console' diff --git a/railties/bin/dbconsole b/railties/bin/dbconsole index caa60ce829..83c8436a9d 100755 --- a/railties/bin/dbconsole +++ b/railties/bin/dbconsole @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/dbconsole' diff --git a/railties/bin/destroy b/railties/bin/destroy index a4df765a39..88d295f7aa 100755 --- a/railties/bin/destroy +++ b/railties/bin/destroy @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/destroy' diff --git a/railties/bin/generate b/railties/bin/generate index 173a9f147d..62a8a4c0c5 100755 --- a/railties/bin/generate +++ b/railties/bin/generate @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/generate' diff --git a/railties/bin/performance/benchmarker b/railties/bin/performance/benchmarker index c842d35d33..3bff809fb3 100755 --- a/railties/bin/performance/benchmarker +++ b/railties/bin/performance/benchmarker @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' +require File.expand_path('../../../config/boot', __FILE__) require 'commands/performance/benchmarker' diff --git a/railties/bin/performance/profiler b/railties/bin/performance/profiler index d855ac8b13..07640575cd 100755 --- a/railties/bin/performance/profiler +++ b/railties/bin/performance/profiler @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../../config/boot' +require File.expand_path('../../../config/boot', __FILE__) require 'commands/performance/profiler' diff --git a/railties/bin/plugin b/railties/bin/plugin index 87cd2070fe..b82201fa83 100755 --- a/railties/bin/plugin +++ b/railties/bin/plugin @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/plugin' diff --git a/railties/bin/runner b/railties/bin/runner index a4a7cb25ba..be4c5d4572 100755 --- a/railties/bin/runner +++ b/railties/bin/runner @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/runner' diff --git a/railties/bin/server b/railties/bin/server index 3c67f39b69..b9fcb71793 100755 --- a/railties/bin/server +++ b/railties/bin/server @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -require File.dirname(__FILE__) + '/../config/boot' +require File.expand_path('../../config/boot', __FILE__) require 'commands/server' -- cgit v1.2.3 From 9d7aae710384fb5f04129c35b86c5ea5fb9d83a9 Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Sun, 19 Jul 2009 14:36:44 +0900 Subject: Ruby 1.9.2 compat: name method was renamed to __name__ since MiniTest 1.4.x [#2922 state:resolved] Signed-off-by: Yehuda Katz --- activesupport/lib/active_support/test_case.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index d5282bad6a..c915bf799d 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -19,7 +19,8 @@ module ActiveSupport class TestCase < ::Test::Unit::TestCase if defined? MiniTest Assertion = MiniTest::Assertion - alias_method :method_name, :name + alias_method :method_name, :name if method_defined? :name + alias_method :method_name, :__name__ if method_defined? :__name__ else # TODO: Figure out how to get the Rails::BacktraceFilter into minitest/unit if defined?(Rails) && ENV['BACKTRACE'].nil? -- cgit v1.2.3