#ruby-on-rails #ruby
Вопрос:
Просмотр этого кода:
params[:id]
Парам считается методом. Поправьте меня, если я ошибаюсь. Но это все равно что читать по хэшу. Итак, в настоящее время я в замешательстве.
Если параметры-это метод: как работает приведенный пример кода?
Ответ №1:
Вы правы, что params
это метод, но здесь params
метод возвращает экземпляр ActionController::Parameters
, и мы вызываем метод доступа к хэшу #[]
для него.
Это распространенный шаблон в ruby для вызова методов возвращаемого объекта. Давайте посмотрим на это на простом примере:
def params
{
id: 101,
key: 'value',
foo: 'bar'
}
end
params[:id] # => 101
params[:foo] # => 'bar'
Как вы можете видеть в примере, метод params
возвращает хэш-объект, и мы вызываем метод доступа к хэшу #[]
для возвращенного объекта.
Ссылка на params
метод рельсов: 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
Примечание для новичков в ruby: В ruby мы можем вызывать методы без круглых скобок. Таким образом, приведенный выше вызов эквивалентен params()[:id]
.
Комментарии:
1. Отличный ответ, может быть, будет полезно добавить, что он эквивалентен
params()[:id]
Ответ №2:
Они известны как средства доступа в квадратных скобках, и вы можете добавить их к любому объекту, реализовав методы []
и. []=
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
Также при вызове params[:id]
— params
метод будет вызван первым, поэтому вы вызываете []
экземпляр ActionController::Параметры, как в этом упрощенном примере:
def foo
Store.new(bar: 1)
end
foo[:bar] # 1
Поскольку родители являются необязательными, это равносильно вызову params()[:id]
.
Ответ №3:
В контексте контроллера params
это действительно метод. Допустим, у нас есть OrganizationsController
объект, который раскрывает #index
действие в спокойной конечной точке. Я добавлю точку останова, используя драгоценный камень pry, чтобы мы могли лучше понять, что params
такое:
class OrganizationsController < ApplicationController
def index
binding.pry # Runtime will stop here
render json: Organization.all
end
end
И давайте перейдем по следующему адресу:
http://localhost:3000/organizations.json?foo=bar
Мы действительно можем проверить, что params
это метод, явно вызвав его с помощью ()
:
> params()
=> #<ActionController::Parameters {"foo"=>"bar", "controller"=>"organizations", "action"=>"index", "format"=>"json"} permitted: false>
или на самом деле спросив Ruby, где определен этот метод:
> 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]
Объект , возвращаемый вызовом params
, является не a Hash
, а ActionController::Parameters
вместо этого:
> params.class
=> ActionController::Parameters
Однако мы можем вызвать метод :[]
на нем, потому что он фактически определен в ActionController::Parameters
классе (см. Код).
Это заставляет его выглядеть так , как будто это на самом деле а Hash
, но на самом деле это не так. Например, мы не можем вызвать Hash
метод invert on params
, так как он не определен:
> params.invert
NoMethodError: undefined method `invert' for #<ActionController::Parameters {"foo"=>"bar", "controller"=>"organizations", "action"=>"index", "format"=>"json"} permitted: false>