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,260 @@
1
+ RSpec.describe Ruleset do
2
+ context 'when empty ruleset is initialized' do
3
+ it 'raises an error' do
4
+ empty_ruleset_klass = Class.new(Ruleset)
5
+ custom_ruleset_klass = Class.new(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 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(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(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(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(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(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(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(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(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(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(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(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(Ruleset)
220
+ custom_ruleset_klass_1.add_rule(:rule_1)
221
+ custom_ruleset_klass_1.add_rule(:rule_2)
222
+ Ruleset2 = Class.new(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(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 StrongRuleset do
2
+ it 'inherites from Ruleset' do
3
+ custom_strong_ruleset_klass = Class.new(StrongRuleset)
4
+ expect(custom_strong_ruleset_klass.ancestors).to include 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(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(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(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(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
@@ -17,10 +17,15 @@
17
17
  #
18
18
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
19
19
 
20
- require "rails-patterns"
20
+ require "active_support/all"
21
+ require "active_support/testing/time_helpers"
21
22
  require "pry"
23
+ require "redis"
24
+ require "rails-patterns"
22
25
 
23
26
  RSpec.configure do |config|
27
+ config.include ActiveSupport::Testing::TimeHelpers
28
+
24
29
  # rspec-expectations config goes here. You can use an alternate
25
30
  # assertion/expectation library such as wrong or the stdlib/minitest
26
31
  # assertions if you prefer.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-patterns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stevo
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-21 00:00:00.000000000 Z
11
+ date: 2020-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby2_keywords
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: rspec
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +86,14 @@ dependencies:
72
86
  requirements:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
- version: '1.0'
89
+ version: '2.0'
76
90
  type: :development
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
- version: '1.0'
96
+ version: '2.0'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: juwelier
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -103,6 +117,7 @@ extra_rdoc_files:
103
117
  - README.md
104
118
  files:
105
119
  - ".document"
120
+ - ".github/workflows/ruby.yml"
106
121
  - ".rspec"
107
122
  - Gemfile
108
123
  - Gemfile.lock
@@ -111,22 +126,33 @@ files:
111
126
  - Rakefile
112
127
  - VERSION
113
128
  - lib/patterns.rb
129
+ - lib/patterns/calculation.rb
114
130
  - lib/patterns/collection.rb
115
131
  - lib/patterns/form.rb
116
132
  - lib/patterns/query.rb
133
+ - lib/patterns/rule.rb
134
+ - lib/patterns/ruleset.rb
117
135
  - lib/patterns/service.rb
136
+ - lib/patterns/strong_ruleset.rb
118
137
  - lib/rails-patterns.rb
119
138
  - rails-patterns.gemspec
139
+ - spec/helpers/custom_calculation.rb
140
+ - spec/helpers/custom_calculation_script.rb
141
+ - spec/helpers/rails_redis_cache_mock.rb
142
+ - spec/patterns/calculation_spec.rb
120
143
  - spec/patterns/collection_spec.rb
121
144
  - spec/patterns/form_spec.rb
122
145
  - spec/patterns/query_spec.rb
146
+ - spec/patterns/rule_spec.rb
147
+ - spec/patterns/ruleset_spec.rb
123
148
  - spec/patterns/service_spec.rb
149
+ - spec/patterns/strong_ruleset_spec.rb
124
150
  - spec/spec_helper.rb
125
151
  homepage: http://github.com/selleo/pattern
126
152
  licenses:
127
153
  - MIT
128
154
  metadata: {}
129
- post_install_message:
155
+ post_install_message:
130
156
  rdoc_options: []
131
157
  require_paths:
132
158
  - lib
@@ -134,16 +160,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
134
160
  requirements:
135
161
  - - ">="
136
162
  - !ruby/object:Gem::Version
137
- version: '0'
163
+ version: 2.5.0
138
164
  required_rubygems_version: !ruby/object:Gem::Requirement
139
165
  requirements:
140
166
  - - ">="
141
167
  - !ruby/object:Gem::Version
142
168
  version: '0'
143
169
  requirements: []
144
- rubyforge_project:
145
- rubygems_version: 2.6.10
146
- signing_key:
170
+ rubygems_version: 3.1.2
171
+ signing_key:
147
172
  specification_version: 4
148
173
  summary: A collection of lightweight, standardized, rails-oriented patterns.
149
174
  test_files: []