I have 2 classes called User and Gig and also a joining table Usergig.
class Usergig
include DataMapper::Resource
property :id, Serial
belongs_to :user
belongs_to :gig
end
class Gig
include DataMapper::Resource
property :id, Serial
property :gigname, String
property :gigtext, Text
has n, :usergigs
has n, :users, :through => :usergigs
end
class User
include DataMapper::Resource
property :id, Serial
property :username, String
property :realname, String
has n, :usergigs
has n, :gigs, :through => :usergigs
end
And when i try to run:
post '/gig/add' do
user = User.get(1)
gig = user.gigs.create(:gigname => params[:gig_gigname], :gigtext => params[:gig_gigtext])
end
I get the error:
NoMethodError at /gig/add
undefined method `include?' for nil:NilClass
I've googled for about two hours now and read the DataMapper documentation.
Anyone know what i'm doing wrong?
In Usergig try the following:
belongs_to :user, :key => true
belongs_to :gig, :key => true
You forgot to call DataMapper.finalize...this is what you need to call after all your models are loaded. Rails does this for you, in Sinatra you must call it manually.
Related
Using Sinatra and DataMapper. This is my first time trying to use more than two model classes. Not sure what is causing the error. Thanks.
The error:
NameError: Cannot find the child_model ContactNote for Contact in contact_notes
The models:
class Contact
include DataMapper::Resource
property :id, Serial
property :fullname, Text, :required => true
property :email, Text
property :phone, Text
has n, :contact_notes
has n, :projects
end
class Contact_Note
include DataMapper::Resource
property :id, Serial
property :contact_content, Text, :required => true
property :created_at, DateTime
property :updated_at, DateTime
belongs_to :contact
end
class Project
include DataMapper::Resource
property :id, Serial
property :project_name, Text, :required => true
property :created_at, DateTime
property :updated_at, DateTime
belongs_to :contact
has n, :project_notes
end
class Project_Note
include DataMapper::Resource
property :id, Serial
property :project_content, Text, :required => true
property :created_at, DateTime
property :updated_at, DateTime
belongs_to :project
end
Datamapper makes expectations on class names based on ruby conventions. It expects your contact notes to be in a ContactNote class, while you've named it Contact_Note, hence the error it can't find ContactNote.
I want to impliment something which is similar to Twitter Repost System, therefore I will use this as an example. So let's say I have a Tweet Model and I want to allow other user to repost a certian tweet of another user, how do I impliment something like this?
I thought I would be a cool idea to put the retweet class into the tweet to be able to acess the repost too when I use Tweet.all to recive all tweets stored in the database, but somehow I didn't worked as expected...
The following Code is just an example which should show how to impliment this even if it is not working...
Any ideas how I could build a working repost model which also allows me to access both tweets and retweet by using Tweet.all?
class Tweet
class Retweet
include DataMapper::Resource
belongs_to :user, key => true
belongs_to :tweet, key => true
end
include DataMapper::Resource
property :text, String
property :timestamp, String
belongs_to :user
end
Important: I should be carrierwave compatible.
class Tweet
include DataMapper::Resource
property :id, Serial
has n, :retweets, 'Tweet', :child_key => :parent_id
belongs_to :parent, 'Tweet', :required => false
belongs_to :user
def is_retweet?
self.parent_id ? true : false
end
end
original = Tweet.create :user => user1
retweet = Tweet.create :parent => original, :user => user2
retweet.is_retweet? # => true
I am having trouble with associations in the following code.
The error I'm getting is a comment on the last line of code.
Edit: I simplified the code...
require 'rubygems'
require 'data_mapper' # requires all the gems listed above
require 'pp'
DataMapper.setup(:default, 'sqlite:///Users/chris/Dropbox/HawkEye-DB Test/store.sqlite')
class Manufacturer
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :products
end
class Product
include DataMapper::Resource
property :id, Serial
property :name, String
belongs_to :manufacturer
has 1, :productoptionset
end
class Productoptionset
include DataMapper::Resource
property :id, Serial
property :name, String
belongs_to :product
end
DataMapper.auto_migrate!
# Make some manufactureres
gortex = Manufacturer.create(:name => 'Gortex')
garmin = Manufacturer.create(:name => 'Garmin')
gps = garmin.products.create(:name => 'GPS Unit')
samegps = Product.get(1)
pp samegps.productoptionset.create # undefined method `create' for nil:NilClass (NoMethodError)
create is a class method (kind of like a static method in Java) so it can't be called on instances (or non instances in this case) :)
You could create your objects like this
class Manufacturer
include DataMapper::Resource
property :id, Serial
property :name, String
has n, :products
end
class Product
include DataMapper::Resource
property :id, Serial
property :manufacturer_id, Integer
property :name, String
belongs_to :manufacturer
has 1, :productoptionset
end
class Productoptionset
include DataMapper::Resource
property :id, Serial
property :product_id, Integer
property :name, String
belongs_to :product
end
DataMapper.auto_migrate!
# Make some manufactureres
gortex = Manufacturer.create(:name => 'Gortex')
garmin = Manufacturer.create(:name => 'Garmin')
garmin.products << Product.create(:name => 'GPS Unit')
samegps = Product.get(1)
samegps.productoptionset = Productoptionset.create(:name => "MyProductoptionset")
The has 1 creates an accessor productoptionsetwhich is initially nil rather than a collection. That nil has no method create. The collection has.
You can create and associate the ProductOptionSet via
Productoptionset.create(:name => 'Foo', :product => gps)
I am working with DataMapper and Sinatra to create a simple app. Here's the structure:
The app has Accounts. Each account has users and campaigns. Each user has comments that should be related to a specific campaign.
Comments should ideally have a user_id and a campaign_id to relate them both.
How can I relate the 2 together? Here's the code that I have which does not work:
class Account
include DataMapper::Resource
property :id, Serial
property :mc_username, String, :required => true
property :mc_user_id, String, :required => true
property :mc_api_key, String, :required => true
property :created_at, DateTime
property :updated_at, DateTime
has n, :users
has n, :campaigns
end
class User
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
property :email, String, :required => true
property :is_organizer, Integer
property :created_at, DateTime
property :updated_at, DateTime
belongs_to :account, :key => true
has n, :comments
end
class Campaign
include DataMapper::Resource
belongs_to :mailchimpaccount, :key => true
has n, :comments
property :id, Serial
property :cid, String
property :name, String
property :current_revision, Integer
property :share_url, Text, :required => true
property :password, String
property :created_at, DateTime
property :updated_at, DateTime
end
class Comment
include DataMapper::Resource
belongs_to :campaign, :key => true
belongs_to :user, :key => true
property :id, Serial
property :at_revision, Integer
property :content, Text
property :created_at, DateTime
end
With this code, I can't save a comment since I can't figure out how to associate it to a campaign and a user at the same time. I can't really get my head around wether I should even try to relate them at all using DataMapper.
I would love to know if this code is correct, how I can go about creating a comment that is related to both. If not, what structure and associations would be optimal for this scenario?
Thanks so much for the help!
What you're doing seems reasonable, I think you just need to get rid of the :key => true options since you don't really want those associations to be part of the comment's primary key.
You should probably start by looking at these datamapper docs on properties.
Alex is right, what you have there is a composite primary key. This would be ok if you only wanted each user to have one comment per campaign, but this is probably not the case but you do want to make sure that the comment is associated to a user and a campaign so use required => true, like so:
class Comment
include DataMapper::Resource
property :id, Serial
belongs_to :campaign, :required => true
belongs_to :user, :required => true
property :at_revision, Integer
property :content, Text
property :created_at, DateTime
end
Also your key in the campaign model may be problematic:
class Campaign
include DataMapper::Resource
belongs_to :mailchimpaccount, :key => true
#......
You probably just want to make that required too.
So it seems that my thinking was correct. I can relate a comment to both a user and a campaign in this way:
# Get a user and a campaign first that we can relate to the comment
user = User.get(user_id)
campaign = Campaign.get(campaign_id)
comment = Comment.new
comment.content = "The comment's content"
user.comments << comment # This relates the comment to a specific user
campaign.comments << comment # This now relates the comment to a specific campaign
comment.save # Save the comment
Dangermouse's suggestion to replace the :key => true option with :required => true also helped clean up the schema. Thanks!
I am unable to determine why I am getting a name error here. I'm new to DataMapper, but trying to associations down. Any help is appreciated.
User:
class User
include DataMapper::Resource
property :id, Serial, :key => true
property :first_name, String
property :last_name, String
property :company, String
property :city, String
property :country, String
property :mobile_number, Integer
property :email_address, String
property :shahash, String
property :isRegistered, Boolean
belongs_to :event, :required => true
end
DataMapper.auto_upgrade!
Event:
class Event
include DataMapper::Resource
property :id, Serial, :key => true
property :name, String
property :occuring, DateTime
has n, :user
end
DataMapper.auto_upgrade!
I think the problem is you're calling DataMapper.auto_upgrade! after each model definition. When you call it after just defining one model, there's no child model there. Instead, you should define and/or require all your models and then do:
DataMapper.finalize # set up all relationships properly
# and do a basic model sanity check
DataMapper.auto_upgrade! # create database table if it doesn't exist
Add an init file in your models directory and move all of your your DataMapper.finalize statements to it (i.e. remove the finalize statement from your individual model files)
app/models/init.rb
require_relative 'file_name'
require_relative 'another_model_file_name'
DataMapper.finalize
Then in your application file require the init file
require_relative 'models/init'