将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多态关联的一个错误,但我不确定如何进一步隔离它。关于我的模块设计的评论也很受欢迎。
最佳, 斯科特
答案 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