Take the following MongoMapper documents.
class Schedule
include MongoMapper::Document
key :name, String
key :description, String
key :active, Boolean
many :periods
timestamps!
userstamps!
end
class Period
include MongoMapper::EmbeddedDocument
key :number, Integer
key :descriptor, String
key :begin, Time
key :end, Time
end
Also, take the following Padrino routing.
post :period, :map => '/schedule/period' do
s = Schedule.first(params[:id])
s.periods = [
:number => 1,
:descriptor => "This is a description.",
:begin => Time.now,
:end => Time.now
]
end
But, if I already have several periods within the schedule, won't I just be overwriting the existing periods? How can I avoid this?
Alas, association methods haven't been documented yet on mongomapper.com. But...
Use the concat operator, which is defined on associations:
s.periods << {
:number => 1,
:descriptor => "This is a description.",
:begin => Time.now,
:end => Time.now
}
You can hand it either a Hash or a document.
Related
I'm quite new to mongodb and I'm using sinatra and mongomapper to update the values of an embedded document with the following set up:
class TeamMember
include MongoMapper::Document
key :name, String, :required => true
many :team_member_projects
end
class TeamMemberProject
include MongoMapper::EmbeddedDocument
key :date, Date, :required => true
one :project
end
class Project
include MongoMapper::Document
key :name, String, :required => true
end
The modifier code is:
team_member = TeamMember.find(params[:team_member])
project = Project.find(params[:project])
date = Date.parse(params[:date])
tm_project = TeamMemberProject.new(:project => project, :date => date)
team_member.push(:team_member_projects => tm_project)
team_member.save
but I get the error for .push line:
BSON::InvalidDocument at /project/add
Cannot serialize an object of class TeamMemberProject into BSON.
Did I not declare my embedded document properly? Or is there another way to update embedded documents, I don't know about. I'm trying to use: http://mongomapper.com/documentation/plugins/modifiers.html#push
This seems to work
team_member = TeamMember.find(params[:team_member])
project = Project.find(params[:project])
date = Date.parse(params[:date])
tm_project = TeamMemberProject.new(:project_id => project.id, :date => date)
team_member.team_member_projects << tm_project
team_member.save
It seems like I have to use project.id. Not sure why. Also not sure why my .push doesn't work, as I would have assumed it does the same thing as <<.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Why are exclamation marks used in Ruby methods?
i am reading tutorials for Rails3 with MongoDB
http://www.mongodb.org/display/DOCS/MongoDB+Data+Modeling+and+Rails
and i see this
key :user_id, ObjectId
timestamps!
what does the exclamation mark mean??
Thanks.
class Story
include MongoMapper::Document
key :title, String
key :url, String
key :slug, String
key :voters, Array
key :votes, Integer, :default => 0
key :relevance, Integer, :default => 0
# Cached values.
key :comment_count, Integer, :default => 0
key :username, String
# Note this: ids are of class ObjectId.
key :user_id, ObjectId
timestamps!
# Relationships.
belongs_to :user
# Validations.
validates_presence_of :title, :url, :user_id
end
in general, when a 'bang' follows a method in Ruby it will change the source.
for example check out the following output:
irb(main):007:0> x = 'string'
=> "string"
irb(main):008:0> x
=> "string"
irb(main):009:0> x.capitalize
=> "String"
irb(main):010:0> x
=> "string"
irb(main):011:0> x.capitalize!
=> "String"
irb(main):012:0> x
=> "String"
x.capitalize returns "String", but variable x remains lower case. When I add the ! ('bang') to the end var x is modified.
i am not overall familiar with mongodb, but this may shed some light onto the purpose of the bang in ruby.
I use mongo mapper (0.8.6) in my sinatra service. I have one problem with stack level too deep. The problem is that there is conflict of the key "changes" in my model. Here is my model:
class ChangeLog
include MongoMapper::Document
belongs_to :resource
key :changes, Hash, :required => true
key :message, String, :required => true
key :note, String
key :user_uuid, String, :required => true
key :user_name, String, :required => true
timestamps!
end
However, I don't want to rename my key as in this case, it's the right name for my web service. Any suggestions?
changes is an instance method that will tell you what fields have changed since you last saved the document. Here's an example from MongoMapper's documentation
user = User.create(:name => 'John', :age => 29)
puts user.changed? # false
puts user.changes.inspect # {}
user.name = 'Steve'
puts user.changed? # true
puts user.changes.inspect # {"name"=>["John", "Steve"]}
Unfortunately, you're probably going to need to choose a different name for that field. Maybe "adjustments" or "variations" or "differences" or "modifications"?
I'm have a user class that can optionally have a billing address. When I post a payment form, assuming the user has indicated they want to save their billing address details, I want to either create a new address record or update the original one.
I have tried many things but the closest I can get to working code is...
class User
include DataMapper::Resource
property :id, Serial
property :provider, String, :length => 100
property :identifier, String, :length => 100
property :username, String, :length => 100
property :remember_billing, Boolean
has 1, :billing_address
end
class BillingAddress
include DataMapper::Resource
property :first, String, :length => 20
property :surname, String, :length => 20
property :address1, String, :length => 50
property :address2, String, :length => 50
property :towncity, String, :length => 40
property :state, String, :length => 2
property :postcode, String, :length => 20
property :country, String, :length => 2
property :deleted_at, ParanoidDateTime
belongs_to :user, :key => true
end
post "/pay" do
#post = params[:post]
#addr = params[:addr]
if #addr == nil
#addr = Hash.new
end
user = User.first(:identifier => session["vya.user"])
user.remember_billing = !!#post["remember"]
if user.remember_billing
user.billing_address = BillingAddress.first_or_create({ :user => user }, #addr)
end
user.save
...
which works fine when there is no record. But if there is already a record, it keeps the original values.
I saw a similar post
DataMapper: Create new record or update existing
but if I alter the code to be
user.billing_address = BillingAddress.first_or_create(:user => user).update(#addr)
I get the error
DataMapper::ImmutableError at /pay
Immutable resource cannot be modified
Any help much appreciated
You're chaining lots of things together, there. How about:
billing = BillingAddress.first_or_new(:user => user, #addr) #don't update, send the hash as second parameter
billing.saved? ? billing.update(#addr) : billing.save
raise "Billing is not saved for some reason: #{billing.errors.inspect}" unless billing && billing.saved?
user.billing_address = billing
user.save
When I define the User has_many meetings, it automatically creates a "user_id"
key/value pair to relate to the User collections. Except I can't run any
mongo_mapper finds using this value, without it returning nil or [].
Meeting.first(:user_id => "1234")
Meeting.all(:user_id => "1234")
Meeting.find(:user_id => "1234")
All return nil. Is there another syntax? Basically I can't run a query on the automatically generated associative ObjectId.
# Methods
class User
include MongoMapper::Document
key :user_name, String, :required => true
key :password, String
many :meetings
end
class Meeting
include MongoMapper::Document
key :name, String, :required => true
key :count, Integer, :default => 1
end
# Sinatra
get '/add' do
user = User.new
user.meetings "foobar") #should read: Meeting.new(:name => "foobar")
user.save
end
get '/find' do
test = Meeting.first(:user_id => "4b4f9d6d348f82370b000001") #this is the _id of the newly create user
p test # WTF! returns []
end
As Jimmy mentioned about checking Meeting.all, I don't think you would have anything.
Based on your example above I see a couple potential issues.
- Your User requires a :user_name so it's not getting saved
- would never get saved, because you didn't set the name which is required
- Your Meeting isn't getting saved either
- One more thing, you need to concat your meeting to user.meetings
This works with mongo_mapper 0.6.10
require 'rubygems'
require 'mongo_mapper'
MongoMapper.database = "meetings"
class User
include MongoMapper::Document
key :user_name, String, :required => true
key :password, String
many :meetings
end
class Meeting
include MongoMapper::Document
key :name, String, :required => true
key :count, Integer, :default => 1
end
user = User.create(:user_name => "Rubyist")
user.meetings << Meeting.create(:name => "foobar")
user.save
Meeting.first(:user_id => user.id)
User.find(user.id).meetings
You may have figured this out already, but I hope this is helpful anyway.
You can try using
Meeting.find_by_user_id "1234"
Also, if you run script/console then does Meeting.all show each record as having a user_id assigned to it?
What about just User.find("1234").meetings ?