简体   繁体   中英

Fat model in Rails: How do I return hash (errors) or object (when success)

I have a Order model, and I'm trying to move the business logic into Order instead of OrderController. Here's the problem I'm facing:

class Api::V1::OrdersController < ApplicationController
  before_action :authenticate_with_token!, only: [:create, :show, :index]
  respond_to :json
  def create
    order = current_retailer.orders.build
    order.checkout_cash(current_retailer, params[:order][:product_ids_with_quantities], order_params[:member_external_id])

    puts order
    if order.key?(:errors)
      render json: order, status: 422
    else
      render json: order, status: 201
    end
    puts "HEY!!!"
    puts order.inspect

end

so the order.checkout_cash method is the business logic I'm implementing in Order model.

I have to know if it's a valid or it returns an error.

Here's my code in Order model:

  def checkout_cash(current_retailer, product_ids_with_quantities, member_external_id)
    puts "CASH!!!"
    order = current_retailer.orders.build
    order.payment_method = "cash"
    order.build_placements(product_ids_with_quantities)
    order.set_total_charge!
    if member_external_id.blank?
      return order
    else
      member = Member.find_by(member_external_id: external_id)
      if member
        order.add_points(member)
        return order
      else
        return {errors: "Not a member or wrong membership id. Please register first"}
      end
    end

In OrderController the line

if order.key?(:

results in:

 NoMethodError:
   undefined method `key?' for #<Order:0x007fb7a29ec260>

I'm sure it's because I'm calling a function on hash, on a Order object. How should I do this in Rails?


Now:

def create
  order = current_retailer.orders.build
  checkout_result = order.checkout_cash(current_retailer, params[:order][:product_ids_with_quantities], order_params[:member_external_id])

  puts order

  if checkout_result.key?(:errors)
    render json: order, status: 422
  else
    order.save!
    order.reload
    render json: order, status: 201
  end

is getting:

1) Api::V1::OrdersController POST #create create with default total_charge return 0 as total_charge
  Failure/Error: get :create, retailer_id: @retailer, order: {product_ids_with_quantities: []}
  NoMethodError:
    undefined method `key?' for #<Order:0x007ff69244fb70>
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activemodel-4.2.0/lib/active_model/attribute_methods.rb:433:in `method_missing'
  # ./app/controllers/api/v1/orders_controller.rb:11:in `create'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/abstract_controller/base.rb:198:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/metal/rendering.rb:10:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:117:in `call'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:117:in `call'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:151:in `block in halting_and_conditional'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:234:in `call'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:234:in `block in halting'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in `call'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in `block in halting'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in `call'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in `_run_callbacks'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:734:in `_run_process_action_callbacks'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:81:in `run_callbacks'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/abstract_controller/callbacks.rb:19:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/metal/rescue.rb:29:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/notifications.rb:164:in `block in instrument'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activesupport-4.2.0/lib/active_support/notifications.rb:164:in `instrument'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/metal/instrumentation.rb:30:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/activerecord-4.2.0/lib/active_record/railties/controller_runtime.rb:18:in `process_action'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/abstract_controller/base.rb:137:in `process'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionview-4.2.0/lib/action_view/rendering.rb:30:in `process'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:629:in `process'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:65:in `process'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/devise-3.5.1/lib/devise/test_helpers.rb:19:in `block in process'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/devise-3.5.1/lib/devise/test_helpers.rb:72:in `catch'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/devise-3.5.1/lib/devise/test_helpers.rb:72:in `_catch_warden'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/devise-3.5.1/lib/devise/test_helpers.rb:19:in `process'
  # /opt/twitter/rvm/gems/ruby-1.9.3-p551/gems/actionpack-4.2.0/lib/action_controller/test_case.rb:505:in `get'
  # ./spec/controllers/api/v1/orders_controller_spec.rb:37:in `block (4 levels) in <top (required)>'

You're not using the checkout_cash return value. The correct code should be:

checkout_result = order.checkout_cash(current_retailer, params[:order][:product_ids_with_quantities], order_params[:member_external_id])

if checkout_result.key?(:errors)
  render json: order, status: 422
else
  render json: order, status: 201
end

UPDATE:

I misread the checkout_cash method. I'd advise you against allowing a method to return different classes depending on the input, it forces the caller to know the method's internal behaviour. Since you're not using the order object which inside checkout_cash , you could simply return a empty hash in case of success.

However, I think this method has too many responsibilities that don't concern to the Order class. You could write a form object to handle this custom validation and keep your model simple. Check this great article: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/ .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM