[RUBY] S'il y a une transition d'état, créons une classe State

S'il y a des données qui passent à un état, ce type de code se produira fréquemment.

erb:view.html.erb


<%#Il semble que l'approbation ne peut pas être faite si l'état du rapport est 1 ou 2, mais quels sont les états 1 et 2?%>
<%= button_tag('Approbation', disabled: (report.state == 1 || report.state == 2)) %>

reports_controller.rb


def accept
  report = Report.find(params[:id])
  #La même instruction conditionnelle que view est requise pour la gestion!
  if report.state == 1 || report.state == 2
    flash[:error] = 'Impossible d'approuver'
    redirect_to root_url
  end
  report.state = 3 #Valeur d'approbation!
  report.save!
end

Si vous utilisez une constante, cela s'améliorera un peu, mais ...

erb:view.html.erb


<%= button_tag('Approbation', disabled: (report.state == Report::STATE_DRAFT || report.state == Report::STATE_TRASH)) %>

reports_controller.rb


def accept
  report = Report.find(params[:id])
  #Après tout, la même instruction conditionnelle que view est requise!
  if report.state == Report::STATE_DRAFT || report.state == Report::STATE_TRASH
    flash[:error] = 'Impossible d'approuver'
    redirect_to root_url
  end
  report.state = Report::STATE_APPROBAL
  report.save!
end

report.rb


class Report < ApplicationRecord
  #Si de nombreuses colonnes nécessitent des constantes, de nombreuses constantes seront nécessaires.
  #De plus, la portée est trop large et il est difficile d'en voir le but.
  STATE_DRAFT = 1
  STATE_TRASH = 2
  STATE_APPROBAL = 3
end

Il existe un moyen d'utiliser ActiveRecord :: Enum au lieu d'une constante, mais cela ne résout pas le besoin de la même instruction conditionnelle à divers endroits ... Il peut être résolu en ajoutant une méthode au modèle ActiveRecord.

report.rb


class Report < ApplicationRecord
  STATE_DRAFT = 1
  STATE_TRASH = 2
  STATE_APPROBAL = 3

  def can_accept?
    state != STATE_DRAFT && state != STATE_TRASH
  end
end

Mais c'est la route vers Fat Model ... Donc ...

Créons une classe qui gère les transitions d'état

Évitez la modélisation en masse du modèle ActiveRecord et créez une classe de transition d'état avec des responsabilités claires. Ce serait bien si l'instruction conditionnelle avait une méthode de changement d'état appelée report_state.to_approval, avec le sentiment de report_state.approval_in_next? [^ 1](peut-il être dans l'état approuvé).

[^ 1]: Le nom de la méthode est censé être "Y a-t-il une approbation dans l'état suivant?", Mais comme c'est un putain de poisson anglais, il peut y avoir d'autres bons noms de méthode.

reports_controller.rb


def accept
  report = Report.find(params[:id])
  report_state = report.state_object
  #Nettoyer
  unless report_state.approval_in_next?
    flash[:error] = 'Impossible d'approuver'
    redirect_to root_url
  end
  #Ceci est un exemple simple avec une seule colonne, mais même si le traitement de la transition d'état est compliqué,_*Peut être caché dans la méthode!
  new_report_state = report_state.to_approval
  report.state = new_report_state.state
  report.save!
end

report.rb


class Report < ApplicationRecord
  # state_object=Il peut être pratique de créer une méthode
  def state_object
    ReportState.new(state)
  end
end

La classe ReportState réelle ressemble à ceci.

report_state.rb


class ReportState
  attr_accessor :state
  #Déplacez les constantes de la classe Report. Ça va mieux tout le temps
  DRAFT = 1
  TRASH = 2
  APPROBAL = 3

  def initialize(state)
    self.state = state
  end

  def approval_in_next?
    state != DRAFT && state != TRASH
  end

  def to_approval
    raise 'Transition d'état illégale!' unless approval_in_next?

    self.class.new(APPROBAL)
  end
end

Maintenant, même si les conditions qui peuvent être approuvées se compliquent ou que le processus de transition de l'état vers le statut d'approbation se complique, il ne vous reste plus qu'à changer ici!


Cette entrée est le State Transition Diagram, State Transition Table de Requirements Analysis Driven Design #% E7% 8A% B6% E6% 85% 8B% E9% 81% B7% E7% A7% BB% E5% 9B% B3% E7% 8A% B6% E6% 85% 8B% E9% 81% B7% E7% A7% BB% E8% A1% A8) a été réorganisé et réécrit en contenu à usage général. C'est une longue histoire, mais c'est une histoire de choses de type DDD dans Rails.

Recommended Posts

S'il y a une transition d'état, créons une classe State
Si vous avez des calculs compliqués, créons une classe CalcRule
Qu'est-ce qu'une classe wrapper?
[Rails DM] Créons une fonction de notification lorsque DM est envoyé!
Enfin, créez une méthode pour savoir s'il y a un caractère
Next () de Resultset n'est pas une "méthode pour déterminer s'il y a un ResultSet next".
[Java] Le branchement conditionnel est une instruction if, mais il existe également un opérateur conditionnel.
Le cas où le prochain () de Resultset a été confondu avec la méthode de jugement de l'existence ou non
Qu'est-ce qu'une classe en langage Java (3 /?)
Existe-t-il une version numérique de include?
Qu'est-ce qu'une classe en langage Java (1 /?)
Créons un environnement de développement Java (mise à jour)
Qu'est-ce qu'une classe en langage Java (2 /?)
Créez une classe temporaire avec le nouvel Object () {}
Comment vérifier si une variable d'instance est définie dans une classe Ruby
[Android] Créez une nouvelle classe en héritant d'ImageView
Créons une API REST à l'aide de WildFly Swarm.
Créons un processus chronométré avec la minuterie de Java! !!
Créer un onglet personnalisé avec SwiftUI 2.0
Comment créer une classe qui hérite des informations de classe
Créons un framework Web ultra-simple avec Java
[Java] Créons un Minecraft Mod 1.14.4 [Introduction]
[Java] Créons un Minecraft Mod 1.16.1 [Introduction]
[Java] Créons un Minecraft Mod 1.14.4 [99. Mod output]