mongoid embeds_one build howto - ruby

Thanks for your time!
The code is simple(mongoid was used without rails):
require 'mongoid' # version 6.0.2
Mongoid.load!('mongoid.yml', :development)
class Office
include Mongoid::Document
embeds_one :owner
embeds_many :addresses
end
class Owner
include Mongoid::Document
end
class Address
include Mongoid::Document
end
I could successfully call office.addresses.build as following.
office = Office.new
office.addresses.build
office.save
But when I call office.owner.build, error pop up saying
embed_one.rb:23:in `<main>': undefined method `build' for nil:NilClass (NoMethodError)
It's supposed to work in this way, right? Where is wrong.
puts office.owner.class # NilClass

After refresh myself from a sleep ...
I use puts office.methods to list all the methods office could invoke.
# Here's all the methods has *owner* in it
owner=
owner?
has_owner?
build_owner
create_owner
owner
office.build_owner is what i'm looking for!

Related

How to properly destroy Mongoid embedded object with relations?

I made test script to demonstrate my problem.
require 'mongoid'
## mongoid.yml
# development:
# clients:
# default:
# database: test
# hosts:
# - localhost:27017
Mongoid.load!("mongoid.yml", :development)
class Foo
include Mongoid::Document
embeds_one :bar
end
class Bar
include Mongoid::Document
embedded_in :any
has_many :bazs, as: :barable
end
class Baz
include Mongoid::Document
belongs_to :barable, polymorphic: true
end
foo = Foo.create(
bar: Bar.new(
bazs: [Baz.create]
)
)
foo.bar.destroy
I need to destroy bar object with all bazs.
When I try to destroy embedded Bar object I get an error undefined method barable for Foo instance. But Bar doesn't relate to Foo as barable. Also I tryied use delete instead destroy.
How can I delete bar with all bazs?
$ ruby test3.rb
/home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/traversable.rb:109:in `remove_child': undefined method `barable' for #<Foo _id: 60978efab4da6149b72c4bb1, > (NoMethodError)
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/persistable/deletable.rb:78:in `delete_as_embedded'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/persistable/deletable.rb:33:in `block in delete'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/persistable/deletable.rb:131:in `prepare_delete'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/persistable/deletable.rb:23:in `delete'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/persistable/destroyable.rb:32:in `block in destroy'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:88:in `__run_callbacks__'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:778:in `_run_destroy_callbacks'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/activesupport-4.2.7/lib/active_support/callbacks.rb:81:in `run_callbacks'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/interceptable.rb:143:in `run_callbacks'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/persistable/destroyable.rb:32:in `destroy'
from /home/vp/.rvm/gems/ruby-2.3.1/gems/mongoid-5.2.1/lib/mongoid/relations/proxy.rb:150:in `method_missing'
from test3.rb:34:in `<main>'
You cannot target an embedded class with referenced associations, because an embedded document cannot be found by its id alone (you need the ids of the each document in the composition hierarchy all the way to the top level).

How to test class methods that rely on associations with RSpec and Sinatra?

I've written some RSpec tests that successfully create objects with :let statements. However, the test environment doesn't maintain the associations that function properly everywhere else. Below is an example of a class that would turn up a NoMethodError (undefined method `money' for nil:NilClass). Money is a column in Inventory. Any thoughts?
class Inventory < ActiveRecord::Base
belongs_to :character
def self.return_money(character)
character.inventory.money
end
end
And here's a corresponding example for a spec doc:
require 'spec_helper'
describe 'Test methods' do
let(:trader) {
Character.create(
name: "Trader",
location_id: 1)
}
let(:trader_inventory) {
Inventory.create(
character_id: trader.id,
storage_capacity: 50000,
money: 20000,
markup: 1.35)
}
it "test method" do
expect(Inventory.return_money(trader)).to eq(100)
end
end
There is no reason this shouldn't work. RSpec isn't special, it's just regular Ruby code. You can confirm this by moving all of your code into a single file, something like:
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
ActiveRecord::Base.logger = Logger.new(STDOUT)
class Inventory < ActiveRecord::Base
end
class Character < ActiveRecord::Base
has_one :inventory
end
describe 'test' do
it 'works' do
puts Character.first.inventory.money.inspect
end
end
Guesses as to what may be broken:
money is a composite field or something like that. Can you post your database schema?
Library files aren't being loaded correctly. Use puts $LOADED_FEATURES to verify that all the files that should be required have been.

Why do I get the error uninitialized constant Stuff::HTTParty?

I have the HTTParty gem on my system and I can use it from within rails.
Now I want to use it standalone.
I am trying:
class Stuff
include HTTParty
def self.y
HTTParty.get('http://www.google.com')
end
end
Stuff.y
but I get
$ ruby test_httparty.rb
test_httparty.rb:2:in `<class:Stuff>': uninitialized constant Stuff::HTTParty (NameError)
from test_httparty.rb:1:in `<main>'
07:46:52 durrantm Castle2012 /home/durrantm/Dropnot/_/rails_apps/linker 73845718_get_method
$
You have to require 'httparty':
require 'httparty'
class Stuff
include HTTParty
# ...
end
Its all because of the include which exists with in the class
If you include a class with a module, that means you're "bringing in" the module's methods as instance methods.
If you need more clarity on include and require
I request you to refer to this wonderful SO Posting
What is the difference between include and require in Ruby?
Here is an example which I have taken from the same posting
module A
def say
puts "this is module A"
end
end
class B
include A
end
class C
extend A
end
B.say => undefined method 'say' for B:Class
B.new.say => this is module A
C.say => this is module A
C.new.say => undefined method 'say' for C:Class

Mongoid 3.1.4 undefined method 'has_key?' when calling 'Document.create'

I'm having a problem trying to use Mongoid (v 3.1.4) to persist a (really simple) entity to MongoDB (v 2.4.4). I'm using MRI and Ruby 2.0.0-p195 on OS X.
Here's my class (Person.rb):
require 'mongoid'
class Person
include Mongoid::Document
include Mongoid::Timestamps # currently can be ommitted
field :name, type: String
def initialize
# is empty
end
def name
#name
end
def name=(value)
#name = value
end
end
Mongoid.load!('config/mongoid.yml', :development)
user = Person.new
user.name = "John Doe"
user.create
That last sentence greets me with a
[...]mongoid/attributes.rb:320:in 'method_missing': undefined method `has_key?' for nil:NilClass (NoMethodError)
Here's my 'mongoid.yml':
development:
sessions:
default:
database: rbtest
hosts:
- localhost:27017
test:
sessions:
default:
database: test
hosts:
- localhost:27017
options:
consistency: :strong
max_retries: 1
retry_interval: 0
Connection to the DB instance seems ok as the DB is created ('rbtest') however, Collections and Documents fail. I've already tried with 'create!' and 'safely.save!' to no avail.
I tried implementing the has_key? method, for which I couldn't find any documentation, so I'm at a bit of a loss here.
As always, any help is much appreciated.
Regards,
UPDATE -- SOLUTION:
#Frederik Cheung's answer was spot on. Here's the working code (updated with #mu-is-too-short's suggestion)
require 'mongoid'
class Person
include Mongoid::Document
field :name, type: String
end
Mongoid.load!('config/mongoid.yml', :development)
person = Person.new(:name => 'John Doe')
person.save!
The problem is your initialize method: you are overriding the one provided by mongoid, so some of mongoid's internals aren't being setup.
You need to either remove your initialize method or call the mongoid's implementation via super

Confusion about ways to use JSON in ruby sinatra application

I'm making a Ruby Sinatra application that uses mongomapper and most of my responses will be in the JSON form.
Confusion
Now I've come across a number of different things that have to do with JSON.
The Std-lib 1.9.3 JSON class: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html
The JSON Gem: http://flori.github.io/json/
ActiveSupport JSON http://api.rubyonrails.org/classes/ActiveSupport/JSON.html because I'm using MongoMapper which uses ActiveSupport.
What works
I'm using a single method to handle responses:
def handleResponse(data, haml_path, haml_locals)
case true
when request.accept.include?("application/json") #JSON requested
return data.to_json
when request.accept.include?("text/html") #HTML requested
return haml(haml_path.to_sym, :locals => haml_locals, :layout => !request.xhr?)
else # Unknown/unsupported type requested
return 406 # Not acceptable
end
end
the line:
return data.to_json
works when data is an instance of one of my MongoMapper model classes:
class DeviceType
include MongoMapper::Document
plugin MongoMapper::Plugins::IdentityMap
connection Mongo::Connection.new($_DB_SERVER_CNC)
set_database_name $_DB_NAME
key :name, String, :required => true, :unique => true
timestamps!
end
I suspect in this case the to_json method comes somewhere from ActiveSupport and is further implemented in the mongomapper framework.
What doesn't work
I'm using the same method to handle errors too. The error class I'm using is one of my own:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
attr_reader :action # :create, :update, :read or :delete
attr_reader :model # Model class
attr_reader :entity # Entity on which the error occured, or an ID for which no entity was found.
def initialize(action, model, entity = nil)
#action = action
#model = model
#entity = entity
end
end
Of course, when calling to_json on an instance of this class, it doesn't work. Not in a way that makes perfect sense: apparantly this method is actually defined. I've no clue where it would come from. From the stack trace, apparently it is activesupport:
Unexpected error while processing request: object references itself
object references itself
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:75:in `check_for_circular_references'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:46:in `encode'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `block in encode_json'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `each'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `map'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `encode_json'
But where is this method actually defined in my class?
The question
I will need to override the method in my class like this:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
def to_json
#fields to json
end
end
But I don't know how to proceed. Given the 3 ways mentioned at the top, what's the best option for me?
As it turned out, I didn't need to do anything special.
I had not suspected this soon enough, but the problem is this:
class EntityCrudError < StandardError
..
attr_reader :model # Model class
..
end
This field contains the effective model class:
class DeviceType
..
..
end
And this let to circular references. I now replaced this with just the class name, which will do for my purposes. Now to_json doesn't complain anymore and I'm happy too :)
I'm still wondering what's the difference between all these JSON implementations though.

Resources