From 4a62274eab44bfbe56e4e4d925a51360ce9e01e6 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 18 May 2015 16:30:14 +0300 Subject: [PATCH 01/13] Added notification system | week8 tasks --- app/controllers/application_controller.rb | 6 +++- app/controllers/notifications_controller.rb | 12 +++++++ app/models/challenge.rb | 9 ++++++ app/models/notification.rb | 31 +++++++++++++++++++ app/models/reply.rb | 15 +++++++++ app/models/task.rb | 9 ++++++ app/models/topic.rb | 6 ++++ app/views/layouts/application.html.haml | 1 + app/views/notifications/index.html.haml | 12 +++++++ config/routes.rb | 2 ++ .../20150518105230_create_notifications.rb | 23 ++++++++++++++ ...0150518121600_create_read_notifications.rb | 17 ++++++++++ 12 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 app/controllers/notifications_controller.rb create mode 100644 app/models/notification.rb create mode 100644 app/views/notifications/index.html.haml create mode 100644 db/migrate/20150518105230_create_notifications.rb create mode 100644 db/migrate/20150518121600_create_read_notifications.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c5388673..59007bd7 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base include CustomPaths helper CustomPaths - before_action :set_time_zone + before_action :set_time_zone, :load_notifications helper_method :can_edit?, :logged_in?, :admin? @@ -41,4 +41,8 @@ def require_admin def set_time_zone Time.zone = 'Sofia' end + + def load_notifications + @notifications = Notification.unread_for_user current_user.id + end end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb new file mode 100644 index 00000000..89549a08 --- /dev/null +++ b/app/controllers/notifications_controller.rb @@ -0,0 +1,12 @@ +class NotificationsController < ApplicationController + + def index + # @notifications = Notification.unread_for_user current_user.id + end + + def show + notification = Notification.find params[:id] + Notification.mark_read notification.id, current_user.id + redirect_to notification.source + end +end diff --git a/app/models/challenge.rb b/app/models/challenge.rb index 9dc3c9be..bd741c8d 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -3,6 +3,8 @@ class Challenge < ActiveRecord::Base validates :name, :description, presence: true + after_create :post_notification + class << self def in_chronological_order order('created_at ASC') @@ -16,4 +18,11 @@ def visible def closed? closes_at.past? end + + def post_notification + notification = Notification.new + notification.title = "Новo предизвикателство: #{name}" + notification.source = self + notification.save + end end diff --git a/app/models/notification.rb b/app/models/notification.rb new file mode 100644 index 00000000..ac4bd44c --- /dev/null +++ b/app/models/notification.rb @@ -0,0 +1,31 @@ +class Notification < ActiveRecord::Base + belongs_to :user + belongs_to :source, polymorphic: true + + def is_read_by?(user) + Notification.has_read_notification(id, user.id).first['count'].to_i > 0 + end + + # attr_protected :full_name, :faculty_number, :email, :admin + + # validates :password, confirmation: true, unless: -> { password.blank? } + + class << self + def unread_for_user(id) + t = Notification.arel_table + + Notification.where( + t[:user_id].eq(id). + or(t[:user_id].eq(nil)) + ).where('id NOT IN (SELECT notification_id FROM read_notifications WHERE user_id = ?)', id) + end + + def has_read_notification(notification_id, user_id) + self.connection.execute(sanitize_sql_array(["SELECT count(*) FROM read_notifications WHERE user_id = ? AND notification_id = ?", user_id, notification_id])) + end + + def mark_read(notification_id, user_id) + self.connection.execute(sanitize_sql_array(["INSERT INTO read_notifications (user_id, notification_id) VALUES (?, ?)", user_id, notification_id])) + end + end +end diff --git a/app/models/reply.rb b/app/models/reply.rb index 838536a9..c73d41fe 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,10 +1,13 @@ class Reply < Post belongs_to :topic + belongs_to :user attr_accessible :body validates :topic_id, presence: true + after_create :post_notification + def topic_title topic.title end @@ -13,4 +16,16 @@ def page_in_topic replies_before_this = topic.replies.where('id < ?', id).count replies_before_this / Reply.per_page + 1 end + + private + + def post_notification + topic.users.each do |user| + notification = Notification.new + notification.title = "Новo отговор в тема: #{topic_title}" + notification.source = topic + notification.user = user + notification.save + end + end end diff --git a/app/models/task.rb b/app/models/task.rb index 3fda9a47..0e8b42fd 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -8,6 +8,8 @@ class Task < ActiveRecord::Base scope :checked, -> { where checked: true } scope :in_chronological_order, -> { order 'created_at ASC' } + after_create :post_notification + class << self def visible in_chronological_order.where(hidden: false) @@ -45,4 +47,11 @@ def restrictions_must_be_valid_yaml rescue Psych::SyntaxError errors.add :restrictions, :invalid end + + def post_notification + notification = Notification.new + notification.title = "Нова задача: #{name}" + notification.source = self + notification.save + end end diff --git a/app/models/topic.rb b/app/models/topic.rb index e4ecb7dd..b62820ac 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1,5 +1,7 @@ class Topic < Post has_many :replies, -> { order 'created_at ASC' } + + has_many :users, :through => :replies attr_accessible :title, :body @@ -29,6 +31,10 @@ def last_poster User.find(last_poster_id) end + def participating_users + User + end + def last_post_at ActiveSupport::TimeZone['UTC'].parse(last_post_at_before_type_cast).in_time_zone end diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 5e249698..ecf06647 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -21,6 +21,7 @@ %ul - if user_signed_in? %li Здрасти, #{link_to current_user.name, edit_profile_path} + %li #{link_to 'Съобщения', notifications_path} (#{@notifications.count}) %li= link_to 'Изход', destroy_user_session_path - else %li= link_to 'Вход', new_user_session_path diff --git a/app/views/notifications/index.html.haml b/app/views/notifications/index.html.haml new file mode 100644 index 00000000..5e63a6c5 --- /dev/null +++ b/app/views/notifications/index.html.haml @@ -0,0 +1,12 @@ +%h1 Съобщения + +%p + Общо съобщения: + %strong= @notifications.count + + +%table + %tbody + - @notifications.each do |notification| + %tr + %td.name= link_to notification.title, notification diff --git a/config/routes.rb b/config/routes.rb index 1366c7d3..e4b557fc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,8 @@ resources :vouchers, only: %w(index new create) + resources :notifications, only: %w(index show) + resources :announcements, except: %w(destroy) resource :profile, only: %w(edit update) resource :leaderboard, only: :show diff --git a/db/migrate/20150518105230_create_notifications.rb b/db/migrate/20150518105230_create_notifications.rb new file mode 100644 index 00000000..f16fd622 --- /dev/null +++ b/db/migrate/20150518105230_create_notifications.rb @@ -0,0 +1,23 @@ +class CreateNotifications < ActiveRecord::Migration + include ForeignKeys + + def self.up + create_table(:notifications) do |t| + t.string :title, :null => false + + # links to object that caused the notification + t.integer :source_id + t.string :source_type + + t.references :user, null: true + + t.timestamps + end + + foreign_key :notifications, :user_id + end + + def self.down + drop_table :notifications + end +end diff --git a/db/migrate/20150518121600_create_read_notifications.rb b/db/migrate/20150518121600_create_read_notifications.rb new file mode 100644 index 00000000..c273a77e --- /dev/null +++ b/db/migrate/20150518121600_create_read_notifications.rb @@ -0,0 +1,17 @@ +class CreateReadNotifications < ActiveRecord::Migration + extend ForeignKeys + + def self.up + create_table :read_notifications do |t| + t.references :notification, :null => false + t.references :user, :null => false + end + + foreign_key :read_notifications, :notification_id + foreign_key :read_notifications, :user_id + end + + def self.down + drop_table :read_notifications + end +end From beed49b20330e1d631cdd12595509721e7e18ed1 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Thu, 21 May 2015 21:05:46 +0300 Subject: [PATCH 02/13] Reworked notification logic to map notifications to users 1:1 --- app/controllers/notifications_controller.rb | 3 ++- app/models/challenge.rb | 8 +++---- app/models/generates_notifications.rb | 13 +++++++++++ app/models/notification.rb | 23 +------------------ app/models/reply.rb | 11 ++++----- app/models/task.rb | 8 +++---- app/models/user.rb | 1 + .../20150518105230_create_notifications.rb | 4 +++- ...0150518121600_create_read_notifications.rb | 17 -------------- 9 files changed, 32 insertions(+), 56 deletions(-) create mode 100644 app/models/generates_notifications.rb delete mode 100644 db/migrate/20150518121600_create_read_notifications.rb diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 89549a08..9909ccaa 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -6,7 +6,8 @@ def index def show notification = Notification.find params[:id] - Notification.mark_read notification.id, current_user.id + notification.is_read = true + notification.save redirect_to notification.source end end diff --git a/app/models/challenge.rb b/app/models/challenge.rb index bd741c8d..96c2ee15 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -1,4 +1,6 @@ class Challenge < ActiveRecord::Base + include GeneratesNotifications + has_many :solutions, class_name: 'ChallengeSolution' validates :name, :description, presence: true @@ -20,9 +22,7 @@ def closed? end def post_notification - notification = Notification.new - notification.title = "Новo предизвикателство: #{name}" - notification.source = self - notification.save + generate_notifications_for User.all, title: "Новo предизвикателство: #{name}" end + end diff --git a/app/models/generates_notifications.rb b/app/models/generates_notifications.rb new file mode 100644 index 00000000..d5b6d06c --- /dev/null +++ b/app/models/generates_notifications.rb @@ -0,0 +1,13 @@ +module GeneratesNotifications + extend self + + def generate_notifications_for(users, title:) + users.each do |user| + notification = Notification.new + notification.title = title + notification.source = self + notification.user = user + notification.save + end + end +end \ No newline at end of file diff --git a/app/models/notification.rb b/app/models/notification.rb index ac4bd44c..f0c2f17c 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -2,30 +2,9 @@ class Notification < ActiveRecord::Base belongs_to :user belongs_to :source, polymorphic: true - def is_read_by?(user) - Notification.has_read_notification(id, user.id).first['count'].to_i > 0 - end - - # attr_protected :full_name, :faculty_number, :email, :admin - - # validates :password, confirmation: true, unless: -> { password.blank? } - class << self def unread_for_user(id) - t = Notification.arel_table - - Notification.where( - t[:user_id].eq(id). - or(t[:user_id].eq(nil)) - ).where('id NOT IN (SELECT notification_id FROM read_notifications WHERE user_id = ?)', id) - end - - def has_read_notification(notification_id, user_id) - self.connection.execute(sanitize_sql_array(["SELECT count(*) FROM read_notifications WHERE user_id = ? AND notification_id = ?", user_id, notification_id])) - end - - def mark_read(notification_id, user_id) - self.connection.execute(sanitize_sql_array(["INSERT INTO read_notifications (user_id, notification_id) VALUES (?, ?)", user_id, notification_id])) + where(user_id: id, is_read: false) end end end diff --git a/app/models/reply.rb b/app/models/reply.rb index c73d41fe..8409d040 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,4 +1,7 @@ class Reply < Post + + include GeneratesNotifications + belongs_to :topic belongs_to :user @@ -20,12 +23,6 @@ def page_in_topic private def post_notification - topic.users.each do |user| - notification = Notification.new - notification.title = "Новo отговор в тема: #{topic_title}" - notification.source = topic - notification.user = user - notification.save - end + generate_notifications_for topic.users, title: "Нов отговор в тема: #{topic_title}" end end diff --git a/app/models/task.rb b/app/models/task.rb index 0e8b42fd..3d7b5921 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -1,4 +1,6 @@ class Task < ActiveRecord::Base + include GeneratesNotifications + validates :name, :description, presence: true validates :max_points, numericality: true, presence: true validate :restrictions_must_be_valid_yaml @@ -49,9 +51,7 @@ def restrictions_must_be_valid_yaml end def post_notification - notification = Notification.new - notification.title = "Нова задача: #{name}" - notification.source = self - notification.save + generate_notifications_for User.all, title: "Нова задача: #{name}" end + end diff --git a/app/models/user.rb b/app/models/user.rb index b6cfbd74..39e57137 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,7 @@ class User < ActiveRecord::Base has_many :solutions has_many :tips has_many :attributions + has_many :notifications attr_protected :full_name, :faculty_number, :email, :admin diff --git a/db/migrate/20150518105230_create_notifications.rb b/db/migrate/20150518105230_create_notifications.rb index f16fd622..25005d4b 100644 --- a/db/migrate/20150518105230_create_notifications.rb +++ b/db/migrate/20150518105230_create_notifications.rb @@ -9,7 +9,9 @@ def self.up t.integer :source_id t.string :source_type - t.references :user, null: true + t.boolean :is_read, default: false + + t.references :user t.timestamps end diff --git a/db/migrate/20150518121600_create_read_notifications.rb b/db/migrate/20150518121600_create_read_notifications.rb deleted file mode 100644 index c273a77e..00000000 --- a/db/migrate/20150518121600_create_read_notifications.rb +++ /dev/null @@ -1,17 +0,0 @@ -class CreateReadNotifications < ActiveRecord::Migration - extend ForeignKeys - - def self.up - create_table :read_notifications do |t| - t.references :notification, :null => false - t.references :user, :null => false - end - - foreign_key :read_notifications, :notification_id - foreign_key :read_notifications, :user_id - end - - def self.down - drop_table :read_notifications - end -end From e6df7921605c3b036514d3503e50d4b91ded591b Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Sun, 24 May 2015 23:48:01 +0300 Subject: [PATCH 03/13] Removed unneeded code --- app/models/topic.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/models/topic.rb b/app/models/topic.rb index b62820ac..cec001fc 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -31,10 +31,6 @@ def last_poster User.find(last_poster_id) end - def participating_users - User - end - def last_post_at ActiveSupport::TimeZone['UTC'].parse(last_post_at_before_type_cast).in_time_zone end From bda8425a6ea46aa85da9718853a4a0f783d3a528 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 25 May 2015 00:23:30 +0300 Subject: [PATCH 04/13] Code clean up --- app/controllers/notifications_controller.rb | 2 -- app/models/challenge.rb | 3 +-- app/models/reply.rb | 1 - app/models/task.rb | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 9909ccaa..bd426f1e 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -1,7 +1,5 @@ class NotificationsController < ApplicationController - def index - # @notifications = Notification.unread_for_user current_user.id end def show diff --git a/app/models/challenge.rb b/app/models/challenge.rb index 96c2ee15..84f8b989 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -24,5 +24,4 @@ def closed? def post_notification generate_notifications_for User.all, title: "Новo предизвикателство: #{name}" end - -end +end \ No newline at end of file diff --git a/app/models/reply.rb b/app/models/reply.rb index 8409d040..56da95f5 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,5 +1,4 @@ class Reply < Post - include GeneratesNotifications belongs_to :topic diff --git a/app/models/task.rb b/app/models/task.rb index 3d7b5921..a7e1e68f 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -53,5 +53,4 @@ def restrictions_must_be_valid_yaml def post_notification generate_notifications_for User.all, title: "Нова задача: #{name}" end - end From 68c5d28c5cae092b3f5eede5eb6024a20c3a4e55 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 25 May 2015 00:29:08 +0300 Subject: [PATCH 05/13] Code improvements --- app/controllers/notifications_controller.rb | 3 +-- app/models/generates_notifications.rb | 18 +++++++++--------- app/models/notification.rb | 5 +++++ 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index bd426f1e..0ea8f980 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -4,8 +4,7 @@ def index def show notification = Notification.find params[:id] - notification.is_read = true - notification.save + Notification.mark_as_read notification redirect_to notification.source end end diff --git a/app/models/generates_notifications.rb b/app/models/generates_notifications.rb index d5b6d06c..746cbb32 100644 --- a/app/models/generates_notifications.rb +++ b/app/models/generates_notifications.rb @@ -1,13 +1,13 @@ module GeneratesNotifications - extend self + extend self - def generate_notifications_for(users, title:) - users.each do |user| - notification = Notification.new - notification.title = title - notification.source = self - notification.user = user - notification.save - end + def generate_notifications_for(users, title:) + users.find_each do |user| + notification = Notification.new + notification.title = title + notification.source = self + notification.user = user + notification.save! end + end end \ No newline at end of file diff --git a/app/models/notification.rb b/app/models/notification.rb index f0c2f17c..c89f50dc 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -6,5 +6,10 @@ class << self def unread_for_user(id) where(user_id: id, is_read: false) end + + def mark_as_read(notification) + notification.is_read = true + notification.save! + end end end From e9f6fa8254f1f7601a0f452c2419f26004b50fb7 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 25 May 2015 00:57:24 +0300 Subject: [PATCH 06/13] Changed API for notification model --- app/controllers/application_controller.rb | 6 +++--- app/controllers/notifications_controller.rb | 2 +- app/models/notification.rb | 20 +++++++++++++++----- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 59007bd7..e9342bc3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base include CustomPaths helper CustomPaths - before_action :set_time_zone, :load_notifications + before_action :set_time_zone, :set_notifications_for_current_user helper_method :can_edit?, :logged_in?, :admin? @@ -42,7 +42,7 @@ def set_time_zone Time.zone = 'Sofia' end - def load_notifications - @notifications = Notification.unread_for_user current_user.id + def set_notifications_for_current_user + @notifications = Notification.unread_for_user current_user end end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 0ea8f980..7a366864 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -4,7 +4,7 @@ def index def show notification = Notification.find params[:id] - Notification.mark_as_read notification + notification.mark_as_read redirect_to notification.source end end diff --git a/app/models/notification.rb b/app/models/notification.rb index c89f50dc..bf694a67 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -2,14 +2,24 @@ class Notification < ActiveRecord::Base belongs_to :user belongs_to :source, polymorphic: true + def mark_as_read + self.is_read = true + save! + end + class << self - def unread_for_user(id) - where(user_id: id, is_read: false) + def unread_for_user(user) + where(user_id: user.id, is_read: false) end - def mark_as_read(notification) - notification.is_read = true - notification.save! + def send_notifications_for(source, to:, title:) + to.find_each do |user| + notification = Notification.new + notification.title = title + notification.source = source + notification.user = user + notification.save! + end end end end From 85d5a8740943580536ade87417787bf01a2190d2 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 25 May 2015 00:57:55 +0300 Subject: [PATCH 07/13] Updated notification API usage in models --- app/models/challenge.rb | 4 +--- app/models/reply.rb | 4 +--- app/models/task.rb | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/app/models/challenge.rb b/app/models/challenge.rb index 84f8b989..e7706284 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -1,6 +1,4 @@ class Challenge < ActiveRecord::Base - include GeneratesNotifications - has_many :solutions, class_name: 'ChallengeSolution' validates :name, :description, presence: true @@ -22,6 +20,6 @@ def closed? end def post_notification - generate_notifications_for User.all, title: "Новo предизвикателство: #{name}" + Notification.send_notifications_for self, to: User.all, title: "Новo предизвикателство: #{name}" end end \ No newline at end of file diff --git a/app/models/reply.rb b/app/models/reply.rb index 56da95f5..66583f24 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,6 +1,4 @@ class Reply < Post - include GeneratesNotifications - belongs_to :topic belongs_to :user @@ -22,6 +20,6 @@ def page_in_topic private def post_notification - generate_notifications_for topic.users, title: "Нов отговор в тема: #{topic_title}" + Notification.send_notifications_for topic, to: topic.users, title: "Нов отговор в тема: #{topic_title}" end end diff --git a/app/models/task.rb b/app/models/task.rb index a7e1e68f..8a75df72 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -1,6 +1,4 @@ class Task < ActiveRecord::Base - include GeneratesNotifications - validates :name, :description, presence: true validates :max_points, numericality: true, presence: true validate :restrictions_must_be_valid_yaml @@ -51,6 +49,6 @@ def restrictions_must_be_valid_yaml end def post_notification - generate_notifications_for User.all, title: "Нова задача: #{name}" + Notification.send_notifications_for self, to: User.all, title: "Нова задача: #{name}" end end From da6d7f11f57038dc5e2c9e12a189435cebe126ba Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 25 May 2015 00:58:15 +0300 Subject: [PATCH 08/13] Cleaned up code --- app/models/generates_notifications.rb | 13 ------------- app/models/topic.rb | 2 +- app/views/notifications/index.html.haml | 3 +-- db/migrate/20150518105230_create_notifications.rb | 2 +- 4 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 app/models/generates_notifications.rb diff --git a/app/models/generates_notifications.rb b/app/models/generates_notifications.rb deleted file mode 100644 index 746cbb32..00000000 --- a/app/models/generates_notifications.rb +++ /dev/null @@ -1,13 +0,0 @@ -module GeneratesNotifications - extend self - - def generate_notifications_for(users, title:) - users.find_each do |user| - notification = Notification.new - notification.title = title - notification.source = self - notification.user = user - notification.save! - end - end -end \ No newline at end of file diff --git a/app/models/topic.rb b/app/models/topic.rb index cec001fc..fc22dea9 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1,7 +1,7 @@ class Topic < Post has_many :replies, -> { order 'created_at ASC' } - has_many :users, :through => :replies + has_many :users, through: :replies attr_accessible :title, :body diff --git a/app/views/notifications/index.html.haml b/app/views/notifications/index.html.haml index 5e63a6c5..72ca54b1 100644 --- a/app/views/notifications/index.html.haml +++ b/app/views/notifications/index.html.haml @@ -3,10 +3,9 @@ %p Общо съобщения: %strong= @notifications.count - %table %tbody - @notifications.each do |notification| %tr - %td.name= link_to notification.title, notification + %td.name= link_to notification.title, notification \ No newline at end of file diff --git a/db/migrate/20150518105230_create_notifications.rb b/db/migrate/20150518105230_create_notifications.rb index 25005d4b..3ebb8ba3 100644 --- a/db/migrate/20150518105230_create_notifications.rb +++ b/db/migrate/20150518105230_create_notifications.rb @@ -3,7 +3,7 @@ class CreateNotifications < ActiveRecord::Migration def self.up create_table(:notifications) do |t| - t.string :title, :null => false + t.string :title, null: false # links to object that caused the notification t.integer :source_id From b241e98860da622a0f0c2b825e3cdb1a6fc9f8ee Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Mon, 25 May 2015 01:05:33 +0300 Subject: [PATCH 09/13] Fixed bug that posted multiple notifications for a user that has contributed to a topic multiple times --- app/models/topic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/topic.rb b/app/models/topic.rb index fc22dea9..0a68fb68 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1,7 +1,7 @@ class Topic < Post has_many :replies, -> { order 'created_at ASC' } - has_many :users, through: :replies + has_many :users, through: :replies, uniq: true attr_accessible :title, :body From ef45e2bee2b02e0324cbed269b2a2e426d617ef0 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Wed, 27 May 2015 13:38:48 +0300 Subject: [PATCH 10/13] Improved code and naming of different parts of it --- app/controllers/application_controller.rb | 2 +- app/models/challenge.rb | 2 +- app/models/notification.rb | 16 ++++++++-------- app/models/reply.rb | 2 +- app/models/task.rb | 2 +- app/models/topic.rb | 2 +- app/views/layouts/application.html.haml | 2 +- app/views/notifications/index.html.haml | 6 +++--- .../20150518105230_create_notifications.rb | 10 ++-------- 9 files changed, 19 insertions(+), 25 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e9342bc3..4e809209 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -43,6 +43,6 @@ def set_time_zone end def set_notifications_for_current_user - @notifications = Notification.unread_for_user current_user + @notifications = Notification.unread_for_user(current_user) if user_signed_in? end end diff --git a/app/models/challenge.rb b/app/models/challenge.rb index e7706284..1992c320 100644 --- a/app/models/challenge.rb +++ b/app/models/challenge.rb @@ -20,6 +20,6 @@ def closed? end def post_notification - Notification.send_notifications_for self, to: User.all, title: "Новo предизвикателство: #{name}" + Notification.create_notifications_for self, to: User.all, title: "Новo предизвикателство: #{name}" end end \ No newline at end of file diff --git a/app/models/notification.rb b/app/models/notification.rb index bf694a67..0cfead47 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -3,22 +3,22 @@ class Notification < ActiveRecord::Base belongs_to :source, polymorphic: true def mark_as_read - self.is_read = true + self.read = true save! end class << self def unread_for_user(user) - where(user_id: user.id, is_read: false) + where(user_id: user.id, read: false) end - def send_notifications_for(source, to:, title:) + def create_notifications_for(source, to:, title:) to.find_each do |user| - notification = Notification.new - notification.title = title - notification.source = source - notification.user = user - notification.save! + Notification.create do |notification| + notification.title = title + notification.source = source + notification.user = user + end end end end diff --git a/app/models/reply.rb b/app/models/reply.rb index 66583f24..08e2816f 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -20,6 +20,6 @@ def page_in_topic private def post_notification - Notification.send_notifications_for topic, to: topic.users, title: "Нов отговор в тема: #{topic_title}" + Notification.create_notifications_for topic, to: topic.participants, title: "Нов отговор в тема: #{topic_title}" end end diff --git a/app/models/task.rb b/app/models/task.rb index 8a75df72..af24d087 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -49,6 +49,6 @@ def restrictions_must_be_valid_yaml end def post_notification - Notification.send_notifications_for self, to: User.all, title: "Нова задача: #{name}" + Notification.create_notifications_for self, to: User.all, title: "Нова задача: #{name}" end end diff --git a/app/models/topic.rb b/app/models/topic.rb index 0a68fb68..1befdcfa 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -1,7 +1,7 @@ class Topic < Post has_many :replies, -> { order 'created_at ASC' } - has_many :users, through: :replies, uniq: true + has_many :participants, -> { uniq }, through: :replies, source: :user attr_accessible :title, :body diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index ecf06647..226a19b1 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -21,7 +21,7 @@ %ul - if user_signed_in? %li Здрасти, #{link_to current_user.name, edit_profile_path} - %li #{link_to 'Съобщения', notifications_path} (#{@notifications.count}) + %li.notifications-link #{link_to 'Известия', notifications_path} (#{@notifications.count}) %li= link_to 'Изход', destroy_user_session_path - else %li= link_to 'Вход', new_user_session_path diff --git a/app/views/notifications/index.html.haml b/app/views/notifications/index.html.haml index 72ca54b1..51217e01 100644 --- a/app/views/notifications/index.html.haml +++ b/app/views/notifications/index.html.haml @@ -1,11 +1,11 @@ -%h1 Съобщения +%h1 Известия %p - Общо съобщения: + Общо известия: %strong= @notifications.count %table %tbody - @notifications.each do |notification| %tr - %td.name= link_to notification.title, notification \ No newline at end of file + %td.name.notification-name= link_to notification.title, notification \ No newline at end of file diff --git a/db/migrate/20150518105230_create_notifications.rb b/db/migrate/20150518105230_create_notifications.rb index 3ebb8ba3..e4d39de9 100644 --- a/db/migrate/20150518105230_create_notifications.rb +++ b/db/migrate/20150518105230_create_notifications.rb @@ -4,15 +4,9 @@ class CreateNotifications < ActiveRecord::Migration def self.up create_table(:notifications) do |t| t.string :title, null: false - - # links to object that caused the notification - t.integer :source_id - t.string :source_type - - t.boolean :is_read, default: false - + t.references :source, polymorphic: true + t.boolean :read, default: false t.references :user - t.timestamps end From afd096fbb9946ffe41ea48b634fd9b800e6fc868 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Wed, 27 May 2015 13:39:18 +0300 Subject: [PATCH 11/13] Added tests --- features/notifications.feature | 45 +++++++++++++++++++ .../step_definitions/notifications_steps.rb | 39 ++++++++++++++++ .../notifications_controller_spec.rb | 22 +++++++++ spec/factories.rb | 6 +++ spec/models/notification_spec.rb | 9 ++++ spec/spec_helper.rb | 5 +++ 6 files changed, 126 insertions(+) create mode 100644 features/notifications.feature create mode 100644 features/step_definitions/notifications_steps.rb create mode 100644 spec/controllers/notifications_controller_spec.rb create mode 100644 spec/models/notification_spec.rb diff --git a/features/notifications.feature b/features/notifications.feature new file mode 100644 index 00000000..61001a76 --- /dev/null +++ b/features/notifications.feature @@ -0,0 +1,45 @@ +# language: bg +Функционалност: Известия + Студентите получават известия, когато бъде публикувана задача, + предизвикателсвто или отговор във форум тема + + Сценарий: Получаване на известие при публикуване на задача + Дадено че съм влязъл като администратор + Когато създам задача: + | Име | Първа задача | + | Условие | Напишете програма | + То трябва да съществува известие "Нова задача: Първа задача" + + Сценарий: Получаване на известие при публикуване на предизвикателство + Дадено че съм влязъл като администратор + Когато създам предизвикателство "Четене на субтитри" + То трябва да съществува известие "Новo предизвикателство: Четене на субтитри" + + Сценарий: Получаване на известие при публикуване на отговор в тема + Дадено че съм студент + Дадено че съществува тема "Въпрос" + И че темата "Въпрос" има "1" отговора, последния от които на "Петър Петров" + Когато отговоря на "Въпрос" с: + """ + Моят текст + """ + То трябва да съществува известие "Нов отговор в тема: Въпрос" + + Сценарий: Потребителите виждат само непрочетени известия + Дадено че съм студент + Дадено че съществува известие "Прочетено известие" + Дадено че съществува непрочетено известие "Ново непрочетено известие" + Когато отворя страницата с известията + То трябва да виждам известие "Ново непрочетено известие" + И трябва да не виждам известие "Прочетено известие" + И броя на новите известия трябва да е 1 + + Сценарий: Когато избера ново известие съм пренасочен към страницата на първоизточника + Дадено че съм влязъл като администратор + Когато създам задача: + | Име | Първа задача | + | Условие | Напишете програма | + И отворя страницата с известията + И избера известието "Нова задача: Първа задача" + То трябва да съм на задачата "Първа задача" + И броя на новите известия трябва да е 0 \ No newline at end of file diff --git a/features/step_definitions/notifications_steps.rb b/features/step_definitions/notifications_steps.rb new file mode 100644 index 00000000..0aafc010 --- /dev/null +++ b/features/step_definitions/notifications_steps.rb @@ -0,0 +1,39 @@ +Дадено 'че съществува известие "$title"' do |title| + create :notification, title: title, user_id: current_user.id, read: true +end + +Дадено 'че съществува непрочетено известие "$title"' do |title| + create :notification, title: title, user_id: current_user.id, read: false +end + +Когато 'отворя страницата с известията' do + visit notifications_path +end + +Когато 'избера известието "$title"' do |title| + notification = Notification.find_by_title! title + visit notification_path(notification) +end + +То 'трябва да виждам известие "$title"' do |title| + within ".notification-name" do + page.should have_content title + end +end + +То 'трябва да не виждам известие "$title"' do |title| + within ".notification-name" do + page.should_not have_content title + end +end + +То 'броя на новите известия трябва да е $count' do |count| + within ".notifications-link" do + page.should have_content "(#{count})" + end +end + +То 'трябва да съществува известие "$title"' do |title| + notification = Notification.find_by_title! title + notification.should_not be_blank +end diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb new file mode 100644 index 00000000..a71ed5bb --- /dev/null +++ b/spec/controllers/notifications_controller_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe NotificationsController do + describe "GET index" do + it "assigns unread user notifications to @notifications" do + controller.stub_chain(:current_user).and_return(:user) + Notification.should_receive(:unread_for_user).with(:user).and_return('notifications') + get :index + assigns(:notifications).should eq 'notifications' + end + end + + describe "GET show" do + notification = FactoryGirl.build_stubbed(:notification) + + it "marks notification as read and redirects to its source" do + Notification.should_receive(:find).with('3').and_return(notification) + get :show, id: '3' + response.should redirect_to notification.source + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index 52517e4e..8dc2de43 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -36,6 +36,12 @@ user end + factory :notification do + title 'Title' + association :source, factory: :topic + user + end + factory :reply do body 'Body' topic diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb new file mode 100644 index 00000000..3796a33e --- /dev/null +++ b/spec/models/notification_spec.rb @@ -0,0 +1,9 @@ +require 'spec_helper' + +describe Notification do + it "can be marked as read" do + notification = create(:notification) + notification.mark_as_read + notification.read.should eq true + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 437e3ff3..c1545de4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -44,5 +44,10 @@ config.include EmailSpec::Helpers, type: :mailer config.include EmailSpec::Matchers, type: :mailer config.include CustomPaths, type: :mailer + config.include Devise::TestHelpers, :type => :controller + + config.before(:each, type: :controller) do + allow(Notification).to receive(:unread_for_user).and_return([]) + end end end From e7bd2407cd32f2db930efc6eb38279a872db74c6 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Wed, 27 May 2015 14:00:57 +0300 Subject: [PATCH 12/13] Added default values for named arguments for Ruby 2.0 --- app/models/notification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/notification.rb b/app/models/notification.rb index 0cfead47..820833bb 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -12,7 +12,7 @@ def unread_for_user(user) where(user_id: user.id, read: false) end - def create_notifications_for(source, to:, title:) + def create_notifications_for(source, to: [], title: nil) to.find_each do |user| Notification.create do |notification| notification.title = title From 27527cdb8a6e54afc8c4c2ee374bda89583f03b7 Mon Sep 17 00:00:00 2001 From: Stefan Kovachev Date: Wed, 27 May 2015 15:48:30 +0300 Subject: [PATCH 13/13] Fixed bug with default value in create_notifications_for method --- app/models/notification.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/models/notification.rb b/app/models/notification.rb index 820833bb..bd8f5655 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -12,12 +12,14 @@ def unread_for_user(user) where(user_id: user.id, read: false) end - def create_notifications_for(source, to: [], title: nil) - to.find_each do |user| - Notification.create do |notification| - notification.title = title - notification.source = source - notification.user = user + def create_notifications_for(source, to: nil, title: nil) + unless to.nil? + to.find_each do |user| + Notification.create do |notification| + notification.title = title + notification.source = source + notification.user = user + end end end end