Rails与委托方法的关联。 AR的可能错误?

时间:2012-08-23 15:16:24

标签: ruby-on-rails activerecord polymorphic-associations

this question隔离到自己的rails应用中:并添加git repo作为示例

模块:

module SeoMeta

  def self.included(base)
    base.extend(ClassMethods)
    base.send :include, InstanceMethods
  end

  module ClassMethods

    def is_seo_meta

      has_one :meta,
        class_name: SeoMetum,
        as:         :metumable,
        dependent:  :destroy,
        autosave:   true

      delegate :browser_title,     :meta_description, :meta_author,
               :meta_keywords,     :browser_title=,   :meta_keywords=,
               :meta_description=, :meta_author=,
               to: :meta

      after_save :save_meta_tags!

      attr_accessible :browser_title,    :meta_keywords,
                      :meta_description, :meta_author

    end

  end

  module InstanceMethods

    class << self
      def included(base)
        base.module_eval do

          alias :original_meta_method :meta

        end
      end
    end

    def meta
      find_meta || build_meta
    end

    def find_meta
      @meta ||= ::SeoMetum.where(metumable_type: self.class.name, metumable_id: self.id).first
    end

    def build_meta
      @meta ||= ::SeoMetum.new(metumable_type: self.class.name, metumable_id: self.id)
    end  

    def save_meta_tags!
      meta.metumable_id ||= self.id
      meta.save
    end

  end
end

型号:

class User < ActiveRecord::Base
  include SeoMeta
  is_seo_meta

  has_many :collections
  accepts_nested_attributes_for :collections

  def collection
    default_collection = self.collections.first
    default_collection ||= self.collections.create
    default_collection
  end
end

class Collection < ActiveRecord::Base
  include SeoMeta
  is_seo_meta
  belongs_to :user

end

class SeoMetum < ActiveRecord::Base
  attr_accessible :browser_title, :meta_author, :meta_description, :meta_keywords,
                  :metumable, :metumable_id, :metumable_type
  belongs_to :metumable, polymorphic: true
end

Rspec测试:

 context "user and collection" do
    context 'responds to' do
      it 'meta_description' do
        user.collection.respond_to?(:meta_description).should be_true
      end

      it 'browser_title' do
        user.collection.respond_to?(:browser_title).should be_true
      end
    end

    context 'individual allows us to assign to' do
      it 'meta_description' do
        the_collection = user.collection
        the_collection.meta_description = 'This is my description of the user for search results.'
        the_collection.meta_description.should == 'This is my description of the user for search results.'
      end

      it 'browser_title' do
        the_collection = user.collection
        the_collection.browser_title = 'An awesome browser title for SEO'
        the_collection.browser_title.should == 'An awesome browser title for SEO'
      end
    end


    context 'allows us to assign to' do
      it 'meta_description' do
        user.collection.meta_description = 'This is my description of the user for search results.'
        user.collection.meta_description.should == 'This is my description of the user for search results.'
      end

      it 'browser_title' do
        user.collection.browser_title = 'An awesome browser title for SEO'
        user.collection.browser_title.should == 'An awesome browser title for SEO'
      end
    end

    context 'allows us to update' do
      it 'meta_description' do
        user.collection.meta_description = 'This is my description of the user for search results.'
        user.collection.save

        user.collection.reload
        user.collection.meta_description.should == 'This is my description of the user for search results.'
      end

      it 'browser_title' do
        user.collection.browser_title = 'An awesome browser title for SEO'
        user.collection.save

        user.collection.reload
        user.collection.browser_title.should == 'An awesome browser title for SEO'
      end
    end
  end

前四次测试通过,后四次测试失败。我认为这可能是rails多态关联的一个错误,但我不确定如何进一步隔离它。关于我的模块设计的评论也很受欢迎。

最佳, 斯科特

1 个答案:

答案 0 :(得分:2)

代码中的问题出在这个地方:

class User
  #Other stuff

  #HERE!
  def collection
    default_collection = self.collections.first
    default_collection ||= self.collections.create
    default_collection
  end
end

每次调用collection方法时,都会在数据库中查找第一个集合。因此,即使您将某些值设置为user.collection.meta_description = "abc",然后在调用user.collection时它也不是同一个集合对象,因为它是从数据库中进行的新查找。因此,未保存到数据库的所有属性都将消失。您可以通过查看日志来看到这一点 - 每次调用user.collection时,您都会获得新的命中数据,并且每次调用user.collection.object_id时都会获得不同的值。

您可以通过执行类似

的操作来解决此问题
def collection
  return @collection if @collection
  default_collection = self.collections.first
  default_collection ||= self.collections.create
  @collection = default_collection
end