views:

308

answers:

3

I have products as an embedded document inside the category class as shown below:

require 'mongo_mapper'

class Category

include MongoMapper::Document

key :Name, String
key :NumberOfProducts, Integer
many :products

end

and here is the Product class:

require 'mongo_mapper'

class Product

include MongoMapper::EmbeddedDocument

  key :Name, String

end

I am using the following code to display the Products but it says no method "Name" found.

require 'rubygems'
require 'mongo'
require 'mongo_mapper'
require 'category'
require 'product'

include Mongo

MongoMapper.database = 'Northwind'

categories = Category.all()

categories.each{|category| puts category.Name

  unless category.Products.nil?

     category.Products.each{|product| puts product.Name}

  end


}

here is the error:

 undefined method `Name' for {"Name"=>"Amiga"}:BSON::OrderedHash (NoMethodError)
+1  A: 

Well, first thing to try is that you have:

many :products

...but then you try to access it with category.Products.each

Definitely keep your naming consistent, and I'd recommend using ruby conventions (underscored, not camel cased, and certainly not capitalized camel case for non-classes).

So, maybe:

class Category
  include MongoMapper::Document
  key :name, String
  many :products
end

class Product
  include MongoMapper::EmbeddedDocument
  key :name, String
end


categories = Category.all
categories.each do |category|
  puts category.name
  category.products.each do |product|
    puts "  " + product.name
  end
end
thenduks
Thanks but I changed to Products instead of products since the MongoDb database serialized it as Products. Thanks again!
azamsharp
? What mongodb serializes it as is irrelevant, you're using a Ruby plugin and you need to follow it's conventions (and it likely follows ruby conventions).
thenduks
Actually the serialization was performed from the .NET (C#) side!
azamsharp
.NET side!? That is almost certainly your problem then :) I'd just write a little script using the bare-metal ruby driver to sanitize the data, in that case.
thenduks
Basically .NET code writes to the MongoDb database and Ruby code reads the data. Just for FUN! :D
azamsharp
A: 

The object you're getting back acts like a hash. In order to access the name you need to use product["Name"] or category["Name"].

e.g.

irb(main):007:0> oh.baz
NoMethodError: undefined method `baz' for {"foobar"=>"baz"}:BSON::OrderedHash
    from (irb):7
irb(main):008:0> oh[:foobar]
=> "baz"
Blaine LaFreniere
MongoMapper's EmbeddedDocument instantiates the appropriately named class with the data in the hashes when you use `many`. If you want just hashes you would use something like `key :products, Array, :default => []` and then `category.products << { :name => 'asdf' }`
thenduks
A: 

This is not an answer to your question, per se. But I had an immediate, visceral reaction to Products being embedded under Category. Purely based on my pre-conceived notion of what the class names represent (which may turn out wrong for your project).

To me, "Product" sounds like an important item -- like something you sell. "Category" sounds more like a simple tag, a way to find and sort and present different aspects of products, maybe.

It is common for Embedded to be used for a Customer's address, for example. The address "object" never needs to be re-used by anyone else. So, if the Customer is deleted, so is their embedded address. In addition, embedding makes the address immediately available without a join.

In your case above, if a Category gets deleted, then all of your Products would be deleted.

Another aspect of having Product as an embedded object, you cannot find a product except through the Category. That seems odd in a product centric world -- again, assuming Product is a critical domain object.

Again, not the intent of your question. But I think an important consideration when it comes to whether to use Embedded or not, has to with how you model Product and Category to reflect the domain.

Jon Kern
The code above was only for demo purposes! In real world application it would be different!
azamsharp