rails-patterns 0.4.0 → 0.8.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.
@@ -0,0 +1,16 @@
1
+ require 'active_support/all'
2
+ require 'active_support/testing/time_helpers'
3
+ require_relative 'rails_redis_cache_mock'
4
+ require_relative '../../lib/patterns/calculation'
5
+
6
+ CustomCalculation = Class.new(Patterns::Calculation) do
7
+ set_cache_expiry_every 1.hour
8
+ class_attribute :counter
9
+ self.counter = 0
10
+
11
+ private
12
+
13
+ def result
14
+ self.class.counter += 1
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'rails_redis_cache_mock'
2
+ require_relative 'custom_calculation'
3
+
4
+ CustomCalculation.result
@@ -0,0 +1,5 @@
1
+ class Rails
2
+ def self.cache
3
+ @cache ||= ActiveSupport::Cache::RedisCacheStore.new
4
+ end
5
+ end
@@ -0,0 +1,200 @@
1
+ RSpec.describe Patterns::Calculation do
2
+ before(:all) do
3
+ class Rails
4
+ def self.cache
5
+ @cache ||= ActiveSupport::Cache::MemoryStore.new
6
+ end
7
+ end
8
+ end
9
+
10
+ after(:all) do
11
+ Object.send(:remove_const, :Rails)
12
+ end
13
+
14
+ after do
15
+ Object.send(:remove_const, :CustomCalculation) if defined?(CustomCalculation)
16
+ Rails.cache.clear
17
+ ActiveSupport::Cache::RedisCacheStore.new.clear
18
+ end
19
+
20
+ describe ".result" do
21
+ it "returns a result of the calculation within a #result method" do
22
+ CustomCalculation = Class.new(Patterns::Calculation) do
23
+ private
24
+
25
+ def result
26
+ 50
27
+ end
28
+ end
29
+
30
+ expect(CustomCalculation.result).to eq 50
31
+ end
32
+
33
+ it "#result, #result_for and #calculate are aliases" do
34
+ CustomCalculation = Class.new(Patterns::Calculation)
35
+
36
+ expect(CustomCalculation.method(:result)).to eq CustomCalculation.method(:result_for)
37
+ expect(CustomCalculation.method(:result)).to eq CustomCalculation.method(:calculate)
38
+ end
39
+
40
+ it "exposes the first argument as a subject" do
41
+ CustomCalculation = Class.new(Patterns::Calculation) do
42
+ private
43
+
44
+ def result
45
+ subject
46
+ end
47
+ end
48
+
49
+ expect(CustomCalculation.result('test')).to eq 'test'
50
+ end
51
+
52
+ it "exposes all keyword arguments using #options" do
53
+ CustomCalculation = Class.new(Patterns::Calculation) do
54
+ private
55
+
56
+ def result
57
+ [options[:arg_1], options[:arg_2]]
58
+ end
59
+ end
60
+
61
+ expect(CustomCalculation.result(nil, arg_1: 20, arg_2: 30)).to eq([20, 30])
62
+ end
63
+ end
64
+
65
+ describe "caching" do
66
+ it "caches result for 'set_cache_expiry_every' period" do
67
+ travel_to DateTime.new(2017, 1, 1, 12, 0) do
68
+ CustomCalculation = Class.new(Patterns::Calculation) do
69
+ set_cache_expiry_every 1.hour
70
+
71
+ class_attribute :counter
72
+ self.counter = 0
73
+
74
+ private
75
+
76
+ def result
77
+ self.class.counter += 1
78
+ end
79
+ end
80
+
81
+ expect(CustomCalculation.result).to eq 1
82
+ expect(CustomCalculation.result).to eq 1
83
+ end
84
+
85
+ travel_to DateTime.new(2017, 1, 1, 13, 1) do
86
+ expect(CustomCalculation.result).to eq 2
87
+ expect(CustomCalculation.result).to eq 2
88
+ end
89
+ end
90
+
91
+ it "caches result for every option passed" do
92
+ CustomCalculation = Class.new(Patterns::Calculation) do
93
+ set_cache_expiry_every 1.hour
94
+
95
+ class_attribute :counter
96
+ self.counter = 0
97
+
98
+ private
99
+
100
+ def result
101
+ self.class.counter += 1
102
+ end
103
+ end
104
+
105
+ expect(CustomCalculation.result(123)).to eq 1
106
+ expect(CustomCalculation.result(123)).to eq 1
107
+ expect(CustomCalculation.result(1024)).to eq 2
108
+ expect(CustomCalculation.result(1024)).to eq 2
109
+ expect(CustomCalculation.result(1024, arg: 1)).to eq 3
110
+ expect(CustomCalculation.result(1024, arg: 1)).to eq 3
111
+ end
112
+
113
+ it "caches result for every option passed dependant on the class" do
114
+ CustomCalculation = Class.new(Patterns::Calculation) do
115
+ set_cache_expiry_every 1.hour
116
+
117
+ class_attribute :counter
118
+ self.counter = 0
119
+
120
+ private
121
+
122
+ def result
123
+ self.class.counter += 1
124
+ end
125
+ end
126
+
127
+ DifferentCalculation = Class.new(Patterns::Calculation) do
128
+ set_cache_expiry_every 1.hour
129
+
130
+ class_attribute :counter
131
+ self.counter = 100
132
+
133
+ private
134
+
135
+ def result
136
+ self.class.counter += 1
137
+ end
138
+ end
139
+
140
+ expect(CustomCalculation.result(123)).to eq 1
141
+ expect(CustomCalculation.result(123)).to eq 1
142
+ expect(DifferentCalculation.result(123)).to eq 101
143
+ expect(DifferentCalculation.result(123)).to eq 101
144
+
145
+ Object.send(:remove_const, :DifferentCalculation)
146
+ end
147
+
148
+ it "does not cache result if 'set_cache_expiry_every' is not set" do
149
+ CustomCalculation = Class.new(Patterns::Calculation) do
150
+ class_attribute :counter
151
+ self.counter = 0
152
+
153
+ private
154
+
155
+ def result
156
+ self.class.counter += 1
157
+ end
158
+ end
159
+
160
+ expect(CustomCalculation.result).to eq 1
161
+ expect(CustomCalculation.result).to eq 2
162
+ end
163
+
164
+ describe "when RedisCacheStore is used" do
165
+ it "does not store data in cache if 'cache_expiry_period' is not set" do
166
+ client = Redis.new
167
+ class Rails
168
+ def self.cache
169
+ @cache ||= ActiveSupport::Cache::RedisCacheStore.new
170
+ end
171
+ end
172
+
173
+ CustomCalculation = Class.new(Patterns::Calculation) do
174
+ class_attribute :counter
175
+ self.counter = 0
176
+
177
+ private
178
+
179
+ def result
180
+ self.class.counter += 1
181
+ end
182
+ end
183
+
184
+ expect(CustomCalculation.result).to eq 1
185
+ expect(CustomCalculation.result).to eq 2
186
+ expect(client.keys).to be_empty
187
+ end
188
+ end
189
+
190
+ it "uses cache keys consistent between processes" do
191
+ `bundle exec ruby spec/helpers/custom_calculation.rb`
192
+ Process.spawn('bundle exec ruby spec/helpers/custom_calculation_script.rb')
193
+ Process.spawn('bundle exec ruby spec/helpers/custom_calculation_script.rb')
194
+ Process.spawn('bundle exec ruby spec/helpers/custom_calculation_script.rb')
195
+ Process.waitall
196
+
197
+ expect(Redis.new.keys.length).to eq 1
198
+ end
199
+ end
200
+ end
@@ -316,7 +316,7 @@ RSpec.describe Patterns::Form do
316
316
  end
317
317
 
318
318
  describe "#to_model" do
319
- it "retruns itself" do
319
+ it "returns itself" do
320
320
  CustomForm = Class.new(Patterns::Form)
321
321
 
322
322
  form = CustomForm.new(double)
@@ -345,86 +345,132 @@ RSpec.describe Patterns::Form do
345
345
  end
346
346
  end
347
347
 
348
- describe "#model_name" do
349
- describe "#param_key" do
350
- context "resource exists" do
351
- context "resource responds to #model_name" do
352
- context "param_key is not defined" do
353
- it "returns object responding to #param_key returning resource#param_key" do
354
- CustomForm = Class.new(Patterns::Form)
355
- resource = double(model_name: double(param_key: "resource_key"))
356
-
357
- form = CustomForm.new(resource)
358
- result = form.model_name
359
-
360
- expect(result).to respond_to(:param_key)
361
- expect(result.param_key).to eq "resource_key"
362
- end
363
- end
364
-
365
- context "param_key is defined" do
366
- it "returns param_key" do
367
- CustomForm = Class.new(Patterns::Form) do
368
- param_key "test_key"
369
- end
370
- resource = double(model_name: double(param_key: "resource_key"))
348
+ describe "#to_param" do
349
+ context "resource exists" do
350
+ context "resource responds to #to_param" do
351
+ it "returns resource#to_param" do
352
+ CustomForm = Class.new(Patterns::Form)
353
+ resource = double(to_param: 100)
371
354
 
372
- form = CustomForm.new(resource)
373
- result = form.model_name
355
+ form = CustomForm.new(resource)
374
356
 
375
- expect(result.param_key).to eq "test_key"
376
- end
377
- end
357
+ expect(form.to_param).to eq 100
378
358
  end
359
+ end
360
+ end
361
+
362
+ context "resource does not exist" do
363
+ it "returns nil" do
364
+ CustomForm = Class.new(Patterns::Form)
379
365
 
380
- context "resource does not respond to #model_name" do
381
- context "param_key is not defined" do
382
- it "raises NoParamKey" do
383
- CustomForm = Class.new(Patterns::Form)
366
+ form = CustomForm.new
384
367
 
385
- form = CustomForm.new(double)
368
+ expect(form.to_param).to eq nil
369
+ end
370
+ end
371
+ end
386
372
 
387
- expect { form.model_name }.to raise_error(Patterns::Form::NoParamKey)
388
- end
389
- end
373
+ describe "#model_name" do
374
+ context "resource exists" do
375
+ context "resource responds to #model_name" do
376
+ context "param_key is not defined" do
377
+ it "returns object's model name param_key, route_key and singular_route_key" do
378
+ CustomForm = Class.new(Patterns::Form)
379
+ resource = double(model_name: double(
380
+ param_key: "resource_key",
381
+ route_key: "resource_keys",
382
+ singular_route_key: "resource_key"
383
+ ))
390
384
 
391
- context "param_key is defined" do
392
- it "returns param_key" do
393
- CustomForm = Class.new(Patterns::Form) do
394
- param_key "test_key"
395
- end
385
+ form = CustomForm.new(resource)
386
+ result = form.model_name
396
387
 
397
- form = CustomForm.new(double)
398
- result = form.model_name
388
+ expect(result).to have_attributes(
389
+ param_key: "resource_key",
390
+ route_key: "resource_keys",
391
+ singular_route_key: "resource_key"
392
+ )
393
+ end
394
+ end
399
395
 
400
- expect(result.param_key).to eq "test_key"
396
+ context "param_key is defined" do
397
+ it "returns param_key, route_key and singular_route_key derived from param key" do
398
+ CustomForm = Class.new(Patterns::Form) do
399
+ param_key "test_key"
401
400
  end
401
+ resource = double(model_name: double(
402
+ param_key: "resource_key",
403
+ route_key: "resource_keys",
404
+ singular_route_key: "resource_key"
405
+ ))
406
+
407
+ form = CustomForm.new(resource)
408
+ result = form.model_name
409
+
410
+ expect(result).to have_attributes(
411
+ param_key: "test_key",
412
+ route_key: "test_keys",
413
+ singular_route_key: "test_key"
414
+ )
402
415
  end
403
416
  end
404
417
  end
405
418
 
406
- context "resource does not exist" do
419
+ context "resource does not respond to #model_name" do
407
420
  context "param_key is not defined" do
408
421
  it "raises NoParamKey" do
409
422
  CustomForm = Class.new(Patterns::Form)
410
423
 
411
- form = CustomForm.new
424
+ form = CustomForm.new(double)
412
425
 
413
426
  expect { form.model_name }.to raise_error(Patterns::Form::NoParamKey)
414
427
  end
415
428
  end
416
429
 
417
430
  context "param_key is defined" do
418
- it "returns param_key" do
431
+ it "returns param_key, route_key and singular_route_key derived from param key" do
419
432
  CustomForm = Class.new(Patterns::Form) do
420
433
  param_key "test_key"
421
434
  end
422
435
 
423
- form = CustomForm.new
436
+ form = CustomForm.new(double)
424
437
  result = form.model_name
425
438
 
426
- expect(result.param_key).to eq "test_key"
439
+ expect(result).to have_attributes(
440
+ param_key: "test_key",
441
+ route_key: "test_keys",
442
+ singular_route_key: "test_key"
443
+ )
444
+ end
445
+ end
446
+ end
447
+ end
448
+
449
+ context "resource does not exist" do
450
+ context "param_key is not defined" do
451
+ it "raises NoParamKey" do
452
+ CustomForm = Class.new(Patterns::Form)
453
+
454
+ form = CustomForm.new
455
+
456
+ expect { form.model_name }.to raise_error(Patterns::Form::NoParamKey)
457
+ end
458
+ end
459
+
460
+ context "param_key is defined" do
461
+ it "returns param_key, route_key and singular_route_key derived from param key" do
462
+ CustomForm = Class.new(Patterns::Form) do
463
+ param_key "test_key"
427
464
  end
465
+
466
+ form = CustomForm.new
467
+ result = form.model_name
468
+
469
+ expect(result).to have_attributes(
470
+ param_key: "test_key",
471
+ route_key: "test_keys",
472
+ singular_route_key: "test_key"
473
+ )
428
474
  end
429
475
  end
430
476
  end
@@ -0,0 +1,44 @@
1
+ RSpec.describe Rule do
2
+ after(:each) do
3
+ Object.send(:remove_const, :CustomRule) if defined?(CustomRule)
4
+ end
5
+
6
+ it 'requires subject as the first argument' do
7
+ CustomRule = Class.new(Rule)
8
+
9
+ expect { CustomRule.new }.to raise_error ArgumentError
10
+ expect { CustomRule.new(Object.new) }.not_to raise_error
11
+ end
12
+
13
+ it 'requires #satisfied? method to be defined' do
14
+ InvalidCustomRule = Class.new(Rule)
15
+ CustomRule = Class.new(Rule) do
16
+ def satisfied?
17
+ true
18
+ end
19
+ end
20
+
21
+ expect { InvalidCustomRule.new(Object.new).satisfied? }.to raise_error NotImplementedError
22
+ expect { CustomRule.new(Object.new).satisfied? }.not_to raise_error
23
+ end
24
+
25
+ describe '#satisfied?' do
26
+ context 'when subject meets the conditions' do
27
+ it 'returns true' do
28
+ article = OpenStruct.new('published?' => true, 'deleted?' => false)
29
+
30
+ ArticleIsPublishedRule = Class.new(Rule) do
31
+ def satisfied?
32
+ subject.published?
33
+ end
34
+
35
+ def not_applicable?
36
+ subject.deleted?
37
+ end
38
+ end
39
+
40
+ expect(ArticleIsPublishedRule.new(article).satisfied?).to eq true
41
+ end
42
+ end
43
+ end
44
+ end