notificon 0.0.1
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.
- data/lib/notificon.rb +201 -0
- data/lib/notificon/cache.rb +23 -0
- data/lib/notificon/controller.rb +44 -0
- data/lib/notificon/mongo_store.rb +53 -0
- data/lib/notificon/notification.rb +33 -0
- data/lib/notificon/notification_store.rb +46 -0
- data/lib/notificon/user_state.rb +18 -0
- data/lib/notificon/user_state_store.rb +32 -0
- data/lib/notificon/version.rb +3 -0
- data/spec/notificon/controller_spec.rb +71 -0
- data/spec/notificon/notification_spec.rb +92 -0
- data/spec/notificon/notification_store_spec.rb +172 -0
- data/spec/notificon/user_state_store_spec.rb +37 -0
- data/spec/notificon_spec.rb +122 -0
- data/spec/spec_helper.rb +26 -0
- metadata +113 -0
data/lib/notificon.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
# Public: Api for the Notificon gem for tracking notifications for users
|
2
|
+
#
|
3
|
+
# Examples
|
4
|
+
#
|
5
|
+
# Notificon.add_notification('sam', 'http://company.com/items/audha,
|
6
|
+
# 'Item title', 'bill', :commented, Time.now)
|
7
|
+
#
|
8
|
+
module Notificon
|
9
|
+
require "notificon/notification"
|
10
|
+
require "notificon/user_state"
|
11
|
+
require "notificon/mongo_store"
|
12
|
+
require "notificon/cache"
|
13
|
+
require "notificon/notification_store"
|
14
|
+
require "notificon/user_state_store"
|
15
|
+
require "notificon/controller"
|
16
|
+
require 'logger'
|
17
|
+
|
18
|
+
class << self
|
19
|
+
# Public: accessor method used to config the gem
|
20
|
+
#
|
21
|
+
# Yields the Notificon module
|
22
|
+
#
|
23
|
+
# Examples
|
24
|
+
#
|
25
|
+
# Notificon.setup do |config|
|
26
|
+
# config.connection_profile = 'mongo://myserver/notifications'
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Returns nothing
|
30
|
+
def setup
|
31
|
+
yield self
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: cache to be used
|
35
|
+
attr_accessor :cache
|
36
|
+
|
37
|
+
# Public: Returns the query string parameter used to identify the notification.
|
38
|
+
# defaults to '_notificon_id' if not set
|
39
|
+
def notification_id_param
|
40
|
+
@_notification_id_param ||= '_notificon_id'
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Returns the query string parameter used to identify the notification item.
|
44
|
+
def notification_item_id_param
|
45
|
+
"#{notification_id_param}_item"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Public: Returns the query string parameter used to identify the user.
|
49
|
+
def notification_username_param
|
50
|
+
"#{notification_id_param}_user"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Sets the String to use for the notification_id_param
|
54
|
+
def notification_id_param=(value)
|
55
|
+
raise ArgumentError.new("value must a string or nil") unless value.nil? || value.is_a?(String)
|
56
|
+
|
57
|
+
@_notification_id_param = value
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Returns the connection profile for the datastore. Currently only mongo
|
61
|
+
# supported. Defaults to mongodb://localhost/notificon_default if not set
|
62
|
+
def connection_profile
|
63
|
+
@_connection_profile ||= "mongodb://localhost/notificon_default"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: Sets the String for the connection_profile
|
67
|
+
def connection_profile=(value)
|
68
|
+
raise ArgumentError.new("value must a string or nil") unless value.nil? || value.is_a?(String)
|
69
|
+
|
70
|
+
@_connection_profile = value
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: Returns the Logger currently enabled for the gem
|
74
|
+
# If none specified, will create one pointing at STDOUT
|
75
|
+
#
|
76
|
+
# Returns the Logger currently configured
|
77
|
+
def logger
|
78
|
+
@_logger ||= build_logger
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Setter for the Logger for the gem to use. Allows host system
|
82
|
+
# loggers to be inject, eg Rails.logger
|
83
|
+
#
|
84
|
+
# value - The Logger to use
|
85
|
+
#
|
86
|
+
# Returns the assigned Logger
|
87
|
+
def logger=(value)
|
88
|
+
@_logger = value
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Mark a notification as read
|
92
|
+
#
|
93
|
+
# id - The String identifying the notification
|
94
|
+
# read_at - The Time the notitication was read
|
95
|
+
#
|
96
|
+
# Returns nothing
|
97
|
+
def mark_notification_read(id, read_at)
|
98
|
+
raise ArgumentError.new("id must be a String") unless id.is_a? String
|
99
|
+
raise ArgumentError.new("read_at must be a Time") unless read_at.is_a? Time
|
100
|
+
|
101
|
+
notification = notification_store.mark_as_read(id, read_at)
|
102
|
+
update_user_unread_counts(notification.username)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Public: Mark a notification as read
|
106
|
+
#
|
107
|
+
# username - The String identifying the user
|
108
|
+
# item_id - The String identifying the notification item
|
109
|
+
# read_at - The Time the notitication was read
|
110
|
+
#
|
111
|
+
# Returns nothing
|
112
|
+
def mark_all_read_for_item(username, item_id, read_at)
|
113
|
+
raise ArgumentError.new("username must be a String") unless username.is_a? String
|
114
|
+
raise ArgumentError.new("item_id must be a String") unless item_id.is_a? String
|
115
|
+
raise ArgumentError.new("read_at must be a Time") unless read_at.is_a? Time
|
116
|
+
|
117
|
+
notification_store.mark_all_read_for_item(username, item_id, read_at)
|
118
|
+
update_user_unread_counts(username)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Public: Add a notification to the users lists
|
122
|
+
#
|
123
|
+
# username - The String identifying the user being notified
|
124
|
+
# item_url - A String url of the item notification relates to
|
125
|
+
# item_text - A String that describes the item
|
126
|
+
# actor - The String identifying the user that performed the action
|
127
|
+
# action - A Symbol representing the action that the actor performed
|
128
|
+
# occured_at - The Time the action was performed
|
129
|
+
# item_id - A String uniquely identifying the item the notification is for
|
130
|
+
#
|
131
|
+
# Returns the String id of the Notification generated
|
132
|
+
def add_notification(username, item_url, item_text, actor, action, occured_at, item_id=nil)
|
133
|
+
raise ArgumentError.new("username must be a String") unless username.is_a? String
|
134
|
+
raise ArgumentError.new("item_url must be a String") unless item_url.is_a? String
|
135
|
+
raise ArgumentError.new("item_text must be a String") unless item_text.is_a? String
|
136
|
+
raise ArgumentError.new("actor must be a String") unless actor.is_a? String
|
137
|
+
raise ArgumentError.new("action must be a Symbol") unless action.is_a? Symbol
|
138
|
+
raise ArgumentError.new("occured_at must be a Time") unless occured_at .is_a? Time
|
139
|
+
|
140
|
+
notification_store.add(Notification.new(:username => username, :item_url => item_url,
|
141
|
+
:item_text => item_text, :actor => actor, :action => action, :occured_at => occured_at, :item_id => item_id))
|
142
|
+
update_user_unread_counts(username)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Public: Retrieve the most recent Notifications for user
|
146
|
+
#
|
147
|
+
# username - The String identifying the user
|
148
|
+
# limit - The Fixnum maximum number of items to return (default: 100)
|
149
|
+
#
|
150
|
+
# Returns Enumerable list of Notification elements
|
151
|
+
def get_notifications(username, limit=100)
|
152
|
+
raise ArgumentError.new("username must be a String") unless username.is_a? String
|
153
|
+
raise ArgumentError.new("limit must be a Fixnum") unless limit.is_a? Fixnum
|
154
|
+
|
155
|
+
notification_store.get_for_user(username, limit)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Public: Retrieve the known state of the user, ie notifcation count
|
159
|
+
#
|
160
|
+
# username - The String identifying the user
|
161
|
+
#
|
162
|
+
# Returns UserState object describing the state
|
163
|
+
def get_user_state(username)
|
164
|
+
raise ArgumentError.new("username must be a String") unless username.is_a? String
|
165
|
+
|
166
|
+
user_state_store.get(username)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Public: Clear all unread notifications for the user
|
170
|
+
#
|
171
|
+
# username - The String identifying the user
|
172
|
+
#
|
173
|
+
# Returns nothing
|
174
|
+
def clear_notifications(username)
|
175
|
+
raise ArgumentError.new("username must be a String") unless username.is_a? String
|
176
|
+
|
177
|
+
notification_store.mark_all_read_for_user(username, Time.now)
|
178
|
+
user_state_store.clear_notifications(username)
|
179
|
+
end
|
180
|
+
|
181
|
+
def update_user_unread_counts(username)
|
182
|
+
user_state_store.set_notifications(username, notification_store.unread_count_for_user(username))
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
def build_logger
|
188
|
+
logger = Logger.new(STDOUT)
|
189
|
+
logger.progname = "Notificon"
|
190
|
+
logger
|
191
|
+
end
|
192
|
+
|
193
|
+
def notification_store
|
194
|
+
@_notification_store ||= NotificationStore.new
|
195
|
+
end
|
196
|
+
|
197
|
+
def user_state_store
|
198
|
+
@_user_states_store ||= UserStateStore.new
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Private: wrapper for the assigned cache
|
2
|
+
#
|
3
|
+
module Notificon
|
4
|
+
module Cache
|
5
|
+
|
6
|
+
def cache_enabled?
|
7
|
+
Notificon.cache
|
8
|
+
end
|
9
|
+
|
10
|
+
def cache_fetch(key)
|
11
|
+
if cache_enabled?
|
12
|
+
Notificon.cache.fetch("notificon_#{key}") { yield }
|
13
|
+
else
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def cache_delete(key)
|
19
|
+
Notificon.cache.delete("notificon_#{key}") if cache_enabled?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Public : Module to include in ApplicationController to support notification read tracking
|
2
|
+
#
|
3
|
+
# class ApplicationController
|
4
|
+
# include Notificon::Controller
|
5
|
+
# before_filter :notificon_tracker
|
6
|
+
#
|
7
|
+
# end
|
8
|
+
module Notificon
|
9
|
+
module Controller
|
10
|
+
# Public : method to use as a before filter on the application controller to track read status
|
11
|
+
# of notifications
|
12
|
+
#
|
13
|
+
# Returns bookean indicating whether updates made
|
14
|
+
def notificon_tracker
|
15
|
+
request.get? && (_track_explicit_read | _track_implicit_read)
|
16
|
+
rescue => e
|
17
|
+
Notificon.logger.error { "Notificon::Controller#notificon_tracker error processing notification params - #{params.inspect} error - #{e.inspect}" }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Private : uses notificon explicit parameters to track reads for items
|
21
|
+
def _track_explicit_read
|
22
|
+
if params[Notificon.notification_item_id_param] && params[Notificon.notification_username_param]
|
23
|
+
Notificon.mark_all_read_for_item(params[Notificon.notification_username_param], params[Notificon.notification_item_id_param], _notificon_read_at)
|
24
|
+
true
|
25
|
+
elsif id = params[Notificon.notification_id_param]
|
26
|
+
Notificon.mark_notification_read(id, _notificon_read_at)
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Private : uses controller methods to track implicit reads for items
|
32
|
+
def _track_implicit_read
|
33
|
+
if respond_to?(:current_username) && respond_to?(:current_item_id) && current_username && current_item_id
|
34
|
+
Notificon.mark_all_read_for_item(current_username, current_item_id, _notificon_read_at)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Private : lazy assignment of read_at time
|
40
|
+
def _notificon_read_at
|
41
|
+
@_notificon_read_at ||= ((respond_to?(:current_time) && current_time) || Time.now)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Base module for interacting with the mongo store used for persistence
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Notificon
|
7
|
+
module MongoStore
|
8
|
+
include Mongo
|
9
|
+
|
10
|
+
def self.included(klass)
|
11
|
+
klass.class_eval do
|
12
|
+
extend ClassMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def open_connection
|
17
|
+
@db ||= open_store
|
18
|
+
end
|
19
|
+
|
20
|
+
def open_store
|
21
|
+
uri = URI.parse(Notificon.connection_profile)
|
22
|
+
Connection.from_uri(Notificon.connection_profile).db(uri.path.gsub(/^\//, ''))
|
23
|
+
end
|
24
|
+
|
25
|
+
def collection
|
26
|
+
@_collection ||= open_connection[self.class.collection_name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def drop_collection
|
30
|
+
collection.drop
|
31
|
+
@_collection = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_indexes
|
35
|
+
self.class.indexes.each do |index| collection.ensure_index(index) end
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
def set_collection_name(name)
|
40
|
+
@_collection_name = name
|
41
|
+
end
|
42
|
+
def collection_name
|
43
|
+
@_collection_name
|
44
|
+
end
|
45
|
+
def set_indexes(indexes)
|
46
|
+
@_indexes = indexes
|
47
|
+
end
|
48
|
+
def indexes
|
49
|
+
@_indexes || []
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Class describing notification objects
|
2
|
+
|
3
|
+
require 'addressable/uri'
|
4
|
+
|
5
|
+
module Notificon
|
6
|
+
class Notification
|
7
|
+
attr_accessor :id, :username, :item_url, :item_text, :actor, :action, :occured_at, :read_at, :item_id
|
8
|
+
|
9
|
+
def initialize(attrs={})
|
10
|
+
attrs.each_pair do |k,v| send("#{k}=", v) end
|
11
|
+
end
|
12
|
+
|
13
|
+
# provided to enable simple creation from mongo hash objects
|
14
|
+
def _id=(value)
|
15
|
+
@id = value.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def read?
|
19
|
+
!read_at.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def url
|
23
|
+
uri = Addressable::URI.parse(item_url)
|
24
|
+
params = {Notificon.notification_id_param => id}
|
25
|
+
if item_id
|
26
|
+
params[Notificon.notification_item_id_param] = item_id
|
27
|
+
params[Notificon.notification_username_param] = username
|
28
|
+
end
|
29
|
+
uri.query_values = (uri.query_values || {}).merge(params)
|
30
|
+
uri.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# Private: Datamapper for Notification object
|
2
|
+
module Notificon
|
3
|
+
class NotificationStore
|
4
|
+
include MongoStore
|
5
|
+
|
6
|
+
set_collection_name :notificon_notifications
|
7
|
+
set_indexes [
|
8
|
+
[['username'], ['occured_at', Mongo::DESCENDING]]
|
9
|
+
]
|
10
|
+
|
11
|
+
def add(notification)
|
12
|
+
collection.insert('username' => notification.username, 'item_url' => notification.item_url,
|
13
|
+
'item_text' => notification.item_text, 'actor' => notification.actor, 'action' => notification.action,
|
14
|
+
'occured_at' => notification.occured_at && notification.occured_at.utc, 'item_id' => notification.item_id).to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(id)
|
18
|
+
if item_hash = collection.find_one('_id' => BSON::ObjectId.from_string(id))
|
19
|
+
Notification.new(item_hash)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_for_user(username, limit)
|
24
|
+
collection.find({'username' => username}, { :limit => limit, :sort => [['occured_at', :descending]]}).collect { |item_hash|
|
25
|
+
Notification.new(item_hash)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def unread_count_for_user(username)
|
30
|
+
collection.count(:query => {'username' => username, 'read_at' => { '$exists' => false }})
|
31
|
+
end
|
32
|
+
|
33
|
+
def mark_as_read(id, read_at)
|
34
|
+
collection.update({'_id' => BSON::ObjectId.from_string(id)}, { '$set' => { 'read_at' => read_at.utc } })
|
35
|
+
get(id)
|
36
|
+
end
|
37
|
+
|
38
|
+
def mark_all_read_for_user(username, read_at)
|
39
|
+
collection.update({'username' => username, 'read_at' => { '$exists' => false } }, { '$set' => { 'read_at' => read_at.utc}}, { :multi => true})
|
40
|
+
end
|
41
|
+
|
42
|
+
def mark_all_read_for_item(username, item_id, read_at)
|
43
|
+
collection.update({'username' => username, 'item_id' => item_id, 'read_at' => { '$exists' => false } }, { '$set' => { 'read_at' => read_at.utc}}, { :multi => true})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Public: Encapsulates the user state
|
2
|
+
module Notificon
|
3
|
+
class UserState
|
4
|
+
|
5
|
+
# Public : Gets the username
|
6
|
+
attr_reader :username
|
7
|
+
|
8
|
+
# Public : Gets the number of unread notifications
|
9
|
+
def notifications
|
10
|
+
@_notifications ||= 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(attrs)
|
14
|
+
@username = attrs['username']
|
15
|
+
@_notifications = attrs['notifications']
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Private: Datamapper for Notification object
|
2
|
+
module Notificon
|
3
|
+
class UserStateStore
|
4
|
+
include MongoStore
|
5
|
+
include Cache
|
6
|
+
|
7
|
+
set_collection_name :notificon_user_state
|
8
|
+
set_indexes [
|
9
|
+
[['username']]
|
10
|
+
]
|
11
|
+
|
12
|
+
def get(username)
|
13
|
+
if item_hash = cache_fetch("user_state_#{username}") { collection.find_one('username' => username) }
|
14
|
+
UserState.new(item_hash)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_notifications(username, notifications)
|
19
|
+
collection.update({'username' => username}, { '$set' => { 'notifications' => notifications }}, { :upsert => true } )
|
20
|
+
delete_cached(username)
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear_notifications(username)
|
24
|
+
collection.update({'username' => username}, { '$set' => { 'notifications' => 0 } }, { :upsert => true })
|
25
|
+
delete_cached(username)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_cached(username)
|
29
|
+
cache_delete("user_state_#{username}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyController
|
4
|
+
include Notificon::Controller
|
5
|
+
|
6
|
+
def params
|
7
|
+
@_params ||= {}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Controller do
|
12
|
+
describe "notificon_tracker" do
|
13
|
+
before(:each) do
|
14
|
+
@controller = DummyController.new
|
15
|
+
@time = random_time
|
16
|
+
@controller.stub(:current_time) { @time }
|
17
|
+
@controller.stub(:request) { mock("Request", :get? => true) }
|
18
|
+
end
|
19
|
+
|
20
|
+
subject { @controller.notificon_tracker }
|
21
|
+
|
22
|
+
context "when id param passed" do
|
23
|
+
before(:each) do
|
24
|
+
@id = random_string
|
25
|
+
@controller.params[Notificon.notification_id_param] = @id
|
26
|
+
end
|
27
|
+
|
28
|
+
it "marks the notification as read" do
|
29
|
+
Notificon.should_receive(:mark_notification_read).with(@id, @time)
|
30
|
+
subject
|
31
|
+
end
|
32
|
+
|
33
|
+
context "when item_id param passed" do
|
34
|
+
before(:each) do
|
35
|
+
@item_id = random_string
|
36
|
+
@username = random_string
|
37
|
+
@controller.params[Notificon.notification_item_id_param] = @item_id
|
38
|
+
@controller.params[Notificon.notification_username_param] = @username
|
39
|
+
end
|
40
|
+
it "does not attempt to mark notification" do
|
41
|
+
Notificon.should_not_receive(:mark_notification_read)
|
42
|
+
subject
|
43
|
+
end
|
44
|
+
it "marks all the notifications for the item read" do
|
45
|
+
Notificon.should_receive(:mark_all_read_for_item).with(@username, @item_id, @time)
|
46
|
+
subject
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when id param not passed" do
|
52
|
+
it "does not attempt to mark notification" do
|
53
|
+
Notificon.should_not_receive(:mark_notification_read)
|
54
|
+
subject
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when controller supports current_username and current_item_id" do
|
59
|
+
before(:each) do
|
60
|
+
@current_username = random_string
|
61
|
+
@current_item_id = random_string
|
62
|
+
@controller.stub(:current_username) { @current_username }
|
63
|
+
@controller.stub(:current_item_id) { @current_item_id }
|
64
|
+
end
|
65
|
+
it "marks all the notifications for the current_item_id read" do
|
66
|
+
Notificon.should_receive(:mark_all_read_for_item).with(@current_username, @current_item_id, @time)
|
67
|
+
subject
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
4
|
+
describe Notification do
|
5
|
+
describe "#_id=" do
|
6
|
+
before(:each) do
|
7
|
+
@notification = Notification.new
|
8
|
+
@id = random_string
|
9
|
+
end
|
10
|
+
|
11
|
+
subject { @notification._id = @id }
|
12
|
+
|
13
|
+
it "sets the id property" do
|
14
|
+
subject
|
15
|
+
@notification.id.should eq(@id)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#read?" do
|
20
|
+
before(:each) do
|
21
|
+
@notification = Notification.new
|
22
|
+
end
|
23
|
+
|
24
|
+
subject { @notification.read? }
|
25
|
+
|
26
|
+
context "when read_at is nil" do
|
27
|
+
it "should be false" do
|
28
|
+
subject.should be_false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when read_at has a value" do
|
33
|
+
before(:each) do
|
34
|
+
@notification.read_at = random_time
|
35
|
+
end
|
36
|
+
it "should be true" do
|
37
|
+
subject.should be_true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#url" do
|
43
|
+
before(:each) do
|
44
|
+
@id = random_string
|
45
|
+
@item_id = nil
|
46
|
+
@username = random_string
|
47
|
+
end
|
48
|
+
|
49
|
+
subject { Notification.new(:id => @id, :item_url => @item_url, :item_id => @item_id, :username => @username).url }
|
50
|
+
|
51
|
+
context "url with query string param" do
|
52
|
+
before(:each) do
|
53
|
+
@param = random_string
|
54
|
+
@value = random_string
|
55
|
+
@item_url = "http://#{random_string}.com?#{@param}=#{@value}"
|
56
|
+
end
|
57
|
+
it "should return url with notification param appended" do
|
58
|
+
# having to parse result to cope with order of query string params
|
59
|
+
uri = Addressable::URI.parse(subject)
|
60
|
+
uri.query_values.should eq({ @param => @value, Notificon.notification_id_param => @id })
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "url without query string param" do
|
65
|
+
before(:each) do
|
66
|
+
@item_url = "http://#{random_string}.co.uk"
|
67
|
+
end
|
68
|
+
it "should return url with notification param appended" do
|
69
|
+
subject.should eq("#{@item_url}?#{Notificon.notification_id_param}=#{@id}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "path with anchor" do
|
74
|
+
before(:each) do
|
75
|
+
@item_url = "/clubs/4f8ea8bb3d99cf0001000008/posts/4f995996cc1db30001000001#4f9fa6f673a9650001000009"
|
76
|
+
end
|
77
|
+
it "should return url" do
|
78
|
+
subject.should eq("/clubs/4f8ea8bb3d99cf0001000008/posts/4f995996cc1db30001000001?#{Notificon.notification_id_param}=#{@id}#4f9fa6f673a9650001000009")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when item_id present" do
|
83
|
+
before(:each) do
|
84
|
+
@item_url = "http://#{random_string}.co.uk"
|
85
|
+
@item_id = random_string
|
86
|
+
end
|
87
|
+
it "returns the url with both item_id and username" do
|
88
|
+
subject.should eq("#{@item_url}?#{Notificon.notification_id_param}=#{@id}&#{Notificon.notification_item_id_param}=#{@item_id}&#{Notificon.notification_username_param}=#{@username}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NotificationStore do
|
4
|
+
before(:each) do
|
5
|
+
@store = NotificationStore.new
|
6
|
+
@store.drop_collection
|
7
|
+
end
|
8
|
+
describe "#add" do
|
9
|
+
before(:each) do
|
10
|
+
@username = random_string
|
11
|
+
@item_text = random_string
|
12
|
+
@text = random_string
|
13
|
+
@actor = random_string
|
14
|
+
@action = random_string
|
15
|
+
@occured_at = random_time
|
16
|
+
@item_id = random_string
|
17
|
+
@notification = Notification.new(:username => @username, :item_url => @item_url,
|
18
|
+
:item_text => @item_text, :actor => @actor, :action => @action, :occured_at => @occured_at, :item_id => @item_id)
|
19
|
+
end
|
20
|
+
|
21
|
+
subject { @store.add(@notification) }
|
22
|
+
|
23
|
+
it "creates a record with username set" do
|
24
|
+
@store.get(subject).username.should eq(@username)
|
25
|
+
end
|
26
|
+
it "creates a record with actor set" do
|
27
|
+
@store.get(subject).actor.should eq(@actor)
|
28
|
+
end
|
29
|
+
it "creates a record with action set" do
|
30
|
+
@store.get(subject).action.should eq(@action)
|
31
|
+
end
|
32
|
+
it "creates a record with occured_at set" do
|
33
|
+
@store.get(subject).occured_at.to_i.should eq(@occured_at.to_i)
|
34
|
+
end
|
35
|
+
it "creates a record with item_url set" do
|
36
|
+
@store.get(subject).item_url.should eq(@item_url)
|
37
|
+
end
|
38
|
+
it "creates a record with item_text set" do
|
39
|
+
@store.get(subject).item_text.should eq(@item_text)
|
40
|
+
end
|
41
|
+
it "creates a record with item_id set" do
|
42
|
+
@store.get(subject).item_id.should eq(@item_id)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#get_for_user" do
|
47
|
+
before(:each) do
|
48
|
+
@username = random_string
|
49
|
+
|
50
|
+
@store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
51
|
+
@store.add(Notification.new(:username => random_string, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
52
|
+
@store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
53
|
+
end
|
54
|
+
|
55
|
+
subject { @store.get_for_user(@username, 100) }
|
56
|
+
|
57
|
+
it "returns 2 records" do
|
58
|
+
subject.count.should eq(2)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#unread_count_for_user" do
|
63
|
+
before(:each) do
|
64
|
+
@username = random_string
|
65
|
+
|
66
|
+
@store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
67
|
+
id = @store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
68
|
+
@store.add(Notification.new(:username => random_string, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
69
|
+
@store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
70
|
+
@store.mark_as_read(id, random_time)
|
71
|
+
end
|
72
|
+
|
73
|
+
subject { @store.unread_count_for_user(@username) }
|
74
|
+
|
75
|
+
it "returns 2" do
|
76
|
+
subject.should eq(2)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#mark_as_read" do
|
81
|
+
before(:each) do
|
82
|
+
@id = @store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
83
|
+
@store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
84
|
+
@read_at = random_time
|
85
|
+
end
|
86
|
+
|
87
|
+
subject { @store.mark_as_read(@id, @read_at) }
|
88
|
+
|
89
|
+
it "marks the record as read_at" do
|
90
|
+
subject
|
91
|
+
@store.get(@id).read_at.to_i.should eq(@read_at.utc.to_i)
|
92
|
+
end
|
93
|
+
it "returns the Notification" do
|
94
|
+
subject.id.should eq(@id)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#mark_all_read_for_user" do
|
99
|
+
before(:each) do
|
100
|
+
@first_id = @store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
101
|
+
@first_read_at = random_time
|
102
|
+
@store.mark_as_read(@first_id, @first_read_at)
|
103
|
+
|
104
|
+
@second_id = @store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
105
|
+
|
106
|
+
@third_id = @store.add(Notification.new(:username => random_string, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
107
|
+
|
108
|
+
@fourth_id = @store.add(Notification.new(:username => @username, :item_url => random_string, :item_text => random_string, :actor => random_string, :occured_at => random_time))
|
109
|
+
|
110
|
+
@read_at = random_time
|
111
|
+
end
|
112
|
+
|
113
|
+
subject { @store.mark_all_read_for_user(@username, @read_at) }
|
114
|
+
|
115
|
+
it "leaves the first record as already read" do
|
116
|
+
subject
|
117
|
+
@store.get(@first_id).read_at.to_i.should eq(@first_read_at.to_i)
|
118
|
+
end
|
119
|
+
it "updates the second record as not read" do
|
120
|
+
subject
|
121
|
+
@store.get(@second_id).read_at.to_i.should eq(@read_at.to_i)
|
122
|
+
end
|
123
|
+
it "leaves the third record as not for the user" do
|
124
|
+
subject
|
125
|
+
@store.get(@third_id).read_at.should be_nil
|
126
|
+
end
|
127
|
+
it "updates the fourth record as not read" do
|
128
|
+
subject
|
129
|
+
@store.get(@fourth_id).read_at.to_i.should eq(@read_at.to_i)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#mark_all_read_for_item" do
|
134
|
+
before(:each) do
|
135
|
+
@item_id = random_string
|
136
|
+
@first_id = @store.add(Notification.new(:username => @username, :item_url => random_string,
|
137
|
+
:item_text => random_string, :actor => random_string, :occured_at => random_time, :item_id => @item_id))
|
138
|
+
@first_read_at = random_time
|
139
|
+
@store.mark_as_read(@first_id, @first_read_at)
|
140
|
+
|
141
|
+
@second_id = @store.add(Notification.new(:username => @username, :item_url => random_string,
|
142
|
+
:item_text => random_string, :actor => random_string, :occured_at => random_time, :item_id => @item_id))
|
143
|
+
|
144
|
+
@third_id = @store.add(Notification.new(:username => random_string, :item_url => random_string,
|
145
|
+
:item_text => random_string, :actor => random_string, :occured_at => random_time, :item_id => @item_id))
|
146
|
+
|
147
|
+
@fourth_id = @store.add(Notification.new(:username => @username, :item_url => random_string,
|
148
|
+
:item_text => random_string, :actor => random_string, :occured_at => random_time, :item_id => random_string))
|
149
|
+
|
150
|
+
@read_at = random_time
|
151
|
+
end
|
152
|
+
|
153
|
+
subject { @store.mark_all_read_for_item(@username, @item_id, @read_at) }
|
154
|
+
|
155
|
+
it "leaves the first record as already read" do
|
156
|
+
subject
|
157
|
+
@store.get(@first_id).read_at.to_i.should eq(@first_read_at.to_i)
|
158
|
+
end
|
159
|
+
it "updates the second record as not read" do
|
160
|
+
subject
|
161
|
+
@store.get(@second_id).read_at.to_i.should eq(@read_at.to_i)
|
162
|
+
end
|
163
|
+
it "leaves the third record as not for the user" do
|
164
|
+
subject
|
165
|
+
@store.get(@third_id).read_at.should be_nil
|
166
|
+
end
|
167
|
+
it "leaves the fourth record as not for the item" do
|
168
|
+
subject
|
169
|
+
@store.get(@fourth_id).read_at.should be_nil
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe UserStateStore do
|
4
|
+
before(:each) do
|
5
|
+
@store = UserStateStore.new
|
6
|
+
@store.drop_collection
|
7
|
+
@username = random_string
|
8
|
+
@store.set_notifications(@username, 2)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#clear_notifications" do
|
12
|
+
|
13
|
+
subject { @store.clear_notifications(@username) }
|
14
|
+
|
15
|
+
it "should set the notifications to zero" do
|
16
|
+
subject
|
17
|
+
@store.get(@username).notifications.should eq(0)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#set_notifications" do
|
22
|
+
before(:each) do
|
23
|
+
@count = random_integer
|
24
|
+
end
|
25
|
+
|
26
|
+
subject { @store.set_notifications(@username, @count) }
|
27
|
+
|
28
|
+
it "should set the notifications count" do
|
29
|
+
subject
|
30
|
+
@store.get(@username).notifications.should eq(@count)
|
31
|
+
end
|
32
|
+
it "should clear the cache" do
|
33
|
+
@store.should_receive(:cache_delete).with("user_state_#{@username}")
|
34
|
+
subject
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Notificon do
|
4
|
+
before(:each) do
|
5
|
+
@notification = Notification.new(:id => @id = random_string, :username => @username = random_string)
|
6
|
+
Notification.stub(:new) { @notification }
|
7
|
+
|
8
|
+
@unread_count = random_integer
|
9
|
+
@notification_store = mock(NotificationStore, :add => true, :unread_count_for_user => @unread_count, :mark_as_read => @notification)
|
10
|
+
Notificon.stub(:notification_store) { @notification_store }
|
11
|
+
@user_state_store = mock(UserStateStore, :set_notifications => true)
|
12
|
+
Notificon.stub(:user_state_store) { @user_state_store }
|
13
|
+
end
|
14
|
+
describe ".add_notification" do
|
15
|
+
before(:each) do
|
16
|
+
@item_url = random_string
|
17
|
+
@item_text = random_string
|
18
|
+
@actor = random_string
|
19
|
+
@action = random_string.to_sym
|
20
|
+
@occured_at = random_time
|
21
|
+
end
|
22
|
+
|
23
|
+
subject { Notificon.add_notification(@username, @item_url, @item_text, @actor, @action, @occured_at) }
|
24
|
+
|
25
|
+
it "creates a notification" do
|
26
|
+
Notification.should_receive(:new).with(:username => @username, :item_url => @item_url,
|
27
|
+
:item_text => @item_text, :actor => @actor, :action => @action, :occured_at => @occured_at, :item_id => nil)
|
28
|
+
subject
|
29
|
+
end
|
30
|
+
it "adds it to the notification store" do
|
31
|
+
@notification_store.should_receive(:add).with(@notification)
|
32
|
+
subject
|
33
|
+
end
|
34
|
+
it "updates the unread count for the user" do
|
35
|
+
@user_state_store.should_receive(:set_notifications).with(@username, @unread_count)
|
36
|
+
subject
|
37
|
+
end
|
38
|
+
context "when item id passed" do
|
39
|
+
before(:each) do
|
40
|
+
@item_id = random_string
|
41
|
+
end
|
42
|
+
|
43
|
+
subject { Notificon.add_notification(@username, @item_url, @item_text, @actor, @action, @occured_at, @item_id) }
|
44
|
+
|
45
|
+
it "should create the notification with the item_id" do
|
46
|
+
Notification.should_receive(:new).with(:username => @username, :item_url => @item_url,
|
47
|
+
:item_text => @item_text, :actor => @actor, :action => @action, :occured_at => @occured_at, :item_id => @item_id)
|
48
|
+
subject
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#mark_notification_read" do
|
54
|
+
before(:each) do
|
55
|
+
@read_at = random_time
|
56
|
+
end
|
57
|
+
|
58
|
+
subject { Notificon.mark_notification_read(@id, @read_at) }
|
59
|
+
|
60
|
+
it "marks the notification as read" do
|
61
|
+
@notification_store.should_receive(:mark_as_read).with(@id, @read_at)
|
62
|
+
subject
|
63
|
+
end
|
64
|
+
it "updates the unread count for the user" do
|
65
|
+
@user_state_store.should_receive(:set_notifications).with(@username, @unread_count)
|
66
|
+
subject
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#update_user_unread_counts" do
|
71
|
+
|
72
|
+
subject { Notificon.update_user_unread_counts(@username) }
|
73
|
+
|
74
|
+
it "gets the unread count for the user" do
|
75
|
+
@notification_store.should_receive(:unread_count_for_user).with(@username)
|
76
|
+
subject
|
77
|
+
end
|
78
|
+
it "sets the value on user state store" do
|
79
|
+
@user_state_store.should_receive(:set_notifications).with(@username, @unread_count)
|
80
|
+
subject
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "#mark_all_read_for_item" do
|
85
|
+
before(:each) do
|
86
|
+
@username = random_string
|
87
|
+
@item_id = random_string
|
88
|
+
@read_at = random_time
|
89
|
+
@notification_store.stub(:mark_all_read_for_item)
|
90
|
+
end
|
91
|
+
|
92
|
+
subject { Notificon.mark_all_read_for_item(@username, @item_id, @read_at) }
|
93
|
+
|
94
|
+
it "marks all notifications for item id" do
|
95
|
+
@notification_store.should_receive(:mark_all_read_for_item).with(@username, @item_id, @read_at)
|
96
|
+
subject
|
97
|
+
end
|
98
|
+
it "updates the unread count for the user" do
|
99
|
+
@user_state_store.should_receive(:set_notifications).with(@username, @unread_count)
|
100
|
+
subject
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe ".clear_notifications" do
|
105
|
+
before(:each) do
|
106
|
+
@username = random_string
|
107
|
+
@notification_store.stub(:mark_all_read_for_user)
|
108
|
+
@user_state_store.stub(:clear_notifications)
|
109
|
+
end
|
110
|
+
|
111
|
+
subject { Notificon.clear_notifications(@username) }
|
112
|
+
|
113
|
+
it "marks all notifications for user as read" do
|
114
|
+
@notification_store.should_receive(:mark_all_read_for_user).with(@username, an_instance_of(Time))
|
115
|
+
subject
|
116
|
+
end
|
117
|
+
it "updates the unread count for the user" do
|
118
|
+
@user_state_store.should_receive(:clear_notifications).with(@username)
|
119
|
+
subject
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec'
|
3
|
+
require "#{Rake.application.original_dir}/lib/notificon"
|
4
|
+
|
5
|
+
include Notificon
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
# Use color in STDOUT
|
9
|
+
config.color_enabled = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def random_string
|
13
|
+
(0...24).map{ ('a'..'z').to_a[rand(26)] }.join
|
14
|
+
end
|
15
|
+
|
16
|
+
def random_integer
|
17
|
+
rand(9999)
|
18
|
+
end
|
19
|
+
|
20
|
+
def random_time
|
21
|
+
Time.now - random_integer
|
22
|
+
end
|
23
|
+
|
24
|
+
def random_object_id
|
25
|
+
BSON::ObjectId.from_time(random_time).to_s
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: notificon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Adam Bird
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mongo
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.6'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.6'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bson_ext
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.6'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.6'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: addressable
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.2'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.2'
|
62
|
+
description: Gem for tracking and managing application notifications
|
63
|
+
email: adam.bird@gmail.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- lib/notificon/cache.rb
|
69
|
+
- lib/notificon/controller.rb
|
70
|
+
- lib/notificon/mongo_store.rb
|
71
|
+
- lib/notificon/notification.rb
|
72
|
+
- lib/notificon/notification_store.rb
|
73
|
+
- lib/notificon/user_state.rb
|
74
|
+
- lib/notificon/user_state_store.rb
|
75
|
+
- lib/notificon/version.rb
|
76
|
+
- lib/notificon.rb
|
77
|
+
- spec/notificon/controller_spec.rb
|
78
|
+
- spec/notificon/notification_spec.rb
|
79
|
+
- spec/notificon/notification_store_spec.rb
|
80
|
+
- spec/notificon/user_state_store_spec.rb
|
81
|
+
- spec/notificon_spec.rb
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
homepage: http://github.com/adambird/notificon
|
84
|
+
licenses: []
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project:
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Gem for tracking and managing application notifications
|
107
|
+
test_files:
|
108
|
+
- spec/notificon/controller_spec.rb
|
109
|
+
- spec/notificon/notification_spec.rb
|
110
|
+
- spec/notificon/notification_store_spec.rb
|
111
|
+
- spec/notificon/user_state_store_spec.rb
|
112
|
+
- spec/notificon_spec.rb
|
113
|
+
- spec/spec_helper.rb
|