I have implemented a Follower/Following Relationship,i want to extend the functionality ,i.e. in my current implementation User 'A' follows User 'B' without the acknowledgement of User 'B'.I want User 'A' to send a request to User 'B' and then User 'B' either accepts or rejects it.I want it to be like the Instagram model not Facebook model.[User A sends follow request to User B.If User B accepts request then User A follows User B and User B is not following User A,to do so User B has to send a request to User A].
My files:
schema.rb
class CreateFollowJoinTable < ActiveRecord::Migration
def change
create_table 'follows' do |t|
t.integer 'following_id', :null => false
t.integer 'follower_id', :null => false
t.boolean :accepted, default: false
t.timestamps null: false
end
add_index :follows, :following_id
add_index :follows, :follower_id
add_index :follows, [:following_id, :follower_id], unique: true
end
end
app/models/follow.rb
class Follow < ActiveRecord::Base
belongs_to :follower, foreign_key: 'follower_id', class_name: 'User'
belongs_to :following, foreign_key: 'following_id', class_name: 'User'
end
app/models/user.rb
has_many :follower_relationships, foreign_key: :following_id, class_name: 'Follow'
has_many :followers, through: :follower_relationships, source: :follower
has_many :following_relationships, foreign_key: :follower_id, class_name: 'Follow'
has_many :following, through: :following_relationships, source: :following
def follow(user_id)
following_relationships.create(following_id: user_id)
end
def unfollow(user_id)
following_relationships.find_by(following_id: user_id).destroy
end
routes.rb
post ':user_name/follow_user', to: 'relationships#follow_user', as: :follow_user
post ':user_name/unfollow_user', to: 'relationships#unfollow_user', as: :unfollow_user
app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
def follow_user
#user = User.find_by! user_name: params[:user_name]
if current_user.follow #user.id
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
end
def unfollow_user
#user = User.find_by! user_name: params[:user_name]
if current_user.unfollow #user.id
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
end
end
first you need to add status:boolean default:false to your following_relationships class
then you need to make a controller that notify the followed user about a new follower,
then you need make another controller for the followed user to edit the status from false to be true
like
def accept_follower
#relationships = current_user.following_relationships.find_by(follower_id: params[:follower_id])
#relationships.update_attributes(active: true)
end
def ignore_follower
current_user.following_relationships.find_by(follower_id: params[:follower_id]).destroy
end
then you need to fix your query about follower/following only select where status= true
has_many :followers, -> { where(status: true) } through: :follower_relationships, source: :follower
Related
I want to create a polymorphic model to favorite each objects I want to create to stock in my user page.
I am developing a web app to learn japanese and we can favorite different types of cards as kanas or kanjis and sentences.
So there are 3 objects and soon more to favorite.
I migrated a table which names Favorite :
create_table "favorites", force: :cascade do |t|
t.integer "user_id"
t.integer "favoritable_id"
t.string "favoritable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Here is the Favorite model belongs_to
class Favorite < ActiveRecord::Base
belongs_to :favoritable, polymorphic: true
belongs_to :user
end
Here are the Cards model has_many
class Symbole < ActiveRecord::Base
accepts_nested_attributes_for :kanji_attribute, :allow_destroy => true
has_many :sentence_symboles, :class_name => "SentenceSymbole", :foreign_key => "symbole_id"
has_many :favorites, as: :favoritable
end
and I added in sentence model too
class Sentence < ActiveRecord::Base
include Authority::Abilities
has_many :sentence_symboles, :class_name => "SentenceSymbole", :foreign_key => "sentence_id", dependent: :destroy
has_many :favorites, as: :favoritable
end
Now here is the Favorite controller and I don't really know how to write the create method. Here is the Controller I do:
class FavoritesController < ApplicationController
def index
#favorites = Favorite.where(user: current_user)
end
def create
#Favorite.create(user_id: User.last.id, favoritable_id: Symbole.last.id, favoritable_type:"Symbole")
#favorite = current_user.favoritable.favorites.create(symbole: #symbole, sentence: #sentence).first
if #favorite.present?
#favorite.destroy
else
#favorite = current_user.favorites.new(symbole: #symbole, sentence: #sentence)
if not #symbole.favorites.where(user: current_user).take
#sentence.favorites.where(user: current_user).take
#favorite.save
end
end
# redirect_to favs_path
# redirect_to :back
respond_to do |format|
format.js { render :ajax_update_favs }
end
end
def destroy
#favorite = Favorite.find(params[:id])
#favorite.destroy
redirect_to :back
end
end
Please could someone give me the right way to favorite all object I want and add in an favorite index#view.
Thank you for your help.
I think my question is simple but no. How to favorite each object I want with the def Create controller what is the best method?
I do that
def create
#Favorite.create(user_id: User.last.id, favoritable_id: Symbole.last.id, favoritable_type:"Symbole")
#favorite = #favoritable.favorites.build(favorite_params)
#favorite.user = current_user
#favorite.save
respond_to do |format|
format.js { render :ajax_update_favorites }
end
end
Not sure I understood the problem entirely. It looks like you're overcomplicating favoritesController#create. If a record only should be favorited once, you should add a uniqueness validation in the Favorite model.
Assuming that you have your user model set up like
class User < ActiveRecord::Base
has_many :favorites
# Rest..
end
def create
#favorite = current_user.favorites.create(favorite_params)
# This will create a new Favorite with user_id: current_user.id, favoritable_type: "Symbole", favoritable_id: 1337
# Is this the desired behaviour?
respond_to do |format|
format.js { render :ajax_update_favs }
end
end
private
def favorite_params
params.require(:favorite).permit(:favoritable_type, :favoritable_id)
end
If this is called from javascript with jquery pass the type and id that you wan't to favorite.
$.post( "/favorites", {
favorites: {
favoritable_type: "Symbole",
favoritable_id: 1337
}
});
these are my 3 models :
model for User:
class User < ActiveRecord::Base
has_many :patients, through: :treatments
has_many :treatments
.
.
.
model for patient:
class Patient < ActiveRecord::Base
has_many :user, through: :treatments
has_many :treatments, dependent: :destroy
.
.
.
model for treatment:
class Treatment < ActiveRecord::Base
belongs_to :patient
belongs_to :user
validates :patient_id, presence: true
default_scope -> { order(created_at: :desc) }
end
And this is my treatment table :
class CreateTreatments < ActiveRecord::Migration
def change
create_table :treatments do |t|
t.date :teartment_date
t.text :remark
t.float :fee
t.references :patient, index: true, foreign_key: true
t.timestamps null: false
end
add_index :treatments, [:patient_id, :created_at]
end
end
now i want to define a controller to create a new treatment that belongs to a specific user's patient.
this is my controller :
def new
#treat = Treatment.new
end
def create
#userpatient = current_user.treatments.build(treat_params)
if #userpatient.save
flash[:success] = "new treatment added"
redirect_to root_url
else
render 'new'
end
end
but this is the error that i receive, while i want to create a new treatment :
ActiveRecord::UnknownAttributeError in TreatmentsController#create
unknown attribute 'user_id' for Treatment.
and this is the current_user :
def current_user
if (user_id = session[:user_id])
#current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(cookies[:remember_token])
log_in user
#current_user = user
end
end
end
i'm new to rails, the basic idea is i want my user to have treatment that belongs to a specific patient.
Thanks to replies i've over come with this issue by adding a reference column . now i receive no, but it does not save any treatments. i mean the part :
if #treat.save
flash[:success] = "new treatment added"
redirect_to root_url
else
render 'new'
end
it does not save and just render 'new' .
i have 2 questions :
1- how can i code my create controller ?
2- how to retrieve my treatments base on patient.what variable should i define in my patient 'show' method to have its treatments retrieved ?
When you say that User has_many :treatments and that Treatment belongs_to :user, both associations are expecting to find a user_id column in your treatments table. You might want to change your migration to include:
t.integer :user_id
and then drop your tables (if they have no data yet!) and rerun the migrations. Alternatively, you could create a new migration and simply run that:
add_column :treatments, :user_id, :integer
This question follows up on Rails has_many :through association: save instance into join table and I am restating things here for more clarity.
In our Rails app, there are 3 models:
class User < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :calendars, through: :administrations
end
class Administration < ActiveRecord::Base
belongs_to :user
belongs_to :calendar
end
class Calendar < ActiveRecord::Base
has_many :administrations, dependent: :destroy
has_many :users, through: :administrations
end
And here are the corresponding migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :email
t.integer :total_calendar_count
t.integer :owned_calendar_count
t.timestamps null: false
end
end
end
class CreateAdministrations < ActiveRecord::Migration
def change
create_table :administrations do |t|
t.references :user, index: true, foreign_key: true
t.references :calendar, index: true, foreign_key: true
t.string :role
t.timestamps null: false
end
end
end
class CreateCalendars < ActiveRecord::Migration
def change
create_table :calendars do |t|
t.string :name
t.timestamps null: false
end
end
end
Here is what we are trying to accomplish:
When a logged in user (current_user) creates a calendar, we should:
Create a new #calendar and save it to the Calendar table
Assign the "Creator" role to the user (current_user) for this newly created calendar through the Role column in the Administration table
Increment the total_calendar_count and the owner_calendar_count columns of the User table
In order to do that, we think we need to work on calendars#create.
In the CalendarsController, we already have the following code:
def create
#calendar = current_user.calendars.create(calendar_params)
if #calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
And we collect data from users through the following _calendar_form.html.erb form:
<%= form_for(#calendar) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder: "Your new calendar name" %>
</div>
<%= f.submit "Create", class: "btn btn-primary" %>
<% end %>
We are considering updating the controller as follows:
def create
#calendar = current_user.calendars.create(calendar_params)
#current_user.total_calendar_count += 1
#current_user.owned_calendar_count += 1
current_user.administrations << #calendar.id
#calendar.administration.role = 'Creator'
if #calendar.save
flash[:success] = "Calendar created!"
redirect_to root_url
else
render 'static_pages/home'
end
end
ActiveRecord::AssociationTypeMismatch in CalendarsController#create
Administration(#70307724710480) expected, got Fixnum(#70307679752800)
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
raise ActiveRecord::AssociationTypeMismatch, message
end
end
app/controllers/calendars_controller.rb:7:in `create'
How can we make it work?
This line is actually causing the error: current_user.administrations << #calendar.id.
current.administrations expects an object of type Administration while you are passing a Fixnum into it.
You can accomplish the same functionality in the following way:
current_user.administrations.create(calendar_id: #calendar.id)
Edit:
As OP asked in comments that it is a good practice or not. See, there is rule that says that controllers should be skinny, and models should be fatty. Well, it means you should try to write minimum code, and all the logic and fetching of objects should be there in models. But that isn't the case in your code scenario. You should move your code into model, and then call that into your controller.
Here's how:
class User < ActiveRecord::Base
def add_calendar_and_role(calendar_id, role)
self.administrations.find_by(calendar_id: calendar_id).update(role: role)
end
end
This way, your code reduces to just:
current_user.add_calendar_and_role(#calendar.id, 'Creator')
And on the same way, you can further refactor your controller code.
I'm trying to create a simple # mentions model similar to twitters for my app. I've started building it, but I don't know how I would handle the actual creation of the mention. I need some way to scan let's say a status before it's created for any # symbols, then checking the text following against the database for any matching usernames. If there's a match then a mention gets created along with the status. Can someone point me in the right direction?
Here's what I have so far:
db/migrate/create_mentions.rb
class CreateMentions < ActiveRecord::Migration
def change
create_table :mentions do |t|
t.belongs_to :mentionable, polymorphic: true
t.timestamps
end
add_index :mentions, [:mentionable_id, :mentionable_type]
end
end
models/mention.rb
class Mention < ActiveRecord::Base
belongs_to :mentionable, polymorphic: true
end
models/status.rb
class Status < ActiveRecord::Base
attr_accessible :content
has_many :mentions, dependent: :destroy
end
models/member.rb
class Member < ActiveRecord::Base
has_many :mentions, as: :mentionable, dependent: :destroy
end
controllers/mentions_controller.rb
class MentionsController < ApplicationController
before_filter :authenticate_member!
before_filter :load_mentionable
before_filter :find_member
def new
#mention = #mentionable.mentions.new
end
def create
#mention = #mentionable.mentions.new(params[:mention])
respond_to do |format|
if #mention.save
format.html { redirect_to :back }
else
format.html { redirect_to :back }
end
end
end
private
def load_mentionable
klass = [Status].detect { |c| params["#{c.name.underscore}_id"] }
#mentionable = klass.find(params["#{klass.name.underscore}_id"])
end
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
end
config/routes.rb
resources :statuses do
resources :mentions
end
Thanks to this question: parse a post for #username I was able to get this working. My set up:
db/migrate/create_mentions.rb
class CreateMentions < ActiveRecord::Migration
def change
create_table :mentions do |t|
t.belongs_to :mentionable, polymorphic: true
t.belongs_to :mentioner, polymorphic: true
t.integer :status_id
t.integer :comment_id
t.timestamps
end
add_index :mentions, [:mentionable_id, :mentionable_type], :name => "ments_on_ables_id_and_type"
add_index :mentions, [:mentioner_id, :mentioner_type], :name => "ments_on_ers_id_and_type"
end
end
models/mention.rb
class Mention < ActiveRecord::Base
attr_accessible :mentioner_id, :mentioner_type, :mentionable_type, :mentionable_id, :status_id, :comment_id
belongs_to :mentioner, polymorphic: true
belongs_to :mentionable, polymorphic: true
end
models/member.rb
class Member < ActiveRecord::Base
has_many :mentions, as: :mentionable, dependent: :destroy
end
models/status.rb
class Status < ActiveRecord::Base
attr_accessor :mention
has_many :mentions, as: :mentioner, dependent: :destroy
after_save :save_mentions
USERNAME_REGEX = /#\w+/i
private
def save_mentions
return unless mention?
people_mentioned.each do |member|
Mention.create!(:status_id => self.id, :mentioner_id => self.id, :mentioner_type => 'Status', :mentionable_id => member.id, :mentionable_type => 'Member')
end
end
def mention?
self.content.match( USERNAME_REGEX )
end
def people_mentioned
members = []
self.content.clone.gsub!( USERNAME_REGEX ).each do |user_name|
member = Member.find_by_user_name(user_name[1..-1])
members << member if member
end
members.uniq
end
end
config/routes.rb
resources :statuses do
resources :mentions
end
helpers/mentions_helper.rb
module MentionsHelper
def statuses_with_mentions(status)
status.content_html.gsub(/#\w+/).each do |user_name|
member = Member.find_by_user_name(user_name[1..-1])
if member
link_to user_name, profile_path(member.user_name)
else
user_name
end
end
end
end
Currently I allow users to follow one another on my rails app (similar to twitter).
I would love if New Users that sign up to the site Automatically follow Admin User.
Similar to how MySpace use to automatically make Tom your first friend
Below is the code I use to create new users and allow users to follow one another.(i know this is a very broad question but .....)
(Can someone please point me in the right direction onto how I can get this started. Would I need to create a method....in my models or add code to the controller?)
New to Rails Please help)... :)
USER CONTROLLER
class UsersController < ApplicationController
before_filter :admin_user, only: [:destroy]
respond_to :html, :js
def new
#user = RegularUser.new
end
def index
#users = User.paginate(page: params[:page], :per_page => 100).search(params[:search])
end
def destroy
User.find_by_username(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
def create
#user = RegularUser.new(params[:regular_user])
if #user.save
UserMailer.registration_confirmation(#user).deliver
UserMailer.welcome_user(#user).deliver
sign_in #user
flash[:success] = "Welcome to the ClickOnComics!"
redirect_to (publishers_path)
else
render 'new'
end
end
private
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def follow_admins
admins = User.find_by_admin(true)
admins.each do |admin|
self.follow!(admin)
end
end
class RelationshipsController < ApplicationController
before_filter :current_user
respond_to :html, :js
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_with #user
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_with #user
end
end
MODELS
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
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
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
after_create :follow_admins
def follow_admins
admins = User.find_all_by_admin(true)
admins.each do |admin|
self.follow!(admin)
end
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
I used this tutorial to help me establish privileged administrative users with a boolean admin attribute in the User model
http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-users#sec-administrative_users
SCHEMA
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.string "role"
t.string "username"
t.timestamp "created_at", :null => false
t.timestamp "updated_at", :null => false
t.boolean "admin", :default => false
t.string "password_reset_token"
t.timestamp "password_reset_sent_at"
end
Would I need to create a Method that defines user_admin?
In user.rb add a after_create filter
after_create :follow_admin!
def follow_admin!
relationships.create!(followed_id: admin_user.id)
end
In create action before sign_in add
#user.follow! admin_user
you need to fetch admin user first somehow.
Good idea would be to make follow!, following? and unfollow! methods to accept either id or object as in
def follow!(user_or_id)
id = (user_or_id.is_a?(User) ? user_or_id.id : user_or_id)
relationships.create!(followed_id: id)
end