rails-patterns 0.6.0 → 0.10.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.
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: rails-patterns 0.6.0 ruby lib
5
+ # stub: rails-patterns 0.10.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "rails-patterns".freeze
9
- s.version = "0.6.0"
9
+ s.version = "0.10.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Stevo".freeze]
14
- s.date = "2018-01-18"
14
+ s.date = "2021-12-10"
15
15
  s.description = "A collection of lightweight, standardized, rails-oriented patterns.".freeze
16
16
  s.email = "b.kosmowski@selleo.com".freeze
17
17
  s.extra_rdoc_files = [
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  ]
21
21
  s.files = [
22
22
  ".document",
23
+ ".github/workflows/ruby.yml",
23
24
  ".rspec",
24
25
  "Gemfile",
25
26
  "Gemfile.lock",
@@ -32,19 +33,29 @@ Gem::Specification.new do |s|
32
33
  "lib/patterns/collection.rb",
33
34
  "lib/patterns/form.rb",
34
35
  "lib/patterns/query.rb",
36
+ "lib/patterns/rule.rb",
37
+ "lib/patterns/ruleset.rb",
35
38
  "lib/patterns/service.rb",
39
+ "lib/patterns/strong_ruleset.rb",
36
40
  "lib/rails-patterns.rb",
37
41
  "rails-patterns.gemspec",
42
+ "spec/helpers/custom_calculation.rb",
43
+ "spec/helpers/custom_calculation_script.rb",
44
+ "spec/helpers/rails_redis_cache_mock.rb",
38
45
  "spec/patterns/calculation_spec.rb",
39
46
  "spec/patterns/collection_spec.rb",
40
47
  "spec/patterns/form_spec.rb",
41
48
  "spec/patterns/query_spec.rb",
49
+ "spec/patterns/rule_spec.rb",
50
+ "spec/patterns/ruleset_spec.rb",
42
51
  "spec/patterns/service_spec.rb",
52
+ "spec/patterns/strong_ruleset_spec.rb",
43
53
  "spec/spec_helper.rb"
44
54
  ]
45
55
  s.homepage = "http://github.com/selleo/pattern".freeze
46
56
  s.licenses = ["MIT".freeze]
47
- s.rubygems_version = "2.6.10".freeze
57
+ s.required_ruby_version = Gem::Requirement.new(">= 2.5.0".freeze)
58
+ s.rubygems_version = "3.0.8".freeze
48
59
  s.summary = "A collection of lightweight, standardized, rails-oriented patterns.".freeze
49
60
 
50
61
  if s.respond_to? :specification_version then
@@ -54,24 +65,27 @@ Gem::Specification.new do |s|
54
65
  s.add_runtime_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
55
66
  s.add_runtime_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
56
67
  s.add_runtime_dependency(%q<virtus>.freeze, [">= 0"])
68
+ s.add_runtime_dependency(%q<ruby2_keywords>.freeze, [">= 0"])
57
69
  s.add_development_dependency(%q<rspec>.freeze, [">= 0"])
58
- s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
59
- s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
70
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 2.0"])
71
+ s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
60
72
  else
61
73
  s.add_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
62
74
  s.add_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
63
75
  s.add_dependency(%q<virtus>.freeze, [">= 0"])
76
+ s.add_dependency(%q<ruby2_keywords>.freeze, [">= 0"])
64
77
  s.add_dependency(%q<rspec>.freeze, [">= 0"])
65
- s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
66
- s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
78
+ s.add_dependency(%q<bundler>.freeze, ["~> 2.0"])
79
+ s.add_dependency(%q<juwelier>.freeze, [">= 0"])
67
80
  end
68
81
  else
69
82
  s.add_dependency(%q<activerecord>.freeze, [">= 4.2.6"])
70
83
  s.add_dependency(%q<actionpack>.freeze, [">= 4.2.6"])
71
84
  s.add_dependency(%q<virtus>.freeze, [">= 0"])
85
+ s.add_dependency(%q<ruby2_keywords>.freeze, [">= 0"])
72
86
  s.add_dependency(%q<rspec>.freeze, [">= 0"])
73
- s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
74
- s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
87
+ s.add_dependency(%q<bundler>.freeze, ["~> 2.0"])
88
+ s.add_dependency(%q<juwelier>.freeze, [">= 0"])
75
89
  end
76
90
  end
77
91
 
@@ -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
@@ -14,6 +14,7 @@ RSpec.describe Patterns::Calculation do
14
14
  after do
15
15
  Object.send(:remove_const, :CustomCalculation) if defined?(CustomCalculation)
16
16
  Rails.cache.clear
17
+ ActiveSupport::Cache::RedisCacheStore.new.clear
17
18
  end
18
19
 
19
20
  describe ".result" do
@@ -59,6 +60,18 @@ RSpec.describe Patterns::Calculation do
59
60
 
60
61
  expect(CustomCalculation.result(nil, arg_1: 20, arg_2: 30)).to eq([20, 30])
61
62
  end
63
+
64
+ it 'executes calculation with given block' do
65
+ CustomCalculation = Class.new(Patterns::Calculation) do
66
+ private
67
+
68
+ def result
69
+ yield(subject)
70
+ end
71
+ end
72
+
73
+ expect(CustomCalculation.result(5) { |a| a * 3 }).to eq(15)
74
+ end
62
75
  end
63
76
 
64
77
  describe "caching" do
@@ -159,5 +172,41 @@ RSpec.describe Patterns::Calculation do
159
172
  expect(CustomCalculation.result).to eq 1
160
173
  expect(CustomCalculation.result).to eq 2
161
174
  end
175
+
176
+ describe "when RedisCacheStore is used" do
177
+ it "does not store data in cache if 'cache_expiry_period' is not set" do
178
+ client = Redis.new
179
+ class Rails
180
+ def self.cache
181
+ @cache ||= ActiveSupport::Cache::RedisCacheStore.new
182
+ end
183
+ end
184
+
185
+ CustomCalculation = Class.new(Patterns::Calculation) do
186
+ class_attribute :counter
187
+ self.counter = 0
188
+
189
+ private
190
+
191
+ def result
192
+ self.class.counter += 1
193
+ end
194
+ end
195
+
196
+ expect(CustomCalculation.result).to eq 1
197
+ expect(CustomCalculation.result).to eq 2
198
+ expect(client.keys).to be_empty
199
+ end
200
+ end
201
+
202
+ it "uses cache keys consistent between processes" do
203
+ `bundle exec ruby spec/helpers/custom_calculation.rb`
204
+ Process.spawn('bundle exec ruby spec/helpers/custom_calculation_script.rb')
205
+ Process.spawn('bundle exec ruby spec/helpers/custom_calculation_script.rb')
206
+ Process.spawn('bundle exec ruby spec/helpers/custom_calculation_script.rb')
207
+ Process.waitall
208
+
209
+ expect(Redis.new.keys.length).to eq 1
210
+ end
162
211
  end
163
212
  end
@@ -0,0 +1,44 @@
1
+ RSpec.describe Patterns::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(Patterns::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(Patterns::Rule)
15
+ CustomRule = Class.new(Patterns::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(Patterns::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
@@ -0,0 +1,260 @@
1
+ RSpec.describe Patterns::Ruleset do
2
+ context 'when empty ruleset is initialized' do
3
+ it 'raises an error' do
4
+ empty_ruleset_klass = Class.new(Patterns::Ruleset)
5
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
6
+ subject = double
7
+
8
+ with_mocked_rules do |rules|
9
+ rules << mock_rule(:rule_1)
10
+ custom_ruleset_klass.add_rule(:rule_1)
11
+
12
+ expect { custom_ruleset_klass.new(subject) }.not_to raise_error
13
+ end
14
+
15
+ expect { empty_ruleset_klass.new(subject) }.to raise_error Patterns::Ruleset::EmptyRuleset
16
+ end
17
+ end
18
+
19
+ describe '#forceable?' do
20
+ context 'all rules are forceable' do
21
+ it 'returns true' do
22
+ with_mocked_rules do |rules|
23
+ subject = double
24
+ rules << mock_rule(:rule_1, is_forceable: true)
25
+ rules << mock_rule(:rule_2, is_forceable: true)
26
+
27
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
28
+ custom_ruleset_klass.add_rule(:rule_1)
29
+ custom_ruleset_klass.add_rule(:rule_2)
30
+
31
+ expect(custom_ruleset_klass.new(subject).forceable?).to eq true
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'at least one rule is not forceable' do
37
+ it 'returns false' do
38
+ with_mocked_rules do |rules|
39
+ subject = double
40
+ rules << mock_rule(:rule_1, is_forceable: false, is_satisfied: false, is_applicable: true)
41
+ rules << mock_rule(:rule_2, is_forceable: true)
42
+
43
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
44
+ custom_ruleset_klass.add_rule(:rule_1)
45
+ custom_ruleset_klass.add_rule(:rule_2)
46
+
47
+ expect(custom_ruleset_klass.new(subject).forceable?).to eq false
48
+ end
49
+ end
50
+
51
+ context 'and rule is satisfied' do
52
+ it 'returns true' do
53
+ with_mocked_rules do |rules|
54
+ subject = double
55
+ rules << mock_rule(
56
+ :rule_1,
57
+ is_forceable: false,
58
+ is_satisfied: true,
59
+ is_applicable: true
60
+ )
61
+ rules << mock_rule(:rule_2, is_forceable: true)
62
+
63
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
64
+ custom_ruleset_klass.add_rule(:rule_1)
65
+ custom_ruleset_klass.add_rule(:rule_2)
66
+
67
+ expect(custom_ruleset_klass.new(subject).forceable?).to eq true
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'and rule is not applicable' do
73
+ it 'returns true' do
74
+ with_mocked_rules do |rules|
75
+ subject = double
76
+ rules << mock_rule(
77
+ :rule_1,
78
+ is_forceable: false,
79
+ is_satisfied: false,
80
+ is_applicable: false
81
+ )
82
+ rules << mock_rule(:rule_2, is_forceable: true)
83
+
84
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
85
+ custom_ruleset_klass.add_rule(:rule_1)
86
+ custom_ruleset_klass.add_rule(:rule_2)
87
+
88
+ expect(custom_ruleset_klass.new(subject).forceable?).to eq true
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#not_applicable?' do
96
+ context 'all rules are not applicable' do
97
+ it 'returns true' do
98
+ with_mocked_rules do |rules|
99
+ subject = double
100
+ rules << mock_rule(:rule_1, is_applicable: false)
101
+ rules << mock_rule(:rule_2, is_applicable: false)
102
+
103
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
104
+ custom_ruleset_klass.add_rule(:rule_1)
105
+ custom_ruleset_klass.add_rule(:rule_2)
106
+
107
+ expect(custom_ruleset_klass.new(subject).not_applicable?).to eq true
108
+ end
109
+ end
110
+ end
111
+
112
+ context 'at least one rule is applicable' do
113
+ it 'returns false' do
114
+ with_mocked_rules do |rules|
115
+ subject = double
116
+ rules << mock_rule(:rule_1, is_applicable: false)
117
+ rules << mock_rule(:rule_2, is_applicable: true)
118
+
119
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
120
+ custom_ruleset_klass.add_rule(:rule_1)
121
+ custom_ruleset_klass.add_rule(:rule_2)
122
+
123
+ expect(custom_ruleset_klass.new(subject).not_applicable?).to eq false
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#satisfied?' do
130
+ context 'all rules are satisfied' do
131
+ it 'returns true' do
132
+ with_mocked_rules do |rules|
133
+ subject = double
134
+ rules << mock_rule(:rule_1)
135
+ rules << mock_rule(:rule_2)
136
+
137
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
138
+ custom_ruleset_klass.add_rule(:rule_1)
139
+ custom_ruleset_klass.add_rule(:rule_2)
140
+
141
+ expect(custom_ruleset_klass.new(subject).satisfied?).to eq true
142
+ end
143
+ end
144
+ end
145
+
146
+ context 'at least one rule is not satisfied' do
147
+ it 'returns false' do
148
+ with_mocked_rules do |rules|
149
+ subject = double
150
+ rules << mock_rule(:rule_1)
151
+ rules << mock_rule(:rule_2, is_satisfied: false)
152
+
153
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
154
+ custom_ruleset_klass.add_rule(:rule_1)
155
+ custom_ruleset_klass.add_rule(:rule_2)
156
+
157
+ expect(custom_ruleset_klass.new(subject).satisfied?).to eq false
158
+ end
159
+ end
160
+
161
+ context 'when rule is not applicable' do
162
+ it 'returns true' do
163
+ with_mocked_rules do |rules|
164
+ subject = double
165
+ rules << mock_rule(:rule_1)
166
+ rules << mock_rule(:rule_2, is_satisfied: false, is_applicable: false)
167
+
168
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
169
+ custom_ruleset_klass.add_rule(:rule_1)
170
+ custom_ruleset_klass.add_rule(:rule_2)
171
+
172
+ expect(custom_ruleset_klass.new(subject).satisfied?).to eq true
173
+ end
174
+ end
175
+ end
176
+
177
+ context 'when provided with force: true' do
178
+ context 'when rule is forceable' do
179
+ it 'returns true' do
180
+ with_mocked_rules do |rules|
181
+ subject = double
182
+ rules << mock_rule(:rule_1)
183
+ rules << mock_rule(:rule_2, is_satisfied: false, is_forceable: true)
184
+
185
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
186
+ custom_ruleset_klass.add_rule(:rule_1)
187
+ custom_ruleset_klass.add_rule(:rule_2)
188
+
189
+ expect(custom_ruleset_klass.new(subject).satisfied?(force: true)).to eq true
190
+ end
191
+ end
192
+ end
193
+
194
+ context 'when rule is not forceable' do
195
+ it 'returns false' do
196
+ with_mocked_rules do |rules|
197
+ subject = double
198
+ rules << mock_rule(:rule_1)
199
+ rules << mock_rule(:rule_2, is_satisfied: false, is_forceable: false)
200
+
201
+ custom_ruleset_klass = Class.new(Patterns::Ruleset)
202
+ custom_ruleset_klass.add_rule(:rule_1)
203
+ custom_ruleset_klass.add_rule(:rule_2)
204
+
205
+ expect(custom_ruleset_klass.new(subject).satisfied?(force: true)).to eq false
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end
212
+
213
+ describe '#each' do
214
+ it 'yields all rules for ruleset' do
215
+ with_mocked_rules do |rules|
216
+ rules << (_, rule_1 = mock_rule(:rule_1))
217
+ rules << (_, rule_2 = mock_rule(:rule_2))
218
+ rules << (_, rule_3 = mock_rule(:rule_3))
219
+ custom_ruleset_klass_1 = Class.new(Patterns::Ruleset)
220
+ custom_ruleset_klass_1.add_rule(:rule_1)
221
+ custom_ruleset_klass_1.add_rule(:rule_2)
222
+ Ruleset2 = Class.new(Patterns::Ruleset)
223
+ Ruleset2.add_rule(:rule_3)
224
+ custom_ruleset_klass_1.add_rule(:ruleset_2)
225
+
226
+ ruleset = custom_ruleset_klass_1.new(double)
227
+
228
+ expect { |b| ruleset.each(&b) }.to yield_successive_args(rule_1, rule_2, rule_3)
229
+ ensure
230
+ remove_class(Ruleset2)
231
+ end
232
+ end
233
+ end
234
+
235
+ private
236
+
237
+ def mock_rule(rule_name, is_applicable: true, is_satisfied: true, is_forceable: true)
238
+ klass = Object.const_set(rule_name.to_s.classify, Class.new(Patterns::Rule))
239
+ rule = double(
240
+ not_applicable?: !is_applicable,
241
+ satisfied?: is_satisfied,
242
+ forceable?: is_forceable
243
+ )
244
+ allow(klass).to receive(:new).with(anything) { rule }
245
+ [klass, rule]
246
+ end
247
+
248
+ def with_mocked_rules
249
+ rules_storage = []
250
+ yield rules_storage
251
+ ensure
252
+ rules_storage.each do |rule_klass, _rule_instance|
253
+ remove_class(rule_klass)
254
+ end
255
+ end
256
+
257
+ def remove_class(klass)
258
+ Object.send(:remove_const, klass.name.to_sym)
259
+ end
260
+ end
@@ -10,7 +10,7 @@ RSpec.describe Patterns::Service do
10
10
  expect(DoSomething.call).to be_kind_of(DoSomething)
11
11
  end
12
12
 
13
- it "instantiates service object passing arguments to constructor" do
13
+ it "instantiates service object passing keyword arguments to constructor" do
14
14
  DoSomething = Class.new(Patterns::Service) do
15
15
  def initialize(argument_1:, argument_2:); end
16
16
  def call; end
@@ -25,6 +25,21 @@ RSpec.describe Patterns::Service do
25
25
  }.not_to raise_error
26
26
  end
27
27
 
28
+ it "instantiates service object passing positional arguments to constructor" do
29
+ DoSomething = Class.new(Patterns::Service) do
30
+ def initialize(argument_1, argument_2); end
31
+ def call; end
32
+ end
33
+
34
+ expect {
35
+ DoSomething.call
36
+ }.to raise_error ArgumentError
37
+
38
+ expect {
39
+ DoSomething.call(10, 20)
40
+ }.not_to raise_error
41
+ end
42
+
28
43
  it "calls #call method on service object instance" do
29
44
  Spy = Class.new do
30
45
  def self.some_method; end
@@ -0,0 +1,79 @@
1
+ RSpec.describe Patterns::StrongRuleset do
2
+ it 'inherites from Ruleset' do
3
+ custom_strong_ruleset_klass = Class.new(Patterns::StrongRuleset)
4
+ expect(custom_strong_ruleset_klass.ancestors).to include Patterns::Ruleset
5
+ end
6
+
7
+ context 'when any of rules is not applicable' do
8
+ it 'is not satisfied' do
9
+ with_mocked_rules do |rules|
10
+ subject = double
11
+ rules << mock_rule(:rule_1, is_applicable: false)
12
+ rules << mock_rule(:rule_2)
13
+
14
+ custom_ruleset_klass = Class.new(Patterns::StrongRuleset)
15
+ custom_ruleset_klass.add_rule(:rule_1)
16
+ custom_ruleset_klass.add_rule(:rule_2)
17
+
18
+ expect(custom_ruleset_klass.new(subject).satisfied?).to eq false
19
+ end
20
+ end
21
+
22
+ context 'when not applicable rule is not satisfied' do
23
+ it 'is not forceable' do
24
+ with_mocked_rules do |rules|
25
+ subject = double
26
+ rules << mock_rule(:rule_1, is_applicable: false, is_satisfied: false)
27
+ rules << mock_rule(:rule_2)
28
+
29
+ custom_ruleset_klass = Class.new(Patterns::StrongRuleset)
30
+ custom_ruleset_klass.add_rule(:rule_1)
31
+ custom_ruleset_klass.add_rule(:rule_2)
32
+
33
+ expect(custom_ruleset_klass.new(subject).forceable?).to eq false
34
+ end
35
+ end
36
+ end
37
+
38
+ it 'is not applicable' do
39
+ with_mocked_rules do |rules|
40
+ subject = double
41
+ rules << mock_rule(:rule_1, is_applicable: false)
42
+ rules << mock_rule(:rule_2)
43
+
44
+ custom_ruleset_klass = Class.new(Patterns::StrongRuleset)
45
+ custom_ruleset_klass.add_rule(:rule_1)
46
+ custom_ruleset_klass.add_rule(:rule_2)
47
+
48
+ expect(custom_ruleset_klass.new(subject).applicable?).to eq false
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def mock_rule(rule_name, is_applicable: true, is_satisfied: true, is_forceable: true)
56
+ klass = Object.const_set(rule_name.to_s.classify, Class.new(Patterns::Rule))
57
+ rule = double(
58
+ not_applicable?: !is_applicable,
59
+ applicable?: is_applicable,
60
+ satisfied?: is_satisfied,
61
+ forceable?: is_forceable
62
+ )
63
+ allow(klass).to receive(:new).with(anything) { rule }
64
+ [klass, rule]
65
+ end
66
+
67
+ def with_mocked_rules
68
+ rules_storage = []
69
+ yield rules_storage
70
+ ensure
71
+ rules_storage.each do |rule_klass, _rule_instance|
72
+ remove_class(rule_klass)
73
+ end
74
+ end
75
+
76
+ def remove_class(klass)
77
+ Object.send(:remove_const, klass.name.to_sym)
78
+ end
79
+ end
data/spec/spec_helper.rb CHANGED
@@ -20,6 +20,7 @@
20
20
  require "active_support/all"
21
21
  require "active_support/testing/time_helpers"
22
22
  require "pry"
23
+ require "redis"
23
24
  require "rails-patterns"
24
25
 
25
26
  RSpec.configure do |config|