Rails:: save возвращает NoMethodError - PullRequest
0 голосов
/ 19 декабря 2018

Я программирую небольшой рельс API для практики.Цель состоит в том, чтобы POST некоторые данные и затем сохранить их (после дальнейшей обработки) в базе данных.Объект создается успешно.В методе сохранения появляется ошибка, которую я не могу объяснить:

NoMethodError (undefined method `[]' for nil:NilClass): app/controllers/api/v1/calculations_controller.rb:19:in `create'

Появляется в методе: create (на POST):

  # POST /calculations
  def create
    @calculation = Calculation.new(calculation_params)
    if @calculation.save # <- This is line 19
      render json: @calculation, status: :created, location: @calculation
    else
      render json: @calculation.errors, status: :unprocessable_entity
    end
  end

Модель выглядит следующим образом:

require 'digest'

class Calculation < ApplicationRecord

  attr_reader :value, :hash_value, :algorithm, :timestamp
  @@known_algorithms = ['SHA2_256', 'SHA2_384', 'SHA2_512']

  def initialize (params)
    @value = params[:value]
    @algorithm = params[:algorithm]
    validate!
    @hash_value, @timestamp = digest
  end

  private

  def digest
    case @algorithm
    when 'SHA2_256'
      result = Digest::SHA2.hexdigest(@value)
    when 'SHA2_384'
      result = Digest::SHA2.new(384).hexdigest(@value)
    when 'SHA2_512'
      result = Digest::SHA2.new(512).hexdigest(@value)
    end
    return result, DateTime.now
  end

  def validate!
    raise ArgumentError.new('Value cannot be empty or NIL!') if @value.nil? or @value.empty?
    raise ArgumentError.new('Unknown hashing algorithm!') unless @@known_algorithms.include? @algorithm
  end
end

Почему появляется NoMethodError?Я переопределил какую-то важную часть в классе модели?

Полная трассировка стека в соответствии с запросом:

/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/transactions.rb:424:in `clear_transaction_record_state'
/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/transactions.rb:330:in `ensure in rollback_active_record_state!'
/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/transactions.rb:330:in `rollback_active_record_state!'
/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/transactions.rb:309:in `save'
/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/suppressor.rb:44:in `save'
/home/pbz/RubymineProjects/hasher_api/app/controllers/api/v1/calculations_controller.rb:20:in `create'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/base.rb:194:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/rendering.rb:30:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/callbacks.rb:42:in `block in process_action'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/callbacks.rb:132:in `run_callbacks'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/callbacks.rb:41:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/rescue.rb:22:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/notifications.rb:168:in `block in instrument'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/notifications/instrumenter.rb:23:in `instrument'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/notifications.rb:168:in `instrument'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/instrumentation.rb:32:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/params_wrapper.rb:256:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/railties/controller_runtime.rb:24:in `process_action'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/base.rb:134:in `process'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal.rb:191:in `dispatch'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal.rb:252:in `dispatch'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:52:in `dispatch'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:34:in `serve'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/journey/router.rb:52:in `block in serve'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/journey/router.rb:35:in `each'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/journey/router.rb:35:in `serve'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:840:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/rack-2.0.6/lib/rack/etag.rb:25:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/rack-2.0.6/lib/rack/conditional_get.rb:38:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/rack-2.0.6/lib/rack/head.rb:12:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/migration.rb:559:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/callbacks.rb:98:in `run_callbacks'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/executor.rb:14:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/debug_exceptions.rb:61:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/railties-5.2.2/lib/rails/rack/logger.rb:38:in `call_app'
/home/pbz/.gem/ruby/2.5.0/gems/railties-5.2.2/lib/rails/rack/logger.rb:26:in `block in call'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/tagged_logging.rb:71:in `block in tagged'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/tagged_logging.rb:28:in `tagged'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/tagged_logging.rb:71:in `tagged'
/home/pbz/.gem/ruby/2.5.0/gems/railties-5.2.2/lib/rails/rack/logger.rb:26:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/request_id.rb:27:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/rack-2.0.6/lib/rack/runtime.rb:22:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/activesupport-5.2.2/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/executor.rb:14:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/static.rb:127:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/rack-2.0.6/lib/rack/sendfile.rb:111:in `call'
/home/pbz/.gem/ruby/2.5.0/gems/railties-5.2.2/lib/rails/engine.rb:524:in `call'
/home/pbz/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/configuration.rb:225:in `call'
/home/pbz/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/server.rb:658:in `handle_request'
/home/pbz/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/server.rb:472:in `process_client'
/home/pbz/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/server.rb:332:in `block in run'
/home/pbz/.rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:133:in `block in spawn_thread'

Ответы [ 2 ]

0 голосов
/ 20 декабря 2018

Я узнал (благодаря @ Зелёный), что для создания значений для модели после ее создания (new) мне нужно использовать after_initialize callback-сервисы Rails.Вместе с этим комментарием о том, как получить доступ к переменным экземпляра модели, я смог достичь желаемой функциональности.Оригинальная ошибка NoMethodError была результатом моего неправильного использования метода initialize.Следующий код является окончательным результатом, который работает с контроллером по умолчанию.

require 'digest'

class Calculation < ApplicationRecord

  @@known_algorithms = ['SHA2_256', 'SHA2_384', 'SHA2_512']

  after_initialize :init

  private

  def init
    validate!
    calculate!
  end

  def calculate!
    self.hash_value, self.timestamp = digest
  end

  def digest
    case algorithm
    when 'SHA2_256'
      result = Digest::SHA2.hexdigest(value)
    when 'SHA2_384'
      result = Digest::SHA2.new(384).hexdigest(value)
    when 'SHA2_512'
      result = Digest::SHA2.new(512).hexdigest(value)
    end
    return result, DateTime.now
  end

  def validate!
    raise ArgumentError.new('Value cannot be empty or NIL!') if value.nil? or value.empty?
    raise ArgumentError.new('Unknown hashing algorithm!') unless @@known_algorithms.include? algorithm
  end
end
0 голосов
/ 19 декабря 2018

Не переопределяйте initialize в AR.Это может сломать много вещей в ваших моделях.

Вы можете использовать after_initialize.

Обратный вызов after_initialize будет вызываться всякий раз, когда создается экземпляр Active Record, либо напрямую использование новой или когда запись загружается из базы данных. Может быть полезно избежать необходимости переопределять метод Active Record initialize напрямую.

def after_initialize
  # Gets called right after Calculation.new
  # Do some stuff here
end

Если вы все еще хотитечтобы использовать initialize, вы должны сделать что-то вроде этого:

def initialize(attributes = {})
  super
  self.attributes = attributes
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...