"Retweets" with Datamapper - ruby

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

Related

Mongoid has_and_belongs_to_many, inverse_of: :nil, store_as

In my code I have a user class:
class User
include Mongoid::Document
has_and_belongs_to_many :person_record_bookmarks, inverse_of: nil, :class_name => "PersonRecord"
end
now it will generate person_record_bookmarks_ids in my document. This name is too long, is there any way to store it as shorter name in the database? In embed documents we can use store_as:, but seems it doesn't work for references.
foreign_key is what you are looking for
class User
include Mongoid::Document
has_and_belongs_to_many :person_record_bookmarks, inverse_of: nil, :class_name => "PersonRecord", foreign_key :shorter_name
end
then your user will be:
{...shorter_name:[ObjectId("..."),ObjectId("...")]...}
You can user user.shorter_name to retrieve the list of ids or user.person_record_bookmarks to retrieve all PersonRecordBookmarks.where({_id: {$in: shorter_name})

Rails 4 Nested Validations not working

I have this model:
class CompetitionEntry < ActiveRecord::Base
has_many :participants
has_one :address
has_many :music_programs
accepts_nested_attributes_for :address
accepts_nested_attributes_for :participants, :music_programs,
:allow_destroy => true,
:reject_if => :all_blank
end
and this one:
class Participant < ActiveRecord::Base
belongs_to :competition_entry
has_one :birthplace
validates :name, :surname, :instrument, presence: true
end
Now the problem is that, if I create a new competition entry, it goes through.
But if I fill ONE field, i.e name, then it comes up with an error!
Why is this happening? It should fail when all are empty!
When you use accepts_nested_attributes_for, you are able to create the participants records at the same time that the competition_entry record, considering that the hash passed to competition_entry.create contains participants_attributes. When you pass only name, it validates the participant to be created and fail because it has no surname and instrument. When you leave all field empty, the behaviour should be the same, but it isn't because you explicitly set :reject_if => :all_blank.
:reject_if => :all_blank states that the participant_attributes hash should be ignored if it is blank?, which happens when you don't fill any field. What is happening then is that a competition_entry is being created without trying to create a participant because the accepts_nested_attributes_for is just ignored.

Active record in standalone Ruby

I have standalone Ruby application and want to use it with active record gem. I've made 3 models:
user.rb
require 'active_record'
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
has_many :comments
validates :name, :presence => true
attr_accessible :name, :state
end
post.rb
require 'active_record'
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
validates :title, :length => { :in => 6..40 }
attr_accessible :title, :content
end
comment.rb
require 'active_record'
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
validates :content, :presence => true
attr_accessible :content, :user_id
end
Now I want to populate database with one user, one post and one comment for this post and user by issuing this code:
require 'active_record'
require 'sqlite3'
require './models/user'
require './models/post'
require './models/comment'
ActiveRecord::Base.configurations = YAML::load(IO.read('config/database.yml'))
ActiveRecord::Base.establish_connection("development")
user1 = User.create name: "Joe", state: "England"
post1 = user1.posts.create title: "RoR introduction", content: "RoR intro."
comment1 = post1.comments.create content: "This is great article!"
But now it populates database but user_id is null. What am I missing here?
I think that your comment gets associated with a post, and not a user ...
just say do comment1.user = user1 and then comment1.save!
I think the key issue here is that the user making the comment is not necessarily the user who made the original post. If that were the case you could enforce it via the through option. However since a post may be commented upon by any user, then saying post1.comments.create etc. shouldn't automatically pull in the user who created the post right? Since it might be another user ...
I'm not sure but I don't think you want those attr_accessible specifications in an active record class - I think they are interfering with the fields that active record provides automatically - try removing all of them

Ruby/Sinatra/DataMapper Many-to-Many how creating an object

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.

Associating one class to belong to two different classes in DataMapper using Sinatra

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!

Resources