I am beginner in rails. I am building a forum application. It has a messaging facility where users can privately message(not real time. it is like a notification.) to other users. I already achived this. But I want to add blocking feature where user can block other users to avoid getting messages from those particular users. How can i do this? I appreciate your answers. Thanks in advance.
Here is my code.
Notifications Controller
class NotificationsController < ApplicationController
layout "posts"
after_action :read_message, only: [:index]
def index
#notifications = Notification.where(:recipient_id => session[:registered_id]).order("created_at DESC")
end
def new
#user = User.find(session[:user_id])
#notification = #user.notifications.new
end
def create
#user = User.find(session[:user_id])
#notification = #user.notifications.new notification_params
if #notification.save
redirect_to(:controller => "posts", :action => "index")
else
render "new"
end
end
def sent_messages
#notifications = Notification.where(:user_id => session[:user_id]).order("created_at DESC")
end
private
def notification_params
params.require(:notification).permit(:message, :user_id, :recipient_id, :status)
end
def read_message
#notifications = Notification.where(:recipient_id => session[:registered_id]).order("created_at DESC")
#notifications.read_all
end
end
Notification model
class Notification < ActiveRecord::Base
belongs_to :user
validates :message, :presence => true
validates :recipient_id, :presence => true
def self.read_all
Notification.all.update_all(status: true)
end
end
Notification migration
class CreateNotifications < ActiveRecord::Migration
def change
create_table :notifications do |t|
t.text :message
t.integer :user_id
t.string :recipient_id
t.boolean :read, default: false
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
** Notifications#index**
<div id = "messages_wrapper">
<% #notifications.each do |notification| %>
<div class="<%= notification.status ? 'message_wrapper_read' : 'message_wrapper_unread' %>">
<p><%= notification.message %></p>
<% if notification.user_id %>
<p class = "message_details">from <span><%= notification.user.registered_id %></span></p>
<% end %>
</div>
<% end %>
</div>
For the blocked users concept, you can add a custom attribute in user model called as blocked_users which is stored as an array in the db.
For postgresql you can use array datatypes.
In you notification.rb file,
#Validations,
validate :is_not_blocked_by_recipient
def is_not_blocked_by_recipient
#Check if the user is blocked or no, and write the logic
#self.errors.add()
end
It should work
Related
I am trying to pull up an edit page for my simple system.
Here's my controller for student look like:
class StudentsController < ApplicationController
def index
#students = Student.newest_first
end
def show
#student = Student.find(params[:student_id])
end
def new
#student = Student.new
end
def create
#student = Student.new(student_params_create)
if #student.save
flash[:notice] = "Student record created successfully"
redirect_to(students_path)
else
render('new')
end
end
def edit
#student = Student.find(params[:student_id])
end
def update
#student = Student.find(params[:student_id])
if #student.update_attributes(student_params_update)
flash[:notice] = "Students updated"
redirect_to(students_path)
else
render('edit')
end
end
def delete
#student = Student.find(params[:student_id])
end
def destroy
#student = Student.find(params[:student_id])
#student.destroy
end
private
def student_params_create
params.require(:student).permit(:first_name, :last_name, :birthday, :email, :subjects, :username, :password)
end
def student_params_update
params.require(:student).permit(:first_name, :last_name, :birthday, :email, :subjects)
end
end
And I am trying to here's my index page where I can click edit on my student.
<% #students.each do |student|%>
<%= student.first_name %>
<%= student.student_id %>
<%= link_to("Show", student_path(student), :class => 'action show') %>
<%= link_to("Edit", edit_student_path(student), :class => 'action edit') %>
<%= link_to("Delete", delete_student_path(student), :class => 'action delete') %>
<% end %>
For my form edit, I got the ff:
<h2>Update Subject</h2>
<%= form_for(#student) do |f| %>
<%= f.text_field(:first_name) %>
<%= f.datetime_select(:created_at, :order => [:month, :day, :year]) %><
<%= f.submit("Update Subject") %>
<% end %>
When I tried to go to my resource for students: http://localhost:3000/students/
And click on one of my students I got the ff: error:
Any idea what am I doing wrong????
EDIT:
I set the 'id' to false during migration.
def up
create_table :students, :id => false do |t|
t.integer "student_id",:primary_key => true
t.string "first_name", :limit => 25
t.string "last_name", :limit => 50
t.string "email", :default => ' ', :null => false
t.string "birthday"
t.string "subjects"
t.string "teachers"
t.string "username", :limit => 25
t.string "password_digest"
t.timestamps
end
reversible do |dir|
dir.up { execute "ALTER TABLE students AUTO_INCREMENT = 1000" }
end
# execute "CREATE SEQUENCE students_student_id_seq OWNED BY students.student_id INCREMENT BY 1 START WITH 1001"
end
def down
drop_table :students
# execute "DELETE SEQUENCE students_student_id_seq"
end
Routes:
resources :students do
member do
get :delete
end
end
UPDATE
You could need to update your routes.rb with
resources :students, param: :student_id
And your Student model with
self.primary_key = :student_id
NOTE that you're having the ActiveRecord::RecordNotFound exception because find method throws exception when it can't actually find the record needed. Try to use find_by_id method instead. It will just return nil in that situation. You'll be able to handle it easily and your app will not crash here. So you could edit your show action like this
def show
#student = Student.find_by_id(params[:student_id])
end
I am new to Rails and currently trying to add a patient to an existing dentist appointment. I am having difficulty setting up my views and controllers properly. How can I properly accomplish this?
Note: With the way I have set things up, I can create an appointment and tie it to a dentist. Of course, the patient_id is missing.
Models:
class Dentist < ActiveRecord::Base
has_many :appointments
has_many :patients, :through => :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :dentists
belongs_to :patients
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :dentists, :through => :appointments
end
Schema:
ActiveRecord::Schema.define(version: 20151107052115) do
create_table "appointments", force: :cascade do |t|
t.integer "dentist_id"
t.integer "patient_id"
t.datetime "appt_date"
end
create_table "dentists", force: :cascade do |t|
t.string "name"
end
create_table "patients", force: :cascade do |t|
t.string "name"
end
end
Routes:
Rails.application.routes.draw do
concern :commentable do
resources :appointments
end
resources :dentists, concerns: :commentable
resources :patients, concerns: :commentable
end
Dentists Controller:
class DentistsController < ApplicationController
def new
#dentist = Dentist.new
end
def create
#dentist = Dentist.new(dentist_params)
if #dentist.save
redirect_to dentists_path
else
render :new
end
end
...
end
Appointments Controller:
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find(params[:dentist_id])
#appointment = #dentist.appointments.new(appt_params)
if Appointment.exists?(:appt_date => #appointment.appt_date)
render :new
else
#appointment.save
redirect_to dentist_path(#dentist)
end
end
...
end
Patients Controller:
TBD
Dentists View (Show):
<p><%= #dentist.name %> DDS</p>
<% if #dentist.appointments.any? %>
<% #dentist.appointments.each do |appt| %>
<ul>
<li><%= appt.appt_date %></li>
<p><%= link_to "Edit", edit_dentist_appointment_path(#dentist, appt) %> |
<%= link_to 'Delete', dentist_appointment_path(#dentist, appt), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %> |
<%= link_to 'Add Patient', new_patient_path %></p>
</ul>
<% end %>
<% else %>
<p>There are currently no appointments scheduled</p>
<% end %>
<p><%= link_to 'Delete Dentist', dentist_path(#dentist), :method => :delete,
data: {:confirm => 'Are you sure you want to delete this record?'} %></p>
<p><%= link_to 'Create an appointment', new_dentist_appointment_path(#dentist) %></p>
<p><%= link_to 'Return to list', root_path %></p>
I am new to Rails
Welcome!
You need to change your belongs_to references to be singular:
class Appointment < ActiveRecord::Base
belongs_to :dentist
belongs_to :patient
end
--
Because I can't see where you're trying to achieve this functionality, I'll show you what I'd do (using the appointment#edit action):
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def edit
#appointment = Appointment.find params[:id]
end
def update
#appointment = Appointment.find params[:id]
#appointment.save appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/edit.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
--
If you're trying to set the patient from your appointments#create method, you'll be best doing this:
#app/controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
def new
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new
end
def create
#dentist = Dentist.find params[:id]
#appointment = #dentist.appointments.new appointment_params
end
private
def appointment_params
params.require(:appointment).permit(:dentist_id, :patient_id, :appt_date)
end
end
#app/views/appointments/new.html.erb
<%= form_for #appointment do |f| %>
<%= f.collection_select :patient_id, Patient.all, :id, :name %>
<%= f.submit %>
<% end %>
I think what you are asking, is can you create an appointment through both the Dentist model and the Patient model at the same time eg. #dentist.#patient.apointment.new
you cannot do that. Based on the relationships you have set up, you will either want to create the appt from the Dentist, like you have now, and pass in the patient ID as an attribute, or vice-versa. OR, create through your Appointment model eg. Appointment.new(dentist: #dentist, patient: #patient, ...)
I'm working on a basic private messaging system for my application. I started with this tutorial on Medium.
I discovered an issue though. It returns all conversations, not just the ones that the current user is actually involved in. The view only shows the ones you're in but all the records are there. Obviously that is horrible if you have more than a handful of users.
I've tweaked the controller to what I thought was the solution but I'm still getting all records so I'm thinking the issue is in the model.
Conversations Controller
class ConversationsController < ApplicationController
before_action :authenticate_user!
before_action :set_conversation, only: [:destroy]
def index
#user = current_user
#conversations = Conversation.where(:sender_id == #user.id || :recipient_id == #user.id)
end
def create
if Conversation.between(params[:sender_id],params[:recipient_id])
.present?
#conversation = Conversation.between(params[:sender_id],
params[:recipient_id]).first
else
#conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
Conversation Model
class Conversation < ActiveRecord::Base
belongs_to :sender, :foreign_key => :sender_id, class_name: 'User'
belongs_to :recipient, :foreign_key => :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :between, -> (sender_id,recipient_id) do
where(sender_id: [sender_id,recipient_id], recipient_id: [sender_id,recipient_id])
end
def unread_message_nr_for(user_id)
messages.where('messages.read = ?', false).where('messages.user_id != ?', user_id).count
end
end
Conversation View
<div class="ibox-content no-side-borders">
<% #conversations.each do |conversation| %>
<div class="conversation-member">
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
<% recipient = User.find(conversation.recipient_id) %>
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
<span class="<%= 'current-conversation' if (params['conversation_id'].to_i == conversation.id) %>">
<% if recipient.avatar.present? %>
<%= image_tag(recipient.avatar_url(:navigation), class: "img-circle m-r-xs") %>
<% end %>
<%= link_to recipient.first_name + " " + recipient.last_name, conversation_messages_path(conversation)%>
</span>
<% if conversation.unread_message_nr_for(current_user.id) > 0 %>
<span class="badge-inline">
<%= conversation.unread_message_nr_for(current_user.id) %>
</span>
<% end %>
<% end %>
</div>
<% end %>
</div>
Conversations Schema
create_table "conversations", force: :cascade do |t|
t.integer "sender_id"
t.integer "recipient_id"
t.datetime "created_at"
t.datetime "updated_at"
end
How can I get only the conversations where the current user is the sender_id or the recipient_id?
You can't use ruby in the where clause. See the guides for more info
Conversation.where("sender_id = ? OR recipient_id = ?", #user.id, #user.id)
I have a Rails 4.2 app which has 'Rooms', 'Bookings' and 'Extras'.
When making a booking it is for a room e.g. website.com/rooms/1/bookings/1
I have extras which I want to be associated with the booking for that room via check-boxes.
How can this be implemented? I've been reading about has_many :foo, :through => :bar associations but I'm not sure if that's the way to go.
The relevant code looks like this:
<!-- app\views\bookings\_form.html.erb -->
<%= form_for([#room, #booking]) do |f| %>
<p>
<%= f.label 'Select Customer:' %>
<%= f.collection_select :user_id, User.all, :id, :customer_name %>
</p>
<p>
<%= f.label 'start_time', 'Start Date and Time:' %>
<%= f.datetime_select :start_time, { minute_step: 15 } %>
</p>
<p>
<%= f.label 'length', 'Length of booking in hours:' %>
<%= f.number_field 'length', min: 1 %>
</p>
<p>
<%= f.label 'Room Price:' %>
<%= number_to_currency #room.price, unit: "£" %>
</p>
<p>
<%= f.label 'Extras:' %>
<%= f.collection_check_boxes :extra_ids, Extra.all, :id, :extra_info %>
</p>
<%= f.submit 'Submit' %>
<% end %>
# app\models\booking.rb
class Booking < ActiveRecord::Base
belongs_to :room
belongs_to :user
has_many :additions
has_many :extras, :through => :additions
end
# app\models\extra.rb
class Extra < ActiveRecord::Base
belongs_to :extracat
has_many :additions
has_many :bookings, :through => :additions
def extra_info
"#{name}"
end
end
# This model is for the has_many through testing I tried
# app\models\addition.rb
class Addition < ActiveRecord::Base
belongs_to :booking
belongs_to :extra
end
# Relevant section of schema
create_table "additions", force: :cascade do |t|
t.integer "booking_id"
t.integer "extra_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "bookings", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.integer "length"
t.integer "room_id"
t.integer "user_id"
t.integer "extra_id"
end
EDIT - The section within the bookings show page.
# app\views\bookings\show.html.erb
<% #booking.extras.each do |e| %>
<%= e.name %>,
<% end %>
EDIT - Adding bookings controller
class BookingsController < ApplicationController
respond_to :html, :xml, :json
before_action :find_room
def index
#bookings = Booking.where("room_id = ? AND end_time >= ?", #room.id, Time.now).order(:start_time)
respond_with #bookings
end
def new
#booking = Booking.new(room_id: #room.id)
end
def create
#booking = Booking.new(params[:booking].permit(:room_id, :start_time, :length))
#booking.room = #room
if #booking.save
redirect_to room_bookings_path(#room, method: :get)
else
render 'new'
end
end
def show
#booking = Booking.find(params[:id])
end
def destroy
#booking = Booking.find(params[:id]).destroy
if #booking.destroy
flash[:notice] = "Booking: #{#booking.start_time.strftime('%e %b %Y %H:%M%p')} to #{#booking.end_time.strftime('%e %b %Y %H:%M%p')} deleted"
redirect_to room_bookings_path(#room)
else
render 'index'
end
end
def edit
#booking = Booking.find(params[:id])
end
def update
#booking = Booking.find(params[:id])
# #booking.room = #room
if #booking.update(params[:booking].permit(:room_id, :start_time, :length))
flash[:notice] = 'Your booking was updated succesfully'
if request.xhr?
render json: {status: :success}.to_json
else
redirect_to resource_bookings_path(#room)
end
else
render 'edit'
end
end
private
def save booking
if #booking.save
flash[:notice] = 'booking added'
redirect_to room_booking_path(#room, #booking)
else
render 'new'
end
end
def find_room
if params[:room_id]
#room = Room.find_by_id(params[:room_id])
end
end
def booking_params
params.require(:booking).permit(:user_id, :extra_id)
end
end
How is it possible to associate the extras with a booking? As so far they are not being saved with the booking into the database. Is this a controller issue?
You're not permitting the parameters correctly - the name is extra_ids. In addition since the parameter is an array you need to permit it like so:
params.require(:booking).permit(:room_id, :start_time, :length, :extra_ids => [])
Personally I recommend setting action controller to raise an error when unpermitted parameters are encountered in development or tests - very easy otherwise to miss the log messages
I have two models, a User model and a Hair Model. I have created a form that successfully allows a user to submit and save their name, location in the user model and hair color in the hair model.
What I can't seem to work out is how to retrieve the user's hair color in my views?
Hair is currently a nested attribute of User in my routes and the current result I am being shown by the view is Rosie Ealing #<Hair:0x4d5e078>
Any help that can be offered really would be much appreciated.
User Model
class User < ActiveRecord::Base
has_one :hair, :dependent => :destroy
accepts_nested_attributes_for :hair
attr_accessible :location, :name, :hair_attributes
end
Hair Model
class Hair < ActiveRecord::Base
belongs_to :user
attr_accessible :color, :user_id
end
User Index
<% #user.each do |user| %>
<%= user.name %>
<%= user.location %>
<%= user.hair %>
<% end %>
User Model Migration
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.string :location
t.timestamps
end
end
end
Hair Model Migration
class CreateHairs < ActiveRecord::Migration
def change
create_table :hairs do |t|
t.string :color
t.integer :user_id
t.timestamps
end
end
end
User Controller
class UsersController < ApplicationController
def new
#user = User.new
#user.build_hair
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to users_path
else
render "user/new"
end
end
def index
#user = User.all
end
end
Try this:
<% #user.each do |user| %>
<%= user.name %>
<%= user.location %>
<%= user.hair.color %>
<% end %>
Your code is return a link to the object Hair, rather the the data contained in it.