[RUBY] Trois anti-patrons de contrôleur que nous voulons transmettre aux Rails du débutant aux intermédiaires

tl; dr Arrêtez d'abuser des variables d'instance, en particulier avec les contrôleurs!

Le début de tout

Un mois après avoir été transféré dans un projet que j'avais à peine touché, je me battais encore férocement avec Rails aujourd'hui dans un pays dévasté où un senior qualifié qui connaissait le code était parti! Alors (?), Cette fois je voudrais introduire des anti-patterns qui ne devraient pas être faits avec le contrôleur.

Putain de code 1. Mettez-le dans une variable d'instance, ouais

C'est une impression que les débutants ont tendance à faire Un tel mec

sample_controller.rb


class SampleController < ApplicationController
  def show
    hoge = Hoge.new
    @hoge_kuso1 = hoge.kuso
    @hoge_kuso2 = hoge.sugoku_kuso
    @hoge_kuso3 = Hoge.fetch_kuso
  end
end

......

** Ce ☆ Ro ☆ Su ☆ zo **

Tout d'abord, les contrôleurs Rails combinent ce qui va dans un répertoire dans à peu près une classe, en d'autres termes, la logique de plusieurs actions dans une classe. Ainsi, par exemple, le code suivant est une occurrence quotidienne.

sample_controller.rb


class SampleController < ApplicationController
  def action1
  end

  def action2
  end
end

Supposons que les deux actions utilisent beaucoup de variables d'instance dans cet état. C'est en dessous

sample_controller.rb


class SampleController < ApplicationController
  def action1
    hoge = Hoge.new
    @hoge_kuso1 = hoge.kuso
    @hoge_kuso2 = Hoge.fetch_kuso
  end

  def action2
    hoge = Hoge.new
    @hoge_kuso1 = hoge.sugoku_kuso
    @hoge_kuso2 = Hoge.fetch_kuso
  end

  ...
end

Au fait, la classe Hoge est comme ça

hoge.rb


require 'rest-client'

class Hoge
  def kuso
    :kuso
  end

  def sugoku_kuso
    "sugoku kuso"
  end

  def self.fetch_kuso
    RestClient.get('https://kuso.com/kuso3').body rescue 'honma kuso'
  end
end

Ce qui ne va pas avec cette situation, c'est qu'il est difficile de dire quelle variable appartient à où dans tout le code en raison de l'utilisation intensive des variables d'instance. Par exemple, @ hoge_kuso1 est une variable de portée intraclasse, elle peut donc entrer en conflit avec des définitions ailleurs et être boguée. De plus, même si vous effectuez une recherche dans la classe, il est possible que plusieurs personnes non apparentées soient attrapées, il est donc difficile de compléter. Il n'y a pas beaucoup de mérite.

Solution

L'histoire simple est d'éviter autant que possible d'utiliser des variables d'instance. Plus précisément:

sample_controller.rb


class SampleController < ApplicationController
  def action1
    @hoge = Hoge.new
  end

  def action2
    @hoge = Hoge.new
  end

  ...
end

hoge.rb


require 'rest-client'

class Hoge
  def kuso
    :kuso
  end

  def sugoku_kuso
    "sugoku kuso"
  end

  #Rendre les éléments accessibles à partir du modèle pour minimiser la possibilité d'utiliser des variables d'instance sur le contrôleur
  def fetched_kuso
    @fetched_kuso ||= self.class.fetch_kuso
  end

  def self.fetch_kuso
    RestClient.get('https://kuso.com/kuso3').body rescue 'honma kuso'
  end
end

Cela signifie que vous ne devez placer le modèle que dans les variables d'instance et inclure les données détaillées dans le modèle. Il y a deux avantages à cela

  1. Le contrôleur rafraîchit
  2. La logique ne dépend pas de plusieurs emplacements

Pour être honnête, il n'y a que du mérite, donc je ne pense pas que je puisse faire quoi que ce soit. En passant, par exemple, je souhaite obtenir les valeurs de plusieurs modèles à la fois. Que dois-je faire dans ce cas? À propos de ça

sample_controller.rb


class SampleController < ApplicationController
  def action1
    @hoges = 10.times.map { Hoge.new }
    Hoge.set_kusos(@hoges)
  end
end

hoge.rb


require 'rest-client'

class Hoge
  def kuso
    :kuso
  end

  def sugoku_kuso
    "sugoku kuso"
  end

  attr_accessor :fetched_kuso
  #Setter dédié
  def self.set_kusos(hoges)
    hoges.each do |hoge|
      hoge.fetched_kuso = self.fetch_kuso
    end
  end

  def self.fetch_kuso
    RestClient.get('https://kuso.com/kuso3').body rescue 'honma kuso'
  end
end

Il est bon de conserver les données dans le modèle. Quoi qu'il en soit, il est important de ne pas écrire de logique sans discernement avec le contrôleur.

Putain de code 2. Si vous écrivez set_〇〇, c'est sûr. Celui-là

Est inutile Par exemple, je pense que la plupart des gens écrivent comme ça

sample_controller.rb


class SampleController < ApplicationController
  before_action :set_hoge, only: :action1

  def action1
  end

  private
  def set_hoge
    @hoge = Hoge.new
  end
end

Absolument 9 sur 10 écrivent: Parce que scaffold recommande également d'écrire comme ça. Alors, ce qui ne va pas avec ceci, ce sont les deux suivants

  1. Le code est difficile à suivre
  2. Difficile de faire passer des arguments

D'abord environ 1

sample_controller.rb


class SampleController < ApplicationController
  before_action :set_hoge,  only: :action1
  before_action :set_hoges, only: :action2

  def action1
  end
  def action2
  end

  private
  def set_hoge
    @hoge = Hoge.new
  end

  def set_hoges
    @hoges = [Hoge.new, Hoge.new]
  end
end

Si c'est écrit comme ça, au moment où vous vérifiez action1, personne ne dira que cela utilise @ hoge. au moins, action1 n'est pas du tout définie-> Ah, before_action est définie signifie-> Après tout, est-ce que @ hoge a été utilisé? N'est-ce pas? Cela peut être une bonne expérience pour une expérience aha, mais n'introduisez pas la science du cerveau dans votre codage. Pour être clair, c'est stressant.

De plus, comme mentionné dans [Fucking Code 1](#Fucking Code 1. Si vous le mettez dans une variable d'instance, ouais), l'emplacement de la variable d'instance utilisée dans le contrôleur a tendance à être difficile à comprendre. Par conséquent, il n'est pas bon d'utiliser les variables d'instance sans discernement.

Suivant environ 2

sample_controller.rb


class SampleController < ApplicationController
  before_action(only: :action1) do
    set_hoge('fuga')
  end

  def action1
  end

  private
  def set_hoge(fuga)
    @hoge = Hoge.new(fuga)
  end
end

Si vous ne le faites pas, vous ne pourrez pas transmettre de variables. Au contraire, si tel est le cas, pouvez-vous le donner? Les gars. Vous pouvez le transmettre, mais vous ne pouvez pas utiliser l'action pratique skip_before_action. Est-il nécessaire d'utiliser set_〇〇 jusque-là? Je suis plein de sentiments.

Solution

La réponse est simple.

sample_controller.rb


class SampleController < ApplicationController
  def action1
    @hoge = fetch_hoge
  end

  private
  def fetch_hoge
    Hoge.new
  end
end

Dans ce cas, puisque la variable d'instance est intégrée dans la logique, vous pouvez voir que @ hoge est utilisé au moment où vous le voyez. Il est également facile de passer des arguments. Très intelligent et donc heureux.

De plus, avec cela, il faut du temps pour écrire redirect_to! Que fais-tu là-bas! ?? Donnons le code suivant pour tout le monde

sample_controller.rb


class SampleController < ApplicationController
  class DataNotFound < RuntimeError; end
  rescue_from DataNotFound do
    redirect_to :root_path, flash: { error: 'Aucune donnée n'a été trouvée' }
  end

  def action1
    @hoge = fetch_hoge
  end

  private
  def fetch_hoge
    hoge = Hoge.new
    raise DataNotFound unless hoge
    hoge
  end
end

Cela conservera la fonctionnalité d'origine.

Putain de code 3. Si vous écrivez set_〇〇, c'est sûr. Partie 2

Tu as dit non! !! !! Cela dépend du cas, mais par exemple, il y a des gens qui font la valeur utilisée dans l'en-tête comme ça.

sample_controller.rb


class SampleController < ApplicationController
  before_action :set_header_de_tukau_yatu

  def set_header_de_tukau_yatu
    @header_info = 'void'
  end
end

Le problème avec ceci est que vous devez savoir que vous devez "appeler: set_header_de_tukau_yatu" pour "utiliser" @ header_info "". Dans ce cas, je ne veux que la vue @ header_info, mais pourquoi dois-je comprendre comment la configurer? L'histoire

Solution

Dans un tel cas, vous devez utiliser helper_method. Par exemple:

sample_controller.rb


class SampleController < ApplicationController
  helper_method :header_info

  def header_info
    @header_info ||= 'void'
  end
end

De cette façon, view peut obtenir les informations en appelant header_info. Tout ce que vous avez à faire est de le savoir. Vous n'avez pas à vous soucier de connaître la logique interne et les variables d'instance sont proches de la logique. Très agréable.

en conclusion

Cet article ne présente que trois anti-modèles, mais je pense qu'il existe probablement plus d'anti-modèles. Mais dans de nombreux cas, n'est-il pas «moins lisible» ou «inutilisable» ou les deux? Et la racine de tous les maux qui ont tendance à produire les deux est l'abus des variables d'instance (argumenter. Il n'y a qu'une seule chose que je voulais vraiment transmettre cette fois. Veuillez réciter tout le monde.

** Arrêtez d'abuser des variables d'instance! !! !! !! !! ** **

Merci pour la lecture.

Recommended Posts

Trois anti-patrons de contrôleur que nous voulons transmettre aux Rails du débutant aux intermédiaires
Lorsque vous souhaitez que Rails désactive une session pour un contrôleur spécifique
Trois sélections par de bons débutants qui veulent recommander aux ingénieurs Swift débutants