diff options
author | Yehuda Katz <wycats@gmail.com> | 2009-07-20 00:27:04 +0900 |
---|---|---|
committer | Yehuda Katz <wycats@gmail.com> | 2009-07-20 00:27:04 +0900 |
commit | 5ffaaa71d149c9807260c950c9a61d01fe734827 (patch) | |
tree | b014dcbe7cc8efabebca38e9e75648905e85017e | |
parent | 45d41d8012c605f21de51e7018fa31e1d07776eb (diff) | |
download | rails-5ffaaa71d149c9807260c950c9a61d01fe734827.tar.gz rails-5ffaaa71d149c9807260c950c9a61d01fe734827.tar.bz2 rails-5ffaaa71d149c9807260c950c9a61d01fe734827.zip |
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
17 files changed, 91 insertions, 32 deletions
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 # * <tt>:submit_value</tt> - 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 '<a href="/people/1">David</a>', 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 |