eventifier 0.0.14 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|