notifly 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +197 -0
  4. data/Rakefile +23 -0
  5. data/app/assets/javascripts/notifly.js +5 -0
  6. data/app/assets/javascripts/notifly/counter.js.erb +23 -0
  7. data/app/assets/javascripts/notifly/get_notifications.js.erb +26 -0
  8. data/app/assets/stylesheets/notifly.css +5 -0
  9. data/app/assets/stylesheets/notifly/layout.css +50 -0
  10. data/app/controllers/notifly/application_controller.rb +4 -0
  11. data/app/controllers/notifly/notifications_controller.rb +36 -0
  12. data/app/helpers/notifly/view_helper.rb +17 -0
  13. data/app/models/notifly/notification.rb +27 -0
  14. data/app/views/notifly/layouts/_actions.html.erb +4 -0
  15. data/app/views/notifly/layouts/_counter.html.erb +4 -0
  16. data/app/views/notifly/layouts/_footer.html.erb +3 -0
  17. data/app/views/notifly/layouts/_index.html.erb +6 -0
  18. data/app/views/notifly/layouts/_notification.html.erb +12 -0
  19. data/app/views/notifly/layouts/_notifly.html.erb +25 -0
  20. data/app/views/notifly/notifications/counter.js.erb +2 -0
  21. data/app/views/notifly/notifications/index.js.erb +20 -0
  22. data/app/views/notifly/notifications/read_specific.js.erb +4 -0
  23. data/app/views/notifly/notifications/toggle_read.js.erb +5 -0
  24. data/app/views/notifly/templates/_default.html.erb +3 -0
  25. data/config/routes.rb +11 -0
  26. data/db/migrate/20141103170528_create_notifly_notifications.rb +13 -0
  27. data/db/migrate/20141104150224_add_data_to_notification.rb +5 -0
  28. data/db/migrate/20141117193436_add_seen_to_notifly_notification.rb +5 -0
  29. data/lib/generators/notifly/install/install_generator.rb +22 -0
  30. data/lib/generators/notifly/install/templates/config/initializers/notifly.rb +4 -0
  31. data/lib/generators/notifly/install/utils.rb +21 -0
  32. data/lib/generators/notifly/views/USAGE +8 -0
  33. data/lib/generators/notifly/views/views_generator.rb +22 -0
  34. data/lib/notifly.rb +24 -0
  35. data/lib/notifly/engine.rb +22 -0
  36. data/lib/notifly/models/base.rb +13 -0
  37. data/lib/notifly/models/flyable.rb +127 -0
  38. data/lib/notifly/models/notifiable.rb +11 -0
  39. data/lib/notifly/models/options/fly.rb +37 -0
  40. data/lib/notifly/railtie.rb +14 -0
  41. data/lib/notifly/version.rb +3 -0
  42. data/lib/tasks/notifly_tasks.rake +4 -0
  43. metadata +219 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 77982bba3899b260f37bc98c4a95a56658bef69c
4
+ data.tar.gz: 8645e6bae431b62fa69743e026687984da53528a
5
+ SHA512:
6
+ metadata.gz: 6b416b97f187382029ea72e5121a4d980872a50b6c3b1a1fb505904efdcb135e74e0231f2f9b99a49da15f4ff314cd779bdd3a766cf508b26a0f02ce5ecf8e56
7
+ data.tar.gz: eac2d4e0026728c73b91e74a05c7bb9bdd49f89d8c10466acf27b65194425d98283b92d50f7756d51a55450e3998d1a2e06e6e1b7d6c78b0a66b262efe0fe084
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2014 YOURNAME
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.
data/README.md ADDED
@@ -0,0 +1,197 @@
1
+ # Notifly
2
+
3
+ This project intend to offer a full notification system, back and front-end.
4
+ In actual version, notifications are composed by:
5
+
6
+ - Receiver (**required**): the object that will receive the notification
7
+ - Sender: who sent the notification
8
+ - Target: object that you will be the subject between the sender and receiver
9
+ - Data: hash where you can store more info about the notification
10
+ - Template (**required**): html template used by notification
11
+ - Read: flag that records if the receiver read the notification
12
+ - Seen: flag that records if the receiver seen the notification
13
+ - If and Unless: used to create notifications conditionally
14
+
15
+
16
+ # Install
17
+
18
+ First we need to add the gem to `Gemfile`
19
+
20
+ ```ruby
21
+ gem 'notifly'
22
+ ```
23
+
24
+ Run the bundle command to install it. After that, you need to run the initializer
25
+
26
+ ```shell
27
+ $ rails generate notifly:install
28
+ ```
29
+
30
+ You can choose to change the namespace for Notifly routes, but the default is `notifly`.
31
+ It will creates `config/initializers/notifly.rb` too. Also, in this file you can
32
+ see/change the default configs
33
+
34
+ Notifly **need** to storage the notifications and to do it you need to run the migrations
35
+
36
+ ```shell
37
+ $ rake db:migrate
38
+ ```
39
+
40
+ # Usage
41
+
42
+ ## Back-end
43
+
44
+ We have two ways to create notifications:
45
+
46
+ #### 1. Using `#notifly` method in your classes (as callback)
47
+
48
+ If you want to create notifications after (or before) **any** method call.
49
+
50
+ ```ruby
51
+ class TicketOrder < ActiveRecord::Base
52
+ belongs_to :ticket
53
+ belongs_to :buyer
54
+ belongs_to :owner
55
+
56
+ notifly default_values: { receiver: :owner }
57
+
58
+ notifly before: :destroy, template: :destroy, sender: :buyer, data: :attributes
59
+ notifly after: :send_gift!, template: :ticket_gift, sender: :buyer,
60
+ target: :ticket, if: -> { completed? }
61
+ notifly after: :accept_gift, sender: :owner, receiver: :buyer, target: :ticket
62
+
63
+ def send_gift!
64
+ # code here
65
+ end
66
+
67
+ def accept_gift
68
+ # code here
69
+ end
70
+ end
71
+ ```
72
+ Value explanation about each parameter:
73
+
74
+ | Parameter | Value |
75
+ | ------------------- | ------------- |
76
+ | `before` or `after` | The method which will create notification before or after its call |
77
+ | `receiver` | The method which returns the notification receiver object |
78
+ | `sender` | The method which returns the notification sender object |
79
+ | `template` | The symbol or string that indicates which partial will be rendered at views. The partial must be inside `app/views/notifly/templates/`. Default is `:default`. |
80
+ | `target` | The method which returns the notification target object. It's a third actor of the notification. Example: In "Max sent you a ticket" notification, Max is the sender, you are the receiver and the **ticket is the target**. |
81
+ | `data` | A method which returns a hash with usefull values to be persisted, like ticket price or whatever you want to persist. |
82
+
83
+ Note that you can use the `default_values` parameter, it is specific to DRY your
84
+ notiflies and set the values to all notiflies. If you need to overwrite some
85
+ default value, just declare it again like the `:accept_gift` notifly above.
86
+
87
+
88
+ #### Using `#notifly!` method on your receiver object
89
+
90
+ If you need to create notifications without callbacks, even in the
91
+ controller scope.
92
+
93
+ ```ruby
94
+ class TicketOrder < ActiveRecord::Base
95
+ belongs_to :ticket
96
+ belongs_to :buyer
97
+ belongs_to :owner
98
+
99
+ before_destroy do
100
+ owner.notifly! template: :destroy, sender: :buyer, data: :attributes
101
+ end
102
+
103
+ def send_gift!
104
+ # code here
105
+
106
+ if completed?
107
+ owner.notifly! template: :ticket_gift, sender: :buyer, target: :ticket
108
+ end
109
+ end
110
+
111
+ def accept_gift
112
+ # code here
113
+
114
+ buyer.notifly! sender: :owner, target: :ticket
115
+ end
116
+ end
117
+ ```
118
+
119
+ The receiver will be always the object which you call `#notifly!`
120
+
121
+ You can access the notifications using the following methods:
122
+
123
+ - `object.notiflies` (**TODO**)
124
+ - Querying `Notifly::Notifications`
125
+ - Using our front-end helpers
126
+
127
+ ## Front-end
128
+
129
+ First, you need to have a `current_user` in `ApplicationController`, if you use
130
+ [Devise](https://github.com/plataformatec/devise) it is already there.
131
+
132
+ After that you need our assets, add them to your `application.js`
133
+
134
+ ```javascript
135
+ //= require notifly
136
+ ```
137
+
138
+ ```css
139
+ /*
140
+ *= require notifly
141
+ */
142
+ ```
143
+
144
+ Now finally you can see the notifications view adding code bellow to your view
145
+
146
+ ```html
147
+ <%= notiflies %>
148
+ ```
149
+
150
+ This will inject our views and it will be like that
151
+
152
+ ![image](http://upl.io/i/4i26o3.png)
153
+
154
+ As you can see, notifications are rendered with their templates. It uses a simple
155
+ default template but if you want to change it or create new ones run the code below
156
+ or create them in `app/views/notifly/templates/_your_template.html.erb`
157
+
158
+ ```shell
159
+ $ rails generate notifly:views
160
+ ```
161
+
162
+ Now if you want to customize the layout, just generate it adding the option `--layout`
163
+ to the code above and change it as you want. But if you already have a layout and just
164
+ want add our features to it, take a look at [Adapting your layout](#adapting).
165
+
166
+ ### <a name='adapting'></a> Adapting your layout
167
+
168
+ All partials that we insert in your layout are in the gem or if you generated them,
169
+ they will be in `app/views/notifly/layouts/`
170
+
171
+ Above are the elements that will loading the Notifly in your layout
172
+
173
+ - **Counter**: this element will show how many notifications are not seen. It
174
+ should have the id `#notifly-counter`, and the html rendered in will be
175
+ the `_counter.html.erb`
176
+ - **Notifications icon**: this element is the trigger to load the notifications
177
+ and you should have an icon to show when the user "have notifications" and
178
+ "do not have notifications" this element should have the id `#notifly-icon`. The
179
+ html icon is defined in our view helper `notifly_icon` you can overwrite it,
180
+ just remember that this method should have the argument `have_notifications=false`
181
+ and is this method that tell us which icon will be in the view.
182
+ - **Notifications**: they will be inserted in `#notifly-notifications-container`,
183
+ this element will contain all notifications (`_notification.html.erb`) rendered
184
+ by `_index.html.erb`
185
+ - **Next page link**: this link will append the next notifications page to the
186
+ `#notifly-notifications-container`, this is rendered by `_footer.html.erb` and
187
+ will be injected in `#notifly-notifications-footer`
188
+ - **Loading**: html element that will be showing while the notifications request
189
+ isn't completed. It should be in `#notifly-notifications-container` and should
190
+ have the class `loading`
191
+ - **Mark as read**: this link will mark all notifications in the page as read,
192
+ it will be rendered in `#notifly-header-actions`
193
+ - **Toggle read**: this link will be rendered by `_actions.html.erb' in
194
+ `_notification.html.erb`
195
+
196
+
197
+
data/Rakefile ADDED
@@ -0,0 +1,23 @@
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 = 'Notifly'
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
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
@@ -0,0 +1,5 @@
1
+ //= require 'jquery'
2
+ //= require 'jquery_ujs'
3
+ //= require 'notifly/counter'
4
+ //= require 'notifly/get_notifications'
5
+ //= require 'twitter/bootstrap'
@@ -0,0 +1,23 @@
1
+ <% notifly = Notifly::Engine.routes.url_helpers %>
2
+ var notiflyCounterRequested;
3
+
4
+ var _notiflyGetCounter = function (notiflyPath) {
5
+ var $counter = $('#notifly-counter');
6
+
7
+ if (!notiflyCounterRequested) {
8
+ $.ajax({
9
+ url: '<%= notifly.counter_notifications_path %>',
10
+ type: 'GET',
11
+ error: _notiflyHandleCounterError
12
+ });
13
+ }
14
+ };
15
+
16
+
17
+ var _notiflyHandleCounterError = function () {
18
+ notiflyCounterRequested = false;
19
+ };
20
+
21
+ $(document).ready(function() {
22
+ _notiflyGetCounter();
23
+ });
@@ -0,0 +1,26 @@
1
+ <% notifly = Notifly::Engine.routes.url_helpers %>
2
+ var notiflyNotificationsRequested;
3
+
4
+ var _notiflyGetNotifications = function () {
5
+ var $notifly = $('#notifly-icon');
6
+
7
+ $notifly.click(function () {
8
+ if (!notiflyNotificationsRequested) {
9
+ notiflyNotificationsRequested = true;
10
+
11
+ $.ajax({
12
+ url: '<%= notifly.notifications_path %>',
13
+ type: 'GET',
14
+ error: _notiflyHandleNotificationsError
15
+ });
16
+ }
17
+ });
18
+ };
19
+
20
+ var _notiflyHandleNotificationsError = function () {
21
+ notiflyNotificationsRequested = false;
22
+ };
23
+
24
+ $(document).ready(function() {
25
+ _notiflyGetNotifications();
26
+ });
@@ -0,0 +1,5 @@
1
+ /*
2
+ *= require notifly/layout
3
+ *= require twitter/bootstrap
4
+ *= require font-awesome
5
+ */
@@ -0,0 +1,50 @@
1
+ .notifly-counter-label {
2
+ padding: 3px 7px 3px 7px;
3
+ background: #cc0000;
4
+ color: #ffffff;
5
+ font-weight: bold;
6
+ border-radius: 9px;
7
+ -moz-border-radius: 9px;
8
+ -webkit-border-radius: 9px;
9
+ position: absolute;
10
+ margin-top: -8px;
11
+ margin-left: 12px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ #notifly-notifications-panel {
16
+ padding: 0px !important;
17
+ border: 0px !important;
18
+ }
19
+
20
+ #notifly-notifications-header {
21
+ padding: 9px;
22
+ font-weight: bold;
23
+ font-size: 13px;
24
+ border-bottom: 1px solid #dddddd;
25
+ }
26
+
27
+ #notifly-notifications-footer {
28
+ padding: 9px;
29
+ text-align: center;
30
+ font-weight: bold;
31
+ font-size: 12px;
32
+ border-top: 1px solid #dddddd;
33
+ }
34
+
35
+ .notifly-notification-not-read {
36
+ background-color: #e9eaed;
37
+ }
38
+
39
+ .notifly-notification {
40
+ padding: 9px;
41
+ }
42
+
43
+ #notifly-notifications-panel {
44
+ width: 430px;
45
+ }
46
+
47
+ #notifly-notifications-content {
48
+ overflow-y: scroll;
49
+ max-height: 300px;
50
+ }
@@ -0,0 +1,4 @@
1
+ module Notifly
2
+ class ApplicationController < ::ApplicationController
3
+ end
4
+ end
@@ -0,0 +1,36 @@
1
+ require_dependency "notifly/application_controller"
2
+
3
+ module Notifly
4
+ class NotificationsController < ApplicationController
5
+ def counter
6
+ @counter = count_unseen
7
+ end
8
+
9
+ def index
10
+ @notifications = current_user_notifications.page(params[:page]).per(Notifly.per_page)
11
+ Notifly::Notification.where(id: @notifications.map(&:id)).update_all(seen: true)
12
+ @counter = count_unseen
13
+ end
14
+
15
+
16
+ def read_specific
17
+ size = params[:pages].to_i * Notifly.per_page
18
+ @notifications = current_user_notifications.limit(size)
19
+ @notifications.update_all read: true
20
+ end
21
+
22
+ def toggle_read
23
+ @notification = Notifly::Notification.find(params[:notification_id])
24
+ @notification.update(read: !@notification.read)
25
+ end
26
+
27
+ private
28
+ def current_user_notifications
29
+ Notifly::Notification.all_from(current_user).order('created_at DESC')
30
+ end
31
+
32
+ def count_unseen
33
+ Notifly::Notification.unseen_from(current_user).count
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ module Notifly
2
+ module ViewHelper
3
+ def notiflies
4
+ notiflies_for current_user
5
+ end
6
+
7
+ def notiflies_for(receiver)
8
+ render partial: 'notifly/layouts/notifly', locals: { receiver: receiver }
9
+ end
10
+
11
+ def notifly_icon(have_notifications=false)
12
+ icon = have_notifications ? Notifly.icon : Notifly.icon_empty
13
+ size = Notifly.icon_size
14
+ fa_icon "#{icon} #{size}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ module Notifly
2
+ class Notification < ActiveRecord::Base
3
+ belongs_to :target, polymorphic: true
4
+ belongs_to :sender, polymorphic: true
5
+ belongs_to :receiver, polymorphic: true
6
+
7
+ before_validation :convert_data, :set_template
8
+
9
+ scope :all_from, ->(receiver) { where(receiver: receiver) }
10
+ scope :unseen_from, ->(receiver) { where(receiver: receiver, seen: false) }
11
+
12
+ validates :receiver, :template, presence: true
13
+
14
+ def data
15
+ YAML.load(read_attribute(:data))
16
+ end
17
+
18
+ private
19
+ def convert_data
20
+ self.data = read_attribute(:data).to_json
21
+ end
22
+
23
+ def set_template
24
+ self.template ||= :default
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ <!-- This partial requires the locals: notification -->
2
+
3
+ <% link_text = notification.read ? 'unread' : 'read' %>
4
+ <%= link_to link_text, notification_toggle_read_path(notification), method: :put, remote: true %>
@@ -0,0 +1,4 @@
1
+ <!-- This partial requires the locals: counter -->
2
+ <span class="notifly-counter-label <%= counter > 0 ? '' : 'hide' %>">
3
+ <%= counter %>
4
+ </span>
@@ -0,0 +1,3 @@
1
+ <!-- This partial requires the locals: notifications -->
2
+
3
+ <%= link_to_next_page notifications, 'More', remote: true, id: 'notifly-next-page' %>
@@ -0,0 +1,6 @@
1
+ <!-- This partial requires the locals: notifications -->
2
+
3
+ <% notifications.each do |notification| %>
4
+ <%= render partial: 'notifly/layouts/notification',
5
+ locals: { notification: notification } %>
6
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <!-- This partial requires the locals: notification -->
2
+
3
+ <% read = notification.read ? '' : 'notifly-notification-not-read' %>
4
+
5
+ <div id='<%= "notifly-notification-#{notification.id}" %>' class="notifly-notification <%= read %>">
6
+ <%= render partial: "notifly/templates/#{notification.template}",
7
+ locals: { notification: notification } %>
8
+
9
+ <span class="notifly-notification-actions">
10
+ <%= render partial: 'notifly/layouts/actions', locals: { notification: notification } %>
11
+ </span>
12
+ </div>
@@ -0,0 +1,25 @@
1
+ <!-- This partial requires the locals: receiver -->
2
+
3
+ <div id="notifly" class="dropdown">
4
+
5
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">
6
+ <span id="notifly-counter"></span>
7
+ <span id="notifly-icon"><%= notifly_icon %></span>
8
+ </a>
9
+
10
+
11
+ <div id="notifly-notifications-panel" class="dropdown-menu">
12
+ <div id="notifly-notifications-header">
13
+ Notifications
14
+
15
+ <span id="notifly-header-actions" class="pull-right">
16
+ </span>
17
+ </div>
18
+
19
+ <div id="notifly-notifications-content">
20
+ <div class="loading">Loading notifications...</div>
21
+ </div>
22
+
23
+ <div id="notifly-notifications-footer"></div>
24
+ </div>
25
+ </div>
@@ -0,0 +1,2 @@
1
+ $('#notifly-counter').html("<%= escape_javascript(render 'notifly/layouts/counter', counter: @counter)%>");
2
+ $('#notifly-icon').html("<%= escape_javascript notifly_icon(@counter > 0) %>")
@@ -0,0 +1,20 @@
1
+ <%
2
+ rendered_notifications = render partial: 'notifly/layouts/index',
3
+ locals: { notifications: @notifications }
4
+
5
+ notifications_pages = { pages: @notifications.current_page }
6
+ rendered_header_actions = link_to 'Mark as read', read_specific_notifications_path(notifications_pages),
7
+ remote: true, method: :put
8
+
9
+ rendered_footer = render partial: 'notifly/layouts/footer',
10
+ locals: { notifications: @notifications }
11
+ %>
12
+
13
+ $('#notifly-header-actions').html("<%= escape_javascript rendered_header_actions %>");
14
+
15
+ $('#notifly-notifications-content .loading').remove();
16
+ $('#notifly-notifications-content').append("<%= escape_javascript rendered_notifications %>");
17
+ $('#notifly-notifications-footer').html("<%= escape_javascript rendered_footer %>");
18
+
19
+ $('#notifly-counter').html("<%= escape_javascript(render 'notifly/layouts/counter', counter: @counter)%>");
20
+ $('#notifly-icon').html("<%= escape_javascript notifly_icon(@counter > 0) %>")
@@ -0,0 +1,4 @@
1
+ <% rendered_index = render partial: 'notifly/layouts/index',
2
+ locals: { notifications: @notifications } %>
3
+
4
+ $('#notifly-notifications-content').html("<%= escape_javascript rendered_index %>");
@@ -0,0 +1,5 @@
1
+ <% rendered_notification = render partial: 'notifly/layouts/notification',
2
+ locals: { notification: @notification } %>
3
+
4
+ $('<%= "#notifly-notification-#{@notification.id}" %>')
5
+ .replaceWith('<%= escape_javascript rendered_notification %>')
@@ -0,0 +1,3 @@
1
+ <!-- This partial requires the locals: notification -->
2
+
3
+ <%= notification.receiver.name %>, you receive a new notification at <%= notification.created_at %>
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Notifly::Engine.routes.draw do
2
+ resources :notifications, only: [:index] do
3
+ put :toggle_read
4
+
5
+ collection do
6
+ get :counter
7
+ put :update_counter
8
+ put :read_specific
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ class CreateNotiflyNotifications < ActiveRecord::Migration
2
+ def change
3
+ create_table :notifly_notifications do |t|
4
+ t.string :template
5
+ t.boolean :read, default: false
6
+ t.references :target, index: true, polymorphic: true
7
+ t.references :sender, index: true, polymorphic: true
8
+ t.references :receiver, index: true, polymorphic: true
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class AddDataToNotification < ActiveRecord::Migration
2
+ def change
3
+ add_column :notifly_notifications, :data, :text
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddSeenToNotiflyNotification < ActiveRecord::Migration
2
+ def change
3
+ add_column :notifly_notifications, :seen, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ require File.expand_path('../utils', __FILE__)
2
+
3
+ module Notifly
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ include Generators::Utils::InstanceMethods
7
+ source_root File.expand_path('../templates', __FILE__)
8
+ argument :namespace, type: :string, required: false, desc: 'Notifly url namespace'
9
+
10
+ desc 'Notifly installation generator'
11
+
12
+ def mount_engine
13
+ namespace = ask_for('Where do you want to mount Notifly?', 'notifly', namespace)
14
+ route("mount Notifly::Engine => '/#{namespace}', as: 'notifly'")
15
+ end
16
+
17
+ def copy_config
18
+ template "config/initializers/notifly.rb"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+ Notifly.setup do |config|
2
+ # Define how many notifications per page.
3
+ config.per_page = 10
4
+ end
@@ -0,0 +1,21 @@
1
+ # github.com/sferik/rails_admin/blob/master/lib/generators/rails_admin/utils.rb
2
+
3
+ module Notifly
4
+ module Generators
5
+ module Utils
6
+ module InstanceMethods
7
+ def display(output, color = :green)
8
+ say(" - #{output}", color)
9
+ end
10
+
11
+ def ask_for(wording, default_value = nil, override_if_present_value = nil)
12
+ if override_if_present_value.present?
13
+ display("Using [#{override_if_present_value}] for question '#{wording}'") && override_if_present_value
14
+ else
15
+ ask(" ? #{wording} Press <enter> for [#{default_value}] >", :yellow).presence || default_value
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ This generator will create Notifly views in your application
3
+
4
+ Example:
5
+ rails generate notifly:views:templates
6
+
7
+ This will create:
8
+ app/views/notifly/templates/default.html.erb
@@ -0,0 +1,22 @@
1
+ module Notifly
2
+ module Generators
3
+ class ViewsGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../../../../../app/views/notifly', __FILE__)
5
+ class_option :layout, type: :boolean, default: false, desc: 'Include layout files.'
6
+
7
+ def copy_views
8
+ copy_file 'templates/_default.html.erb', "#{main_app_path}/templates/_default.html.erb"
9
+ layout_files if options.layout?
10
+ end
11
+
12
+ private
13
+ def main_app_path
14
+ 'app/views/notifly'
15
+ end
16
+
17
+ def layout_files
18
+ directory 'layouts', "#{main_app_path}/layouts"
19
+ end
20
+ end
21
+ end
22
+ end
data/lib/notifly.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'notifly/engine'
2
+ require 'notifly/railtie'
3
+ require 'font-awesome-rails'
4
+
5
+ module Notifly
6
+ # How many notifications per page.
7
+ mattr_accessor :per_page
8
+ @@per_page = 10
9
+
10
+ mattr_accessor :icon_size
11
+ @@icon_size = '2x'
12
+
13
+ mattr_accessor :icon
14
+ @@icon = 'bell'
15
+
16
+ mattr_accessor :icon_empty
17
+ @@icon_empty = 'bell-o'
18
+
19
+ # Default way to setup Notifly. Run rails generate notifly:install to create
20
+ # a fresh initializer with all configuration values.
21
+ def self.setup
22
+ yield self
23
+ end
24
+ end
@@ -0,0 +1,22 @@
1
+ require 'jquery-rails'
2
+ require 'kaminari'
3
+
4
+ module Notifly
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Notifly
7
+
8
+ config.generators do |g|
9
+ g.test_framework :rspec
10
+ end
11
+
12
+ # http://pivotallabs.com/leave-your-migrations-in-your-rails-engines/
13
+
14
+ initializer :append_migrations do |app|
15
+ unless app.root.to_s.match root.to_s
16
+ config.paths["db/migrate"].expanded.each do |expanded_path|
17
+ app.config.paths["db/migrate"] << expanded_path
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'notifiable'
2
+ require_relative 'flyable'
3
+
4
+ module Notifly
5
+ module Models
6
+ module Base
7
+ extend ActiveSupport::Concern
8
+
9
+ include Notifly::Models::Notifiable
10
+ include Notifly::Models::Flyable
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,127 @@
1
+ require_relative 'options/fly'
2
+
3
+ module Notifly
4
+ module Models
5
+ module Flyable
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ attr_reader :flies, :default_fly, :flyable_callbacks
10
+
11
+ def notifly(options = {})
12
+ @flies ||= []
13
+ @flyable_callbacks ||= []
14
+
15
+ fly = Notifly::Models::Options::Fly.new options
16
+
17
+ if options[:default_values]
18
+ @default_fly = fly
19
+ else
20
+ @flies << fly
21
+
22
+ if [:create, :save, :update, :destroy].include? fly.method_name
23
+ _create_callback_for_active_record_from fly
24
+ end
25
+ end
26
+ end
27
+
28
+ def method_added(method_name)
29
+ _create_callbacks_for method_name
30
+ super
31
+ end
32
+
33
+ private
34
+ def _create_callbacks_for(method_name)
35
+ method_flies = _flies_for method_name
36
+
37
+ method_flies.each do |fly|
38
+ _create_callback_for_instance_method_from(fly)
39
+ end
40
+ end
41
+
42
+ def _flies_for(method_name)
43
+ if flies.present?
44
+ flies.select { |fly| fly.method_name == method_name }
45
+ else
46
+ []
47
+ end
48
+ end
49
+
50
+ def _create_callback_for_active_record_from(fly)
51
+ callback_name = "#{fly.hook}_#{fly.method_name}"
52
+ flyable_callbacks << "#{callback_name}_#{fly.object_id}"
53
+
54
+ send(callback_name, if: fly.if, unless: fly.unless) do |record|
55
+ _create_notification_for(fly)
56
+ end
57
+ end
58
+
59
+ def _create_callback_for_instance_method_from(fly)
60
+ notifly_callback_name = _format_callback_name_for(fly)
61
+
62
+ if not flyable_callbacks.include? notifly_callback_name
63
+ flyable_callbacks << notifly_callback_name
64
+
65
+ define_callbacks notifly_callback_name
66
+ set_callback notifly_callback_name, fly.hook, if: fly.if, unless: fly.unless do |record|
67
+ _create_notification_for(fly)
68
+ end
69
+
70
+ old_method = instance_method(fly.method_name)
71
+
72
+ define_method(fly.method_name) do |*args|
73
+ run_callbacks(notifly_callback_name) do
74
+ old_method.bind(self).call(*args)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ def _format_callback_name_for(fly)
81
+ ending_chars = {
82
+ '!' => :_dangerous,
83
+ '?' => :_question
84
+ }
85
+
86
+ method_name = fly.method_name.to_s.gsub(/(?<char>[\?|\!])/, ending_chars)
87
+
88
+ "notifly_#{fly.hook}_#{method_name}_#{fly.object_id}"
89
+ end
90
+ end
91
+
92
+ def _create_notification_for(fly)
93
+ new_fly = _default_fly.merge(fly)
94
+ Notifly::Notification.create! _get_attributes_from(new_fly)
95
+ end
96
+
97
+ def notifly_notifications(options={})
98
+ Notifly::Notification.where(receiver: self)
99
+ end
100
+
101
+ private
102
+ def _default_fly
103
+ self.class.default_fly || Notifly::Models::Options::Fly.new
104
+ end
105
+
106
+ def _get_attributes_from(fly)
107
+ evaluated_attributes = {}
108
+
109
+ fly.attributes.each do |key, value|
110
+ evaluated_attributes[key] = _eval_for(key, value)
111
+ end
112
+
113
+ evaluated_attributes
114
+ end
115
+
116
+ def _eval_for(key, value)
117
+ if key.to_sym == :template
118
+ value
119
+ elsif value == :self
120
+ self
121
+ else
122
+ send(value)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,11 @@
1
+ module Notifly
2
+ module Models
3
+ module Notifiable
4
+ extend ActiveSupport::Concern
5
+
6
+ def notifly!(args={})
7
+ Notifly::Notification.create! args.merge(receiver: self)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ module Notifly
2
+ module Models
3
+ module Options
4
+ class Fly
5
+ attr_accessor :before, :after, :template, :sender, :receiver, :target,
6
+ :if, :unless, :data
7
+
8
+ def initialize(options={})
9
+ options = options.fetch(:default_values, options)
10
+ options.each { |key, value| try "#{key}=", value }
11
+ end
12
+
13
+ def hook
14
+ if @before.nil?
15
+ :after
16
+ else
17
+ :before
18
+ end
19
+ end
20
+
21
+ def method_name
22
+ self.send(hook)
23
+ end
24
+
25
+ def attributes
26
+ instance_values.reject { |key| [hook, :if, :unless].include? key.to_sym }
27
+ end
28
+
29
+ def merge(fly)
30
+ raise TypeError, "#{fly} is not a Fly" unless fly.is_a? self.class
31
+
32
+ Notifly::Models::Options::Fly.new attributes.merge(fly.attributes)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../../app/helpers/notifly/view_helper'
2
+ require_relative 'models/base'
3
+
4
+ module Notifly
5
+ class Railtie < Rails::Railtie
6
+ initializer 'Notifly.view_helpers' do
7
+ ActionView::Base.send :include, ViewHelper
8
+ end
9
+
10
+ initializer 'Notifly.active_model_helpers' do
11
+ ActiveRecord::Base.send :include, Notifly::Models::Base
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Notifly
2
+ VERSION = '0.0.3'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :notifly do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: notifly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Pedro Passalini
8
+ - Rafael Carvalho
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-11-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '4'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '4'
28
+ - !ruby/object:Gem::Dependency
29
+ name: jquery-rails
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '3.0'
35
+ - - "<"
36
+ - !ruby/object:Gem::Version
37
+ version: '5'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '3.0'
45
+ - - "<"
46
+ - !ruby/object:Gem::Version
47
+ version: '5'
48
+ - !ruby/object:Gem::Dependency
49
+ name: kaminari
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.16.1
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.16.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: font-awesome-rails
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 4.2.0
69
+ type: :runtime
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 4.2.0
76
+ - !ruby/object:Gem::Dependency
77
+ name: pg
78
+ requirement: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: rspec-rails
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.1.0
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 3.1.0
104
+ - !ruby/object:Gem::Dependency
105
+ name: capybara
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 2.4.4
111
+ type: :development
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 2.4.4
118
+ - !ruby/object:Gem::Dependency
119
+ name: poltergeist
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 1.5.1
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: 1.5.1
132
+ - !ruby/object:Gem::Dependency
133
+ name: shoulda-matchers
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: 2.7.0
139
+ type: :development
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 2.7.0
146
+ description: This project intend to offer a full notification system, back and front-end.
147
+ email:
148
+ - henrique.passalini@gmail.com
149
+ - rafael@algorich.com.br
150
+ executables: []
151
+ extensions: []
152
+ extra_rdoc_files: []
153
+ files:
154
+ - MIT-LICENSE
155
+ - README.md
156
+ - Rakefile
157
+ - app/assets/javascripts/notifly.js
158
+ - app/assets/javascripts/notifly/counter.js.erb
159
+ - app/assets/javascripts/notifly/get_notifications.js.erb
160
+ - app/assets/stylesheets/notifly.css
161
+ - app/assets/stylesheets/notifly/layout.css
162
+ - app/controllers/notifly/application_controller.rb
163
+ - app/controllers/notifly/notifications_controller.rb
164
+ - app/helpers/notifly/view_helper.rb
165
+ - app/models/notifly/notification.rb
166
+ - app/views/notifly/layouts/_actions.html.erb
167
+ - app/views/notifly/layouts/_counter.html.erb
168
+ - app/views/notifly/layouts/_footer.html.erb
169
+ - app/views/notifly/layouts/_index.html.erb
170
+ - app/views/notifly/layouts/_notification.html.erb
171
+ - app/views/notifly/layouts/_notifly.html.erb
172
+ - app/views/notifly/notifications/counter.js.erb
173
+ - app/views/notifly/notifications/index.js.erb
174
+ - app/views/notifly/notifications/read_specific.js.erb
175
+ - app/views/notifly/notifications/toggle_read.js.erb
176
+ - app/views/notifly/templates/_default.html.erb
177
+ - config/routes.rb
178
+ - db/migrate/20141103170528_create_notifly_notifications.rb
179
+ - db/migrate/20141104150224_add_data_to_notification.rb
180
+ - db/migrate/20141117193436_add_seen_to_notifly_notification.rb
181
+ - lib/generators/notifly/install/install_generator.rb
182
+ - lib/generators/notifly/install/templates/config/initializers/notifly.rb
183
+ - lib/generators/notifly/install/utils.rb
184
+ - lib/generators/notifly/views/USAGE
185
+ - lib/generators/notifly/views/views_generator.rb
186
+ - lib/notifly.rb
187
+ - lib/notifly/engine.rb
188
+ - lib/notifly/models/base.rb
189
+ - lib/notifly/models/flyable.rb
190
+ - lib/notifly/models/notifiable.rb
191
+ - lib/notifly/models/options/fly.rb
192
+ - lib/notifly/railtie.rb
193
+ - lib/notifly/version.rb
194
+ - lib/tasks/notifly_tasks.rake
195
+ homepage: https://github.com/algorich/notifly
196
+ licenses:
197
+ - MIT
198
+ metadata: {}
199
+ post_install_message:
200
+ rdoc_options: []
201
+ require_paths:
202
+ - lib
203
+ required_ruby_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ required_rubygems_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ requirements: []
214
+ rubyforge_project:
215
+ rubygems_version: 2.2.2
216
+ signing_key:
217
+ specification_version: 4
218
+ summary: A full notification system
219
+ test_files: []