[RUBY] J'ai essayé de comprendre comment la méthode des rails "redirect_to" est définie

C'est le deuxième projet pour apprendre à écrire comme ruby en interprétant la définition de la méthode rails. Le thème cette fois est redirect_to

Définition de redirect_to

# File actionpack/lib/action_controller/metal/redirecting.rb, line 56
def redirect_to(options = {}, response_status = {})
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
  raise AbstractController::DoubleRenderError if response_body

  self.status        = _extract_redirect_to_status(options, response_status)
  self.location      = _compute_redirect_to_location(request, options)
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
end

La première ligne

def redirect_to(options = {}, response_status = {})

Prend deux arguments. Dans les deux cas, un hachage vide est attribué si aucun argument n'est passé.

2e ligne

raise ActionControllerError.new("Cannot redirect to nil!") unless options

Génère une erreur lorsque les options sont vides. Au fait, ruby traite tout sauf false et nil comme true, donc un hachage vide {} ne provoquera pas d'erreur. Une erreur se produit lorsque l'utilisateur ose attribuer nil / false.

3e ligne

raise AbstractController::DoubleRenderError if response_body

Une erreur se produira si vous avez déjà reçu une réponse HTTP. Je ne sais pas comment response_body est affecté ici, mais il semble renvoyer une erreur lorsque plusieurs communications HTTP sont échangées en même temps.

4ème ligne

self.status = _extract_redirect_to_status(options, response_status)

Une nouvelle méthode est également sortie cette fois.

Méthode \ _extract_redirect_to_status

Définition

#Fileactionpack/lib/action_controller/metal/redirecting.rb, line 117
def _extract_redirect_to_status(options, response_status)
  if options.is_a?(Hash) && options.key?(:status)
    Rack::Utils.status_code(options.delete(:status))
  elsif response_status.key?(:status)
    Rack::Utils.status_code(response_status[:status])
  else
    302
  end
end

2e ligne

if options.is_a?(Hash) && options.key?(:status)

Un élément de la condition if is_a? Méthode est le type dans lequel le type du récepteur (ici "options") est écrit entre parenthèses. Une méthode qui renvoie true si elle correspond (ici "hash").

Les deux éléments key? de la condition if sont les clés dont les noms sont écrits entre parenthèses. Une méthode qui renvoie true si c'est le cas.

En d'autres termes, si l'argument passé à redirect_to est un hachage et a le statut de clé, le contenu de l'instruction if sera exécuté. Comme vous pouvez le voir dans le document Rails mentionné au début

redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }

Status est un paramètre défini par le programmeur qui utilise la méthode redirect_to. Au fur et à mesure que vous le lisez, vous pouvez voir quel rôle jouent les paramètres.

3e ligne

Rack::Utils.status_code(options.delete(:status))

Rack est un groupe de programmes qui servent d'intermédiaire entre les applications Rails et les serveurs d'applications. (Appelé middleware) status_code est une méthode définie dans une bibliothèque appelée Utils.

La valeur affectée au statut de la clé d'option par la méthode de suppression est transmise à la méthode status_code.

méthode status_code

def status_code(status)
  if status.is_a?(Symbol)
    SYMBOL_TO_STATUS_CODE[status] || 500
  else
    status.to_i
  end
end

Cette méthode est juste une méthode pour afficher la valeur de status. Il est appelé SYMBOL_TO_STATUS_CODE uniquement lorsqu'il est passé par Symbol (déterminé par la deuxième ligne est_a). Le hachage de conversion défini dans Rack :: Utils est utilisé.

La définition elle-même

SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
}.flatten]

Ça ressemble à ça. Le hachage HTTP_STATUS_CODES également défini par ce code dans Rack :: Utils {: nom d'état HTTP Convertit en un tableau de hachage appelé => code d'état HTTP}.

En particulier

 pry(main)> Rack::Utils::SYMBOL_TO_STATUS_CODE
=> {:continue=>100,
 :switching_protocols=>101,
 :processing=>102,
 :early_hints=>103,
 :ok=>200,
 :created=>201,
 :accepted=>202,
 :non_authoritative_information=>203,
 :no_content=>204,
 :reset_content=>205,
 :partial_content=>206,
 :multi_status=>207,
 :already_reported=>208,
 :im_used=>226,
 :multiple_choices=>300,
 :moved_permanently=>301,
 :found=>302,
 :see_other=>303,
 :not_modified=>304,
 :use_proxy=>305,
 :"(unused)"=>306,
 :temporary_redirect=>307,
 :permanent_redirect=>308,
:bad_request=>400,
 :unauthorized=>401,
 :payment_required=>402,
 :forbidden=>403,
 :not_found=>404,
 :method_not_allowed=>405,
 :not_acceptable=>406,
 :proxy_authentication_required=>407,
 :request_timeout=>408,
 :conflict=>409,
 :gone=>410,
 :length_required=>411,
 :precondition_failed=>412,
 :payload_too_large=>413,
 :uri_too_long=>414,
 :unsupported_media_type=>415,
 :range_not_satisfiable=>416,
 :expectation_failed=>417,
 :misdirected_request=>421,
 :unprocessable_entity=>422,
 :locked=>423,
 :failed_dependency=>424,
 :too_early=>425,
 :upgrade_required=>426,
 :precondition_required=>428,
 :too_many_requests=>429,
 :request_header_fields_too_large=>431,
 :unavailable_for_legal_reasons=>451,
 :internal_server_error=>500,
 :not_implemented=>501,
 :bad_gateway=>502,
 :service_unavailable=>503,
 :gateway_timeout=>504,
 :http_version_not_supported=>505,
 :variant_also_negotiates=>506,
 :insufficient_storage=>507,
 :loop_detected=>508,
 :bandwidth_limit_exceeded=>509,
 :not_extended=>510,
 :network_authentication_required=>511}
#Exécutez sur la console Rails.

En d'autres termes, lorsque vous revenez à la définition de status_code

SYMBOL_TO_STATUS_CODE[status] || 500

Dans status :: not_found, "404" est renvoyé. S'il n'y a pas d'article correspondant, "500" est renvoyé.

Si une valeur telle que "404" est affichée dans les options [: status] au lieu du symbole, les 4ème à 5ème lignes renverront la valeur telle qu'elle est au format entier.

else
  status.to_i
end

Méthode \ _extract_redirect_to_status

(Republier)

#Fileactionpack/lib/action_controller/metal/redirecting.rb, line 117
def _extract_redirect_to_status(options, response_status)
  if options.is_a?(Hash) && options.key?(:status)
    Rack::Utils.status_code(options.delete(:status))
  elsif response_status.key?(:status)
    Rack::Utils.status_code(response_status[:status])
  else
    302
  end
end

Jusqu'à présent, j'ai lu ce que je fais sur la troisième ligne.

4ème ligne

elsif response_status.key?(:status)

La méthode key? Rend cette instruction conditionnelle vraie lorsque l'argument response_status a un statut de clé. La méthode à exécuter est la même qu'avant.

Cela semble être une branche if pour permettre à l'état HTTP d'être renvoyé correctement, que la valeur d'état soit passée dans options ou response_status.

Si l'état HTTP à renvoyer n'est pas passé L'instruction else des lignes 6-7 renvoie 302. (Cela signifie «traitement de redirection».)

redirect_to (Republier)

# File actionpack/lib/action_controller/metal/redirecting.rb, line 56
def redirect_to(options = {}, response_status = {})
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
  raise AbstractController::DoubleRenderError if response_body

  self.status        = _extract_redirect_to_status(options, response_status)
  self.location      = _compute_redirect_to_location(request, options)
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
end

Dans la 4ème ligne, j'ai décidé que le code d'état HTTP soit renvoyé du côté utilisateur.

5ème ligne

self.location = _compute_redirect_to_location(request, options)

Une nouvelle méthode de rails est arrivée. La requête affectée ici est l'objet qui est créé à chaque fois sur le contrôleur. Chaque paramètre de la requête HTTP envoyée du côté utilisateur au côté application rails est stocké.

Quels types de paramètres existe-t-il? [Guide des rails](https://railsguides.jp/action_controller_overview.html#request%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3 % 83% 88% E3% 81% Réponse A8% E3% 82% AA% E3% 83% 96% E3% 82% B8% E3% 82% A7% E3% 82% FA% E3% 83% 88) Je suis.

Vous pouvez récupérer la valeur comme request.host.

La définition elle-même est faite sur le modèle ActionDispatch :: Request. Les méthodes disponibles pour ce modèle sont répertoriées ici (https://api.rubyonrails.org/classes/ActionDispatch/Request.html).

\ _ Méthode Compute_redirect_to_location

Définition

# File actionpack/lib/action_controller/metal/redirecting.rb, line 96
    def _compute_redirect_to_location(request, options) #:nodoc:
      case options
      # The scheme name consist of a letter followed by any combination of
      # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
      # characters; and is terminated by a colon (":").
      # See https://tools.ietf.org/html/rfc3986#section-3.1
      # The protocol relative scheme starts with a double slash "//".
      when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/
        options
      when String
        request.protocol + request.host_with_port + options
      when Proc
        _compute_redirect_to_location request, instance_eval(&options)
      else
        url_for(options)
      end.delete("\00\\r\n")
    end

Cette méthode générera l'URL vers laquelle rediriger. Le branchement conditionnel par l'instruction case est effectué par le contenu des opsions.

Lignes 9-10

when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/
  options

http://のように先頭に○○://とつく文字列のときに、この条件がtrueとなります。 Puisqu'il est jugé qu'il est sous forme d'URL, la valeur des options est renvoyée telle quelle.

Lignes 11-12

when String
  request.protocol + request.host_with_port + options

Appelé lorsque options est une chaîne. L'URL est générée sur la base de l'objet de requête qui stocke les informations de requête du côté utilisateur introduites précédemment. http: // et https: // sont stockés dans le protocole. host_with_port générera des hôtes après //. Enfin, des options sont données.

La méthode host_with_port est une méthode simple qui appelle les propriétés = host et port_string stockées dans l'objet de requête et les combine sous forme de chaîne.

# File actionpack/lib/action_dispatch/http/url.rb, line 250
def host_with_port
  "#{host}#{port_string}"
end

Lignes 13-14

when Proc
  _compute_redirect_to_location request, instance_eval(&options)

Qu'est-ce que Proc

Un objet procédural qui objective un bloc avec un contexte (portée de variable locale ou cadre de pile). (https://docs.ruby-lang.org/ja/latest/class/Proc.html)[https://docs.ruby-lang.org/ja/latest/class/Proc.html]

redirect_to peut être utilisé au format bloc, et il est normal de reconnaître qu'il entrera dans cette branche lorsqu'un bloc est donné.

Par exemple

get 'jokes/:number', to: redirect { |params, request|
  path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
  "http://#{request.host_with_port}/#{path}"
  }

Vous pouvez écrire quelque chose comme ça.

Lorsque le bloc est appelé, la méthode _compute_redirect_to_location est appelée de manière récursive. Pour les débutants, la récurrence est l'appel de la même méthode au sein d'une méthode. Par exemple

def method
  method
end

Il a la forme. (Dans ce cas, ce sera une boucle infinie. Normalement, elle est écrite de manière à ne pas boucler indéfiniment avec if branch etc.)

(Republier)

\_compute_redirect_to_location request, instance_eval(&options)

instance_eval (& options) est assigné au deuxième terme de la méthode \ _compute_redirect_to_location (request, options).

Il y a un & devant les options, ce qui signifie attribuer un bloc. instance_eval est une méthode qui renvoie la sortie lorsque le code du bloc est exécuté.

Prenons l'exemple précédent (Republier)

get 'jokes/:number', to: redirect { |params, request|
  path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
  "http://#{request.host_with_port}/#{path}"
  }

Dans ce cas, le code sur les 2ème et 3ème lignes sera exécuté, et par conséquent, l'URL commençant par http: // sur la 3ème ligne sera renvoyée sous forme de chaîne de caractères. En d'autres termes L'URL est affectée à la méthode \ _compute_redirect_to_location. L'instruction case est appelée et se ramifie aux conditions des 9ème à 10ème lignes mentionnées ci-dessus. (Republier)

when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/
  options

Lignes 15-16

else
  url_for(options)

Pour résumer les conditions jusqu'à présent http://~といった具体的なURL、ファイル名を指す文字列、ブロックについて分岐されてきたが、いずれにも該当しない場合はこのelse文の内容が実行されることになります。

Ce qui ressort ici est la méthode url_for qui a également été utilisée dans link_to.

def url_for(options)
    if options[:only_path]
      path_for options
    else
      full_url_for options
    end
end
url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :port => '8080'
# => 'http://somehost.org:8080/tasks/testing'
url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :anchor => 'ok', :only_path => true
# => '/tasks/testing#ok'
url_for :controller => 'tasks', :action => 'testing', :trailing_slash => true
# => 'http://somehost.org/tasks/testing/'
url_for :controller => 'tasks', :action => 'testing', :host => 'somehost.org', :number => '33'
# => 'http://somehost.org/tasks/testing?number=33'

(Citation) Générez une URL comme celle-ci.

17e ligne

end.delete("\00\\r\n")

Delete est appelé à la fin de la déclaration de cas.

Puisqu'il est appelé pour une chaîne de caractères, le comportement est différent de la suppression qui est apparue jusqu'à présent. Selon la référence Ruby

delete(*strs) -> String Génère et renvoie une chaîne avec les caractères contenus dans les chaînes supprimées.

La référence comprend également un article de référence pour le format strs. Je ne suis pas sûr car c'est moins important et je ne le comprends pas, Puisque \ r et \ n sont des caractères spéciaux qui représentent respectivement "retour" et "saut de ligne", on suppose qu'ils ont le sens de supprimer ces caractères qui sont mélangés avec l'URL. Alors je ne sais pas ce qu'est \ 00 , et je peux peut-être le séparer de \ 0, 0, \, r, \ n.

Jusqu'à présent, la méthode \ _compute_redirect_to_location.

redirect_to (Republier)

# File actionpack/lib/action_controller/metal/redirecting.rb, line 56
def redirect_to(options = {}, response_status = {})
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
  raise AbstractController::DoubleRenderError if response_body

  self.status        = _extract_redirect_to_status(options, response_status)
  self.location      = _compute_redirect_to_location(request, options)
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
end

La ligne 4 détermine le code d'état HTTP à renvoyer à l'utilisateur après la redirection. L'URL de la destination de la redirection a été décidée sur la 5ème ligne.

6ème ligne

self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"

Maintenant, décidez des données html à renvoyer côté utilisateur. Ce code HTML ne sera pas affiché sur le navigateur de l'utilisateur car le processus de redirection est en fait effectué en fonction de la valeur de l'emplacement.

Ici, la valeur stockée dans self. ~ Est transmise au navigateur de l'utilisateur en tant que réponse HTTP. En regardant l'URL de la propriété d'emplacement (attribut) dans l'en-tête HTTP, le navigateur de l'utilisateur effectue à nouveau la demande.

Voir ici pour savoir comment le navigateur traite.

Sommaire

(Republier)

# File actionpack/lib/action_controller/metal/redirecting.rb, line 56
def redirect_to(options = {}, response_status = {})
  raise ActionControllerError.new("Cannot redirect to nil!") unless options
  raise AbstractController::DoubleRenderError if response_body

  self.status        = _extract_redirect_to_status(options, response_status)
  self.location      = _compute_redirect_to_location(request, options)
  self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
end

Lorsque redirect_to est appelé

  1. Erreur si les options incluent zéro
  2. Erreur si la réponse a déjà été générée
  3. Stockez l'état de la réponse HTTP renvoyée à l'utilisateur (200: succès, 404: erreur introuvable, etc.) dans l'objet de réponse.
  4. Stockez l'URL de destination de la redirection dans l'objet de réponse
  5. Stockez les données HTML dans un objet de réponse (mais pratiquement jamais affiché)

Impressions

J'ai trouvé que c'est le rôle de générer les données de réponse nécessaires pour cela, en supposant que le traitement réel est laissé au client (le navigateur utilisé par l'utilisateur) plutôt que d'effectuer le traitement dans cette méthode.

Si vous approfondissez votre compréhension du traitement effectué par les applications et les intergiciels Rails, il semble que vous serez en mesure de mettre en œuvre des traitements plus complexes.

Recommended Posts

J'ai essayé de comprendre comment la méthode des rails "redirect_to" est définie
J'ai essayé de comprendre comment la méthode des rails "link_to" est définie
J'ai essayé d'expliquer la méthode
[Rails] Comment utiliser la méthode de la carte
[Rails] J'ai essayé de faire passer la version de Rails de 5.0 à 5.2
[Rails] Je ne sais pas comment utiliser le modèle ...
[Rails] J'ai essayé d'utiliser la méthode button_to pour la première fois
Comment utiliser la méthode link_to
Comment utiliser la méthode include?
Comment utiliser la méthode form_with
[Rails] J'ai essayé de supprimer l'application
Tri des données Décroissant, croissant / Rails
J'ai essayé d'implémenter la fonction de prévisualisation d'image avec Rails / jQuery
[Rails] Comment omettre l'affichage de la chaîne de caractères de la méthode link_to
[Java] J'ai essayé de faire un labyrinthe par la méthode de creusage ♪
J'ai essayé de résumer les méthodes utilisées
[Java] Comment utiliser la méthode toString ()
Je voulais ajouter @VisibleForTesting à la méthode
J'étais accro à la méthode du rouleau
J'ai essayé d'implémenter le modèle Iterator
J'ai essayé de résumer l'API Stream
[Ruby on Rails] Comment utiliser redirect_to
Qu'est-ce que Docker? J'ai essayé de résumer
Emplacement de la définition de la méthode Résumé de la vérification Lorsque défini dans le projet et Rails / Gem
[Rails] Comment utiliser la méthode d'assistance, confimartion
[Introduction à Java] J'ai essayé de résumer les connaissances que j'estime essentielles
[Rails] Comment résoudre le décalage temporel de created_at après la méthode de sauvegarde
[Rails] Comment décider de la destination par "voies ferrées"
Sortie de la façon d'utiliser la méthode slice
Code utilisé pour connecter Rails 3 à PostgreSQL 10
Comment utiliser la méthode replace () (Java Silver)
J'ai essayé de configurer tomcat pour exécuter le servlet.
Comment vérifier les commandes Rails dans le terminal
[Introduction à Ruby] Comment utiliser la méthode slice
[Rails / ActiveRecord] Je souhaite valider la valeur avant la conversion du type (_before_type_cast)
[JavaScript] Le cas le plus fort lorsque j'ai essayé de résumer les parties que je ne comprends pas
[Rails] Comment utiliser la méthode d'assistance utilisée dans l'application principale avec Administrer
[JDBC ③] J'ai essayé d'entrer à partir de la méthode principale en utilisant des espaces réservés et des arguments.
[Rails] Comment se connecter à une API externe à l'aide du client HTTP (j'ai essayé de me connecter à l'API Qiita)
Comment régler l'heure d'affichage sur l'heure japonaise dans les rails
Jusqu'où est la bonne réponse pour diviser le processus?
[rails] Comment utiliser la méthode d'assistance de devise before_action: authenticate_user!
Comment écrire des rails
[Ruby on Rails] Comment changer le nom de la colonne
05. J'ai essayé de supprimer la source de Spring Boot
J'ai essayé de réduire la capacité de Spring Boot
[Rails] Comment changer le nom de colonne de la table
Comment désinstaller Rails
Rails6 J'ai essayé d'introduire Docker dans une application existante
Je veux appeler la méthode principale en utilisant la réflexion
[Rails] Comment obtenir le contenu des paramètres forts