How can we notify a user when he has a new follower?
create_notification is suppose to trigger a notification, but I get different errors with different attempts.
relationship.rb
class Relationship < ActiveRecord::Base
after_create :create_notification
has_many :notifications
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
private
def create_notification # How do we need to rewrite this?
notifications.create(
followed_id: followed_id,
follower_id: follower_id,
user: follower_id.user,
read: false
)
end
end
I used this tutorial as a guide for building my notifications and the Hartl tutorial for building the users and relationships.
relationships_controller.rb
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create # The notification is trigger after create.
#user = User.find(params[:followed_id])
current_user.follow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow(#user)
respond_to do |format|
format.html { redirect_to #user }
format.js
end
end
end
Sorry my user model is a bit messy.
class User < ActiveRecord::Base
acts_as_tagger
acts_as_taggable
has_many :notifications
has_many :activities
has_many :activity_likes
has_many :liked_activities, through: :activity_likes, class_name: 'Activity', source: :liked_activity
has_many :liked_comments, through: :comment_likes, class_name: 'Comment', source: :liked_comment
has_many :valuation_likes
has_many :habit_likes
has_many :goal_likes
has_many :quantified_likes
has_many :comment_likes
has_many :authentications
has_many :habits, dependent: :destroy
has_many :levels
has_many :combine_tags
has_many :valuations, dependent: :destroy
has_many :comments
has_many :goals, dependent: :destroy
has_many :quantifieds, dependent: :destroy
has_many :results, through: :quantifieds
has_many :notes
accepts_nested_attributes_for :habits, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :notes, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :quantifieds, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }, unless: -> { from_omniauth? }
has_secure_password
validates :password, length: { minimum: 6 }
scope :publish, ->{ where(:conceal => false) }
User.tag_counts_on(:tags)
def count_mastered
#res = habits.reduce(0) do |count, habit|
habit.current_level == 6 ? count + 1 : count
end
end
def count_challenged
#challenged_count = habits.count - #res
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.provider = auth.provider
user.image = auth.info.image
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.password = (0...8).map { (65 + rand(26)).chr }.join
user.email = (0...8).map { (65 + rand(26)).chr }.join+"#mailinator.com"
user.save!
end
end
def self.koala(auth)
access_token = auth['token']
facebook = Koala::Facebook::API.new(access_token)
facebook.get_object("me?fields=name,picture")
end
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Forgets a user. NOT SURE IF I REMOVE
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def good_results_count
results.good_count
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
private
def from_omniauth?
provider && uid
end
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase unless from_omniauth?
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
:following, :followers]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def tag_cloud
#tags = User.tag_counts_on(:tags)
end
def index
#users = User.paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
if current_user == #user
#habits = #user.habits
#valuations = #user.valuations
#accomplished_goals = #user.goals.accomplished
#unaccomplished_goals = #user.goals.unaccomplished
#averaged_quantifieds = #user.quantifieds.averaged
#instance_quantifieds = #user.quantifieds.instance
else
#habits = #user.habits.publish
#valuations = #user.valuations.publish
#accomplished_goals = #user.goals.accomplished.publish
#unaccomplished_goals = #user.goals.unaccomplished.publish
#averaged_quantifieds = #user.quantifieds.averaged.publish
#instance_quantifieds = #user.quantifieds.instance.publish
end
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Please check your email to activate your account."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
def following
#title = "Following"
#user = User.find(params[:id])
#users = #user.following.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
private
def user_params
if params[:conceal] = true
params.require(:user).permit(:name, :email, :tag_list, :password, :conceal, :password_confirmation, valuations_attributes: [:name, :tag_list, :conceal], activities_attributes: [:conceal, :action, :trackable_id, :trackable_type])
else
params[:user][:valuations][:conceal] = false
params.require(:user).permit(:name, :image, :tag_list, :email, :password, :password_confirmation, valuations_attributes: [:name, :tag_list], activities_attributes: [:action, :trackable_id, :trackable_type])
end
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
schema.rb
create_table "notifications", force: true do |t|
t.integer "habit_id"
t.integer "quantified_id"
t.integer "valuation_id"
t.integer "goal_id"
t.integer "comment_id"
t.integer "user_id"
t.integer "habit_like_id"
t.integer "quantified_like_id"
t.integer "valuation_like_id"
t.integer "goal_like_id"
t.integer "comment_like_id"
t.integer "likes"
t.integer "followed_id"
t.integer "follower_id"
t.boolean "read"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "relationships", force: true do |t|
t.integer "follower_id"
t.integer "followed_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Please let me know if you need further explanation or code :-] Keep the dream alive!
You could simply extend your User#follow method to something like this:
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
UserMailer.new_follower(other_user).deliver_now
end
Then add a new_follower(user) method to your UserMailer in a similar way than the already existing password_reset method.
Related
I have a list of classrooms that belong_to a school and a school has_many classrooms. I have users who belong to a school and fill out a form of information. On this form I have the following selector:
Pins/_form.html.erb
<div class="form-group">
<%= label_tag(:classroom, "Select your classroom:") %>
<%= select_tag "pin[code]", options_from_collection_for_select(Classroom.all, "code", "code", ) %>
</div>
Right now this form shows all the classrooms listed in the data base. I need it to show just the classrooms that belong to the school. Classrooms have a name and a code, the code is what ties the students form submission to that classroom.
I'm still learning RoR and have struggled to figure this type of problem one out on a couple of occasions.
Here's the Classroom Controller
class ClassroomsController < ApplicationController
before_action :set_classroom, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized
respond_to :html
def home
#classrooms = Classroom.all
respond_with(#classrooms)
authorize #classrooms
end
def index
#classrooms = Classroom.all
respond_with(#classrooms)
authorize #classrooms
end
def show
respond_with(#classroom)
end
def new
#school = School.find(params[:school_id])
#classroom = #school.classrooms.new
#teachers = #school.teachers
respond_with(#classroom)
flash[:notice] = "Classroom created."
authorize #classroom
end
def edit
end
def create
#school = School.find(params[:school_id])
#classroom = #school.classrooms.new(classroom_params)
#classroom.save
respond_with #school
authorize #classroom
end
def update
#classroom.update(classroom_params)
respond_with([#school,#classroom])
end
def destroy
#classroom.destroy
redirect_to #school
flash[:notice] = "You have succesfully deleted the classroom."
end
private
def set_classroom
#classroom = Classroom.find(params[:id])
#school = School.find(params[:school_id])
authorize #classroom
end
def classroom_params
params.require(:classroom).permit(:teacher_id, :name, :code)
end
end
Pins_controller
class PinsController < ApplicationController
before_action :set_pin, only: [:show, :edit, :update, :destroy]
respond_to :html
def show
respond_with(#pin)
end
def new
#pin = Pin.new
#emotions = Emotion.all
#causes = Cause.all
#school = School.find(params[:school])
#classrooms = #school.classrooms
respond_with(#pin)
authorize #pin
end
def edit
end
def create
code = params[:pin][:code]
#classroom = Classroom.where('code LIKE ?', code).first
unless #classroom
flash[:error] = "Classroom code incorrect"
#emotions = Emotion.all
#causes = Cause.all
render :new
else
params[:pin][:classroom_id] = #classroom.id
#pin = Pin.new(pin_params)
#pin.save
params[:pin][:cause_ids].each do |cause_id|
#cause = Cause.find(cause_id)
#pin.causes << #cause
end
params[:pin][:emotion_ids].each do |emotion_id|
#emotion = Emotion.find(emotion_id)
#pin.emotions << #emotion
end
if #pin.save
redirect_to signout_path and return
end
respond_with(#pin)
authorize #pin
end
end
def update
#pin.update(pin_params)
respond_with(#pin)
authorize #pin
end
def destroy
#pin.destroy
respond_with(#pin)
authorize #pin
end
private
def set_pin
#pin = Pin.find(params[:id])
authorize #pin
end
def pin_params
params.require(:pin).permit(:user_id, :question, :question1, :question2,
:question3, :question4, :question5, :classroom_id, :sad,
:happy, :mad, :brave, :embarrassed, :sorry, :frustrated,
:silly, :left_out, :excited, :hurt, :jealous, :confused,
:proud, :other)
end
end
School.rb model
class School < ActiveRecord::Base
has_many :users
has_many :classrooms
validates_uniqueness_of :code
def classrooms
self.users.classrooms
end
def students
self.users.students
end
def teachers
self.users.teachers
end
def admins
self.users.admins
end
end
User Model
class User < ActiveRecord::Base
devise :timeoutable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :pins
has_many :reflections
has_many :classrooms, :foreign_key => :teacher_id
belongs_to :school
validates :name, presence: true
validates :role, presence: true
# validates :school, presence: true
scope :students, -> { where(role: "student") }
scope :teachers, -> { where(role: "teacher")}
scope :teachers_and_admins, -> { where(:role => ["teacher", "admin"]) }
scope :admins, -> { where(role: "admin" ) }
scope :online, lambda{ where("updated_at > ?", 15.days.ago) }
def online?
updated_at > 15.days.ago
end
def admin?
role == "admin"
end
def teacher?
role == "teacher"
end
def student?
role == "user" || role == "student"
end
def superadmin?
role == "superadmin"
end
def admin_of_school?(school)
self.admin? && self.school == school
end
def teacher_of_school?(school)
self.teacher? && self.school == school
end
def admin_or_teacher_of_school?(school)
self.admin_of_school?(school) || self.teacher_of_school?(school)
end
end
classroom model
class Classroom < ActiveRecord::Base
belongs_to :school
belongs_to :teacher, :class_name => "User"
has_and_belongs_to_many :users
has_many :pins
has_many :reflections
validates_presence_of :school
validates_presence_of :teacher
validates :code, :uniqueness => { :scope => :school_id }
end
I found a similar answer here: Rails only give records that "belong_to"
But it didn't help me understand how to limit the drop down list to only Classrooms of the current school.
Any ideas on how to handle his situation?
You already select the school in the controller, so just use it in the view, instead of doing a Classroom.all do a #school.classrooms
<%= select_tag "pin[code]",
options_from_collection_for_select(#school.classrooms, "code", "code")
%>
This part isn't an answer, but improvements and fixes to your code
First the User model can stay as it is
class User < ActiveRecord::Base
#devise place holder
# assocciations
has_many :pins
has_many :reflections
has_many :classrooms, foreign_key: :teacher_id
belongs_to :school
#validations place holder
#scopes
scope :students, -> { where(role: "student") }
scope :teachers, -> { where(role: "teacher")}
scope :admins, -> { where(role: "admin" ) }
scope :teachers_and_admins, -> { teachers.admins }
scope :online, -> { where("updated_at > ?", 15.days.ago) }
# some check methods
end
classroom class
class Classroom < ActiveRecord::Base
belongs_to :school
belongs_to :teacher, ->{ User.teachers }, class_name: 'User'
has_many :pins
has_many :reflections
end
school class
class School < ActiveRecord::Base
has_many :users
has_many :admins, ->{ User.admins }, class: User
has_many :students, ->{ User.students }, class: User
has_many :teachers, ->{ User.teachers }, class: User
has_many :classrooms
end
Now the controllers
You need to clean up the duplications, so here's an example from ur pins#new action
def new
#pin = Pin.new
prepare_lookups
respond_with(#pin)
authorize #pin
end
def prepare_lookups
#emotions = Emotion.all
#causes = Cause.all
#school = School.find(params[:school])
#classrooms = #school.classrooms
end
I removed the common code to a separate method, and call it whenever i need, of course you can add or remove from that method according to your needs.
Anyways, I think you should read more about active record assocciations and the conventions in writing controller actions
I'm creating a ticket booking app for my sample project using Ruby on Rails 4.1. Three are three models - Events, Tickets and Bookings. Events have many tickets and bookings. Tickets have many bookings and they belong to events. Bookings belongs to events and tickets.
Here's the ticket model:
class Ticket < ActiveRecord::Base
belongs_to :event
has_many :bookings
belongs_to :user
validates :ticket_name, :terms_conditions, presence: true
validates_date :booking_start_date, on: :create, on_or_after: :today
validates_date :booking_end_date, after: :booking_start_date
validates :ticket_price, presence: true, numericality: true
validates :ticket_quantity, :minimum_quantity, :maximum_quantity, presence: true, numericality: { only_integer: true }
before_create :check_start_date
before_update :check_start_date
def check_start_date
if (self.booking_start_date >= DateTime.now) && (self.booking_end_date != DateTime.now)
self.status = 'Open'
else
self.status = 'Closed'
end
end
def maximum_tickets_allowed
(1..maximum_quantity.to_i).to_a
end
end
The bookings model:
class Booking < ActiveRecord::Base
belongs_to :event
belongs_to :ticket
has_many :charges
validates :buyer_name, presence: true
validates :order_quantity, presence: true, numericality: { only_integer: true }
validates :email, presence: true, format: { with: /\A[^#\s]+#([^#.\s]+\.)+[^#.\s]+\z/ }
def total_amount
ticket.ticket_price.to_i * order_quantity.to_i
end
def check_ticket_count
count = ticket.ticket_quantity.to_i - order_quantity.to_i
ticket.update_attribute(:ticket_quantity, count)
end
end
The bookings controller:
class BookingsController < ApplicationController
before_action :authenticate_user!, only: [:index, :destroy]
def index
#event = Event.find(params[:event_id])
##bookings = #event.bookings.all
#bookings = #event.bookings.paginate(page: params[:page], per_page: 10)
end
def new
#event = Event.find(params[:event_id])
#ticket = #event.tickets.find(params[:ticket_id])
#booking = Booking.new
end
def create
#event = Event.find(params[:event_id])
#ticket = #event.tickets.find(params[:ticket_id])
#booking = #event.bookings.create(booking_params)
#booking.ticket = #ticket
Stripe.api_key = Rails.configuration.stripe[:secret_key]
#token = params[:stripeToken]
#amount = #booking.total_amount
begin
customer = Stripe::Customer.create(
:email => #booking.email,
:card => params[:stripeToken]
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:currency => "usd",
#:card => token
)
flash[:notice] = "Thanks for the order"
rescue Stripe::CardError => e
flash[:danger] = e.message
end
if #booking.save
BookingMailer.booking_confirmation_user(#booking).deliver
flash[:notice] = "You've successfully booked the tickets!"
redirect_to [#event, #booking]
else
render 'new'
end
end
def show
#event = Event.find(params[:event_id])
#booking = #event.bookings.find(params[:id])
end
def destroy
#event = Event.find(params[:event_id])
#booking = #event.bookings.find(params[:id])
#booking.destroy
redirect_to event_bookings_path
end
private
def booking_params
params.require(:booking).permit(:buyer_name, :email, :mobile, :address, :order_quantity, :ticket_id)
end
end
The check_ticket_count method in Booking.rb works fine as long as I don't an add after_create :check_ticket_count method. The moment I add that after_create method, the app throws the "undefined method `ticket_quantity' for nil:NilClass" error. How to get past this?
Looks like you should first associate ticket with booking, and only then create booking.
#booking = #event.bookings.new(booking_params)
#booking.ticket = #ticket
#booking.save
Hopefully you will ask questions with less code next time.
I am working on a project, i need to have different type of users, some will be admins and others will be just users, i have a model for all users, and i have a model "login" where i would store an ecrypted password. The problem is, i create the user, and it saves on the database, but the model where the password should be stored is empty...
I tried to make assossiation on both user and login controller, and both models as well.
Ask me for the code you need to see and ill post it here.
this is my user.rb code
class User < ActiveRecord::Base
has_and_belongs_to_many :requisitions
has_many :historics
has_one :login
belongs_to :rank
belongs_to :sub_unit
belongs_to :user_type
#attr_accessor :password, :password_confirmation, :salt, :encrypted_password
validates :nim, :posto_id, :apelido, :nome, :telefone, :sub_un_id, :tipo_util_id, presence: true
validates :email, format: { with: /#/ }
validates :nim, uniqueness: true
validates :password, :confirmation => true
validates_length_of :password, :in => 6..20, :on => :create
# Encrypting calls for new user's password
before_save :encrypt_password
after_save :clear_password
# Authentication process for user's login
def match_password(login_password = '')
encrypted_password == BCrypt::Engine.hash_secret(login_password, salt)
end
def self.authenticate(nim_as_login = '', login_password = '')
user = User.find_by_nim(nim_as_login)
if user && user.match_password(login_password)
return user
else
return false
end
end
# Encrypting process for new user's password
private
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.encrypted_password = BCrypt::Engine.hash_secret(password, salt)
end
end
private
def clear_password
self.password = nil
end
end
This is my login.rb code
class Login < ActiveRecord::Base
belongs_to :user
attr_accessor :password, :password_confirmation, :salt, :encrypted_password
#validating fields
validates :user_id, :password, presence: true
validates :password, confirmation: true
validates :password, length: { in: 6..30 }
# Encrypting calls for new user's password
before_save :encrypt_password
after_save :clear_password
# Authentication process for user's login
def match_password(login_password = '')
encrypted_password == BCrypt::Engine.hash_secret(login_password, salt)
end
def self.authenticate(nim_as_login = '', login_password = '')
user = User.find_by_nim(nim_as_login)
login = Login.find_by_user_id(user.id)
if login && login.match_password(login_password)
return login
else
return false
end
end
# Encrypting process for new user's password
private
def encrypt_password
if password.present?
self.salt = BCrypt::Engine.generate_salt
self.encrypted_password = BCrypt::Engine.hash_secret(password, salt)
end
end
private
def clear_password
self.password = nil
end
end
this is my users controller code
class UsersController < ApplicationController
before_filter :save_login_state, only: [:new, :create]
def new
#user = User.new
#ranks = Rank.all
#types = UserType.all
#unit = SubUnit.all
#logins = Login.all
#page_title = "LoginUser | Novo"
end
def create
#user = User.new(user_params, login_params)
#LoginsController(:password, :password_confirmation)
#ranks = Rank.all
#types = UserType.all
#unit = SubUnit.all
#logins = Login.all
if #user.save
flash[:notice] = "Bem vindo #{#user.apelido}, sua conta foi criada com sucesso!"
redirect_to sessions_path
else
#user.errors.full_messages.each do |e|
if e == "Nim has already been taken"
flash.now[:error] = "Este Utilizador já está registado!"
else
flash.now[:error] = "Corrija os campos do formulário!"
end
end
render 'new'
end
end
def show
#user = User.find(params[:id])
#ranks = Rank.all
#types = UserType.all
#unit = SubUnit.all
#logins = Login.all
end
private
def user_params
params.require(:user).permit(:id, :nim, :posto_id, :apelido, :nome, :telefone, :telemovel, :email, :sub_un_id, :tipo_util_id)
end
def login_params
params.require(Login).permit(:password, :password_confirmation, :user_id )
end
end
this is my login controller
class LoginsController < ApplicationController
def create
#login = Login.all
end
end
this is my sessions controller
class SessionsController < ApplicationController
before_filter :authenticate_user, only: [:home, :profile, :setting]
before_filter :save_login_state, only: [:login, :login_attempt]
def login
#page_title = "LoginUser | Entrar"
end
def login_attempt
authorized_user = User.authenticate(params[:nim_as_login], params[:login_password])
if authorized_user
session[:user_id] = authorized_user.id
flash[:notice] = "Benvindo de volta #{authorized_user.apelido}"
redirect_to user_path
else
flash.now[:error] = "Email ou palavra passe inválida!"
render 'sessions/login'
end
end
def home
end
def profile
end
def setting
end
def logout
session[:user_id] = nil
redirect_to sessions_path
end
end
My question is, can i store the encrypted password and the user on different tables?
I've been developing a web app in Rails 4, and used the Michael Hartl tutorial as the backbone for it, but using Devise as well. After building some of it, I went back and changed the Status model to be polymorphic in order to allow users, venues and groups to be able to post a status. I've got a certain way with this so far, but am now stuck. When trying to view a user's profile, I am currently getting this error:
NoMethodError in Users#show
Showing /home/app_name/app/views/users/show.html.erb where line #6 raised:
undefined method `avatar' for #<ActiveRecord::AssociationRelation::ActiveRecord_AssociationRelation_Status:0xb6859fd8>
Extracted source (around line #6):
<aside class="col-md-4">
<section>
<h1>
<%= image_tag #user.avatar.url(:thumb) %>
<%= #user.name %>
</h1>
</section>
The user definitely has an avatar, and as far as I can work out it seems to be having trouble working out the association through the polynomial relationship. It's on the user#show page, so presumably I shouldn't change "user" to "statusable", I have already tried this anyway, but don't understand where the problem is coming from. The profile shows a feed of statuses at present and an avatar for the user in each.
Any help would be greatly appreciated.
EDIT
Here are my user and status models:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :omniauth_providers => [:facebook, :twitter, :google_oauth2]
before_save { self.username = username.downcase }
validates :first_name, presence: true,
length: { maximum: 25 }
validates :last_name, presence: true,
length: { maximum: 25 }
VALID_USERNAME_REGEX = /\A[\w+\-._]+\z/i
validates :username, presence: true,
length: { maximum: 20 },
format: { with: VALID_USERNAME_REGEX },
uniqueness: { case_sensitive: false }
validates :email, presence: true
validates :password, presence: true
validates :birthday, presence: true
validates :gender, presence: true
validates :postcode, presence: true
validates_format_of :postcode, :with => /\A([A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]|[A-HK-Y][0-9]([0-9]|[ABEHMNPRV-Y]))|[0-9][A-HJKS-UW])\s?[0-9][ABD-HJLNP-UW-Z]{2}|(GIR\ 0AA)|(SAN\ TA1)|(BFPO\ (C\/O\ )?[0-9]{1,4})|((ASCN|BBND|[BFS]IQQ|PCRN|STHL|TDCU|TKCA)\ 1ZZ))\z/i, :message => "invalid postcode"
has_attached_file :avatar, :styles => { :medium => "300x300", :thumb => "100x100", :micro => "30x30", :large => "500x500>" }, :default_url => "/images/:style/missing.jpg"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
geocoded_by :address
after_validation :geocode
self.per_page = 20
def address
[town, postcode, country].compact.join(', ')
end
def feed
Status.from_users_favourited_by(self)
end
def self.all_except(user)
where.not(id: user)
end
end
def name
first_name + " " + last_name
end
has_many :statuses, as: :statusable, dependent: :destroy
accepts_nested_attributes_for :statuses
end
class Status < ActiveRecord::Base
before_create :set_latlong
belongs_to :statusable, polymorphic: true
default_scope -> { order('created_at DESC') }
validates :content, presence: true, length: { maximum: 140 }
validates :statusable_id, presence: true
validates :statusable_type, presence: true
def set_latlong
self.latitude = statusable.latitude
self.longitude = statusable.longitude
end
def self.from_users_favourited_by(user)
favourited_user_ids = "SELECT favourite_id FROM favouriteusers
WHERE favourited_id = :user_id"
where("statusable_id IN (#{favourited_user_ids}) OR statusable_id = :user_id", user_id: user.id)
end
end
Here is the Status controller:
class StatusController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
before_filter :load_statusable
def new
#status = Status.new(status_params)
end
def create
#statusable = load_statusable
#status = #statusable.statuses.build(status_params)
if #status.save
flash[:success] = "Status created!"
redirect_to root_url
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#status.destroy
redirect_to root_url
end
private
def status_params
params.require(:status).permit(:content)
end
def load_statusable
resource, id = request.path.split('/')[1, 2]
resource_name = resource.singularize.classify
if resource_name = "user"
#statusable = current_user
else
#statusable = resource_name.constantize.find(id)
end
end
end
And finally the status schema:
create_table "statuses", force: true do |t|
t.string "content"
t.integer "statusable_id"
t.string "statusable_type"
t.float "latitude"
t.float "longitude"
t.datetime "created_at"
t.datetime "updated_at"
end
I've taken a few bits out to make it easier to read, but hopefully have left in the bits needed.
EDIT
class UsersController < ApplicationController
before_filter :authenticate_user!, only: [:index, :show,:edit,
:update, :destroy, :favourite_users, :favourited_users]
def index
#users = User.all_except(current_user).paginate(page: params[:page]).order('created_at DESC')
end
def show
#user = User.find(params[:id])
#user = #user.statuses.paginate(page: params[:page])
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
# Handle a successful update.
else
render 'edit'
end
end
def create
#user = User.create( user_params )
end
def favourite_users
#title = "Favourites"
#user = User.find(params[:id])
#users = #user.favourite_users.paginate(page: params[:page])
render 'show_favourite'
end
def favourited_users
#title = "Listed as a Favourite by"
#user = User.find(params[:id])
#users = #user.favourited_users.paginate(page: params[:page])
render 'show_favourite'
end
Well, it sure looks like #user does not contain a User but rather a ActiveRecord::AssociationRelation::ActiveRecord_AssociationRelation_Status. No wonder it doesn't have an avatar method! Check your controller code to see what #user is actually set to. If you can't make sense of what's going on, post the controller here. The User model and schema will probably be needed, too.
I am working on the unread folder for my inbox messaging system. I received a undefined method `unread_messages' when going to /users/1/messages?mailbox=unread and it's pointing to the messages controller. However I have unread_messages defined in the user.rb so that it shows unread messages in the inbox, so I'm assuming the code should also work for the unread folder. Any help would be appreciated.
user.rb:
class User < ActiveRecord::Base
has_secure_password
attr_accessible :role, :age, :age_end, :password_confirmation, :about_me, :feet, :inches, :password, :birthday, :career, :children, :education, :email, :ethnicity, :gender, :height, :name, :password_digest, :politics, :religion, :sexuality, :user_drink, :user_smoke, :username, :zip_code
validates_uniqueness_of :email
validates_format_of :email, with: /^[-a-z0-9_+\.]+\#([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i
validates_presence_of :password, :on => :create
has_many :galleries
has_many :photos, :through => :galleries
before_create { generate_token(:auth_token) }
ROLES = %w[admin user guest banned]
# models/user.rb
after_create :setup_gallery
def received_messages
Message.received_by(self)
end
def unread_messages?
unread_message_count > 0 ? true : false
end
def sent_messages
Message.sent_by(self)
end
# Returns the number of unread messages for this user
def unread_message_count
eval 'messages.count(:conditions => ["recipient_id = ? AND read_at IS NULL", self.user_id])'
end
def to_s; username
end
def has_role?(role_name)
role.present? && role.to_sym == role_name.to_sym
end
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
private
def setup_gallery
self.galleries << Gallery.create
end
end
messages_controller:
def index
if params[:mailbox] == "sent"
#messages = #user.sent_messages
elsif params[:mailbox] == "inbox"
#messages = #user.received_messages
#elsif params[:mailbox] == "archieved"
# #messages = #user.archived_messages
end
if params[:mailbox] == "unread"
#messages = #user.unread_messages
end
end
def new
#message = Message.new
if params[:reply_to]
#reply_to = User.find_by_user_id(params[:reply_to])
unless #reply_to.nil?
#message.recipient_id = #reply_to.user_id
end
end
end
def create
#message = Message.new(params[:message])
#message.sender_id = #user.id
if #message.save
flash[:notice] = "Message has been sent"
redirect_to user_messages_path(current_user, :mailbox=>:inbox)
else
render :action => :new
end
end
def show
#message = Message.find(params[:id])
#message.readingmessage if #message.recipient == current_user
end
def destroy
#message = Message.find(params[:id])
#message.destroy
flash[:notice] = "Successfully deleted message."
redirect_to user_messages_path(#user, #messages)
end
def delete_multiple
if params[:delete]
params[:delete].each { |id|
#message = Message.find(id)
#message.mark_message_deleted(#message.id,#user.id) unless #message.nil?
}
flash[:notice] = "Messages deleted"
end
redirect_to user_messages_path(#user, #messages)
end
private
def set_user
#user = current_user
end
end
message.rb:
attr_accessible :subject, :body, :sender_id, :recipient_id, :read_at,:sender_deleted,:recipient_deleted
validates_presence_of :subject, :message => "Please enter message title"
belongs_to :sender,
:class_name => 'User',
:foreign_key => 'sender_id'
belongs_to :recipient,
:class_name => 'User',
:foreign_key => 'recipient_id'
# marks a message as deleted by either the sender or the recipient, which ever the user that was passed is.
# When both sender and recipient marks it deleted, it is destroyed.
def mark_message_deleted(id,user_id)
self.sender_deleted = true if self.sender_id == user_id
self.recipient_deleted = true if self.recipient_id == user_id
(self.sender_deleted && self.recipient_deleted) ? self.destroy : self.save!
end
# Read message and if it is read by recipient then mark it is read
def readingmessage
self.read_at ||= Time.now
save
end
# Based on if a message has been read by it's recipient returns true or false.
def read?
self.read_at.nil? ? false : true
end
def self.received_by(user)
where(:recipient_id => user.id)
end
def self.not_recipient_deleted
where("recipient_deleted = ?", false)
end
def self.sent_by(user)
Message.where(:sender_id => user.id)
end
end
First of all, you've defined unread_messages? method, not unread_messages
Secondly, you should not use ? : in unread_messages? method:
unread_messages_count > 0
is enough.
You should define unread_messages method in your User model. I can't show the inner code in this method because I don't know your models relations.
You defined
unread_messages? # note the question mark
Then referred to it as
#user.unread_messages # no question mark
You probably want a method
def unread_messages ## which holds the actual unread messages and does not return boolean