hertz 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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