Storing users in a comment model in Sinatra and Mongoid - ruby

There is sparse documentation on how to go about this. I have a comment model which has a field that stores the user who created the comment, and the user the comment is directed at.
I figured there are two ways of doing this:
class Comment
include Mongoid::Document
field :from, type: Moped::BSON::ObjectId # The user who created the comment
field :to, type: Moped::BSON::ObjectId # The user the comment is directed at
field :details, type: String
-OR-
class Comment
field Mongoid::Document
belongs_to :user # The user who created the comment
field :to, type: Moped::BSON::ObjectId # The user the comment is directed at
field :details, type: String
Are there any benefits to doing it one way or another? Or are these two the same? Or is there a better way? Using Mongoid and Sinatra.

The first way you have works fine, but if you do it that way and want the user information you will need to add helper methods to use the id to pull the user.
The second is part way there but I would do:
belongs_to :from_user, class_name: 'User'
belongs_to :to_user, class_name: 'User'
That way you can do comment.from_user and comment.to_user and get the actual users. The ids are still there just not explicitly called out but produced from the belongs to.
All of that being said, since this is mongo and a document database, if there is specific info from the user you need for the comment I might be inclined to just embed the info you need right in the comment so you don't have to do a relation.

Related

One to many Rails relationship. Is has_many required (Rails 5)?

I'm trying to generate a one-to-many relationship between users and advice. One user can have many advices.
rails g model User
rails g model Advice user:references
I noticed that this doesn't automatically generate the line has_many :advices in the user.rb file. However, in the advice.rb file, the line belongs_to :user was auto-generated.
In the rails console, I am still able to create multiple Advices for one user without an error.
My question is, is the has_many :advices line necessary in the user.rb file? If not, then why do the Rails guides recommend it?
The has_many association tells Rails that the objects are related and adds methods to the object for querying the associated objects. You could live without it, but it makes things easier.
See the first chapter here:
http://guides.rubyonrails.org/v2.3.11/association_basics.html
Another reference:
https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many
When you use one-to-many associations you are telling your User model that he has zero or more instances of the model Advice, while Advice belongs to only one model User and its reference to it.
This is how your models should be:
class User < ApplicationRecord
has_many :advices
end
class Advice < ApplicationRecord
belongs_to :user
end
Even if you don't need to declare it right now, you might need it in a long run, so it is always better to have it set. You can read more about this topic here.

Rails - Contextual validations based on state

Let's say there is a post model like this:
class Post < ActiveRecord::Base
.......
.......
end
It has two attributes :title and :body.
Now a Post object can go through multiple stages: 'draft' -> 'published'.
Now while saving a post in drafts mode, the :title isn't required. But while saving it in published mode, it needs to have a presence validation on the title:
validates_presence_of :title
Now, what is the best way to do this in Rails? I think some implementation of a decorator pattern would be great, wherein in a controller, I would dynamically add validations to an active record object.
This is a simplified version of a bigger problem I have. In the actual case, there are a lot more validations including those done on associated objects.
If you are using state_machine for your state transitions, it supports just what you are looking for, with examples in the readme file.
Otherwise, all rails validations have optional if paramter. If, for example, your post has a published? method that returns whether it's in a published state, you could write validates_presence_of :title, if: :published? and have it do exactly what you need.

Multiple controllers, two "depths" of relationships

I'm writing my first app in Ruby on Rails (I've only went through railstutorial.org before ) which is simple Electronic Prescription Service and I run on one thing I can't cope with.
I want to create form for adding new prescription as well as adding some medicines which belong to this newly created prescription.
First problem is - how can I actually add medicines to the prescription when during filling the form prescription doesn't exist yet? What I did before was I first create prescription with #user.prescription.create(presc_attributes) and later #prescription.relations.create(medicine_id).
Now I need to do this probably on one form sending the whole thing using one button ( one HTML request, am I right? ) unless you guys have got better idea. I'm also struggling with an idea where to actually put method creating this prescription. Should it be in PrescriptionController#new or maybe connected to RelationsController#new as well?
I've read couple of articles about nested fields_for but they don't seem to match exactly what I need.
Im really new to RoR so sorry if I missed something important.
Here is my EER as well
http://i.stack.imgur.com/sa9CB.png
UPDATE---
ahhh i see, I think what you want is a relationship with an inverse_of.
If you are using a belongs_to on the join model, it is a good idea to set the :inverse_of option on the belongs_to, which will mean that the following example works correctly (where tags is a has_many :through association):
#post = Post.first
#tag = #post.tags.build name: "ruby"
#tag.save
The last line ought to save the through record (a Taggable). This will only work if the :inverse_of is set:
class Taggable < ActiveRecord::Base
belongs_to :post
belongs_to :tag, inverse_of: :taggings
end
If you do not set the :inverse_of record, the association will do its best to match itself up with the correct inverse. Automatic inverse detection only works on has_many, has_one, and belongs_to associations.
FROM http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Optional belongs_to relationship in Ruby

I'm migrating an application over to ROR from PHP that defines an object, let's call it a Document that can be created by a User who is either a registered user of the application, or an anonymous user who only gives their email address (in this case without an associated User account). There is no requirement for a visitor to create a User account, but they should still have the ability to create a Document.
In my model, I am first led to create the Document model that references the user with a belongs_to association. This however is not always the case. In pure SQL and PHP this is not hard, although it comes with it's own challenges, but I would like to model this in the "purest" Ruby/Rails way.
If a visitor does not have an associated User, their email address will be stored directly against the Document object.
To start the discussion, here are the snippets for each model.
# user.rb
class User < ActiveRecord::Base
has_many :documents
end
# document.rb
class Document < ActiveRecord::Base
attr_accessible :title, :description, :email_address
belongs_to :user
end
My goal is to be able to retrieve all user documents using the standard dot notation:
user.documents
And to also check for the reverse relationship to see if the document belongs to a User or if it contains an email address.
if document.user.nil? ...
if document.email_address.nil? ...
I have read up on the has_one relationship but am not sure if this is the right path to go down.
Any help or suggestions would be appreciated, thanks!
You might want to consider making the User class more flexible so it supports anonymous users and only stores the email address.
It might make your code a bit cleaner.

Filtering child objects in a has_many :through relationship in Rails 3

Greetings,
I have an application where Companies and Users need to belong to each other through a CompanyMembership model, which contains extra information about the membership (specifically, whether or not the User is an admin of the company, via a boolean value admin). A simple version of the code:
class CompanyMembership < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class Company < ActiveRecord::Base
has_many :company_memberships
has_many :users, :through => :company_memberships
end
class User < ActiveRecord::Base
has_many :company_memberships
has_many :companies, :through => :company_memberships
end
Of course, this makes it simple to get all the members of a company via company.users.all, et al. However, I am trying to get a list of all Users in a Company who are admins of that Company (and also to test whether a user is an admin of a given company). My first solution was the following in company.rb:
def admins
company_memberships.where(:admin => true).collect do |membership|
membership.user
end
end
def is_admin?(user)
admins.include? user
end
While this works, something feels inefficient about it (it's iterating over each membership, executing SQL each time, right? Or is Relation smarter than that?), and I'm not sure if there's a better way to go about this (perhaps using scopes or the fancy new Relation objects that Rails 3 uses?).
Any advice on the best way to procede (preferably using Rails 3 best practices) would be greatly appreciated!
I believe I was going about this the wrong way, specifying conditions on company_memberships instead of users, which was what I actually wanted (a list of Users, not a list of CompanyMemberships). The solution I think I was looking for is:
users.where(:company_memberships => {:admin => true})
which generates the following SQL (for company with ID of 1):
SELECT "users".* FROM "users"
INNER JOIN "company_memberships"
ON "users".id = "company_memberships".user_id
WHERE (("company_memberships".company_id = 1))
AND ("company_memberships"."admin" = 't')
I'm not sure yet if I'll need it, but the includes() method will perform eager loading to keep down the number of SQL queries if necessary:
Active Record lets you specify in
advance all the associations that are
going to be loaded. This is possible
by specifying the includes method of
the Model.find call. With includes,
Active Record ensures that all of the
specified associations are loaded
using the minimum possible number of
queries.queries. RoR Guides: ActiveRecord Querying
(I'm still open to any suggestions from anyone who thinks this isn't the best/most effective/right way to go about this.)
An even cleaner way would be to add an association to your Company model, something like this:
has_many :admins, :through => :company_memberships, :class_name => :user, :conditions => {:admin => true}
You might have to dig into the rails doc to get the exact syntax right.
You shouldn't need :include, unless you have other classes associated with :user that you might reference in your view.
How about something like this:
Company.find(:id).company_memberships.where(:admin => true).joins(:user)
I stumbled across this answer and believe that nowadays there is a nicer way using has_many association scopes (has_many documentation):
has_many :admins, -> { where(admin: true) }, through: :company_memberships, class_name: :user
The second parameter of a has_many association can be a proc or lambda that contains your filter.
I made the activerecord_where_assoc gem to do this.
With it, no need for joins or includes. Doing eager loading remains your choice, not an obligation.
users.where_assoc_exists(:company_memberships, admin: true, company_id: #company.id)
The question is unclear on how to filter the company, so I added it myself. Without this, if a user can be in multiple company, it being admin in one company could mean being treated as an admin in another company.
The gem doesn't work in Rails 3, but we are 9 years later and Rails 4.1 and more are supported.
Here are the introduction and examples. Read more details in the documentation.

Resources