Rails and HAML - how to reference deeply nested array items individually - ruby-on-rails

I'm working on someone else's site, and am trying to reference a value's ID in my HAML file, but I've tried every iteration I can think of and can't figure out how to do this. I'm trying to get the ID of a want, which belongs to a project, which belongs to a user. Here's some code from the projects model:
class Project
belongs_to :user
has_many :wants
accepts_nested_attributes_for :wants
In another view, someone's written this:
(id="project_#{ project.id }_wants")= render project.wants, :user => project.user
This returns an array of wants, which it iterates through. I was initially confused because the wants seem to have ids in some of the views, but when I tried referencing project.want[0], for example, that worked perfectly. My question is - how do I pass user(id of blah).project(id of blah).want[place blah in array] ?

Assuming you know the user ID and project ID (we'll call them user_id and project_id), you can:
User.find(user_id).projects.find(project_id).wants[index of the want you want]
or
User.find(user_id).projects.find(project_id).wants.find(wants_id)

Related

Rails - Creating and Validating Many-to-Many relationship with the same model?

This is probably a really simple question, but I've been searching the web for probably around an hour and I can't really find an answer to my problem. It should be clear by what follows that I am very new to Rails, so my terminology and explanation might be a bit confusing.
Let's say that I were making a social media app on Rails, where one of the models is User. I want to make a many-to-many relationship called "friends", which links two users together. Let's say in this situation I also wanted to make a many-to-many between two users called "enemies".
This is all completely hypothetical, but the idea is the same one that I want to use for something I'm working on.
Because a user can have many friends and enemies, but also be many friends and enemies, I would use:
class User < ActiveRecord::Base
has_and_belongs_to_many :users #this should be the friends association
has_and_belongs_to_many :users #this should be the enemies association
end
Now I'm guessing I can't just do that, because I would have to have two tables both named users_users. So, then I switch to:
class User < ActiveRecord::Base
has_and_belongs_to_many(:users, join_table: 'friends',
foreign_key: 'user_id', associate_foreign_key: 'friend_id')
end
With a similar statement for the enemies table. Now, my problem is that I want to have a form that the user can use when they sign up, where they can input their information (this is the User object details), and also list their friends and enemies.
Because the user won't have the database id key for their friends or enemies, they'll have to input the users' names. This is fine, though because the name is also a unique key, guaranteed by the validation.
However, if the user types in the name of a friend, I can't join the two if the friend happens to not exist. So, I use a custom validation class that looks something like this:
class FriendValidator < ActiveModel::Validator
def validate(object)
#lookup user and throw error if not found.
end
end
which will access the variable (object.friends) and (object.enemies)
With something similar for enemies. So therefore, above my has_and_belongs_to_many statements, I have lines that say:
attr_accessor :friends, :enemies #these are attrs because they don't exist within the model's db
validates_with FriendValidator
When I create the form with erb, I have the standard form_for block
<%= form_for(#user) do |f| %>
It seems to me that I can't just stick
<%= f.text_area :friends %>
because friends isn't actually something that will get passed to the User object, but rather a separate table. (Can I, though? Because the attr_accessor is declared in the user's model class?)
So now, we have my main problem. I have two many-to-many tables with a model to its own model class, and I don't know how to ensure that the validation class will take the two attributes, lookup and throw necessary errors, and then add a row to the join tables using the id of the user, rather than the string inputted. What form fields should I use to pass the input to the right place? Where do I change the controller methods so that the input gets sent to the join table rather than the user object?
This definitely seems like a pretty specific situation, so I can't really find an answer in the Rails documentation, which I've been learning from.
My initial impression of this problem has to do with your associations. To me, a user has_many enemies and has_many friends.
friends belong_to user
enemies belong_to user
Not sure if a many to many relationship makes sense in this case. Maybe that's why you are having such a hard time finding an answer online. Just my two cents.

Ruby on Rails: Accept nested attributes for parent rather than child records?

In my Rails app Users can have many People which in turn can (but don't have to) belong to Organisations.
In short, this:
Users --< People >-- Organisations
Now, it would be nice to be able to create new organisations from within a people view somehow. It tried this:
class Person < ActiveRecord::Base
attr_accessible :name, :organisation_attributes
belongs_to :user
belongs_to :organisation
accepts_nested_attributes_for :organisation
end
But it's not working because Organisation is not a child of Person.
Is there another way to realise this?
Thanks for any help.
I can see that Person is actually a child of Organisation and its possible to make nested form for parent model also. And you are already using accepts_nested_attributes_for.
Im assuming that you want to show a Organisation form for a already saved person. Then
In your PeopleController#show method build the organisation
#person.build_organisation
And in people/show.html.erb
form_for(#person) do |f|
f.fields_for(:organisation) do |fo|
# show the fields of organisation here.
end
end
It should work.
Update:
I tried something similar and it worked :) Ive made a gist including the snippets.
Please follow the link https://gist.github.com/3841507 to see it working.

Mongoid and embedding tags

I have a few models that each have tags. Say for example, a User and a Post. Both should have embedded tags I think.
The user's embedded tags are their "favorites" and a post's tags are tags that have to do with the post.
I'm using Mongoid, and I can't figure out how to have a Tag model where I can find all the tags (These tags aren't embedded, I want a separate collection of all available tags).
When a user tries to add a tag, it is checked against Tag.all to make sure it exists. If it does, then it should be embedded in the User model.
I can't figure out how to embed Tag models in more than one model (User and Post) or how to embed Tag models as well as retrieve them like normal (I get the error: Access to the collection for Tag is not allowed since it is an embedded document, please access a collection from the root document.).
Ideas? Am I designing this totally wrong? I don't need complicated queries like "Retrieve all users who have the example tag", so I figured that I should embed to be more efficient.
Well, the first step is to make a tag a polymorphic embed.
class User
embeds_many :tags, :as => :taggable
end
class Post
embeds_many :tags, :as => :taggable
end
class Tag
embedded_in :taggable, :polymorphic => true
end
You could perhaps then have an instance of a user or a post that contains all tags available. And then instead of calling .all on the Tag class, you can call it on your instance
available_user = User.new
available_user.tags << Tag.new
available_user.tags.all
My solution is to have a Registry model that embeds_many tags, then to have an instance that has the registered tags that is checked before adding to a user or post. This is essentially the same as Ron's answer, but without overloading User.
First of all, the error you get when you try to get embedded tags is normal, that's not possible with actual MongoDB versions.
I'm not an expert, but I can suggest you a workable solution: use embedded tags for User and Post, and create a Tag collection that you'll use for indexing purposes.
When a user tries to add a tag: call Tag.all
If the tag doesn't exist: create it in both User and Tag
If it does, simply embedded it in User.
The same goes for Post.

Limiting an attribute in Rails

I have a Tournament model that needs 0, 1, or 2 contacts. I created a Contact model and set has_many :contacts on the Tournament and belongs_to :tournament on the Contact. The Tournament accepts_nested_attributes_for :contacts.
However, when I build the form for Tournament I don't quite see how I should do it. I'm thinking about having two fields_for :contacts but it feels messy. I also considered having two specific attributes on the Tournament model (something along the line of primary_contact and secondary_contact) but I'm not sure about how to do that.
Is there a "correct" way to do this? Any suggestions?
I'm on Rails 3.1 BTW.
fields_for :contacts is the right way to go.
Take advantage of the fact that, if tournament.contacts has multiple items, then a single fields_for :contacts will show multiple fieldsets.
Then take advantage of the fact that tournament.contacts.build will create an unsaved Contact and add it to the contacts collection. If you do this in the controller before showing the form then your fields_for will display this empty contact and use it correctly with its nested attributes
I think you shouldn't limit the contacts for 2 fields, because I think you should keep the flexibility of adding more contacts for a tournament later
I have done a small example (by using check boxes) between Project to users, you might be able to get idea
https://github.com/sameera207/HABTMsample
I'd suggest maybe adding a non-persistent contact_list attribute and then you could enter as many contacts as you need separated by commas into one field:
has_many :contacts
attr_accessor :contact_list
def contact_list=value
value.split(',').each do |email|
self.contacts.build(:email => email).save
end
end
def contact_list
self.contacts.join(',')
end
If you need to enter more information for each contact (not just a name, email, or phone number), then you would need more fields.
The following railscast may help you:
http://railscasts.com/episodes/196-nested-model-form-part-1

How do I implement this code without needing to access current_user.id from a model in RoR?

I have a note model, with the following association
note.rb
has_many :note_categories, :dependent => :destroy
has_many :categories, :through => :note_categories
The NoteCategory model was created to function as a join table between notes and categories.
I need to implement the following:
A user removes a category from a note. This can be done by either removing one category from a note (deletes one entry in the note_categories table), or by deleting the note entirely (deletes all entries in the note_categories table relating to the note)
Before the row/s in note_categories is/are deleted, I need to determine if the user who is deleting the category from a note is the same user who initially created the category (creator field in the category model)
If it is the same user, the category entry itself is to be deleted
Obviously to do this, I need to access the id of the user, to check against the creator field of the Category. I am already using a before_destroy method in the NoteCategory model to do some other things, but I can't access current_user.id in there because it's a model, and current_user is a method in the Application Controller. From the questions I've read here on SO, it seems that accessing the id of the current user from a model is bad form.
I don't think I can use the controller in this circumstance because when a note is deleted, the :dependent => :destroy line means that the associated rows in note_categories are deleted as well. I need to do the creator check in this situation as well, but the note_categories rows are removed via the destroy method in the model, not the controller, which is the behavior specified by :dependent => :destroy.
So how should I go about doing it? Thanks for reading!
One way of doing this could be to add an attr_accessor to the Note model like so:
# in Note.rb
attr_accessor :destroyed_by
and set it before destroying the record:
# NotesController#destroy
#note.destroyed_by = current_user.id
#note.destroy
Then in your Note.rb before_destroy call, you can check the Category's creator id against the destroyed_by id.
You say
Before the row/s in note_categories is/are deleted, I need to determine if the user who is deleting the category from a note is the same user who initially created the category (creator field in the category model)
If it is the same user, the category entry itself is to be deleted
Are you sure you wan't to do such a thing? What if a user has used the same category for many notes, and he wants to delete it form one of them? You sure would want to delete an entry from note_categories, but should you also delete the category itself?
A common implementation for such a scenario is to check while deleting a note_category (through perhaps a before_destroy) whether this one is the last note_categories for the category, and delete it if it is. This also means that if a note is deleted, only the related note_categories should be deleted, and not the categories themselves.
I think that you want to maintain a log of who created the post and who is deleting it. The before_destroy method does the part of deleting associations and I think it is working fine for you. as of maintaining record of whether the user who created the note is deleting the note or not comes under logging part. I hope this helps you around this problem
http://rohitsharma9889.wordpress.com/2010/08/19/logging-in-ruby-on-rails/
EDIT:
You can also try reading this article. I would recommend you to prefer this one on the above one
Environment variables in Model

Resources