我有一个rails 4项目,我正在使用MTI。它是一个论坛应用程序,除了几个继承自Topic的模型之外,我还有一个核心主题模型,如公告和私人消息。
以下是一切如何运作:
models / topic.rb - 这是基本模型
class Topic < ActiveRecord::Base
acts_as_taggable
default_scope order('created_at DESC')
#string title
belongs_to :discussable, polymorphic: true, dependent: :destroy
belongs_to :author, class_name: User
has_many :posts, inverse_of: :topic, order: :created_at
has_many :viewings
has_many :viewers, class_name: User, through: :viewings, uniq: true
has_many :watchings
has_many :watchers, class_name: User, through: :watchings, uniq: true
accepts_nested_attributes_for :posts, allow_destroy: true
validates :title, presence: true
validates :author_id, presence: true
def base
self
end
end
模型/ announcement.rb
# this is a "child" class -- acts_as_topic sets that up, see next snippet
class Announcement < ActiveRecord::Base
acts_as_topic
default_scope includes(:topic).order('topics.created_at DESC')
#datetime :displays_at
validates :expires_at, timeliness: { after: lambda { self.displays_at }, allow_nil: true }
end
配置/初始化/ discussable.rb
module Discussable
def self.included(base)
base.has_one :topic, as: :discussable, autosave: true
base.validate :topic_must_be_valid
base.alias_method_chain :topic, :autobuild
end
def topic_with_autobuild
topic_without_autobuild || build_topic
end
def method_missing(meth, *args, &blk)
topic.send(meth, *args, &blk)
rescue NoMethodError
super
end
protected
def topic_must_be_valid
unless topic.valid?
topic.errors.each do |attr, message|
errors.add(attr, message)
end
end
end
end
class ActiveRecord::Base
def self.acts_as_topic
include Discussable
end
end
迁移:
class CreateTopics < ActiveRecord::Migration
def change
create_table :topics do |t|
t.string :title, null: false
t.references :author, null: false
t.references :discussable
t.string :discussable_type
t.integer :posts_count, null: false, default: 0
t.timestamps
end
end
end
class CreateAnnouncements < ActiveRecord::Migration
def change
create_table :announcements do |t|
t.datetime :displays_at
t.datetime :expires_at
end
end
end
视图/通知/ new.html.erb
<%= form_for @announcement do |f| %>
<%= f.text_field :title, placeholder: 'Title' %><br />
<%= f.fields_for :posts do |post_f| %>
<%= post_f.text_area :content %>
<%= post_f.hidden_field :author_id, value: current_user.id %><br />
<% end %>
<%= f.text_field :tag_list, placeholder: 'Tags' %><br />
<%= f.text_field :displays_at, placeholder: 'Display At' %><br />
<%= f.text_field :expires_at, placeholder: 'Expire At' %><br />
<%= f.hidden_field :author_id %><br />
<%= f.submit %>
<% end %>
公告#create action很简单 - 它只是从params发出一个新的公告,并试图保存它。
此设置没有错误,一切似乎都能正常运行。
问题是公告#create创建公告并完全忽略帖子的嵌套属性。
这是有道理的,因为acts_as_topic
实际上只是在Announcement模型和Topic模型之间创建了一个has_one关系,而Discussable模块就像公告is_a Topic一样伪造它,即使它确实不是。
所以我的问题是:如何伪造它,使得Announcement模型的ActiveRecord初始化程序的内部接受帖子的嵌套属性,这实际上是与Topic模型的关联?