2016-01-29 18 views
13

Sto cercando di fare un app in Rails 4.Rails 4 con Pundit & Statesman gioiello - politica quando un oggetto è in uno stato

Sto cercando di utilizzare gemma di stato per gli Stati e quindi esperto per le politiche.

mio Gemfile ha:

gem 'statesman', '~> 1.3', '>= 1.3.1' 
gem 'pundit' 

Ho un modello di articolo e di un modello di transizioni articolo e un modello article_state_machine.

Il mio obiettivo è definire una politica di pubblicazione (tramite pundit) nella mia politica articoli che consente a un utente proprietario di un articolo di pubblicare quell'articolo se è in stato 'approvato'.

sto cercando questo nel mio politica articolo esperto:

class ArticlePolicy < ApplicationPolicy 

def publish? 

user.present? && user == article.user 
# if requires approval, then approved 

# and article.in_state(:approve) - why doesnt this work - see statesman docs? 

# user && user.article.exists?(article.id) 

end 
end 

Quando provo a controllare se l'articolo è in stato di: approvare (come commentato in precedenza), ottengo un messaggio di errore che dice metodo non definito 'in_state'.

Come è possibile utilizzare la macchina a stati nella politica? O è inteso che la politica consente all'utente di pubblicare in ogni momento, ma si mostra solo il pulsante nella pagina di presentazione dell'articolo quando l'articolo è in stato di approvazione (anche se ho pensato che fosse il punto di pundit).

Article.rb

class Article < ActiveRecord::Base 
    include Statesman::Adapters::ActiveRecordQueries 
has_many :transitions, class_name: "ArticleTransition", autosave: false 
def state_machine 
    @state_machine ||= ArticleStateMachine.new(self, transition_class: ArticleTransition, association_name: :transitions) 
    end 

    # delegate :can_transition_to?. :trans 

    # def reindex_articles 
    # article.reindex_async 
    # end 

    private 

    def self.transition_name 
    :transitions 
    end 

    def self.transition_class 
    ArticleTransition 
    end 

    def self.initial_state 
    # ArticleTransition.initial_state 
    :draft 
    end 
end 

articolo modello di macchina a stati:

class ArticleStateMachine 
    include Statesman::Machine 

    state :draft, initial: :true #while author is drafting 
    state :review #while approver comments are being addressed (really still in draft) 
    state :reject # not suitable for publication 
    state :approve # suitable for publication 
    state :publish #published 
    state :remove # destroyed 
    # state :spotlight 

    transition from: :draft, to: [:reject, :approve, :publish, :remove] 
    # transition from: :review, to: [:rejected, :approved, :removed] 
    transition from: :reject, to: [:draft, :remove] 
    transition from: :approve, to: [:publish, :remove] 
    transition from: :publish, to: :remove 

end 

articolo modello di transizione:

class ArticleTransition < ActiveRecord::Base 
    include Statesman::Adapters::ActiveRecordTransition 


    belongs_to :article, inverse_of: :article_transitions 



end 

articolo regolatore:

def approve 
    article = Article.find(params[:id]) 
    if article.state_machine.transition_to!(:approve) 
     flash[:notice] = "This article has been approved for publication" 
     redirect_to action: :show, id: article_id 
     # add mailer to send message to article owner that article has been approved 
    else 
     flash[:error] = "You're not able to approve this article" 
     redirect_to action: :show, id: article_id 
    end 
    end 

def publish 
    article = Article.find(params[:id]) 
    authorize @article 

    if article.state_machine.transition_to!(:publish) 
     redirect_to action: :show, id: article_id 
     # how do you catch the date the state became published? 
    else 
     flash[:error] = "You're not able to publish this article" 
     redirect_to action: :show, id: article_id 
    end 
    end 

Qualcuno può vedere cosa ho fatto di sbagliato?

L'intero regolatore articoli ha:

class ArticlesController < ApplicationController 
    before_action :set_article, only: [:show, :edit, :update, :destroy, :reject, :approve, :publish, :remove] 
    before_action :authenticate_user!, except: [:index, :show, :search, :reject, :approve, :publish, :remove] 


    respond_to :html, :json 
# GET /articles 
    # GET /articles.json 
    def index 
    @articles = policy_scope(Article) 
    # query = params[:query].presence || "*" 
    # @articles = Article.search(query) 
    end 

    # def index 
    # if params[:query].present? 
    #  @books = Book.search(params[:query], page: params[:page]) 
    # else 
    #  @books = Book.all.page params[:page] 
    # end 
    # end 

    # GET /articles/1 
    # GET /articles/1.json 
    def show 

    end 

    # GET /articles/new 
    def new 
    @article = Article.new 
    @article.comments.build 
    end 

    # GET /articles/1/edit 
    def edit 

    authorize @article 
    end 

    # POST /articles 
    # POST /articles.json 
    def create 
    # before_action :authenticate_user! 
    # authorize @article 
    @article = current_user.articles.new(article_params) 

    respond_to do |format| 
     if @article.save 
     format.html { redirect_to(@article) } 
     format.json { render :show, status: :created, location: @article } 
     else 
     format.html { render :new } 
     format.json { render json: @article.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

    def search 
    if params[:search].present? 
     @articless = Article.search(params[:search]) 
    else 
     @articles = Articles.all 
    end 
    end 


    # PATCH/PUT /articles/1 
    # PATCH/PUT /articles/1.json 
    def update 
    # before_action :authenticate_user! 
    authorize @article 
    respond_to do |format| 
    # if @article.update(article_params) 
    #  format.json { render :show, status: :ok, location: @article } 
    # else 
    #  format.html { render :edit } 
    #  format.json { render json: @article.errors, status: :unprocessable_entity } 
    # end 
    # end 
     if @article.update(article_params) 
     format.html { redirect_to(@article) } 
     format.json { render :show, status: :ok, location: @article } 
     else 
     format.json { render json: @article.errors, status:  :unprocessable_entity } 
     end 
     format.html { render :edit } 
    end 
    end 



    # DELETE /articles/1 
    # DELETE /articles/1.json 
    def destroy 
    before_action :authenticate_user! 
    authorize @article 
    @article.destroy 
    respond_to do |format| 
     format.json { head :no_content } 
    end 
    end 

    # def review 
    # article = Article.find(params[:id]) 
    # if article.state_machine.transition_to!(:review) 
    #  flash[:notice] = "Comments on this article have been made for your review" 
    #  redirect_to action: :show, id: article_id 
    # else 
    #  flash[:error] = "You're not able to review this article" 
    #  redirect_to action: :show, id: article_id 
    # end 
    # end 

    def reject 
    end 

    def approve 
    article = Article.find(params[:id]) 
    if article.state_machine.transition_to!(:approve) 
     flash[:notice] = "This article has been approved for publication" 
     redirect_to action: :show, id: article_id 
     # add mailer to send message to article owner that article has been approved 
    else 
     flash[:error] = "You're not able to approve this article" 
     redirect_to action: :show, id: article_id 
    end 
    end 

    def publish 
    article = Article.find(params[:id]) 
    if article.state_machine.transition_to!(:publish) 
     redirect_to action: :show, id: article_id 
     # how do you catch the date the state became published? 
    else 
     flash[:error] = "You're not able to publish this article" 
     redirect_to action: :show, id: article_id 
    end 
    end 

    def remove 
    article = Article.find(params[:id]) 
    if article.state_machine.transition_to!(:remove) 
     redirect_to root_path 
    else 
     flash[:error] = "You're not able to destroy this article" 
     redirect_to action: :show, id: article_id 
    end 
    end 

    private 
    # Use callbacks to share common setup or constraints between actions. 
    def set_article 
     @article = Article.find(params[:id]) 
     authorize @article 
    end 

    # Never trust parameters from the scary internet, only allow the white list through. 
    def article_params 
     params.require(:article).permit(:body, :title, :image, :tag_list, 
     comment_attributes: [:opinion]) 
    end 

end 
+0

Dove chiami "autorizza" di Pundit? –

+0

Buon punto. Quando aggiungo authorize @ article all'azione di pubblicazione, viene visualizzato un messaggio di errore: Statesman :: TransitionFailedError in ArticlesController # publish Impossibile passare da "publish" a "publish" – Mel

+0

Quindi sembra che non ci siano problemi da parte di Pundit. Il motivo dovrebbe essere da qualche parte nel messaggio di eccezione/traccia dello stack. BTW il metodo bang 'transition_to!' Solleva eccezioni invece di restituire 'false'. Potrebbe essere una buona idea usare il metodo non-bang e stampare il messaggio di errore appropriato se non si stanno prendendo queste eccezioni (l'uso di eccezioni per il flusso del programma non è comunque una buona idea). –

risposta

3

La versione di statista gemma che si sta utilizzando non ha in_state? definito. È possibile aggiornare la gemma.Oppure si può definire da soli usando codici simili come legati da smallbuttoncom

https://github.com/gocardless/statesman/blob/1fd4ee84c87765b7855688b8eb5dddea7ddddbdd/lib/statesman/machine.rb#L180-L182

Tuttavia, per il vostro caso, un semplice controllo dovrebbe essere sufficiente. Prova a seguire il codice nella tua politica

article.state_machine.current_state == "approve" 

Spero che questo aiuti.

2

When I try to check if the article is in state :approve (as commented out above), I get an error message that says undefined method 'in_state'.

Hai provato a cambiare article.in_state? (: Approvare) per article.state_machine.in_state? (: Approvare) nella vostra politica ?.

+0

Buon punto Guilherme, l'ho provato, ma dà questo errore: NoMethodError in ArticlesController # publish metodo non definito 'in_state 'per # Mel

+0

Secondo i documenti non dovresti usare in_state? con un punto interrogativo. N. macchina in stato? (: Stato_1,: stato_2, ...) – Guilherme

+0

Ho provato anche questo – Mel

2

si sta perdendo la ? alla fine del metodo:

il metodo in_state è in realtà un metodo di classe e si comporta come un ambito.

è necessario utilizzare il metodo in_state?, che è un metodo di istanza, in questo modo:

article.state_machine.in_state?(:approve)