Why is my many-to-many-through association failing? - ruby-on-rails

I have set up a twitter-like following model. Users can all subscribe to each other. I am getting an error in my users controller when trying to create the relationship.
user.rb:
has_many :subscriptions
has_many :providers, :through => :subscriptions
has_many :followings, :class_name => "Subscription"
has_many :followers, :through => :followings
subscription.rb
belongs_to :provider, :class_name => 'User', :foreign_key => "provider_id"
belongs_to :follower, :class_name => 'User', :foreign_key => "follower_id"
users_controller.rb
69 def follow
70 logger.debug params.to_yaml
71 #user = User.find(params["user_id"])
72 logger.debug #user.to_yaml
73 if current_user.providers << #user
74 flash[:notice] = "Subscribed"
75 else
76 flash[:error] = "Unable to subscribe."
77 end
78 end
This is the error when I call follow:
ActiveRecord::UnknownAttributeError (unknown attribute: user_id):
app/controllers/users_controller.rb:73:in `follow'
I have verified that I ran rake db:migrate - the subscription table has two fields provider_id and follower_id. Can anyone help me with the error and explain why it is looking for a 'user_id' attribute?
Update:
show.html.erb:
<%= button_to "Subscribe", user_follow_path(#user), :remote => true %>
routes.rb:
resources :users do
resources :locations
resources :saved_events
resources :saved_locations
post "follow"
end
rake routes | grep follow:
user_follow POST /users/:user_id/follow(.:format) {:action=>"follow", :controller=>"users"}

Using Michael Hartl's tutorial as a guide, I came up with this solution, which fixes the data model so that collection functions work as they should.
Make the provider id accessible and remove the foreign keys in the subscription model.
subscription.rb:
attr_accessible :provider_id
belongs_to :provider, :class_name => 'User'
belongs_to :follower, :class_name => 'User'
Add foreign keys for subscriptions and reverse_subscriptions in the user model.
user.rb:
has_many :subscriptions, :foreign_key => "subscriber_id", :dependent => :destroy
has_many :subscribed_to, :through => :subscriptions, :source => :provider
has_many :reverse_subscriptions, :class_name => "Subscription", :foreign_key => "provider_id", :dependent => :destroy
has_many :followers, :through => :reverse_subscriptions
I also added helper methods to the user model.
user.rb:
def following?(provider)
subscriptions.find_by_provider_id(provider)
end
def follow!(provider)
subscriptions.create!(:provider_id => provider.id)
end
def unfollow!(provider)
subscriptions.find_by_provider_id(provider).destroy
end
Then, in the controller, we can call follow! or unfollow!
user_controller.rb:
...
current_user.unfollow!(#user)
...
current_user.follow!(#user)
...

The reason it's giving you a error is because the when you call << on the providers collection the user doesn't know it's supposed to be a follower. So it's basically saying, "I'm a user, add me to this collection of providers!" instead of "This guy is going to follow me, I'm now a provider and he's a follower"
The simplest answer may be to just do
user.rb:
def follow(other_user)
Subscription.create(:provider => other_user, :follower => self)
end
users_controller.rb
def follow
#user = User.find(params["user_id"])
if current_user.follow(#user)
flash[:notice] = "Subscribed"
else
flash[:error] = "Unable to subscribe."
end
end

Related

How not get current_user

i'm breaking my head to get the user of this situation:
a conversation model:
has_many :conversation_participants, :dependent => :destroy
has_many :users,
:through => :conversation_participants
has_many :messages, :dependent => :destroy
has_one :display_message,
:class_name => 'Message',
:order => 'created_at DESC'
def participants(options={})
if options[:not].is_a? User
users - [options[:not]]
else
users
end
end
and conversation_participants:
belongs_to :user
belongs_to :conversation
attr_accessible :user_id
on a conversation helper:
def self_or_other
#conversation.conversation_participants.find_by_user_id(:not => current_user)
end
please, someone could clear me how to get the other user inside conversation_participants model?
I believe like follows:
#conversation.conversation_participants.where.not(user_id: current_user.id).first

receiver_id not saving - rails messaging

I'm implementing my own user messaging feature in my application, but I'm having difficulty retaining the receiver_id.
User model:
class User < ActiveRecord::Base
has_many :tickets
has_many :sent_messages, :class_name => 'Message', :foreign_key => 'sender_id', :dependent => :destroy
has_many :received_messages, :class_name => 'Message', :foreign_key => 'receiver_id', :dependent => :destroy
end
Message model:
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id'
belongs_to :receiver, :class_name => 'User', :foreign_key => 'receiver_id'
end
Message controller:
def new
#user = User.find( params[:user_id] )
#message = Message.new(
:receiver => #user )
end
def create
#message = current_user.sent_messages.create( params[:message] )
redirect_to( tickets_path, :message => 'Message has been sent.' )
end
receiver_id in the db remains null.
Thanks for the help!
EDIT
Routes question here - ticket has users that can send messages - rails messaging
HTTP is stateless, so each request knows nothing about the one(s) before it. Thus, setting an attribute when calling Message.new in the controller doesn't carry over to the create request on its own—you need to have a hidden form field containing it. Assuming you're using form_for, it would be something like so:
<%= f.hidden_field :receiver_id %>

RoR: Stack level too deep with after create method?

I have the the following after create method in my record model that keeps throwing an Stack level too deep error every time i try creating a new record:
class record
has_many :authorizations
has_many :roles, :through => :authorizations, :dependent => :destroy, :primary_key => :record_secondary_id
after_create :new_record
def create_roles
self.roles.create :name => "#{self.record_title} edit", :record_id => self.id, :edit => true, :review => false
self.roles.create :name => "#{self.record_title} review", :record_id => self.id, :edit => false, :review => true
end
def set_secondary_id
self.update_attribute :record_secondary_id, self.id
end
def new_record
if self.record_secondary_id.blank?
set_secondary_id
create_roles
end
end
end
end
You are having infinite recursion between create record and create_roles.
You need to create role with the record itself instead of callback.
It will be easy as you are using has_many through relationship.
Edited:
In your controller,
#record = Record.new(params[:record])
#role1 = Role.new('some_params')
#role2 = Role.new('some_params')
#record.roles = [#role1, #role2]
#record.save
I think your problem might be coming from the following call:
has_many :authorizations
has_many :roles, :through => :authorizations, :dependent => :destroy, :primary_key => :record_secondary_id
change that to:
has_many :authorizations, :dependent => :destroy, :primary_key => :record_secondary_id
has_many :roles, :through => :authorizations
that should fix your problem.

How do I define a foreign key that points to a class of a different name in ActiveRecord with Rails?

I have a model Follow that defines a user_id and a followed_user_id. If you've used Twitter, this should make sense.
I'm trying to make followed_user_id point to a User model, so I can access the user that is being followed through f.followed_user (in the same way that if I have an Entry with belongs_to :user and a user_id column I can use entry.user to get the user.)
How can I do this?
Thanks!
Check this screencast
http://railscasts.com/episodes/163-self-referential-association
It shows how a self referential association (what you are referring here) should be implemented in Rails.
# models/user.rb
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
# friendships_controller.rb
def create
#friendship = current_user.friendships.build(:friend_id => params[:friend_id])
if #friendship.save
flash[:notice] = "Added friend."
redirect_to root_url
else
flash[:error] = "Unable to add friend."
redirect_to root_url
end
end
def destroy
#friendship = current_user.friendships.find(params[:id])
#friendship.destroy
flash[:notice] = "Removed friendship."
redirect_to current_user
end
You can do it like this:
belongs_to :followed_user, :class_name => "User"
This article walks you through exactly what you need to know on how to solve this issue:
http://www.spacevatican.org/2008/5/6/creating-multiple-associations-with-the-same-table
Straight from the article:
class Sale < ActiveRecord::Base
belongs_to :buyer, :class_name => 'User', :foreign_key => 'buyer_id'
belongs_to :seller, :class_name => 'User', :foreign_key => 'seller_id'
end
class User < ActiveRecord::Base
has_many :purchases, :class_name => 'Sale', :foreign_key => 'buyer_id'
has_many :sales, :class_name => 'Sale', :foreign_key => 'seller_id'
end
The article explains in full what, why and how.

How to restrict a view by

I have successfully set up a friendship self referencial association for users in my Ruby on Rails app after following Ryan Bates' railscast. I can successfully follow, unfollow, and view who is following. Now I want to pull in another element and I am not sure how to do it.
When visiting a user/show page of a user that the current_user is already friends with...what is the syntax for checking the Friendships table for an existing friendship.
Here is how the associations are set up.
Friendship.rb
belongs_to :user
belongs_to :friend, :class_name => "User"
User.rb
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
def friends_workouts
#friends_workouts ||= Workout.find_all_by_user_id(self.friends.map(&:id), :order => "created_at DESC", :limit => 3)
end
I'd go with something like this:
def friends_with?(other_user)
friends.include?(other_user) || inverse_friends.include?(other_user)
end

Resources