Rails - nestes resources, wrong id - ruby-on-rails

I have a Group model, which has many lists.
Group
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
has_many :lists
accepts_nested_attributes_for :lists
cattr_accessor :current_user
end
List
class List < ApplicationRecord
belongs_to :group
validates_presence_of :group_id
end
I iterate over #group.lists
<% #group.lists.each do |elem| %>
<p><strong><%= elem.title %></strong></p>
<p><%= elem.body %></p>
<%= link_to 'Delete list', [elem.group, elem], method: :delete,
data: {confirm: "Are you sure?"} %>
<%= link_to 'Update list', edit_group_list_path(:group_id => #group.id, :list_id => elem.id), method: :put %>
<% end %>
Delete method works good, but update method works wrong. Instead of creating links like http://localhost:3000/groups/9/lists/10/ where group_id is 9 and list_id is 10, it's doing something like this
http://localhost:3000/groups/9/lists/9/edit?list_id=15
Routes:
resources :groups do
resources :lists
end

Run
rails routes
to understand where you are wrong
Anyway, the nested resource requires just id, so
edit_group_list_path(:group_id => #group.id, :id => elem.id)
Any other parameter is added in query string

As #Ursus said, the default behavior of nested resources in rails to to accept a plain :id parameter for the innermost resource, /groups/:group_id/lists/:id. You can also simply pass in the objects directly instead of mapping to ID integers:
edit_group_list_path(#group, elem)
You can read more about nested resources here: http://guides.rubyonrails.org/routing.html#nested-resources

Related

NoMethodError in Users#show / undefined method `friendships' for #<Profile:0x007ff052f60b68> Rails

I can´t really figure out why I´m getting this error....Earlier today this was working fine, then I did some modifications that I clearly lost track off, and now when I start up my App I always get this error when I try logging in as a user.
NoMethodError in Users#show
undefined method `friendships' for #<Profile:0x007ff052f60b68>
I have 3 Users in my App and they do all have Profiles.
On current_user Profile page the user is able to see his friends and click on their names to see their profile.
Can anyone help me with this?
TIA Dadi
In views/users/show.html.erb
<h4> <%= current_user.profile.name%> Friends</h4>
<ul>
<% #user.friendships.each do |friendship| %>
<li>
<%= link_to user_profile_path(user), :method => :get do %>
<%= friendship.friend.profile.name %>
<%#= link_to compare_friends_path(#user), :method => :get do %>
<%#= friendship.friend.profile.name %>
(<%= link_to "remove friend", friendship, :method => :delete %>)
</li>
<% end %>
<% end %>
</ul>
In users_controller.rb
def show
#user = User.find(params[:id]).profile
end
In user.rb model
has_many :friendships
has_many :friends, through: :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, through: :inverse_friendships, :source => :user
has_one :profile
In profiles_controller.rb
def show
#user = User.find(params[:user_id])
##profile = #user.profile
end
In profile.rb model
class Profile < ActiveRecord::Base
belongs_to :user
end
In friendship.rb model
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
end
In routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { registrations: 'registrations' }
resources :users do
resource :profile
end
resources :friendships
end
EDITED
in the same view I´m linking to the same route and it works, why is that? I mean this is basically the same link? (see below)
<h3>followers <%= current_user.profile.name%> </h3>
<ul>
<% #user.inverse_friends.each do |user| %>
<%= link_to user_profile_path(user), :method => :get do %>
<li><%= user.profile.name %></li>
<% end%>
<% end %>
</ul>
def show
#user = User.find(params[:id]).profile
end
This seems wrong, as you're assigning User.profile to #user.
This would explain the error undefined method 'friendships' for #<Profile:0x007ff052f60b68> as your User model has has_many :friendships, but Profile doesn't.
Note: Using web_console or better_errors can really help track down problems like this and is well-worth spending some time setting up. You get a Ruby console in your browser on an exception, and just typing #user would have told you this is a Profile instance, and not a User instance.
I did some modifications that I clearly lost track off
Another lesson: Change as little as possible, TEST, change as little as possible, TEST, and keep repeating this. That way, if something breaks you know exactly which changes caused the error. Keep this feedback loop as short as feasible. This is also something where tools such as better_errors really help.
For a more in-depth explanation, see Feedback Loops in Software Development.

Accessing model has_many property

I have the following models:
Post.rb
has_many :likes
belongs_to :user
Like.rb
belongs_to :user
belongs_to :post
User.rb
has_many :posts
has_many :likes
I'm trying to query the user likes in the view like this:
<% #user.likes.each do |i| %>
<%= i.post.title %>
<% end %>
and it returns undefined method 'title' for nil:NilClass
I only can reach the array of posts like this:
<% #user.likes.each do |i| %>
<%= i.post %>
<% end %>
Or like this <%= #user.likes.first.post.title %>
I can't understand why I can't reach the "title" or any other property.
Any help?
It seems at least one of #user.likes doesn't have its associated post. So Like#post returns nil, and on that nil object you try to call title which raises an error. If this situation is not desired, you probably have to validate likes so that every Like has its post:
class Like < ActiveRecord::Base
# ...
validate :post, presence: true
if so, you probably should also make sure associated likes are destroyed when the post is destroyed:
class Post < ActiveRecord::Base
# ...
has_many :likes, dependent: :destroy
You also can fetch likes only with associated post like this:
<% #user.likes.joins(:post).each do |i| %>
or use try in view, so that when like without post is reached, no error is raised:
<%= i.post.try(:title) %>
If your like always belongs to post, it would be better to have user association with likes using "through: :posts" option. That is correct way of association in this scenario.
So your code will be as follows:
Post.rb
has_many :likes
belongs_to :user
Like.rb
belongs_to :post
User.rb
has_many :posts
has_many :likes, through: :posts
and then you can access user's likes as:
<% #user.likes.each do |i| %>
<%= i.post.title %>
<% end %>
Which will always contain post title.

Creating a resource that belongs_to another resource

Here are my relevant models:
class ListItem < ActiveRecord::Base
belongs_to :inventory_item
belongs_to :shopping_list
belongs_to :item
end
class ShoppingList < ActiveRecord::Base
has_many :list_items
belongs_to :user, :foreign_key => :user_id
end
class InventoryItem < ActiveRecord::Base
belongs_to :item, :foreign_key => :item_id
belongs_to :vendor
has_many :list_items
end
I want to have a button to create ListItems that belong to a user specified list that they own. The new ListItem also needs to be passed the respective :item_id and :inventory_item_id. Here's the relevant part of my current view:
<tr>
<% item.inventory_items.each do |product| %>
<td><%= button_to "#{product.price}",
{:controller => :list_items,
:action => 'create',
:id => #what goes here??,
:method => :create %></td>
<% end %>
</tr>
And my ListItems controller create method:
def create
ListItem.create
flash[:success] = "List Item Added."
redirect_to search_results_path(params[:search])
end
Clearly my create method isn't all that useful right now because it just creates a ListItem with no attributes other than :id. What's the best way to pass the appropriate parameters to my controller? Any help is much appreciated! Thanks in advance.
After doing a bunch of snooping around SO etc. I think the best way to accomplish this is to use a form with hidden fields, as below:
<%= form_tag("/list_items", method: "post") do %>
<%= hidden_field_tag(:item_id, item.id) %>
<%= hidden_field_tag(:inventory_item_id, product.id) %>
<%= hidden_field_tag(:shopping_list_id, ShoppingList.first.id) %>
<%= submit_tag("#{product.price}") %>
This is working well for me and is cleaner in this instance than using a button_to.

How to delete a join table association between two models?

I have the following setup of models and controllers:
Models:
class Company < ActiveRecord::Base
has_many :follow_companies, dependent: :destroy
has_many :followers, through: :follow_companies, source: :user
end
#join table
class FollowCompany < ActiveRecord::Base
attr_accessible :company_id
belongs_to :user
belongs_to :company
end
class User < ActiveRecord::Base
#a user can follow many companies
has_many :follow_companies, dependent: :destroy
has_many :followed_companies, through: :follow_companies, source: :company
end
Controllers:
class FollowCompaniesController < ApplicationController
def create
company = Company.find params[:follow_company][:company_id]
current_user.follow_companies.create! company_id:company.id
redirect_to company
end
def destroy
company = Company.find params[:id]
current_user.follow_companies.find_by(company_id: company.id).destroy
redirect_to company
end
end
The join table as well as companies and users is a resource:
resources :users
resources :companies
resources :follow_companies, only: [:create, :destroy]
Now I'd like to have buttons in my frontend for users to UNfollow a company assuming, they already follow that company:
The following view is part of the Company show action and not the FollowCompany show action
View:
<%= follow_company = current_user.follow_companies.find_by_company_id(#company.id) %>
<%= form_for(follow_company, :html => { :method => :delete }) do |f| %>
<%= f.submit "Unfollow", class: "btn pull-right" %>
<% end %>
When browsing to companies/show however, I get an error in the form_for line above:
ActionController::RoutingError at /companies/10
No route matches {:action=>"destroy", :controller=>"follow_companies", :format=>nil, :id=>#<FollowCompany user_id: 2, company_id: 10, created_at: "2013-03-21 23:34:36", updated_at: "2013-03-21 23:34:36">}
Request parameters
{"action"=>"show", "controller"=>"companies", "id"=>"10"}
Why can't rails find the route?
Pretty sure you need to pull :method => :delete out of the html args:
<%= form_for(follow_company, :method => :delete) do |f| %>
Not sure if that's the only problem but that's what caught my eye.
Something like this seems a bit more elegant too (automagically creates a form):
= button_to "Unfollow", follow_company_path(follow_company), :method => 'delete'
An alternative way to achieve this without writing a form is below. If you want you can do this with a single link.
<%= link_to "Unfollow", follow_compony_path(follow_company), :method => :delete %>

Routing question in nested resources form for ".each do" structure - Rails 3

In my routes i have:
resources :accounts do
resources :transfers
put '/transfers/:id(.:format)' => 'transfers#accept'
end
In my model:
class Transfer
include DataMapper::Resource
belongs_to :account
belongs_to :alias_from, "Alias"
belongs_to :alias_to, "Alias"
class Account
include DataMapper::Resource
belongs_to :user
has n, :transfers
In my view:
<% #transfers_in.each do |income|%>
Amount: <%= income.amount%> <%= income.account.currency%>
<% form_for ([???, income]), :as => :transfer, :url => {:controller=>'transfers', :action => 'accept'} do |f|%>
Choose the account <%= f.collection_select :account, #accounts, :name, :name %>
<%= f.submit :value => "Accept" %>
<% end %>
<% end %>
How should I call for the account here, if here #transfers_in is called by other association?
#aliases = #owner.aliases.all()
#transfers_in = #aliases.transfers_in.all()
I've tried something like
<% #acc = Account.all()%>
<% #trs = #acc.transfers.get(:id => income.account)%>
<% form_for ([#trs, income]), ....
but that gave me
No route matches
{:controller=>"transfers",
:action=>"accept"}
In rake routes such route exists.
Would be thankful for any help.
In your routes, you should have better results using the macros Rails provides for routing. Instead of doing the old-style route map, try:
resources :accounts do
resources :transfers do
put 'accept', :on => :member
end
end
The router is really smart when it comes to RESTful routes, but when you start manually mapping things, it can get confused... especially when you're doing it inside of nested, RESTful routes.

Resources