Viendo este código:
params[:id]
Params se considera un método. Corrígeme si me equivoco ahí. Pero eso es como leer de un hash. Entonces, actualmente estoy confundido.
Si params es un método: ¿Cómo funciona el código de ejemplo que se muestra?
Tiene razón en que params es un método, pero aquí el método params devuelve una instancia de ActionController::Parameters y llamamos al método de acceso hash #[] en él.
Este es un patrón común en Ruby para llamar a métodos en el objeto devuelto. Veámoslo con un ejemplo sencillo:
def params { id: 101, key: 'value', foo: 'bar' } end params[:id] # => 101 params[:foo] # => 'bar' Como puede ver en el ejemplo, el método params devuelve un objeto hash y llamamos al método de acceso hash #[] en el objeto devuelto.
Referencia al método de params de rieles: https://github.com/rails/rails/blob/5e1a039a1dd63ab70300a1340226eab690444cea/actionpack/lib/action_controller/metal/strong_parameters.rb#L1215-L1225
def params @_params ||= begin context = { controller: self.class.name, action: action_name, request: request, params: request.filtered_parameters } Parameters.new(request.parameters, context) end end Nota para principiantes de Ruby: en Ruby, podemos llamar a métodos sin paréntesis. Entonces, la llamada anterior es equivalente a params()[:id] .
Esos se conocen como accesores de corchetes y puede agregarlos a cualquier objeto implementando los métodos [] y []= .
class Store def initialize(**kwargs) kwargs.each { |k,v| instance_variable_set("@#{k}", v) } end def [](key) instance_variable_get("@#{key}") end def []=(key, value) instance_variable_set("@#{key}", value) end end store = Store.new(foo: 1, bar: 2, baz: 3) store[:foo] # 1 store[:foo] = 100 store[:foo] # 100 Además, cuando llama a params[:id] , primero se llamará al método params , por lo que está llamando a [] en una instancia de ActionController::Parameters como en este ejemplo simplificado:
def foo Store.new(bar: 1) end foo[:bar] # 1 Dado que los paréntesis son opcionales, es equivalente a llamar a params()[:id] .
En el contexto de un controlador, params es de hecho un método. Digamos que tenemos un OrganizationsController que expone la acción #index en un punto final tranquilo. Agregaré un punto de interrupción usando la gema de palanca para que podamos entender mejor qué son los params :
class OrganizationsController < ApplicationController def index binding.pry # Runtime will stop here render json: Organization.all end endY vamos a visitar la siguiente URL:
http://localhost:3000/organizations.json?foo=bar
De hecho, podemos verificar que params es un método llamándolo explícitamente con () :
> params() => #<ActionController::Parameters {"foo"=>"bar", "controller"=>"organizations", "action"=>"index", "format"=>"json"} permitted: false>o preguntándole a Ruby dónde se define ese método:
> method(:params).source_location => ["/home/myuser/.rvm/gems/ruby-3.0.2@myproject/gems/actionpack-6.1.4.1/lib/action_controller/metal/strong_parameters.rb", 1186] El objeto devuelto al llamar a params no es un Hash , sino un ActionController::Parameters en su lugar:
> params.class => ActionController::Parameters Sin embargo, podemos llamar al método :[] porque en realidad está definido en la clase ActionController::Parameters (ver código )
Esto hace que parezca que en realidad es un Hash , pero en realidad no lo es. Por ejemplo, no podemos llamar al método Hash invert on params , ya que no está definido:
> params.invert NoMethodError: undefined method `invert' for #<ActionController::Parameters {"foo"=>"bar", "controller"=>"organizations", "action"=>"index", "format"=>"json"} permitted: false>