Understanding MongoMappers many relationship - ruby-on-rails

Ok, so firstly Im not very in the know when it comes to Ruby, Rails, Mongo or even ActiveRecord so I apologise if these questions are fairly basic. Firstly, I have an "Event" model and I have added a many relationship to my "Comment" model. Now it is my (basic) understanding of mongo (or any document db) that foreign key lookups (or whatever they are called in mongo) are not advised, instead opting for storing as much as possible in a single document. With this in mind I would expect to have a single Events collection with the Comment JSON/BSON embedded in each document i.e.
event:
{
Title: "some event"
Comments : [
{ user_id: [object id], comment: "comment"},
{ user_id: [object id], comment: "other comment"}
]
}
Instead I am seeing that a comments collection has been created as well as the event collection. Would this be the correct way to map this relationship, if so how do I stop the comment collection been created and instead have the info embedded directly into the event? My guess is that I have to tell my Comment mapping not to have a _id property and thus not belong to its own collection.
EDIT: I found out that what I was looking for here in my first question was to use MongoMapper::EmbeddedDocument instead of including MongoMapper::Document
Secondly, I want users to be able to flag themselves as "attending" or just "interested" an event, which I assumed would be modelled as an array of reference user_id's on the event. So I initially thought it would be mapped as two "many" relationships, but as far as I can tell the name of the constant I am passing to the many method is used to create the getter/setters, which wouldn't work for me if I had two many relationships for the same type.
In other words, if I have this mapping:
class Event
many :users
end
Then it's my understanding that I will then have getters and setters generated for the users property. So I can do something like:
event.users << someAttendingUser
This is all good, and at this point in time I would want the reference to the user to be stored and not the whole user BSON (unlike with the comments example above). Now the problem is how do I do this when I have two many mappings to the user collection, as with my need for both "attending" and "interested" users?
So to summarise this rambling:
Sometimes I want to store many relationships directly as BSON in the orinal document, and not as a reference to a document in another collection. How do I do this?
Sometimes I want to store many relationships of the same type on a document and I DO want them to be references to a document in another collection. How do I do this?
Hope this made sense, and I apologise if Im asking the obvious here.
EDIT 2:
Ok, well I really did search before asking this question, but it appears that I have now found the answer to both of my problems so I will close the question myself.
The second part required me to specify a class name option for the mapping i.e.
class Event
many :attendees, :class_name => "User"
many :interested, :class_name => "User"
end

Ok, well I really did search before asking this question, but it appears that I have now found the answer to both of my problems so I will close the question myself.
The second part required me to specify a class name option for the mapping i.e.
class Event
many :attendees, :class_name => "User"
many :interested, :class_name => "User"
end

Related

How to structure this so I get all the benefits from STI with none of the consequences? (Pretty irresponsible, I know.)

Say I have the following example of associations in a Rails app:
I'm considering combining the *Posting models under STI. One problem with STI is the potential for many attributes that are only related to one subclass (i.e., a lot of denormalized nil values). This is especially worrisome when your subclasses and going to evolve and grow in the future. I've read a few related posts (such as this), however, as you can see in my example, the potential subclass-specific fields will not necessarily be just attributes, but rather, a lot of belongs_to associations.
My question is, how could I restructure this to use STI from a Posting model for all the common attributes/methods (of which there will be quite a few in my actual app), but keep the unique subclass-specific attributes and belongs_to associations from piling up in the Posting model? Also, the ability to access #board.postings and work with those standard methods is important.
For example, I've thought about moving the type-specific attributes to another model:
class CarPosting < Posting
has_one: car_posting_detail
end
class CarPostingDetail < ActiveRecord::Base
belongs_to :car_posting
belongs_to :car_make
belongs_to :car_model
end
Although, this starts to create a lot of joins, I'm not sure I have the has_one/belongs_to declarations in the right direction, and you have to start chaining calls (e.g., #posting.car_posting_detail.car_make).
Are there other design patterns you have seen for accomplishing this?
You basically have to 2 options for accomplishing inheritance.
First, you can use rails STI as you suggested. The downside is that end up with nil attribute for the child classes that do not use all of the fields. Your idea to reduce this by adding type-specific attributes to another model is a great way to reduce this. However, you should keep the implementation as DRY as possible by defining a has_one :detail for the Posting. Then you can simply assign specific detail types in the Posting childs. For example, CarPosting's detail would be CarPostingDetail. This is convenient because then all Posting children will have their details accessed identically, but will still have different details. So the query now looks like #posting.detail.car_make. To take this one step further, you can define a custom helper method in your Posting model to grab each attribute in the current Posting's detail and create an accessor for it. Now the entire detail layer is transparent and you can simply access those attributes by saying #posting.car_make.
Second, you can use an abstract class. This is essentially the reverse of STI. You create an abstract model class which can never be instantiated. Thus, you cannot define any relationships in the Posting class because it has no table. Each child of the abstract Posting class has its own separate table. The main advantage of doing this would be the ability to define methods for all of your Posting types without copy and pasting them into every model. So this options is better if there are some overlapping functionality across the models, but very little data overlap.
You could use polymorphic associations for this.
Post model belongs_to :postable, :polymorphic => true
car, event and all the other "postable" classes would have this relationship
has_many :posts, as: :postable
Post would hold the postable_id and postable_type
More info here
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations

How to filter a relation in place

This is a pretty fundamental question, I guess, but I'm having a hard time finding an answer. It may be that this just isn't a thing people do, and if that's the case I'd like to know why.
In a show action, usually I have some code like this:
#structure = Structure.where(id: params[:id])
This gets me a relation containing the unique structure with the given id. Structure has_many plannings. What I'd like is a way for my "Structure" relation to contain structures that have plannings relations that have been filtered. So, I don't want the Structures scoped to their plannings, I want the plannings on those structures to themselves have been filtered. I want to get back a relation containing a structure that has only a subset of its plannings. Does this even make sense?
edit:
I'm starting to see why this doesn't make sense, but also having a hard time articulating it. This,
#structure = Structure.where(id: params[:id])
#structure.plannings = #structure.plannings.where(audience_ids: "1")
throws an error along the lines of Cannot modify association 'Structure#plannings' because the source reflection class 'Planning' is associated to 'Course' via :has_many. The message I'm getting here is: what you are trying to do is more complicated than you think.
I guess I should just return json that looks like what I want it to look like, rather than trying to modify the relation in place.
If your criteria are constantly the same for all plannings that are fetched as a structure's relation, you could use a default scope in the relation (available on Rails 4)
class Structure < ActiveRecord::Base
has_many :plannings -> { where(attribute_a: true) }
end
that way
Structure.where( ... ).first.plannings
will apply the desired scope to plannings.

Represent multiple Null/Generic objects in an ActiveRecord association?

I have a Casefile model that belongs_to a Doctor. In additional to all the "real" doctors, there are several generic Doctors: "self-treated", "not specified", and "removed" (it used to have a real doctor, but no longer does). I suspect there will be even more generic values in the future.
I started with special "doctors" in the database, generated from seed. The generic Doctors only need to respond to the name, title, company, published? methods.
This worked with one, was strained with two, and now feels completely broken. I want to change the behavior and can't figure out how to test it, a bad sign. Creating all the generic objects for testing is also trouble, including fake values to pass validation of the required Doctor attributes.
The Null Object pattern works well for one generic object. The "name" method could return "self-treated", as demonstrated by Craig Ambrose.
What pattern should I use when there are multiple generic objects with very limited state?
It seems to me like you could just add an extra field to the Casefile model called, say, treatment (which would be set to "self-treated", "not specified" etc.)
You could add a validation to ensure a Casefile either has a doctor or treatment assigned:
validate :has_doctor_or_treatment, :on => :save
def has_doctor_or_treatment
(self.doctor.exists? || !treatment.blank?)
end
Then you could use the treatment field to find Casefile's using .where:
Casefile.where(:treatment => "self-treated")
If you wanted, you could have treatment as an extra model, where Casefile has_one Doctor and has_one Treatment - but it seems like your needs are too simple to warrant that.

Rails nested form with uniquness condition

Rails 2.3.5, Ruby 1.8.7.
I have three models, Person, AcademicTerm, and PersonTermStatus.
class PersonTermStatus {
belongs_to :academic_term
belongs_to :person
validates_uniquness_of :academic_term_id, :scope => :person_id
# ...
}
class Person {
has_many :person_term_statuses
}
In a dynamic nested form for a Person record, I allow the editing of the person_term_statuses. But I get validation errors if the user does either of the following:
Deletes a status and creates a new one with the same academic term in the same change.
Swaps the academic terms between two existing statuses.
I understand why this is happening. In (1), the status marked for deletion is not actually deleted before validation of the new status's uniquness condition. In (2), the uniquness condition again is applied before any changes, and it finds another record with the same academic_term.
The problem is, I can't figure a way around this. Is there a known solution?
(My nested form implmenetation is currrently using pretty much exactly the technique from RailsCast [ Part I and Part II )
There is no workaround for this that I know of. However, you can add foreign keys to your database to enforce the uniqueness on the database side and then use the following approach.
Add a before_validation to the parent model that deletes and recreates as new records all the children. Then add a custom validation function that manually checks the children records for uniqueness based on what's in memory (rather than what's in the database).
The downsides to this approach include:
The children don't retain the same IDs.
The created timestamp changes.

Rails: If Foo has_many :bars, do all :bars need to belong_to a Foo?

I'm working on an app that has a lot of photos in it, which are contributed by users. The photo-sharing aspect is the primary purpose of the app.
Sometimes it's helpful to create featured "Collections" of photos. So, this is easy enough in ActiveRecord: Collection has_many :photos and Photo belongs_to :collection.
The question I have is, for an 'ordinary' photo that doesn't belong to a special collection, should I:
A. Not have it belong to any collection at all. ie: photo.collection == nil
-or-
B. Create a generic "Community Collection" that holds every photo. ie by default photo.collection == 'Community'
I am also considering whether this should be a HABTM relationship.
Feedback would be much appreciated!
It depends on what you're going to do with your uncollected photos, really. If you want the ability to show the uncollected photos with the same action you use to show a collection, then you'll want to have that generic collection. Otherwise, I'd leave it nil; I don't like mixing "seed" data with user data in the same table (which you'd be doing with the "uncollected" collection) - it's too easy to accidentally wipe out your user data when reseeding.
Should it be a HABTM relationship? Again, depends on how you want to use it. If there's a good chance you'll want photos to belong to multiple collections, go for it.
And the things that I've seen mentioned here that I would not do: Adding a 'ptype' attribute to tell singletons from collected photos (waste of DB space - you can already get that info by calling photo.collection_id.nil?); and using polymorphic models (you don't want singleton photos to behave drastically different, I assume, so this is added complexity for little to no benefit).
Named scopes, though, are a great idea, and will be particularly useful if you don't have the "uncollected" collection to find your singletons by:
#Apologies for the Rails 2.3 code - it's what I know...
named_scope :singletons, {:conditions => {:collection_id => nil}}
Or the HABTM version:
named_scope :singletons, lambda {
{:conditions => ['NOT EXISTS (SELECT * FROM collections_photos WHERE photo_id = ?)', self.id]}
}
Hope this helps!
What about using a ptype attribute ? You could have photo.ptype = 'collection' or photo.ptype = 'single'. This way you would be able to identify each photo by that type. Moreover, you can make it even more elegant by using a scope on the photos and specify collection_photos scope or single_photos.
Another possibility is polymorphic models. Have a photo model and a collectionPhoto and singlePhoto model that are using it.
I would only set the collection property when it belonged to a collection. Just be sure that your view code can handle this case.

Resources