tl; dr Arrêtez d'abuser des variables d'instance, en particulier avec les contrôleurs!
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.
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.
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
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.
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
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.
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.
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
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.
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.