eventifier 0.0.14 → 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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/README.textile +32 -4
- data/app/assets/javascripts/eventifier/notifications.coffee +29 -5
- data/app/assets/javascripts/eventifier/templates/dropdown.hamlc +1 -1
- data/app/assets/stylesheets/eventifier/notifications.scss +1 -1
- data/app/controllers/eventifier/preferences_controller.rb +7 -9
- data/app/helpers/eventifier/notification_helper.rb +1 -1
- data/app/helpers/eventifier/path_helper.rb +2 -2
- data/app/models/eventifier/event.rb +1 -3
- data/app/models/eventifier/notification.rb +0 -2
- data/app/models/eventifier/notification_setting.rb +0 -2
- data/app/views/eventifier/notifications/index.jbuilder +2 -2
- data/db/migrate/5_system_events.rb +9 -0
- data/eventifier.gemspec +7 -5
- data/lib/eventifier.rb +2 -0
- data/lib/eventifier/event_builder.rb +4 -1
- data/lib/eventifier/notification_translator.rb +27 -5
- data/lib/eventifier/notifier/notification_subscriber.rb +6 -6
- data/lib/eventifier/notifier/notifier.rb +5 -5
- data/lib/eventifier/preferences.rb +14 -0
- data/lib/eventifier/tracker.rb +7 -0
- data/lib/generators/eventifier/install/install_generator.rb +1 -1
- data/lib/generators/eventifier/install/templates/{events.en.yaml → events.en.yml} +3 -0
- data/spec/controllers/eventifier/notifications_controller_spec.rb +3 -1
- data/spec/controllers/eventifier/preferences_controller_spec.rb +5 -23
- data/spec/event_tracking_spec.rb +2 -2
- data/spec/eventifier/notification_translator_spec.rb +118 -0
- data/spec/eventifier/preferences_spec.rb +47 -10
- data/spec/eventifier/relationship_spec.rb +8 -8
- data/spec/eventifier_spec.rb +14 -10
- data/spec/helpers/eventifier/notification_helper_spec.rb +13 -13
- data/spec/helpers/eventifier/path_helper_spec.rb +3 -3
- data/spec/integration/eventifier_spec.rb +11 -11
- data/spec/integration/internationalisation_spec.rb +4 -4
- data/spec/integration/system_events_spec.rb +40 -0
- data/spec/models/eventifier/event_spec.rb +1 -1
- data/spec/models/eventifier/ghost_spec.rb +3 -4
- data/spec/models/eventifier/notification_spec.rb +3 -3
- data/spec/notifier/notification_mapping_spec.rb +3 -3
- metadata +61 -41
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86d60265a4396c1ceb7dbf3a127244ccd8dd2544
|
4
|
+
data.tar.gz: 64b679101dc154bd27a9dae71e152c20ed469446
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e90e8779758969401a869fdd588de7647c9be752534e3ffcbe028c7d36401f7db1816dd8742392440e6ad291192ca1260d16b5b2f10fafeb632c68270382eb4
|
7
|
+
data.tar.gz: b2c9601e599c49d40808265111d2226b13379f5ebd3d465f40aed21235d041614471cc049fb3563f176745c19c439624661a15b252dfa3c430021946f9fd3d38
|
data/Gemfile
CHANGED
data/README.textile
CHANGED
@@ -17,13 +17,14 @@ class EventTracking
|
|
17
17
|
|
18
18
|
def initialize
|
19
19
|
events_for Post do
|
20
|
-
track_on [:create, :update, :destroy], :
|
21
|
-
notify :
|
20
|
+
track_on [:create, :update, :destroy], attributes: { except: %w(updated_at) }
|
21
|
+
notify group: :members, on: :create
|
22
|
+
notify group: :commenters, on: :update, unless: ->post,author { post.age > 1.week }
|
22
23
|
end
|
23
24
|
|
24
25
|
events_for Announcement do
|
25
|
-
track_on :create
|
26
|
-
notify :
|
26
|
+
track_on :create
|
27
|
+
notify group: :members, on: :create, if: ->announcement,admin { announcement.is_important? }
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -90,6 +91,21 @@ h4. Customise views
|
|
90
91
|
#{object.user.username} commented on your post
|
91
92
|
</code></pre>
|
92
93
|
|
94
|
+
You can create a custom view for each context
|
95
|
+
|
96
|
+
Dropdown notification: app/views/eventifier/dropdown/_comment.haml
|
97
|
+
Email notification: app/views/eventifier/email/_comment.haml
|
98
|
+
|
99
|
+
Helpers made available to these views are:
|
100
|
+
|
101
|
+
*notification*: The notification object
|
102
|
+
Eventifier::Notification(event: event, user: user_being_notified, parent: parent_notification, sent: email_sent_flag)
|
103
|
+
|
104
|
+
*event*: The event object
|
105
|
+
Eventifier::Event(user: event_owner, eventable: object_for_event, verb: [:create, :update, :destroy], change_data: {"name" => ["Bill", "Bob"]}, groupable: grouped_object)
|
106
|
+
|
107
|
+
*object*: The object the event was created for
|
108
|
+
|
93
109
|
h2. Sending of emails
|
94
110
|
|
95
111
|
Firstly, you'll need to set the FROM address for the Eventifier mailer
|
@@ -106,6 +122,18 @@ You want to add a scheduled task to run the following task every x minutes
|
|
106
122
|
rake eventifier:email:deliver
|
107
123
|
</code></pre>
|
108
124
|
|
125
|
+
h4. Customise email settings descriptions
|
126
|
+
|
127
|
+
<pre><code>
|
128
|
+
# config/locales/events.en.yml
|
129
|
+
en:
|
130
|
+
events:
|
131
|
+
labels:
|
132
|
+
preferences:
|
133
|
+
default: "All notifications"
|
134
|
+
create_relationships_notify_followed: "When you get a new follower"
|
135
|
+
</code></pre>
|
136
|
+
|
109
137
|
h2. Requirements
|
110
138
|
|
111
139
|
* ActiveRecord
|
@@ -9,9 +9,10 @@ class window.NotificationDropdown
|
|
9
9
|
settingsTemplate: JST['eventifier/templates/settings']
|
10
10
|
|
11
11
|
constructor: (options) ->
|
12
|
-
{@el, @limit, @pollTime} = options
|
12
|
+
{@el, @limit, @pollTime, @push} = options
|
13
13
|
@limit = @limit || 5
|
14
14
|
@pollTime = @pollTime || 15
|
15
|
+
@push = @push || false
|
15
16
|
|
16
17
|
|
17
18
|
[@notifications, @renderedNotifications, @unreadCount, @lastReadAt] = [[], [], 0, new Date()]
|
@@ -23,6 +24,7 @@ class window.NotificationDropdown
|
|
23
24
|
@el.html(@template(@)).attr('tabindex', 0)
|
24
25
|
|
25
26
|
# @checkVisibility()
|
27
|
+
|
26
28
|
@setEvents()
|
27
29
|
@poll()
|
28
30
|
|
@@ -48,11 +50,22 @@ class window.NotificationDropdown
|
|
48
50
|
@el.on 'addNotifications', @renderNotifications
|
49
51
|
@el.on 'addNotifications', @setUnreadCount
|
50
52
|
@el.on 'poll', @poll
|
51
|
-
@el.on 'scroll',
|
53
|
+
@el.find('ol.notifications_dropdown_list').on 'scroll', @scrolling
|
52
54
|
$(window).on 'click', @blurNotifications
|
55
|
+
if @push
|
56
|
+
@el.on 'click', '#notification_dropdown ol a', @pushUrl
|
53
57
|
|
54
58
|
@
|
55
59
|
|
60
|
+
pushUrl: (e)=>
|
61
|
+
location = $(e.currentTarget).attr('href')
|
62
|
+
location = $('<a />').attr(href: location).get(0).pathname if location.match /^https?\:\/\//
|
63
|
+
|
64
|
+
Backbone?.history.navigate(location, true) || history.pushState({trigger: true}, '', location)
|
65
|
+
@hide()
|
66
|
+
|
67
|
+
false
|
68
|
+
|
56
69
|
renderNotifications: =>
|
57
70
|
@el.find(".none").remove() if @notifications.length > 0
|
58
71
|
$.each @notifications, (index, notification)=>
|
@@ -150,6 +163,17 @@ class window.NotificationDropdown
|
|
150
163
|
@el.addClass('alerting')
|
151
164
|
|
152
165
|
@el.find(".notification_alert").html(displayCount)
|
166
|
+
$('title').html (index, old_html) ->
|
167
|
+
if old_html.match /^\(\d+\).*/
|
168
|
+
if displayCount > 0
|
169
|
+
old_html.replace(/^\(\d+\)/, "(#{displayCount})");
|
170
|
+
else
|
171
|
+
old_html.replace(/^(\(\d+\))\s/, "");
|
172
|
+
else
|
173
|
+
if displayCount > 0
|
174
|
+
"(#{displayCount}) #{old_html}"
|
175
|
+
else
|
176
|
+
old_html
|
153
177
|
|
154
178
|
setUnreadCount: =>
|
155
179
|
@unreadCount = $.grep(@notifications, (notification)=>
|
@@ -187,13 +211,13 @@ class window.NotificationDropdown
|
|
187
211
|
0
|
188
212
|
|
189
213
|
lastLookTime: =>
|
190
|
-
Math.max(@lastReadAt.getTime()/1000, @newestNotificationTime())
|
214
|
+
Math.max(@lastReadAt.getTime()/1000, @newestNotificationTime()/1000)
|
191
215
|
|
192
216
|
scrolling: =>
|
193
|
-
scrollWindow =
|
217
|
+
scrollWindow = @el.find('ol')
|
194
218
|
|
195
219
|
if (scrollWindow.scrollTop() + scrollWindow.innerHeight() >= scrollWindow[0].scrollHeight - 50)
|
196
|
-
@loadMore(after: @oldestNotificationTime())
|
220
|
+
@loadMore(after: @oldestNotificationTime()/1000)
|
197
221
|
|
198
222
|
arrayFromObject: (collection)->
|
199
223
|
serializedObject = {}
|
@@ -1,19 +1,17 @@
|
|
1
1
|
class Eventifier::PreferencesController < Eventifier::ApplicationController
|
2
2
|
def show
|
3
|
-
render :json =>
|
3
|
+
render :json => preferences.to_hashes
|
4
4
|
end
|
5
5
|
|
6
6
|
def update
|
7
|
-
|
8
|
-
settings.preferences['email'] ||= {}
|
9
|
-
params[:preferences] ||= {}
|
7
|
+
preferences.update params[:preferences] || {}
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
end
|
9
|
+
render :json => {'status' => 'OK'}
|
10
|
+
end
|
14
11
|
|
15
|
-
|
12
|
+
private
|
16
13
|
|
17
|
-
|
14
|
+
def preferences
|
15
|
+
Eventifier::Preferences.new(current_user)
|
18
16
|
end
|
19
17
|
end
|
@@ -37,7 +37,7 @@ module Eventifier
|
|
37
37
|
else
|
38
38
|
key = "events.#{event.eventable_type.downcase}.#{event.verb}"
|
39
39
|
end
|
40
|
-
message = I18n.translate key, :default => :"events.default.#{event.verb}", "user.name" => event.user.name, :"event.type" => event.eventable_type
|
40
|
+
message = I18n.translate key, :default => :"events.default.#{event.verb}", "user.name" => event.user.try(:name), :"event.type" => event.eventable_type
|
41
41
|
|
42
42
|
replace_vars(message, event).html_safe
|
43
43
|
end
|
@@ -5,11 +5,11 @@ module Eventifier
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def partial_path notification, context = nil
|
8
|
-
if lookup_context.exists?(notification.event.eventable_type.underscore, [:eventifier, context].compact.join("/"), true)
|
8
|
+
if lookup_context.exists?(notification.event.eventable_type.underscore, [[:eventifier, context].compact.join("/")], true)
|
9
9
|
[:eventifier, context, notification.event.eventable_type.underscore].compact.join("/")
|
10
10
|
else
|
11
11
|
[:eventifier, context, 'notification'].compact.join("/")
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end
|
@@ -2,15 +2,13 @@ module Eventifier
|
|
2
2
|
class Event < ActiveRecord::Base
|
3
3
|
self.table_name = 'eventifier_events'
|
4
4
|
|
5
|
-
attr_accessible :user, :eventable, :verb, :change_data, :groupable
|
6
|
-
|
7
5
|
belongs_to :user, class_name: Eventifier.user_model_name
|
8
6
|
belongs_to :eventable, polymorphic: true
|
9
7
|
belongs_to :groupable, polymorphic: true
|
10
8
|
has_many :notifications, class_name: 'Eventifier::Notification',
|
11
9
|
dependent: :destroy
|
12
10
|
|
13
|
-
validates :user, presence: true
|
11
|
+
validates :user, presence: true, unless: :system?
|
14
12
|
validates :eventable, presence: true
|
15
13
|
validates :verb, presence: true
|
16
14
|
validates :groupable, presence: true
|
@@ -1,6 +1,6 @@
|
|
1
|
-
json.last_read_at current_user.notifications_last_read_at.to_i
|
1
|
+
json.last_read_at current_user.notifications_last_read_at.to_i*1000
|
2
2
|
json.notifications @notifications do |notification|
|
3
3
|
json.(notification, :id)
|
4
|
-
json.created_at notification.created_at.to_i
|
4
|
+
json.created_at notification.created_at.to_i*1000
|
5
5
|
json.html render_partial_view(notification, :dropdown)
|
6
6
|
end
|
data/eventifier.gemspec
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = "eventifier"
|
4
|
-
s.version = '0.0
|
4
|
+
s.version = '0.1.0'
|
5
5
|
s.authors = ["Nathan Sampimon", "Peter Murray", "Pat Allan"]
|
6
6
|
s.email = ["nathan@inspire9.com"]
|
7
7
|
s.homepage = "http://github.com/inspire9/eventifier"
|
8
8
|
s.summary = "Event tracking and notifying for active record models"
|
9
9
|
s.description = "Tracks and logs events and sends notifications of events on Active Record models."
|
10
|
+
s.license = 'MIT'
|
10
11
|
|
11
12
|
s.rubyforge_project = "eventifier"
|
12
13
|
|
@@ -15,17 +16,18 @@ Gem::Specification.new do |s|
|
|
15
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
16
17
|
s.require_paths = ["lib"]
|
17
18
|
|
18
|
-
s.add_runtime_dependency 'rails', '~>
|
19
|
+
s.add_runtime_dependency 'rails', '~> 4.0.3'
|
19
20
|
s.add_runtime_dependency "bson_ext"
|
20
21
|
s.add_runtime_dependency 'haml-rails', '~> 0.4'
|
21
22
|
s.add_runtime_dependency 'haml_coffee_assets'
|
22
|
-
s.add_runtime_dependency 'coffee-rails', '~>
|
23
|
+
s.add_runtime_dependency 'coffee-rails', '~> 4.0.0'
|
23
24
|
s.add_runtime_dependency 'compass-rails'
|
24
25
|
s.add_runtime_dependency 'multi_json', '~> 1.7.4'
|
25
26
|
s.add_runtime_dependency 'jbuilder', '~> 1.4.2'
|
27
|
+
s.add_runtime_dependency 'rails-observers', '~> 0.1.2'
|
26
28
|
|
27
29
|
s.add_development_dependency 'combustion', '~> 0.5.0'
|
28
|
-
s.add_development_dependency 'fabrication', '~> 2.
|
30
|
+
s.add_development_dependency 'fabrication', '~> 2.11.0'
|
29
31
|
s.add_development_dependency "pg"
|
30
|
-
s.add_development_dependency 'rspec-rails', '~>
|
32
|
+
s.add_development_dependency 'rspec-rails', '~> 3.0.0.beta2'
|
31
33
|
end
|
data/lib/eventifier.rb
CHANGED
@@ -10,7 +10,8 @@ class Eventifier::EventBuilder
|
|
10
10
|
|
11
11
|
def store
|
12
12
|
Eventifier::Event.create user: user, eventable: object,
|
13
|
-
groupable: groupable, verb: verb, change_data: change_data
|
13
|
+
groupable: groupable, verb: verb, change_data: change_data,
|
14
|
+
system: options[:system]
|
14
15
|
end
|
15
16
|
|
16
17
|
private
|
@@ -18,6 +19,8 @@ class Eventifier::EventBuilder
|
|
18
19
|
attr_reader :object, :user, :verb, :groupable, :options
|
19
20
|
|
20
21
|
def change_data
|
22
|
+
return options[:change_data] unless options[:change_data].nil?
|
23
|
+
|
21
24
|
changes = object.changes.stringify_keys
|
22
25
|
|
23
26
|
changes.reject! { |attribute, value|
|
@@ -1,23 +1,45 @@
|
|
1
1
|
class Eventifier::NotificationTranslator
|
2
|
-
def initialize(prefix,
|
3
|
-
@prefix, @
|
2
|
+
def initialize(prefix, options, *args)
|
3
|
+
@prefix, @options = prefix, options
|
4
4
|
@event = ActiveSupport::Notifications::Event.new(*args).payload[:event]
|
5
5
|
end
|
6
6
|
|
7
7
|
def translate
|
8
|
+
return if skip?
|
8
9
|
users_and_relations do |user, relations|
|
9
|
-
next if user == event.user
|
10
|
+
next if user == event.user && !options[:notify_self]
|
11
|
+
next if skip?(user)
|
10
12
|
|
11
13
|
Eventifier::Notification.create event: event, user: user,
|
12
14
|
relations: relations
|
13
15
|
|
14
|
-
Eventifier::Delivery.deliver_for user if
|
16
|
+
Eventifier::Delivery.deliver_for user if options[:email] == :immediate
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
20
|
private
|
19
21
|
|
20
|
-
attr_reader :event, :prefix, :
|
22
|
+
attr_reader :event, :prefix, :options
|
23
|
+
|
24
|
+
def skip?(user = nil)
|
25
|
+
if conditional
|
26
|
+
!conditional_call *[event.eventable, user][0..conditional.arity-1]
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def conditional
|
33
|
+
options[:if] || options[:unless]
|
34
|
+
end
|
35
|
+
|
36
|
+
def conditional_call(*args)
|
37
|
+
if options[:if]
|
38
|
+
conditional.call(*args)
|
39
|
+
else
|
40
|
+
!conditional.call(*args)
|
41
|
+
end
|
42
|
+
end
|
21
43
|
|
22
44
|
def users_and_relations(&block)
|
23
45
|
Eventifier::NotificationMapping.users_and_relations event, prefix, &block
|
@@ -1,23 +1,23 @@
|
|
1
1
|
class Eventifier::NotificationSubscriber
|
2
|
-
def self.subscribe_to_method(klass, method_name,
|
3
|
-
new(klass, method_name,
|
2
|
+
def self.subscribe_to_method(klass, method_name, options)
|
3
|
+
new(klass, method_name, options).subscribe_to_method
|
4
4
|
end
|
5
5
|
|
6
|
-
def initialize(klass, method_name,
|
7
|
-
@klass, @method_name, @
|
6
|
+
def initialize(klass, method_name, options)
|
7
|
+
@klass, @method_name, @options = klass, method_name, options
|
8
8
|
end
|
9
9
|
|
10
10
|
def subscribe_to_method
|
11
11
|
return if notifications.notifier.listening?(name)
|
12
12
|
|
13
13
|
notifications.subscribe(name) do |*args|
|
14
|
-
Eventifier::NotificationTranslator.new(prefix,
|
14
|
+
Eventifier::NotificationTranslator.new(prefix, options, *args).translate
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
|
-
attr_reader :klass, :method_name, :
|
20
|
+
attr_reader :klass, :method_name, :options
|
21
21
|
|
22
22
|
def name
|
23
23
|
"#{prefix}.notification.eventifier"
|
@@ -1,21 +1,21 @@
|
|
1
1
|
class Eventifier::Notifier
|
2
|
+
OPTION_KEYS = [:email, :if, :unless, :notify_self]
|
2
3
|
# arguments will either be [:relation, {:on => :create}] or
|
3
4
|
# [{:relation => :second_relation, :on => :create}]
|
4
5
|
# If it's the first one, relation is the first in the array, otherwise treat
|
5
6
|
# the whole thing like a hash
|
6
7
|
def initialize(klasses, *arguments)
|
7
|
-
relation = arguments.shift if arguments.length
|
8
|
+
relation = arguments.shift if arguments.length >= 2
|
8
9
|
arguments = arguments.first || {}
|
9
10
|
|
10
11
|
methods = Array arguments.delete(:on)
|
11
|
-
|
12
|
-
|
13
|
-
relation ||= arguments
|
12
|
+
options = arguments.slice *OPTION_KEYS
|
13
|
+
relation ||= arguments.except *OPTION_KEYS
|
14
14
|
|
15
15
|
klasses.each do |target_klass|
|
16
16
|
methods.each do |method_name|
|
17
17
|
Eventifier::NotificationMapping.add "#{method_name}.#{target_klass.name.tableize}", relation
|
18
|
-
Eventifier::NotificationSubscriber.subscribe_to_method target_klass, method_name,
|
18
|
+
Eventifier::NotificationSubscriber.subscribe_to_method target_klass, method_name, options
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|