notifly 0.0.3

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 (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: []