2011-12-23 7 views
5

Sono impantanato su come gestire questa situazione utilizzando una relazione HABTM autoreferenziale, cancan e ActiveRecord.Come puoi scrivere un'abilità di cancan così? e accessibile_di entrambi funzionano sulla relazione HABTM autoreferenziale?

Sto tentando di utilizzare accessibile da per determinare un set di video visibili in base a una relazione tra video e canali, ma l'SQL risultante ha il nome di tabella errato per parte della query. Qui ci sono le relazioni:

# video.rb 
class Video < ActiveRecord::Base 
    belongs_to :channel 
end 

# channel.rb 
class Channel < ActiveRecord::Base 
    has_many :videos 
    has_and_belongs_to_many :subchannels, :class_name => "Channel", :join_table => "channels_channels", :foreign_key => :parent_id, :association_foreign_key => :subchannel_id 
    has_and_belongs_to_many :parent_channels, :class_name => "Channel", :join_table => "channels_channels", :foreign_key => :subchannel_id, :association_foreign_key => :parent_id 
end 

# The appropriate channels_channels table exists with subchannel_id and parent_id fields. 

Ho bisogno di un capacità cancan per trovare tutti i video pubblici che sono pubblici sotto-canali del canale predefinito. Ho provato quanto segue:

# ability.rb 
can :read, Video, :permission => 'public', :channel => {:permission => 'public', :parent_channels => {:name => "Default"}} 

Nella console, quando provo questo fuori, ottengo il seguente:

> Video.accessible_by(Ability.new(nil)) 
SELECT "videos".* FROM "videos" INNER JOIN "channels" ON "channels"."id" = "videos"."channel_id" INNER JOIN "channels_channels" ON "channels_channels"."subchannel_id" = "channels"."id" INNER JOIN "channels" "parent_channels_channels" ON "parent_channels_channels"."id" = "channels_channels"."parent_id" WHERE "videos"."permission" = 'public' AND "channels"."permission" = 'public' AND "channels"."name" = 'Default' 
=> [] 

Ho alcuni record nel posto che dovrebbe tradursi in un video, e, più in al punto, ci si sarebbe aspettati che la fine della query avesse "parent_channels_channels". "name" = 'Default', ma la tabella è "canali" invece, con conseguente dataset errato poiché l'SQL fa riferimento il nome del canale, non il canale genitore.

Ho provato a cambiare la capacità nel modo seguente per forzare il nome della tabella:

can :read, Video, :permission => 'public', :channel => {:permission => 'public', :parent_channels => {"parent_channels_channels.name" => "Default"}} 

che si traduce nella query corretta, e accessible_by restituisce il set di dati a destra, ma poi lattina? (: Leggere, il video) per un video in tale set di risultati genera un errore di runtime.

NoMethodError: undefined method `parent_channels_channels.name' for #<Channel:0x007fef35593748> 

Come faccio a specificare questa capacità in modo che funziona sia con lattina? e accessibile da?

risposta

0

Non mi è chiaro dal codice se si sta modellando correttamente. Vuoi davvero parent_channels e sottocanali per essere una relazione HABTM, o addirittura essere lo stesso modello? Forse alcuni esempi del mondo reale dalla tua app mi aiuterebbero qui ma sembra che quello che vuoi sia una VideoCategoria genitore, e tu vuoi le relazioni HABTM con i Canali, che poi hanno una relazione has_many con i video.

Si potrebbe quindi avere VideoCategory che ha una relazione has_many: videos,: through =>: channels.

class VideoCategory 
    has_and_belongs_to_many :channels 

    has_many :videos, :through => :channels 
end 

class Video 
    belongs_to :channel 
end 

class Channel 
    has_many :videos 

    has_and_belongs_to_many :video_cateogories 
end 

Sembra che si sta tentando di rendere la classe Canale fare di più che essere un canale. Il concetto di genitori e figli non ha molto senso nelle relazioni di habtm. In genere se si desidera una classe genitore e sottoclassi, allora si desidera una relazione 1-a-molti. Molti-a-molti implica che nessuna delle due è la classe genitore (sebbene ciò possa essere diverso da come lo si presenta nell'interfaccia utente, ovviamente).

Se davvero devi fare questo come autoreferenziale, hai provato a utilizzare gli ambiti invece?

class Video 
    scope :public, lambda do 
    joins(:channel).where("videos.permission = 'public' and channels.permission = 'public'") 
    end 
end 

Sembra che mettere la logica in ambiti e fuori ability.rb potrebbe aiutare a controllare meglio esattamente quello SQL viene scritto - si rimuove lo strato di astrazione che CanCan sta facendo.

Problemi correlati