ama_layout 5.12.0 → 6.3.0.pre

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -2
  3. data/ama_layout.gemspec +20 -21
  4. data/app/assets/javascripts/ama_layout/desktop/foundation-custom.js +8 -10
  5. data/app/assets/javascripts/ama_layout/desktop/index.js +0 -1
  6. data/app/helpers/ama_layout_path_helper.rb +5 -5
  7. data/app/views/ama_layout/_siteheader.html.erb +3 -8
  8. data/lib/ama_layout.rb +20 -28
  9. data/lib/ama_layout/decorators/moneris_decorator.rb +2 -3
  10. data/lib/ama_layout/decorators/navigation_decorator.rb +6 -52
  11. data/lib/ama_layout/decorators/navigation_item_decorator.rb +2 -3
  12. data/lib/ama_layout/moneris.rb +1 -4
  13. data/lib/ama_layout/navigation.rb +1 -7
  14. data/lib/ama_layout/navigation.yml +6 -58
  15. data/lib/ama_layout/navigation_item.rb +1 -4
  16. data/lib/ama_layout/version.rb +1 -1
  17. data/spec/ama_layout/decorators/navigation_decorator_spec.rb +8 -124
  18. data/spec/ama_layout/decorators/navigation_item_decorator_spec.rb +4 -2
  19. data/spec/ama_layout/navigation_spec.rb +43 -13
  20. data/spec/helpers/ama_layout_path_helper_spec.rb +6 -6
  21. data/spec/internal/app/controllers/application_controller.rb +0 -21
  22. data/spec/internal/config/routes.rb +0 -1
  23. data/spec/spec_helper.rb +16 -9
  24. data/styles.scss +0 -0
  25. metadata +41 -79
  26. data/PULL_REQUEST_TEMPLATE.md +0 -10
  27. data/app/assets/javascripts/ama_layout/notifications.coffee +0 -17
  28. data/app/controllers/ama_layout/api/v1/notifications_controller.rb +0 -18
  29. data/app/views/ama_layout/_notification.html.erb +0 -10
  30. data/app/views/ama_layout/_notification_sidebar.html.erb +0 -22
  31. data/app/views/ama_layout/_notifications.html.erb +0 -6
  32. data/app/views/ama_layout/_sign_out_link.html.erb +0 -3
  33. data/config/routes.rb +0 -9
  34. data/lib/ama_layout/decorators/notification_decorator.rb +0 -46
  35. data/lib/ama_layout/draper_replacement.rb +0 -27
  36. data/lib/ama_layout/navigation_helper.rb +0 -31
  37. data/lib/ama_layout/notification.rb +0 -87
  38. data/lib/ama_layout/notification_scrubber.rb +0 -13
  39. data/lib/ama_layout/notification_set.rb +0 -140
  40. data/lib/ama_layout/notifications.rb +0 -73
  41. data/lib/ama_layout/notifications/abstract_store.rb +0 -17
  42. data/lib/ama_layout/notifications/redis_store.rb +0 -38
  43. data/spec/ama_layout/controllers/ama_layout/api/v1/notifications_controller_spec.rb +0 -13
  44. data/spec/ama_layout/decorators/notification_decorator_spec.rb +0 -57
  45. data/spec/ama_layout/navigation_helper_spec.rb +0 -63
  46. data/spec/ama_layout/notification_scrubber_spec.rb +0 -10
  47. data/spec/ama_layout/notification_set_spec.rb +0 -281
  48. data/spec/ama_layout/notification_spec.rb +0 -193
  49. data/spec/ama_layout/notifications/abstract_store_spec.rb +0 -23
  50. data/spec/ama_layout/notifications/redis_store_spec.rb +0 -94
  51. data/spec/ama_layout/notifications_spec.rb +0 -109
  52. data/spec/factories/users.rb +0 -35
  53. data/spec/support/shared_examples/member_navigation.rb +0 -105
@@ -1,73 +0,0 @@
1
- module AmaLayout
2
- # Usage:
3
- #
4
- # class MyClass
5
- # include AmaLayout::Notifications
6
- #
7
- # notification_store AmaLayout::Notifications::RedisStore.new(options)
8
- # notification_foreign_key :a_method_name_or_proc # defaults to :id
9
- #
10
- # ...
11
- # end
12
- #
13
- module Notifications
14
- InvalidNotificationStore = Class.new(StandardError)
15
-
16
- def self.included(base)
17
- base.extend(ClassMethods)
18
- base.include(InstanceMethods)
19
- end
20
-
21
- module InstanceMethods
22
- def notifications
23
- @notifications ||= NotificationSet.new(_store, _foreign_key)
24
- end
25
-
26
- def notifications=(other)
27
- @notifications = other
28
- end
29
-
30
- private
31
-
32
- def _store
33
- self.class._notification_store || invalid_store!
34
- end
35
-
36
- def _foreign_key
37
- self.class._notification_foreign_key.call(self)
38
- end
39
-
40
- def invalid_store!
41
- raise InvalidNotificationStore, 'a notification store must be specified'
42
- end
43
- end
44
-
45
- module ClassMethods
46
- def notification_store(store)
47
- self._notification_store = store
48
- end
49
-
50
- def notification_foreign_key(key)
51
- self._notification_foreign_key = key
52
- end
53
-
54
- def _notification_foreign_key
55
- @_notification_foreign_key || Proc.new(&:id)
56
- end
57
-
58
- def _notification_store
59
- @_notification_store
60
- end
61
-
62
- private
63
-
64
- def _notification_store=(store)
65
- @_notification_store = store
66
- end
67
-
68
- def _notification_foreign_key=(key)
69
- @_notification_foreign_key = key.to_proc
70
- end
71
- end
72
- end
73
- end
@@ -1,17 +0,0 @@
1
- module AmaLayout
2
- module Notifications
3
- class AbstractStore
4
- def get(key, opts = {})
5
- raise NotImplementedError, 'you must define a #get method in a subclass'
6
- end
7
-
8
- def set(key, value, opts = {})
9
- raise NotImplementedError, 'you must define a #set method in a subclass'
10
- end
11
-
12
- def delete(key, opts = {})
13
- raise NotImplementedError, 'you must define a #delete method in a subclass'
14
- end
15
- end
16
- end
17
- end
@@ -1,38 +0,0 @@
1
- module AmaLayout
2
- module Notifications
3
- class RedisStore < AbstractStore
4
- delegate :clear, to: :base
5
-
6
- attr_accessor :base
7
-
8
- def initialize(opts = {})
9
- self.base = ActiveSupport::Cache.lookup_store(
10
- :redis_store,
11
- opts.merge(raw: true)
12
- )
13
- end
14
-
15
- def get(key, opts = {})
16
- if opts.fetch(:default, false)
17
- base.fetch(key) { opts[:default] }
18
- else
19
- base.read(key)
20
- end
21
- end
22
-
23
- def set(key, value, opts = {})
24
- base.write(key, value, opts) == 'OK'
25
- end
26
-
27
- def delete(key, opts = {})
28
- base.delete(key, opts) == 1
29
- end
30
-
31
- def transaction
32
- base.data.multi do
33
- yield self
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,13 +0,0 @@
1
- describe AmaLayout::Api::V1::NotificationsController, type: :controller do
2
- describe 'DELETE api/v1/notifications' do
3
- routes { AmaLayout::Engine.routes }
4
-
5
- before(:each) do
6
- delete :dismiss_all
7
- end
8
-
9
- it 'returns a 204 No Content status' do
10
- expect(response).to have_http_status(:no_content)
11
- end
12
- end
13
- end
@@ -1,57 +0,0 @@
1
- describe AmaLayout::NotificationDecorator do
2
- let(:notification) do
3
- AmaLayout::Notification.new(
4
- header: 'test',
5
- content: 'content',
6
- type: :warning,
7
- created_at: Date.yesterday.beginning_of_day,
8
- active: true
9
- )
10
- end
11
- subject { described_class.new(notification) }
12
-
13
- describe '#created_at' do
14
- around(:each) do |example|
15
- Timecop.freeze(Time.zone.local(2017, 8)) do
16
- example.run
17
- end
18
- end
19
-
20
- it 'returns the time elapsed in english words' do
21
- expect(subject.created_at).to eq('1 day ago')
22
- end
23
- end
24
-
25
- describe '#icon' do
26
- it 'returns a div' do
27
- expect(subject.icon).to include('<div')
28
- end
29
-
30
- it 'contains the proper icon class' do
31
- expect(subject.icon).to include('fa-exclamation')
32
- end
33
-
34
- it 'contains the proper colour class' do
35
- expect(subject.icon).to include('right-sidebar__content-icon--orange')
36
- end
37
- end
38
-
39
- describe '#active_class' do
40
- context 'when active' do
41
- it 'returns the proper class' do
42
- expect(subject.active_class).to_not include('inactive')
43
- expect(subject.active_class).to include('active')
44
- end
45
- end
46
-
47
- context 'when inactive' do
48
- before(:each) do
49
- notification.dismiss!
50
- end
51
-
52
- it 'returns the proper class' do
53
- expect(subject.active_class).to include('inactive')
54
- end
55
- end
56
- end
57
- end
@@ -1,63 +0,0 @@
1
- describe AmaLayout::NavigationHelper do
2
- subject { FactoryGirl.create(:user) }
3
-
4
- describe '#navigation' do
5
- before(:each) do
6
- subject.class.include(AmaLayout::NavigationHelper).new
7
- end
8
-
9
- context 'non-member' do
10
- subject { FactoryGirl.create(:user, :non_member) }
11
-
12
- it 'shows non-member sidebar menu' do
13
- expect(subject.navigation).to eq 'non-member'
14
- end
15
- end
16
-
17
- context 'member' do
18
- it 'shows member sidebar menu' do
19
- expect(subject.navigation).to eq 'member'
20
- end
21
- end
22
-
23
- context 'member with accr' do
24
- subject { FactoryGirl.create(:user, :with_accr) }
25
-
26
- it 'shows member sidebar menu' do
27
- expect(subject.navigation).to eq 'member'
28
- end
29
- end
30
-
31
- context 'member with mpp' do
32
- subject { FactoryGirl.create(:user, :with_mpp) }
33
-
34
- it 'shows member sidebar menu' do
35
- expect(subject.navigation).to eq 'member'
36
- end
37
- end
38
-
39
- context 'member in-renewal' do
40
- subject { FactoryGirl.create(:user, :in_renewal) }
41
-
42
- it 'shows in-renewal sidebar menu' do
43
- expect(subject.navigation).to eq 'member-in-renewal'
44
- end
45
- end
46
-
47
- context 'member in-renewal late' do
48
- subject { FactoryGirl.create(:user, :in_renewal_late) }
49
-
50
- it 'shows in-renewal-late sidebar menu' do
51
- expect(subject.navigation).to eq 'member-in-renewal-late'
52
- end
53
- end
54
-
55
- context 'member with outstanding balance' do
56
- subject { FactoryGirl.create(:user, :outstanding_balance) }
57
-
58
- it 'shows member-with-outstanding-balance sidebar menu' do
59
- expect(subject.navigation).to eq 'member-with-outstanding-balance'
60
- end
61
- end
62
- end
63
- end
@@ -1,10 +0,0 @@
1
- describe AmaLayout::NotificationScrubber do
2
- describe '#initialize' do
3
- let(:sanitized) { Loofah.fragment(string).scrub!(subject).to_s }
4
- let(:string) { '<script>alert("haxxed");</script><a href="#" invalid="test">test</a>waffles' }
5
-
6
- it 'scrubs HTML tags from a string' do
7
- expect(sanitized).to eq('alert("haxxed");<a href="#">test</a>waffles')
8
- end
9
- end
10
- end
@@ -1,281 +0,0 @@
1
- describe AmaLayout::NotificationSet do
2
- let(:store) do
3
- AmaLayout::Notifications::RedisStore.new(
4
- db: 4,
5
- namespace: 'test_notifications',
6
- host: 'localhost'
7
- )
8
- end
9
- let(:key) { 1 }
10
- let(:duration) { AmaLayout::Notification::DEFAULT_LIFESPAN }
11
- let(:store_key) { key.to_s }
12
- let(:digest) { base_notification.digest }
13
- let(:base_notification) do
14
- AmaLayout::Notification.new(
15
- type: :notice,
16
- header: 'test',
17
- content: 'test',
18
- lifespan: duration,
19
- version: '1.0.0',
20
- created_at: Time.zone.local(2017, 06, 19),
21
- active: true
22
- )
23
- end
24
- let(:json) do
25
- <<-JSON
26
- {
27
- "#{digest}": {
28
- "type": "notice",
29
- "header": "test",
30
- "content": "test",
31
- "created_at": "2017-06-19T06:00:00.000Z",
32
- "active": true,
33
- "lifespan": #{duration},
34
- "version": "1.0.0"
35
- }
36
- }
37
- JSON
38
- end
39
- let(:stale_json) do
40
- <<-JSON
41
- {
42
- "d3c2bc71904100674325791b371db7446097f956ea76a304e787abd5f2588665": {
43
- "type": "notice",
44
- "header": "stale",
45
- "content": "stale",
46
- "created_at": "2012-06-19T06:00:00.000Z",
47
- "active": true,
48
- "lifespan": #{duration},
49
- "version": "1.0.0"
50
- }
51
- }
52
- JSON
53
- end
54
-
55
- subject { described_class.new(store, key) }
56
-
57
- around(:each) do |example|
58
- Timecop.freeze(Time.zone.local(2017, 6, 19)) do
59
- store.clear
60
- example.run
61
- store.clear
62
- end
63
- end
64
-
65
- describe '#initialize' do
66
- context 'with valid JSON in data store' do
67
- before(:each) do
68
- store.set(store_key, notification)
69
- end
70
-
71
- context 'without stale notifications in the data store' do
72
- let(:notification) { json }
73
-
74
- it 'fetches the notifications' do
75
- expect(subject.size).to eq(1)
76
- end
77
- end
78
-
79
- context 'with stale notifications in the data store' do
80
- let(:notification) { stale_json }
81
-
82
- it 'returns an empty set' do
83
- expect(subject).to be_empty
84
- end
85
-
86
- it 'cleans out stale notifications from the data store' do
87
- subject
88
- expect(store.get(store_key)).to eq('{}')
89
- end
90
- end
91
- end
92
-
93
- context 'with invalid JSON in data store' do
94
- before(:each) do
95
- store.set(store_key, '{"invalid_json":')
96
- end
97
-
98
- it 'logs to Rails logger' do
99
- expect(Rails.logger).to receive(:error).with(instance_of(String))
100
- subject
101
- end
102
-
103
- it 'deletes the key in data store' do
104
- subject
105
- expect(store.get(store_key)).to be nil
106
- end
107
-
108
- it 'returns an empty set' do
109
- expect(subject).to be_empty
110
- end
111
-
112
- it 'sets the base attribute to a hash' do
113
- expect(subject.base).to be_a(Hash)
114
- end
115
- end
116
-
117
- context 'with no entry in data store' do
118
- it 'returns an empty set' do
119
- expect(subject).to be_empty
120
- end
121
- end
122
- end
123
-
124
- describe '#create' do
125
- it 'returns the NotificationSet instance' do
126
- expect(subject.create(header: 'test', content: 'test')).to be_a(described_class)
127
- end
128
-
129
- it 'creates a new active notification' do
130
- subject.create(header: 'test', content: 'test')
131
- expect(subject.size).to eq(1)
132
- end
133
-
134
- it 'saves a notification in data store' do
135
- subject.create(header: 'test', content: 'test')
136
- expect(store.get(store_key)).to be_a(String)
137
- end
138
-
139
- context 'when the same notification exists but is dismissed' do
140
- before(:each) do
141
- store.set(store_key, json)
142
- subject.first.dismiss!
143
- subject.save
144
- subject.create(header: 'test', content: 'test')
145
- end
146
-
147
- it 'does not overwrite the notification' do
148
- expect(subject.active).to be_empty
149
- end
150
-
151
- it 'still has the dismissed notification in the data store' do
152
- data = JSON.parse(store.get(store_key))
153
- notification = data.values.first
154
- expect(data.values.first['active']).to be false
155
- end
156
- end
157
- end
158
-
159
- describe '#delete' do
160
- before(:each) do
161
- subject.create(base_notification.to_h)
162
- end
163
-
164
- context 'with an array as an argument' do
165
- it 'deletes the notification from the data store' do
166
- data = subject.delete([digest])
167
- expect(data).to be_a(described_class)
168
- expect(data).to be_empty
169
- expect(store.get(store_key)).to eq('{}')
170
- end
171
-
172
- it 'returns falsey if nothing is deleted' do
173
- expect(subject.delete(['missing'])).to be_falsey
174
- end
175
- end
176
-
177
- context 'with string arguments' do
178
- it 'deletes the notification from the data store' do
179
- data = subject.delete(digest)
180
- expect(data).to be_a(described_class)
181
- expect(data).to be_empty
182
- expect(store.get(store_key)).to eq('{}')
183
- end
184
-
185
- it 'returns falsey if nothing is deleted' do
186
- expect(subject.delete('missing')).to be_falsey
187
- end
188
- end
189
- end
190
-
191
- describe '#destroy' do
192
- context 'when data is removed' do
193
- before(:each) do
194
- subject.create(header: 'test', content: 'test', lifespan: 1.day)
195
- end
196
-
197
- it 'returns a NotificationSet instance' do
198
- expect(subject.destroy!).to be_a(described_class)
199
- end
200
-
201
- it 'removes the notifications from the data store' do
202
- subject.destroy!
203
- expect(store.get(store_key)).to be nil
204
- end
205
-
206
- it 'returns an empty set' do
207
- expect(subject.destroy!).to be_empty
208
- end
209
- end
210
-
211
- context 'when data is not removed' do
212
- it 'returns false' do
213
- expect(subject.destroy!).to be false
214
- end
215
- end
216
- end
217
-
218
- describe '#find' do
219
- context 'when id is not preset' do
220
- it 'returns nil' do
221
- expect(subject.find('invalid')).to be nil
222
- end
223
- end
224
-
225
- context 'when id is present' do
226
- let(:id) { subject.last.id }
227
-
228
- before(:each) do
229
- subject.create(header: 'test', content: 'test')
230
- end
231
-
232
- it 'returns the notification' do
233
- expect(subject.find(id)).to eq(subject.last)
234
- end
235
- end
236
- end
237
-
238
- describe '#save' do
239
- before(:each) do
240
- subject.create(header: 'test', content: 'test')
241
- end
242
-
243
- it 'saves the notifications' do
244
- expect(subject.last.active?).to be true
245
- subject.last.dismiss!
246
- subject.save
247
- expect(subject.active).to be_empty
248
- end
249
-
250
- it 'returns the NotificationSet instance' do
251
- expect(subject.save).to be_a(described_class)
252
- end
253
- end
254
-
255
- describe '#inspect' do
256
- it 'returns a stringified instance' do
257
- expect(subject.inspect).to eq('<AmaLayout::NotificationSet>: []')
258
- end
259
- end
260
-
261
- context 'scoping' do
262
- before(:each) do
263
- subject.create(header: 'test', content: 'test')
264
- subject.create(header: 'inactive', content: 'inactive')
265
- subject.last.dismiss!
266
- subject.save
267
- end
268
-
269
- describe '#all' do
270
- it 'returns both active and inactive notifications' do
271
- expect(subject.all.size).to eq(2)
272
- end
273
- end
274
-
275
- describe '#active' do
276
- it 'returns only active notifications' do
277
- expect(subject.active.size).to eq(1)
278
- end
279
- end
280
- end
281
- end