clear_logic 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a5c1820917b795fd279d87e1fb80123d6912ca317200906e5535e1768feb1418
4
+ data.tar.gz: 35a2da8237ddff477f586597a86f353834ce066f22c5fd052050625ad26ee82e
5
+ SHA512:
6
+ metadata.gz: 889d1c305931fc619cd7ff316f7b5747fd83fb9478e3a24e207fe244dad9add4cb01c60ae8dd21268ffa80466d95b6f684cec7636eaab4f9d37afa5a8014d41c
7
+ data.tar.gz: f704322fff29893f8863f66bdd4741e8f80fd17e010786abe5c1dd7580e4371e8886ec75cae95ead6b740421458187decfdd62b80ab3c043a57146dd32ec41b9
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ /vendor/bundle
13
+ /log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.1
7
+ before_install: gem install bundler -v 1.17.3
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at yaroslav.bezrukavyi@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in clear_logic.gemspec
8
+ gemspec
@@ -0,0 +1,86 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ clear_logic (0.1.0)
5
+ dry-inflector
6
+ dry-initializer (>= 2.5.0)
7
+ dry-matcher (>= 0.7)
8
+ dry-monads (>= 0.3.1)
9
+ dry-transaction (>= 0.12.0)
10
+ dry-types (>= 1.0.0)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ coderay (1.1.2)
16
+ concurrent-ruby (1.1.5)
17
+ diff-lcs (1.3)
18
+ dry-configurable (0.8.3)
19
+ concurrent-ruby (~> 1.0)
20
+ dry-core (~> 0.4, >= 0.4.7)
21
+ dry-container (0.7.0)
22
+ concurrent-ruby (~> 1.0)
23
+ dry-configurable (~> 0.1, >= 0.1.3)
24
+ dry-core (0.4.7)
25
+ concurrent-ruby (~> 1.0)
26
+ dry-equalizer (0.2.2)
27
+ dry-events (0.1.1)
28
+ concurrent-ruby (~> 1.0)
29
+ dry-core (~> 0.4)
30
+ dry-equalizer (~> 0.2)
31
+ dry-inflector (0.1.2)
32
+ dry-initializer (3.0.1)
33
+ dry-logic (1.0.0)
34
+ concurrent-ruby (~> 1.0)
35
+ dry-core (~> 0.2)
36
+ dry-equalizer (~> 0.2)
37
+ dry-matcher (0.7.0)
38
+ dry-monads (1.2.0)
39
+ concurrent-ruby (~> 1.0)
40
+ dry-core (~> 0.4, >= 0.4.4)
41
+ dry-equalizer
42
+ dry-transaction (0.13.0)
43
+ dry-container (>= 0.2.8)
44
+ dry-events (>= 0.1.0)
45
+ dry-matcher (>= 0.7.0)
46
+ dry-monads (>= 0.4.0)
47
+ dry-types (1.0.1)
48
+ concurrent-ruby (~> 1.0)
49
+ dry-container (~> 0.3)
50
+ dry-core (~> 0.4, >= 0.4.4)
51
+ dry-equalizer (~> 0.2, >= 0.2.2)
52
+ dry-inflector (~> 0.1, >= 0.1.2)
53
+ dry-logic (~> 1.0)
54
+ ffaker (2.11.0)
55
+ method_source (0.9.2)
56
+ pry (0.12.2)
57
+ coderay (~> 1.1.0)
58
+ method_source (~> 0.9.0)
59
+ rake (10.5.0)
60
+ rspec (3.8.0)
61
+ rspec-core (~> 3.8.0)
62
+ rspec-expectations (~> 3.8.0)
63
+ rspec-mocks (~> 3.8.0)
64
+ rspec-core (3.8.0)
65
+ rspec-support (~> 3.8.0)
66
+ rspec-expectations (3.8.3)
67
+ diff-lcs (>= 1.2.0, < 2.0)
68
+ rspec-support (~> 3.8.0)
69
+ rspec-mocks (3.8.0)
70
+ diff-lcs (>= 1.2.0, < 2.0)
71
+ rspec-support (~> 3.8.0)
72
+ rspec-support (3.8.0)
73
+
74
+ PLATFORMS
75
+ ruby
76
+
77
+ DEPENDENCIES
78
+ bundler (~> 1.17)
79
+ clear_logic!
80
+ ffaker
81
+ pry
82
+ rake (~> 10.0)
83
+ rspec (~> 3.0)
84
+
85
+ BUNDLED WITH
86
+ 1.17.3
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 bezrukavyi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,482 @@
1
+ # ClearLogic
2
+
3
+ Service object based on [dry-rb](https://dry-rb.org/)
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem 'clear_logic'
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ - [Interface](#interface)
14
+ - [Types](#types)
15
+ - [Context](#context)
16
+ - [Stride](#stride)
17
+ - [Result](#result)
18
+ - [Logger](#logger)
19
+ - [Matcher](#matcher)
20
+
21
+
22
+ ## Interface
23
+
24
+ DSL for building class initializer with params and options based on [`dry-initializer`](https://github.com/dry-rb/dry-initializer)
25
+
26
+ ```ruby
27
+ class MyService < ClearLogic::Service
28
+ context :name, Dry::Types['strict.string'], as: :param
29
+ context :test, Dry::Types['strict.bool'], default: -> { false }
30
+
31
+ stride :some_step
32
+
33
+ def some_step(context)
34
+ context.name # => first argument
35
+ success(context)
36
+ end
37
+ end
38
+ ```
39
+
40
+ In console:
41
+
42
+ ```ruby
43
+ result = MyService.call('Liskov', test: true)
44
+
45
+ result.success? # => true
46
+ result.context.name # => 'some string'
47
+ result.context.test # => true
48
+ ```
49
+
50
+ ```ruby
51
+ result = MyService.call('Lenon')
52
+
53
+ result.success? # => true
54
+ result.context.name # => 'some string'
55
+ result.context.test # => false
56
+ ```
57
+
58
+ # Types
59
+
60
+ You can register own `class` types.
61
+
62
+ In your initializer:
63
+
64
+ ```ruby
65
+ ActiveRecord::Base.connection.tables.map { |x| x.classify.safe_constantize }.compact.each do |model|
66
+ ClearLogic::Types.register(model, prefix: 'model')
67
+ end
68
+ ```
69
+
70
+ In your initializer:
71
+
72
+ ```ruby
73
+ Dry::Types['model.user']
74
+ ```
75
+
76
+ ## Context
77
+
78
+ In each step you work with `context`. Context have next methods which are available to you:
79
+ - `args`
80
+ - `[]`
81
+ - `[]=`
82
+ - `exit_success?`
83
+ - `catched_error?`
84
+ - `failure_error?`
85
+ - `to_h`
86
+
87
+ And next accessors:
88
+ - `catched_error`
89
+ - `failure_error`
90
+ - `service`
91
+ - `exit_success`
92
+ - `step`
93
+
94
+ ```ruby
95
+ class MyService < ClearLogic::Service
96
+ context :name, Dry::Types['strict.string'], as: :param
97
+
98
+ stride :some_step
99
+
100
+ def some_step(context)
101
+ fetch_user
102
+
103
+ success(context)
104
+ end
105
+
106
+ private
107
+
108
+ def fetch_user
109
+ context[:user] = User.find_by(name: context.name)
110
+ end
111
+ end
112
+ ```
113
+
114
+ In console:
115
+
116
+ ```ruby
117
+ result = MyService.call('Buscemi')
118
+
119
+ result.context.name # => 'Buscemi'
120
+ result.context.step # => :some_step # return last step
121
+ result.context.args # => ['Buscemi']
122
+
123
+ result.context[:some_option] # => true
124
+
125
+ result.context.to_h # => {:catched_error=>nil, :failure_error=>nil, :service=>MyService, :exit_success=>nil, :step=>:some_step, :options=>{:some_option=>true}, :args=>['Buscemi']}
126
+
127
+ result.context.exit_success? # => false
128
+ result.context.catched_error? # => false
129
+ result.context.failure_error? # => false
130
+ ```
131
+
132
+ ## Stride
133
+
134
+ Business transaction DSL based on [`dry-transaction`](https://github.com/dry-rb/dry-transaction)
135
+
136
+ ### Catch failed step
137
+
138
+ ```ruby
139
+ class MyService < ClearLogic::Service
140
+ stride :first, failure: :some_rollback
141
+ stride :second
142
+
143
+ def first(context)
144
+ # ...
145
+ failure(context)
146
+ end
147
+
148
+ def second(context)
149
+ # ...
150
+ success(context)
151
+ end
152
+
153
+ def some_rollback(context)
154
+ # Do sth if first step is failed
155
+ success(context)
156
+ end
157
+ end
158
+ ```
159
+
160
+ In console:
161
+
162
+ ```ruby
163
+ result = MyService.call
164
+
165
+ result.success? # => true
166
+ ```
167
+
168
+ ### Catch specific errors
169
+
170
+ ```ruby
171
+ class MyService < ClearLogic::Service
172
+ stride :first
173
+ stride :second, rescue: { StandardError => :some_rescue }
174
+
175
+ def first(context)
176
+ # ...
177
+ success(context)
178
+ end
179
+
180
+ def second(context)
181
+ # ...
182
+ raise StandardError
183
+ end
184
+
185
+ def some_rescue(context)
186
+ # Do sth if first step is raised error from `rescue` option
187
+ failure(context)
188
+ end
189
+ end
190
+ ```
191
+
192
+ In console:
193
+
194
+ ```ruby
195
+ result = MyService.call
196
+
197
+ result.success? # => false
198
+ result.context.catched_error? # => true
199
+ result.context.catched_error.class # => StandardError
200
+ ```
201
+
202
+ ### Exit success from process
203
+
204
+ ```ruby
205
+ class MyService < ClearLogic::Service
206
+ stride :first
207
+ stride :second, rescue: { StandardError => :some_rescue }
208
+
209
+ def first(context)
210
+ context[:test] = 'first'
211
+ exit_success(context)
212
+ end
213
+
214
+ def second(context)
215
+ context[:test] = 'second'
216
+ success(context)
217
+ end
218
+ end
219
+ ```
220
+
221
+ In console:
222
+
223
+ ```ruby
224
+ result = MyService.call
225
+
226
+ result.success? # => true
227
+ result.context[:test] # => 'first'
228
+ result.exit_success? # => true'
229
+ ```
230
+
231
+ ## Result
232
+
233
+ Result with status of failed execution based on [`dry-monads`](https://github.com/dry-rb/dry-monads)
234
+
235
+ ### Default errors
236
+
237
+ Aside from default monads methods `success` and `failure` you have next failure methods which give you status of failed execution:
238
+
239
+ - `unauthorized`
240
+ - `forbidden`
241
+ - `not_found`
242
+ - `invalid`
243
+
244
+ ```ruby
245
+ class MyService < ClearLogic::Service
246
+ stride :first
247
+ stride :second
248
+
249
+ def first(context)
250
+ # ...
251
+ not_found(context)
252
+ end
253
+
254
+ def second(context)
255
+ # If will not be handled
256
+ failure(context)
257
+ end
258
+ end
259
+ ```
260
+
261
+ In console:
262
+
263
+ ```ruby
264
+ result = MyService.call
265
+
266
+ result.failure? # => false
267
+ result.context.failure_error? # => true
268
+ result.context.failure_error.status # => :not_found
269
+ ```
270
+
271
+ ### Custom errors
272
+
273
+ You also can use own errors
274
+
275
+ ```ruby
276
+ class MyService < ClearLogic::Service
277
+ errors :failed_logic
278
+
279
+ stride :first
280
+ stride :second
281
+
282
+ def first(context)
283
+ # ...
284
+ failed_logic(context)
285
+ end
286
+
287
+ def second(context)
288
+ # If will not be handled
289
+ success(context)
290
+ end
291
+ end
292
+ ```
293
+
294
+ In console:
295
+
296
+ ```ruby
297
+ result = MyService.call
298
+
299
+ result.failure? # => false
300
+ result.context.failure_error? # => true
301
+ result.context.failure_error.status # => :failed_logic
302
+ ```
303
+
304
+ ## Logger
305
+
306
+ DSL for insert log context state on each step
307
+
308
+ ### Default logger
309
+
310
+ ```ruby
311
+ class MyService < ClearLogic::Service
312
+ stride :first, log: true
313
+ stride :second
314
+ stride :third, log: true
315
+
316
+ def first(context)
317
+ context[:test] = ['first']
318
+ success(context)
319
+ end
320
+
321
+ def second(context)
322
+ context[:test] += ['second']
323
+ success(context)
324
+ end
325
+
326
+ def third(context)
327
+ context[:test] += ['third']
328
+ success(context)
329
+ end
330
+ end
331
+ ```
332
+
333
+ In console:
334
+
335
+ ```ruby
336
+ result = MyService.call
337
+ ```
338
+
339
+ In `log/my_service.log`
340
+
341
+ ```pl
342
+ # Logfile created on 2019-06-06 10:28:42 +0300 by logger.rb/61378
343
+
344
+ [19-06-06 10:28:42.087 #14159#79000] INFO -- : {
345
+ "catched_error": null,
346
+ "failure_error": null,
347
+ "service": "LoggerService",
348
+ "exit_success": null,
349
+ "step": "first_log",
350
+ "options": {
351
+ "test": [
352
+ "first"
353
+ ]
354
+ },
355
+ "args": [
356
+
357
+ ]
358
+ }
359
+ [19-06-06 10:28:42.087 #14159#79000] INFO -- : {
360
+ "catched_error": null,
361
+ "failure_error": null,
362
+ "service": "LoggerService",
363
+ "exit_success": null,
364
+ "step": "third_log",
365
+ "options": {
366
+ "test": [
367
+ "first",
368
+ "second",
369
+ "third"
370
+ ]
371
+ },
372
+ "args": [
373
+
374
+ ]
375
+ }
376
+
377
+ ```
378
+
379
+ ### Custom logger
380
+
381
+ ```ruby
382
+ class MyLogger < ::Logger
383
+ def format_message(severity, time, progname, context)
384
+ context[:test][0] + "\n"
385
+ end
386
+ end
387
+
388
+ class MyService < ClearLogic::Service
389
+ logger MyLogger
390
+
391
+ stride :first, log: true
392
+
393
+ def first(context)
394
+ context[:test] = ['first']
395
+ success(context)
396
+ end
397
+
398
+ def second(context)
399
+ context[:test] += ['second']
400
+ success(context)
401
+ end
402
+ end
403
+ ```
404
+
405
+ In `log/my_service.log`
406
+
407
+ ```pl
408
+ # Logfile created on 2019-06-06 10:28:42 +0300 by logger.rb/61378
409
+ first
410
+ first
411
+ ```
412
+
413
+
414
+ ## Matcher
415
+
416
+ Service for handling failed result based on [`dry-matcher`](https://github.com/dry-rb/dry-matcher)
417
+
418
+ ```ruby
419
+ class MyService < ClearLogic::Service
420
+ context :user_id, Dry::Types['strict.integer']
421
+
422
+ errors :custom_error
423
+
424
+ stride :find_user
425
+ stride :check_policy
426
+
427
+ def initilize
428
+ end
429
+
430
+ def find_user(context)
431
+ return not_found(context) unless fetched_user
432
+
433
+ success(context)
434
+ end
435
+
436
+ def check_policy(context)
437
+ return forbidden(context) unless user_policy.has_ability?
438
+
439
+ success(context)
440
+ end
441
+
442
+ def fetched_user
443
+ @fetched_user ||= User.find_by(id: context.user_id)
444
+ end
445
+
446
+ def user_policy
447
+ @user_policy ||= PolicyUser.call(fetched_user)
448
+ end
449
+ end
450
+ ```
451
+
452
+ In matcher you can check [custom errors](#custom-errors) and [default result](#default-errors) errors
453
+
454
+ In console:
455
+
456
+ ```ruby
457
+ result = MyService.call(user_id: 1)
458
+
459
+ match_result = ClearLogic::Matcher.call(result) do |on|
460
+ on.failure(:unauthorized) { :unauthorized_result }
461
+ on.failure(:forbidden) { :forbidden_result }
462
+ on.failure(:not_found) { :not_found_result }
463
+ on.failure(:invalid) { :invalid_result }
464
+ on.failure(:custom_error) { :custom_error_result }
465
+ on.failure { :failure_result }
466
+ on.success { :success_result }
467
+ end
468
+
469
+ match_result # => :not_found_result
470
+ ```
471
+
472
+ ## Contributing
473
+
474
+ Bug reports and pull requests are welcome on GitHub at https://github.com/bezrukavyi/clear_logic. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
475
+
476
+ ## License
477
+
478
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
479
+
480
+ ## Code of Conduct
481
+
482
+ Everyone interacting in the ClearLogic project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/bezrukavyi/clear_logic/blob/master/CODE_OF_CONDUCT.md).