hertz 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +188 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/javascripts/hertz/application.js +13 -0
  6. data/app/assets/stylesheets/hertz/application.css +15 -0
  7. data/app/controllers/hertz/application_controller.rb +6 -0
  8. data/app/helpers/hertz/application_helper.rb +5 -0
  9. data/app/mailers/hertz/notification_mailer.rb +24 -0
  10. data/app/models/hertz/notification.rb +46 -0
  11. data/app/views/layouts/hertz/application.html.erb +14 -0
  12. data/config/routes.rb +2 -0
  13. data/db/migrate/20160415174901_create_hertz_notifications.rb +14 -0
  14. data/lib/generators/hertz/install_generator.rb +11 -0
  15. data/lib/generators/hertz/templates/initializer.rb +5 -0
  16. data/lib/hertz.rb +19 -0
  17. data/lib/hertz/courier/base.rb +10 -0
  18. data/lib/hertz/courier/email.rb +10 -0
  19. data/lib/hertz/engine.rb +6 -0
  20. data/lib/hertz/notifiable.rb +18 -0
  21. data/lib/hertz/notification_deliverer.rb +18 -0
  22. data/lib/hertz/version.rb +4 -0
  23. data/lib/tasks/hertz_tasks.rake +4 -0
  24. data/spec/dummy/README.rdoc +28 -0
  25. data/spec/dummy/Rakefile +6 -0
  26. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  27. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  28. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  29. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  30. data/spec/dummy/app/mailers/application_mailer.rb +2 -0
  31. data/spec/dummy/app/models/test_notification.rb +7 -0
  32. data/spec/dummy/app/models/user.rb +7 -0
  33. data/spec/dummy/app/views/hertz/notification_mailer/test_notification.html.erb +1 -0
  34. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  35. data/spec/dummy/bin/bundle +3 -0
  36. data/spec/dummy/bin/rails +4 -0
  37. data/spec/dummy/bin/rake +4 -0
  38. data/spec/dummy/bin/setup +29 -0
  39. data/spec/dummy/config.ru +4 -0
  40. data/spec/dummy/config/application.rb +26 -0
  41. data/spec/dummy/config/boot.rb +5 -0
  42. data/spec/dummy/config/database.example.yml +19 -0
  43. data/spec/dummy/config/database.yml +28 -0
  44. data/spec/dummy/config/environment.rb +5 -0
  45. data/spec/dummy/config/environments/development.rb +41 -0
  46. data/spec/dummy/config/environments/production.rb +79 -0
  47. data/spec/dummy/config/environments/test.rb +42 -0
  48. data/spec/dummy/config/initializers/assets.rb +11 -0
  49. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  51. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  52. data/spec/dummy/config/initializers/hertz.rb +4 -0
  53. data/spec/dummy/config/initializers/inflections.rb +16 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  55. data/spec/dummy/config/initializers/session_store.rb +3 -0
  56. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  57. data/spec/dummy/config/locales/en.yml +23 -0
  58. data/spec/dummy/config/routes.rb +4 -0
  59. data/spec/dummy/config/secrets.yml +22 -0
  60. data/spec/dummy/db/migrate/20160415175358_create_hertz_notifications.hertz.rb +15 -0
  61. data/spec/dummy/db/migrate/20160415175837_create_users.rb +8 -0
  62. data/spec/dummy/db/migrate/20160418122437_add_email_to_users.rb +5 -0
  63. data/spec/dummy/db/schema.rb +35 -0
  64. data/spec/dummy/log/development.log +136 -0
  65. data/spec/dummy/log/test.log +2618 -0
  66. data/spec/dummy/public/404.html +67 -0
  67. data/spec/dummy/public/422.html +67 -0
  68. data/spec/dummy/public/500.html +66 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/examples.txt +13 -0
  71. data/spec/factories/hertz/notifications.rb +14 -0
  72. data/spec/factories/users.rb +6 -0
  73. data/spec/lib/hertz/courier/email_spec.rb +26 -0
  74. data/spec/lib/hertz/notifiable_spec.rb +22 -0
  75. data/spec/lib/hertz/notification_deliverer_spec.rb +31 -0
  76. data/spec/mailers/notification_mailer_spec.rb +13 -0
  77. data/spec/models/hertz/notification_spec.rb +61 -0
  78. data/spec/rails_helper.rb +24 -0
  79. data/spec/spec_helper.rb +73 -0
  80. data/spec/support/database_cleaner.rb +19 -0
  81. data/spec/support/factory_girl.rb +5 -0
  82. metadata +308 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0988dec23d3f623853e523c7fc2e6f530c6dcdc2
4
+ data.tar.gz: fd300b90871a186b9068f6c94002631c21b928af
5
+ SHA512:
6
+ metadata.gz: 6145eb3c0c8e18ec36dcbcc6901b4003e8d863ec11b60031ca4fe895a752fee78f96197f35c2b9282b8d75bf6c42d203e84797946608adbdeac3ff6e66fb33e1
7
+ data.tar.gz: e1cfa1891739d7658eb1ad98089d4294f6491ed602ca4a349ba5e35e8ce84486686c329bd448f8331c61433dc6081c3efb34a51e618c163e21da5c296292ebab
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Alessandro Desantis
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,188 @@
1
+ # Hertz
2
+
3
+ [![Dependency Status](https://gemnasium.com/badges/github.com/alessandro1997/hertz.svg)](https://gemnasium.com/github.com/alessandro1997/hertz)
4
+ [![Code Climate](https://codeclimate.com/github/alessandro1997/hertz/badges/gpa.svg)](https://codeclimate.com/github/alessandro1997/hertz)
5
+
6
+ Hertz is a Ruby on Rails engine for sending in-app notification to your users.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'hertz'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ ```console
19
+ $ bundle
20
+ ```
21
+
22
+ Or install it yourself as:
23
+
24
+ ```console
25
+ $ gem install hertz
26
+ ```
27
+
28
+ Then, run the installer generator:
29
+
30
+ ```console
31
+ $ rails g hertz:install
32
+ $ rake db:migrate
33
+ ```
34
+
35
+ Finally, add the following to the model that will receive the notifications
36
+ (e.g. `User`):
37
+
38
+ ```ruby
39
+ class User < ActiveRecord::Base
40
+ include Hertz::Notifiable
41
+ end
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ### Using couriers
47
+
48
+ Couriers are what Hertz uses to deliver notifications to your users. For
49
+ instance, you might have a courier for delivering notifications by SMS and
50
+ another one for delivering them by email.
51
+
52
+ Creating a new courier in Hertz is easy:
53
+
54
+ ```ruby
55
+ module Hertz
56
+ module Courier
57
+ class Sms
58
+ def self.deliver_notification(notification)
59
+ # ...
60
+ end
61
+ end
62
+ end
63
+ end
64
+ ```
65
+
66
+ Again, you don't have to use couriers if you only display notifications on your
67
+ website using standard AR methods.
68
+
69
+ ### Creating new notification types
70
+
71
+ In Hertz, every notification is a model. If you want to create a new
72
+ notification type, just create a new model inheriting from
73
+ `Hertz::Notification`:
74
+
75
+ ```ruby
76
+ class CommentNotification < Hertz::Notification
77
+ end
78
+ ```
79
+ Since not all notifications might implement interfaces for all couriers, you
80
+ have to manually specify which couriers they implement via `deliver_by`:
81
+
82
+ ```ruby
83
+ class CommentNotification < Hertz::Notification
84
+ deliver_by :sms, :email
85
+ end
86
+ ```
87
+
88
+ Notifications are not required to implement any couriers.
89
+
90
+ ### Attaching metadata to a notification
91
+
92
+ You can attach custom metadata to a notification, but make sure it can be
93
+ cleanly stored in an hstore:
94
+
95
+ ```ruby
96
+ notification = CommentNotification.new(meta: { comment_id: comment.id })
97
+ user.notify(notification)
98
+ ```
99
+
100
+ You can then unserialize any data in the model:
101
+
102
+ ```ruby
103
+ class CommentNotification < Hertz::Notification
104
+ def comment
105
+ Comment.find(meta['comment_id'])
106
+ end
107
+ end
108
+ ```
109
+
110
+ Note that you should always access your metadata with string keys, regardless of
111
+ the type you use when attaching it.
112
+
113
+ ### Notifying users
114
+
115
+ You can use `#notify` for notifying a user:
116
+
117
+ ```ruby
118
+ notification = CommentNotification.new
119
+ user.notify(notification)
120
+ ```
121
+
122
+ You can access a user's notifications with `#notifications`:
123
+
124
+ ```ruby
125
+ current_user.notifications
126
+ current_user.notifications.unread
127
+ ```
128
+
129
+ You can also mark notifications as read/unread:
130
+
131
+ ```ruby
132
+ notification.mark_as_read
133
+ notification.mark_as_unread
134
+ ```
135
+
136
+ ## Built-in couriers
137
+
138
+ ### Email
139
+
140
+ The email courier delivers your notifications by email. In order to use this
141
+ courier, add `:email` to `deliver_by` in the notification model(s):
142
+
143
+ ```ruby
144
+ class CommentNotification < Hertz::Notification
145
+ deliver_by :email
146
+ end
147
+ ```
148
+
149
+ You will also need to expose the `hertz_email` method in your receiver class:
150
+
151
+ ```ruby
152
+ class User < ActiveRecord::Base
153
+ def hertz_email
154
+ email
155
+ end
156
+ end
157
+ ```
158
+
159
+ And the `email_subject` method in your notification class:
160
+
161
+ ```ruby
162
+ class CommentNotification < Hertz::Notification
163
+ def email_subject
164
+ 'You have a new comment!'
165
+ end
166
+ end
167
+ ```
168
+
169
+ Finally, you should create a template for every notification you send by email.
170
+ For `CommentNotification` you'd create a template at
171
+ `app/views/hertz/notification_mailer/comment_notification.html.erb`:
172
+
173
+ ```erb
174
+ <p>Hey <%= @notification.receiver.hertz_email %>,</p>
175
+ <p>you've got a new comment!</p>
176
+ ```
177
+
178
+ As you can see, templates have access to the `@notification` instance variable.
179
+
180
+ ## Contributing
181
+
182
+ Bug reports and pull requests are welcome on GitHub at
183
+ https://github.com/alessandro1997/hertz.
184
+
185
+ ## License
186
+
187
+ The gem is available as open source under the terms of the
188
+ [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Hertz'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+ load 'rails/tasks/statistics.rake'
20
+
21
+ Bundler::GemHelper.install_tasks
22
+
23
+ Rake::TestTask.new(:spec) do |t|
24
+ t.libs << 'lib'
25
+ t.libs << 'spec'
26
+ t.pattern = 'spec/**/*_spec.rb'
27
+ t.verbose = false
28
+ end
29
+
30
+ task default: :spec
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module Hertz
3
+ class ApplicationController < ActionController::Base
4
+ protect_from_forgery with: :exception
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ module Hertz
3
+ module ApplicationHelper
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Hertz
3
+ class NotificationMailer < Hertz.base_mailer
4
+ def notification_email(notification)
5
+ @notification = notification
6
+
7
+ mail(
8
+ to: notification.receiver.hertz_email,
9
+ subject: notification.email_subject,
10
+ template_name: view_for(notification)
11
+ )
12
+ end
13
+
14
+ private
15
+
16
+ def view_for(notification)
17
+ if notification.respond_to?(:email_template)
18
+ notification.email_template
19
+ else
20
+ notification.class.to_s.underscore
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ module Hertz
3
+ class Notification < ActiveRecord::Base
4
+ @couriers = []
5
+
6
+ scope :unread, -> { where 'read_at IS NULL' }
7
+
8
+ belongs_to :receiver, inverse_of: :notifications, polymorphic: true
9
+
10
+ after_create :deliver
11
+
12
+ class << self
13
+ def couriers
14
+ @couriers ||= []
15
+ end
16
+
17
+ protected
18
+
19
+ def deliver_by(*couriers)
20
+ @couriers = couriers.flatten.map(&:to_sym)
21
+ end
22
+ end
23
+
24
+ def read?
25
+ read_at.present?
26
+ end
27
+
28
+ def unread?
29
+ read_at.nil?
30
+ end
31
+
32
+ def mark_as_read
33
+ update read_at: Time.zone.now
34
+ end
35
+
36
+ def mark_as_unread
37
+ update read_at: nil
38
+ end
39
+
40
+ private
41
+
42
+ def deliver
43
+ Hertz::NotificationDeliverer.deliver(self)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Hertz</title>
5
+ <%= stylesheet_link_tag "hertz/application", media: "all" %>
6
+ <%= javascript_include_tag "hertz/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,2 @@
1
+ Hertz::Engine.routes.draw do
2
+ end
@@ -0,0 +1,14 @@
1
+ class CreateHertzNotifications < ActiveRecord::Migration
2
+ def change
3
+ enable_extension 'hstore'
4
+
5
+ create_table :hertz_notifications do |t|
6
+ t.string :type, null: false
7
+ t.string :receiver_type, null: false
8
+ t.integer :receiver_id, null: false
9
+ t.hstore :meta, default: {}, null: false
10
+ t.datetime :read_at
11
+ t.datetime :created_at, null: false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ module Hertz
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def copy_initializer_file
7
+ copy_file 'initializer.rb', 'config/initializers/hertz.rb'
8
+ rake 'hertz:install:migrations'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ Hertz.configure do |config|
3
+ # Your base mailer class, for delivering notifications by email.
4
+ # config.base_mailer = '::ApplicationMailer'
5
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ require 'hertz/engine'
3
+
4
+ require 'hertz/notifiable'
5
+ require 'hertz/notification_deliverer'
6
+ require 'hertz/courier/base'
7
+ require 'hertz/courier/email'
8
+
9
+ module Hertz
10
+ mattr_writer :base_mailer
11
+
12
+ def self.configure(&block)
13
+ block.call(self)
14
+ end
15
+
16
+ def self.base_mailer
17
+ (@@base_mailer || '::ApplicationMailer').constantize
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ module Hertz
3
+ module Courier
4
+ class Base
5
+ def self.deliver_notification(_notification)
6
+ fail NotImplementedError
7
+ end
8
+ end
9
+ end
10
+ end