2015-09-06 66 views
0

剛剛開始使用STI和Rails 4項目。假設我有UserBlogUser可以將他的非公開博客分享給editorsnormal viewersRails:實現共享博客; has_many with STI:未定義方法'klass'for nil:NilClass

它沒有任何意義,我把typeusers表,因爲在這個項目中,用戶與不只是blogs有關,但也喜歡的東西posts。 (這裏的博客更像是一個平臺,文章是文章,只是一個想法,可能是另外兩件事)。

所以我用另一個叫BlogUserAssociation的模型來管理上面的共享關係。基本上這個模型包含一個type列,我有BlogEditorAssociationBlogViewerAssociation從它繼承。 (名字有點笨拙。)第一個問題,這是一個推薦的方式來處理「共享」的情況?

通過上述想法,我有:

# blog.rb 
class Blog < ActiveRecord::Base 
    ... 
    has_many :blog_user_associations, dependent: :destroy 
    has_many :editors, through: :blog_editor_associations, source: :user 
    has_many :allowed_viewers, through: :blog_viewer_associations, source: :user # STI 
    ... 

而且

# user.rb 
class User < ActiveRecord::Base 
    ... 
    has_many :blog_user_associations, dependent: :destroy 
    has_many :editable_blogs, through: :blog_editor_associations, source: :blog 
    has_many :blogs_shared_for_view, through: :blog_viewer_associations, source: :blog 
    ... 

但是,當我試圖使用RSpec來測試這個,

it { should have_many(:editors).through(:blog_editor_associations).source(:user) } 

我得到了錯誤undefined method 'klass' for nil:NilClass

我相信這是因爲我沒有在User中說has_many blog_editor_associations。但我以爲blog_editor_associations繼承自blog_viewer_associations,我不必再爲子模型說has_many。所以有沒有自動綁定has_many到子模型的原因?

回答

1

STI似乎對這種情況有點矯枉過正。我更願意爲關聯模型添加一個屬性,並使用作用域來檢索集合,具體取決於屬性的值。例如,您可以命名關聯模型BlogUser,並添加布爾值can_edit列。值true表示用戶可以編輯關聯的博客。

然後模型是這樣的:

class Blog < ActiveRecord::Base 
    has_many :blog_users 
    has_many :users, through: :blog_users 
    scope :editable, -> { where(blog_users: {can_edit: true}) } 
end 

class BlogUser < ActiveRecord::Base 
    belongs_to :blog 
    belongs_to :user 
end 

class User < ActiveRecord::Base 
    has_many :blog_users 
    has_many :blogs, through: :blog_users 
    scope :editors, -> { where(blog_users: {can_edit: true}) } 
end 

所以user.blogs檢索與用戶相關聯的所有博客,並user.blogs.editable檢索,用戶可以編輯所有博客。 blog.users檢索與博客關聯的所有用戶,blog.users.editors檢索可編輯博客的所有用戶。

一些測試,證明:

require 'rails_helper' 

RSpec.describe User, type: :model do 
    describe "A user with no associated blogs" do 
    let(:user) { User.create! } 
    it "has no blogs" do 
     expect(user.blogs.empty?).to be true 
     expect(user.blogs.editable.empty?).to be true 
    end 
    end 

    describe "A user with a non-editable blog association" do 
    let(:user) { User.create! } 
    let(:blog) { Blog.create! } 
    before do 
     user.blogs << blog 
    end 
    it "has one blog" do 
     expect(user.blogs.count).to eq 1 
    end 
    it "has no editable blogs" do 
     expect(user.blogs.editable.empty?).to be true 
    end 
    end 

    describe "A user with an editable blog association" do 
    let(:user) { User.create! } 
    let(:blog) { Blog.create! } 
    before do 
     user.blog_users << BlogUser.new(blog: blog, user: user, can_edit: true) 
    end 
    it "has one blog" do 
     expect(user.blogs.count).to eq 1 
    end 
    it "has one editable blog" do 
     expect(user.blogs.editable.count).to eq 1 
    end 
    end 
end 
+0

謝謝提醒範圍。這確實使代碼更簡單。所以你不需要BlogUser和Blog之間的任何繼承?示波器如何基本工作?只是想等看有沒有更多的建議。如果沒有,我會選擇你的 – zkytony

+0

所以你的意思是添加一個can_edit列到用戶表?如果我在博客(更像是一個平臺)內部還有Post並且用戶可以分享其可編輯性會怎麼樣?基本上不只是對於博客 – zkytony

+0

將'can_edit'列添加到BlogUser表。關於範圍的更多細節請看Rails指南:http://guides.rubyonrails.org/association_basics.html 至於你關於Post模型的問題,也許你可以把它變成另一個問題。 – zetetic

相關問題