简体   繁体   中英

How do I modify a Ruby object in the controller before I render a view?

This is a question I've long wanted answered by never bothered to ask as I found other ways.

In my Index action for a controller - I have this line:

@all_pos = PurchaseOrder.where(:merchant_id => session[:admin_id], :received => false).sort([sort_column,sort_direction])

So I get a Plucky query at this point - the results haven't been grabbed, just locked a cursor (I get this part).

What I want to do though is modify this @all_pos object (hash) in place before I send it up to the view.

Why?

Because I've done some relational stuff (gasp) where I'm storing an ObjectId as one of the values inside this object.

:po_vendor is stored as the ObjectId that points to the document describing vendors. I didn't want to use MongoMapper's "belongs_to" feature (for whatever reason I don't recall - i just didn't), so when I render my view, if I want to call @all_pos.po_vendor I'm going to print an ObjectId on the page. No bueno.

Now I could look up the document from the view - but that's not very Railsy!

So what I want to do is modify each of the elements inside the @all_pos array-hash.

@all_pos.each do |po|
        vn = Vendor.find_by_id(po.po_vendor).name
        po.po_vendor = vn
      end

In my head the above works - I access the po_vendor key and set it's value. But it's not filtering up into the @all_pos, and the biggest problem is I don't have the Rails vocabulary to describe what I'm trying to do here.

map! ? collect! ? I could Google and figure it out if I knew the words to describe this :(

use association to solve your problem in your PurchaseOrder model add association for Vendor

class PurchaseOrder < ActiveRecord::Base
  belongs_to :vendor, foreign_key: :po_vendor
  delegate :name, to: :vendor, prefix: true, allow_nil: true
  ...
  ...
  ...
end

in controller

@all_pos = PurchaseOrder.includes(:vendor).where(:merchant_id => session[:admin_id], :received => false).sort([sort_column,sort_direction])

And then, in View you can use

  @all_pos.each do |po|
    po.vendor_name 
  end

The main problem is that as assigned, @all_pos is a query and NOT documents resulting from the query. On iteration of the query, you look up purchase orders and then the vendors, but you don't have a variable handle to reference the fetched purchase orders (@all_pos still just references the query, not the fetched docs), and without references, you can't get to them, and they'll be garbage collected.

The following test is what I think that you want, with two examples. The first does individual vendor db finds as in your question. The second creates a local Hash for lookup from a single common db find for the vendors for efficiency.

test/unit/purchase_order_test.rb

require 'test_helper'
require 'pp'

class PurchaseOrderTest < ActiveSupport::TestCase
  def setup
    PurchaseOrder.delete_all
  end

  test "the truth" do
    puts "\nMongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}"
    session = {admin_id: 1}
    sort_column = :merchant_id
    sort_direction = 1
    @query = PurchaseOrder.where(:merchant_id => session[:admin_id], :received => false).sort([sort_column,sort_direction])
    assert_equal(Mongoid::Contextual::Mongo, @query.class)

    vendor = Vendor.create(name: 'Amazon')
    PurchaseOrder.create(merchant_id: 1, received: false, po_vendor: vendor._id)

    @all_pos = @query.to_a
    assert_equal(PurchaseOrder, @all_pos.first.class)
    @all_pos.each do |po|
      po.po_vendor = Vendor.find(po.po_vendor).name
    end
    puts "with individual vendor db find:\n#{@all_pos.inspect}"

    @all_pos = @query.to_a
    vendors = Vendor.find(@all_pos.collect{|po| po.po_vendor})
    vendor_hash = Hash[*vendors.collect{|v| [v._id, v.name]}.flatten]
    @all_pos.each do |po|
      po.po_vendor = vendor_hash[po.po_vendor]
    end
    puts "with single common vendor db find:\n#{@all_pos.inspect}"
  end
end

rake test

Run options:

# Running tests:

[1/1] PurchaseOrderTest#test_the_truth
Mongoid::VERSION:3.1.6
Moped::VERSION:1.5.2
with individual vendor db find:
[#<PurchaseOrder _id: 535803b87f11ba5720000002, merchant_id: 1, received: false, po_vendor: "Amazon">]
with single common vendor db find:
[#<PurchaseOrder _id: 535803b87f11ba5720000002, merchant_id: 1, received: false, po_vendor: "Amazon">]
Finished tests in 0.040748s, 24.5411 tests/s, 49.0822 assertions/s.
1 tests, 2 assertions, 0 failures, 0 errors, 0 skips

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