Follow BETA Inviter on Sign Up - ruby-on-rails

I have created a BETA Invitation system by following this tutorial. http://railscasts.com/episodes/124-beta-invitations. Users can also follow one another in my Rails app.
How can I have the Invitee follow the person who Invited him on sign up?
Currently, I am trying to establish this in my User Model using a method, but I am having trouble creating the method that allows the Invitee to follow the Inviter via sender_id/user_id.
This is the code I have used so far.
SCHEMA
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.integer "invitation_id"
t.integer "invitation_limit"
t.timestamp "created_at", :null => false
t.timestamp "updated_at", :null => false
t.string "password_reset_token"
t.timestamp "password_reset_sent_at"
end
create_table "invitations", :force => true do |t|
t.integer "sender_id"
t.string "recipient_email"
t.string "token"
t.datetime "sent_at"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
MODELS
USER
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :invitation_token
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
has_many :sent_invitations, :class_name => 'Invitations', :foreign_key => 'sender_id'
belongs_to :invitation
after_create :follow_inviter #---------- HERE!!
def follow_inviter #---------- HERE!!
inviters = Invitation.find_by_sender_id
inviters.each do |invite|
self.follow!(invite)
end
end
def invitation_token
invitation.token if invitation
end
def invitation_token=(token)
self.invitation = Invitation.find_by_token(token)
end
def following?(other_user)
relationships.find_by_followed_id(other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by_followed_id(other_user.id).destroy
end
end
RELATIONSHIPS
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
INVITATION
class Invitation < ActiveRecord::Base
attr_accessible :recipient_email, :sender_id, :sent_at, :token
belongs_to :sender, :class_name => "User"
has_one :recipient, :class_name => "User"
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
before_create :generate_token
private
def generate_token
self.token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end

This should work.
def follow_inviter
if invitation = Invitation.find_by_recipient_email(email)
follow!(invitation.sender)
end
end
But your models associations are not well defined. For example has_one :recipient, :class_name => "User" in the Invitation expects that there is a recipient_id in the table, but is not. You should review that.

I might be wrong, I'm a junior rails developer, but it looks like where you have inviters = Invitation.find_by_sender_id you should have something like this inviters = Invitation.find_by_sender_id(id_of_sender) where id_of_sender is the id of the person who sent the invitation.
The find_by_sender_id takes 1 argument (the ID of the sender to be found) and that is why you get the error wrong number of arguments (0 for 1).
Also for what it's worth, I'm pretty sure the find_by_*_id methods, where * is a model in your database, are deprecated. Rails 4 uses something like Invitation.find(id_of_sender). When you call the find method on an active record model, it takes an id as a parameter.
You can also use Invitation.find_by(email: 'user#example.com') to find records based on any property you give them.

Apologies if I am misunderstanding your question... I have a similar requirement, where one user can mark another as a 'favorite' user (i.e. users table has a self-relationship)
To implement this, I added a table called user_favorite like this:
db\schema.rb
create_table "user_favorites", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "favorite_user_id"
end
add_index "user_favorites", ["user_id"], :name => "index_user_favorites_on_user_id"
app\models\user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :favorite_users, :class_name => 'User', :join_table => "user_favorites", :foreign_key => "user_id", :association_foreign_key => "favorite_user_id"
def is_favorite?(user)
self.favorite_users.include?(user)
end
def toggle_favorite(favorite_user)
if favorite_user
if self.favorite_users.include?(favorite_user)
self.favorite_users.delete favorite_user
else
self.favorite_users << favorite_user
end
end
end
end

Related

Rails nested attributes: validates_uniqueness_of doesn't work

I have 3 models:
class UserLanguage < ActiveRecord::Base
belongs_to :user
belongs_to :language
end
class Language < ActiveRecord::Base
has_many :user_languages
has_many :users, :through => :user_languages
end
class User < ActiveRecord::Base
has_many :user_languages, :dependent => :destroy
has_many :languages, :through => :user_languages
accepts_nested_attributes_for :user_languages, :allow_destroy => true
end
I'm using nested_form gem to help user select which language(s) they can speak in. The CRUD for that is working fine.
But, I can't validate uniqueness of the UserLanguage. I try this 2 syntax but they didn't work for me:
validates_uniqueness_of :language_id, scope: :user_id
validates :language_id, :uniqueness => {:scope => user_id}
My schema for user_languages table:
create_table "user_languages", force: :cascade do |t|
t.integer "user_id"
t.integer "language_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "user_languages", ["language_id"], name: "index_user_languages_on_language_id", using: :btree
add_index "user_languages", ["user_id"], name: "index_user_languages_on_user_id", using: :btree
What should I do to make sure one user can choose only a language once? Say, if I select English inside the dropdown, my second English will not be saved (duplicate) and rejected.
This is how I did it finally:
class UserLanguage < ActiveRecord::Base
belongs_to :user
belongs_to :language
def self.delete_duplicated_user_languages(user_id)
user_languages_ids = UserLanguage.where(user_id: user_id).pluck(:language_id).sort
duplicate_language_ids = user_languages_ids.select {|language| user_languages_ids.count(language) > 1}
duplicate_language_ids.uniq!
keep_this_language = []
duplicate_language_ids.each do |language_id|
keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language_id).id
end
single_language = user_languages_ids.select {|language| user_languages_ids.count(language) == 1}
single_language.each do |language|
keep_this_language << UserLanguage.find_by(user_id: user_id, language_id: language).id
end
UserLanguage.where(user_id: user_id).where.not(id: keep_this_language).destroy_all
end
end
I save all UserLanguages first and delete them (duplicate ones) later.
If User has and should only have one Language, then you could change the cardinality between the models:
class Language < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
has_one :language
end
Then by definition your users will only have one language at a time and your overall model will be simpler.
You can read more about active record associations and cardinalities in this Association Basics guide

Rails join same table two times

I have a table with friendships. It's build like in http://railscasts.com/episodes/163-self-referential-association. Now I want to the most efficient way to get all relations, which are made from both sides (The normal and the inverse friendship).
In MySQL it should look like:
SELECT * FROM Friendship as f1, Friendship as f2 WHERE f1.user_id = f2.friend_id AND f1.friend_id = f2.user_id
In a comment on railscast someone build the extension of the mutual_friends part. But these three lines don't work in my case:
Devise User Model:
class User < ActiveRecord::Base
has_many :features
has_and_belongs_to_many :tags
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
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessible :first_name, :last_name, :email, :password, :date_of_birth, :gender, :password_confirmation
validates_inclusion_of :gender, :in => ['female', 'male'], message: 'Kein Geschlecht ausgewaehlt'
validates :first_name, presence: true
validates :last_name, presence: true
validates_date :date_of_birth, :on_or_before => lambda { Date.current }
def mutual_friends
inverse_friends.where('user_id in (?)', friend_user_ids)
end
end
Devise User Migration:
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
t.string :email, :null => false, :default => ""
t.string :encrypted_password, :null => false, :default => ""
t.string :reset_password_token
t.datetime :reset_password_sent_at
t.datetime :remember_created_at
t.integer :sign_in_count, :default => 0
t.datetime :current_sign_in_at
t.datetime :last_sign_in_at
t.string :current_sign_in_ip
t.string :last_sign_in_ip
t.string :first_name
t.string :last_name
t.datetime :date_of_birth
t.string :gender
t.integer :tag_id
t.timestamps
end
add_index :users, :email, :unique => true
add_index :users, :reset_password_token, :unique => true
end
end
Friendship Model
class Friendship < ActiveRecord::Base
attr_accessible :user_id, :friend_id
belongs_to :user
belongs_to :friend, class_name: 'User'
end
Friendship Migration
class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships do |t|
t.integer :user_id
t.integer :friend_id
t.timestamps
end
end
def self.down
drop_table :friendships
end
end
The Error in this solution is:
undefined local variable or method `friend_user_ids' for #<User:0x007fcb93e86de8>
The orginal constellation in the comment on Railscast is:
Friend Model
class Friend < ActiveRecord::Base
belongs_to :profile
belongs_to :friend, :class_name => 'Profile'
end
Profile Model
class Profile < ActiveRecord::Base
has_many :friends
has_many :friend_profiles, :through => :friends, source: :friend
has_many :friended_by, class_name: "Friend" , :foreign_key => "friend_id"
has_many :friended_by_profiles, :through => :friended_by, source: :profile
def mutual_friends
friended_by.where('profile_id in (?)', friend_profile_ids)
end
def mutual_friend_profiles
Profile.where('id in (?)', mutual_friends.pluck(:profile_id))
end
def requesting_friends
friended_by.where('profile_id not in (?)', friend_profile_ids)
end
def requesting_friend_profiles
Profile.where('id in (?)', requesting_friends.pluck(:profile_id))
end
def pending_friends
friends.where('friend_id not in (?)', friended_by_profile_ids)
end
def pending_friend_profiles
Profile.where('id in (?)', pending_friends.pluck(:friend_id))
end
end
Thanks for helping!
The solution how I get it to work:
Remove mutual_friend
def mutual_friends
inverse_friends.where('user_id in (?)', friend_user_ids)
end
Change the logic in the FriendshipController to:
#all mutual friendships with marked true in both relations
#user = current_user
#user_friends = #user.friendships.select{ |friendship| friendship.marked == true }
#user_inverse_friends = #user.inverse_friendships.select{ |friendship| friendship.marked == true }
#friendships = #user_friends.select{ |friend| #user_inverse_friends.map{|inverse_friend| inverse_friend.user_id}.include? friend.friend_id}
#friendships with false mark
#skiped_friendships = current_user.friendships.select { |friendship| friendship.marked != true}
#marked friend: users friendships without the mutual valid friendships
#marked_friends = current_user.friendships - #friendships - #skiped_friendships

References in Rails using custom Name?

So I am developing a rails app that will have two kinds of Users, student/tutor. but I only have one User model (using cancan for auth), so when I try to set up the meeting model (which has one tutor and one student) how do I do this? This is the model:
class Meeting < ActiveRecord::Base
belongs_to :student
belongs_to :tutor
attr_accessible :price, :subject, :time
end
and here's the relevant part of the schema:
create_table "meetings", :force => true do |t|
t.string "subject"
t.integer "student_id"
t.integer "tutor_id"
t.datetime "time"
t.integer "price"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "meetings", ["student_id"], :name => "index_meetings_on_student_id"
add_index "meetings", ["tutor_id"], :name => "index_meetings_on_tutor_id"
Without having to have two extra models containing student and tutor can I use those labels?
one way to do it..
class Meeting < ActiveRecord::Base
belongs to :student, class_name: 'User'
belongs to :tutor, class_name: 'User'
class User < ActiveRecord::Base
has_many :meet_with_students, class_name: 'Meeting', foreign_key: :tutor_id
has_many :students, through: :meet_with_students, source: :student
has_many :meet_with_tutors, class_name: 'Meeting', foreign_key: :student_id
has_many :tutors, through: :meet_with_tutors:, source: :tutor
I think you're looking for class_name:
class Meeting < ActiveRecord::Base
belongs_to :student, class_name: "User"
belongs_to :tutor, class_name: "User"
end

Ruby on Rails Many to Many Relationship with Self

I am trying to save a person's Facebook friends into my database. I want to store the Facebook users in a table and then store their friendships in another table. The friendships would have the integer of the FacebookUser that requested the friendship and the integer of the friend, both of which are foreign keys to the facebook_users table. However I keep getting this message when I try to link the a user's facebook friends with friendships.
Error
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :friend or :friends in model Friendship. Try 'has_many :friends, :through => :friendships, :source
=> <name>'. Is it one of :FacebookUser or :FacebookFriend?
friendship.rb
class Friendship < ActiveRecord::Base
attr_accessible :facebook_user_id, :facebook_friend_id
belongs_to :FacebookUser
belongs_to :FacebookFriend, :class_name => :FacebookUser
end
facebook_user.rb
class FacebookUser < ActiveRecord::Base
attr_accessible :first_name, :gender, :last_name
has_many :friendships, :foreign_key => :facebook_user_id
has_many :friends, :through => :friendships, :source => :FacebookUser
end
Schema
create_table "facebook_users", :force => true do |t|
t.string "first_name"
t.string "last_name"
t.string "gender"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "friendships", :force => true do |t|
t.integer "facebook_user_id"
t.integer "facebook_friend_id"
end
the convention Rails uses is to use associations as defined by the class name and the foreign key. if you've set up your tables like above, you should change your models to the following.
class Friendship < ActiveRecord::Base
attr_accessible :facebook_user_id, :facebook_friend_id
belongs_to :facebook_user # implies a foreign key of facebook_user_id and class of FacebookUser
belongs_to :facebook_friend, class_name: 'FacebookUser' #implies a foreign key of facebook_friend_id
end
class FacebookUser < ActiveRecord::Base
attr_accessible :first_name, :gender, :last_name
has_many :friendships
has_many :friends, :through => :friendships, :source => :facebook_friend
end

Is there a way to set the extra value in a join table

Ok so I have this relationship in Rails:
class Position < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class User < ActiveRecord::Base
has_many :companies, :through => :positions
has_many :positions
class Company < ActiveRecord::Base
has_many :positions
has_many :users, :through => :positions
Here is the schema for positions:
create_table "positions", :force => true do |t|
t.integer "company_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.boolean "regular_user", :default => true
end
The regular_user is signaling the admins and the employees so my question is how do set the regular_user to 0 or false from this data:
#user = User.find(params[:user_id])
#company = Company.find(params[:company_id])
#user.companies << #company
Is there a better way to do this? I was thinking:
Position.create(user_id: params[:user_id], company_id: params[:company_id], regular_user: 0)
But is there a standard for setting associations?
Try this:
class User < ActiveRecord::Base
has_many :positions
has_many :companies, :through => :positions
has_many :companies_as_non_regular_user, :through => :positions,
:conditions => {:"positions.regular_user" => false}
...
end
#user = User.find(params[:user_id])
#company = Company.find(params[:company_id])
#user.companies_as_non_regular_user << #company

Resources