interactify 0.2.0.pre.alpha.1 → 0.3.0.pre.RC1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c2dffe1928c3b1653ea12057fd660eacbef1d8fbd946d1bdbddef00494a8c8b
4
- data.tar.gz: 7078fe7a6b217febd47f4598eb2e88fdcfb8affcd434dba239ee86a39a072176
3
+ metadata.gz: 319dd56313d82aa987c8eaec40691e43c58e8b72513969e2a6a7a83de17ae311
4
+ data.tar.gz: 3f5e4ad0464157910bca7bfd1300fa8e23ab825068c84a4c5c0ef5ad0cdb29d6
5
5
  SHA512:
6
- metadata.gz: 9e3eb106e5b896965c6b1fbfea135c62f4eb7f95779041c121bcb2040fd8568c54e94b4f77a3e1bcca6d922d8a8bdfcacda354d3a6396e19a49fbfce78f39af1
7
- data.tar.gz: 3f998d9f35105a47becc0555ed80dff264fad2e5fe58f93ec0c731b8cab4281e457da9e3fc00d0e4709e1b62b4e2416a8a75b66f56723b610531bca8fd458ea3
6
+ metadata.gz: fea0120a4456f108b41ccd48c994e365762bad8ff6424eba9d7ecd0c09c6858805e0456df0655eec86042ed7dc9fa589ffb8f3bdb49a14e79a7e05e30b6737ae
7
+ data.tar.gz: 64345da2e8b036ad65c639bddb9c0f90f93b1fdbc1415a5cb29f26596fa9169c6a9b0309d9d0ecbb515706251ff473f4ac98f7b41be7d89c089ce3b8537ade54
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.4
data/Appraisals ADDED
@@ -0,0 +1,21 @@
1
+ appraise "railties-7-sidekiq" do
2
+ gem "railties", "7"
3
+ gem "sidekiq", "7"
4
+ end
5
+
6
+ appraise "railties-7-no-sidekiq" do
7
+ gem "railties", "7"
8
+ end
9
+
10
+ appraise "railties-6-sidekiq" do
11
+ gem "railties", "6"
12
+ gem "sidekiq", "7"
13
+ end
14
+
15
+ appraise "railties-6-no-sidekiq" do
16
+ gem "railties", "6"
17
+ end
18
+
19
+ appraise "no-railties-no-sidekiq" do
20
+ # nothing extra
21
+ end
data/CHANGELOG.md CHANGED
@@ -1,9 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2023-12-16
3
+ ## [0.1.0-alpha.1] - 2023-12-16
4
4
 
5
5
  - Initial release
6
6
 
7
- ## [0.2.0] - 2023-12-27
7
+ ## [0.2.0-alpha.1] - 2023-12-27
8
8
 
9
9
  - Added support for Interactify.promising syntax in organizers
10
+
11
+ ## [0.3.0-alpha.1] - 2023-12-29
12
+
13
+ - Added support for `{if: :condition, then: A, else: B}` in organizers
14
+
15
+ ## [0.3.0-alpha.2] - 2023-12-29
16
+
17
+ - Remove deep_matching development dependency
18
+
19
+ ## [0.3.0-RC1] - 2023-12-29
20
+
21
+ - Fixed to work with and make optional dependencies for sidekiq and railties. Confirmed as working with ruby >= 3.1.4
22
+
data/README.md CHANGED
@@ -1,24 +1,28 @@
1
1
  # Interactify
2
2
 
3
- [Interactors](https://github.com/collectiveidea/interactor) are a great way to encapsulate business logic in a Rails application.
4
- However, sometimes in complex interactor chains, the complex debugging happens at one level up from your easy to read and test interactors.
5
-
6
- [interactor-contracts](https://github.com/michaelherold/interactor-contracts) does a fantastic job of making your interactor chains more reliable.
7
-
8
- Interactify wraps the interactor and interactor-contracts gem and provides additional functionality making chaining and understanding interactor chains easier.
9
-
10
- This is a bells and whistles gem and assumes you are working in a Rails project with Sidekiq.
11
- However, I'm open to the idea of making it more focused and making these more pluggable.
3
+ [![Gem Version](https://badge.fury.io/rb/interactify.svg)](https://badge.fury.io/rb/interactify)
4
+ ![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=markburns/interactify)
5
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
+ ![Ruby 3.3.0](https://img.shields.io/badge/ruby-3.3.0-green.svg)
7
+ ![Ruby 3.2.2](https://img.shields.io/badge/ruby-3.2.2-green.svg)
8
+ ![Ruby 3.1.4](https://img.shields.io/badge/ruby-3.1.4-green.svg)
9
+ [![Code Climate](https://codeclimate.com/github/markburns/interactify/badges/gpa.svg)](https://codeclimate.com/github/markburns/interactify)
10
+
11
+ Interactify enhances Rails applications by simplifying complex interactor chains.
12
+ This gem builds on [interactors](https://github.com/collectiveidea/interactor) and [interactor-contracts](https://github.com/michaelherold/interactor-contracts) to improve readability and maintainability of business logic.
13
+ We depend on activesupport, and optionally on railties and sidekiq. So it's a good fit for Rails projects using Sidekiq, offering advanced features for chain management and debugging.
14
+ Interactify is about making interactor usage in Rails more efficient and less error-prone, reducing the overhead of traditional interactor orchestration.
12
15
 
13
16
  ## Installation
14
17
 
15
- ```Gemfile
18
+ ```ruby
16
19
  gem 'interactify'
17
20
  ```
18
21
 
19
22
  ## Usage
20
23
 
21
24
  ### Initializer
25
+
22
26
  ```ruby
23
27
  # in config/initializers/interactify.rb
24
28
  Interactify.configure do |config|
@@ -39,10 +43,15 @@ end
39
43
  ```ruby
40
44
  # e.g. in spec/supoort/interactify.rb
41
45
  require 'interactify/rspec/matchers'
46
+
47
+ expect(described_class).to expect_inputs(:foo, :bar, :baz)
48
+ expect(described_class).to promise_outputs(:fee, :fi, :fo, :fum)
42
49
  ```
43
50
 
44
51
  ### Syntactic Sugar
45
52
  - Everything is an Organizer/Interactor and supports interactor-contracts.
53
+ - They only becomes considered an organizer once `organize` is called.
54
+ - They could technically be both (if you want?) but you have to remember to call `super` within `call` to trigger the organized interactors.
46
55
  - Concise syntax for most common scenarios with `expects` and `promises`. Verifying the presence of the keys/values.
47
56
  - Automatic delegation of expected and promised keys to the context.
48
57
 
@@ -63,7 +72,6 @@ class LoadOrder
63
72
  required(:order).filled
64
73
  end
65
74
 
66
-
67
75
  def call
68
76
  context.order = Order.find(context.id)
69
77
  end
@@ -89,8 +97,8 @@ end
89
97
 
90
98
  ### Lambdas
91
99
 
92
- With vanilla interactors, it's not possible to use lambdas in organizers, and sometimes we only want a lambda.
93
- So we added support.
100
+ With vanilla interactors, it wasn't possible to use lambdas in organizers.
101
+ But sometimes we only want a lambda. So we added support.
94
102
 
95
103
  ```ruby
96
104
  organize LoadOrder, ->(context) { context.order = context.order.decorate }
@@ -157,7 +165,6 @@ class DoSomethingWithOrder
157
165
  end
158
166
  ```
159
167
 
160
-
161
168
  ```ruby
162
169
  # after
163
170
  class OuterOrganizer
@@ -178,7 +185,6 @@ class LoadOrder
178
185
  end
179
186
  end
180
187
 
181
-
182
188
  class DoSomethingWithOrder
183
189
  # ... boilerplate ...
184
190
  def call
@@ -187,7 +193,7 @@ class DoSomethingWithOrder
187
193
  end
188
194
  ```
189
195
 
190
- ### Conditionals (if/else)
196
+ ### Conditionals (if/else) with lambda
191
197
 
192
198
  Along the same lines of each/iteration. We sometimes have to 'break the chain' with interactors just to conditionally call one interactor chain path or another.
193
199
 
@@ -212,26 +218,22 @@ class InnerThing
212
218
  end
213
219
  ```
214
220
 
215
-
216
221
  ```ruby
217
222
  # after
218
223
  class OuterThing
219
224
  # ... boilerplate ...
220
225
  organize \
221
226
  SetupStep,
222
- self.if(->(c){ c.thing == 'a' }, DoThingA, DoThingB),
223
- end
224
227
 
225
- ```
228
+ # lambda conditional
229
+ self.if(->(c){ c.thing == 'a' }, DoThingA, DoThingB),
226
230
 
227
- ### More Conditionals
231
+ # context conditional
232
+ self.if(:some_key_on_context, DoThingA, DoThingB),
228
233
 
229
- ```ruby
230
- class OuterThing
231
- # ... boilerplate ...
232
- organize \
233
- self.if(:key_set_on_context, DoThingA, DoThingB),
234
- AfterBothCases
234
+ # alternative hash syntax
235
+ {if: :key_set_on_context, then: DoThingA, else: DoThingB},
236
+ AfterDoThis
235
237
  end
236
238
  ```
237
239
 
@@ -251,6 +253,7 @@ class SomeOrganizer
251
253
  end
252
254
 
253
255
  ```
256
+
254
257
  ### Contract validation failures
255
258
  Sometimes contract validation fails at runtime as an exception. It's something unexpected and you'll have an `Interactor::Failure` sent to rollbar/sentry/honeybadger.
256
259
  If the context is large it's often hard to spot what the actual problem is or where it occurred.
@@ -299,7 +302,6 @@ Actual promises are:
299
302
  step1
300
303
  ```
301
304
 
302
-
303
305
  ### Interactor wiring specs
304
306
  Sometimes you have an interactor chain that fails because something is expected deeper down the chain and not provided further up the chain.
305
307
  The existing way to solve this is with enough integration specs to catch them, hunting and sticking a `byebug`, `debugger` or `binding.pry` in at suspected locations and inferring where in the chain the wiring went awry.
@@ -339,55 +341,33 @@ expect(described_class).to promise_outputs(:order)
339
341
  ### Sidekiq Jobs
340
342
  Sometimes you want to asyncify an interactor.
341
343
 
342
- ```ruby
343
- # before
344
- class SomeInteractor
345
- include Interactify
346
-
347
- def call
348
- # ...
349
- end
350
- end
351
-
352
- clsas SomeInteractorJob
353
- include Sidekiq::Job
354
-
355
- def perform(*args)
356
- SomeInteractor.call(*args)
357
- end
358
- end
359
- ```
360
-
344
+ #### before
361
345
  ```diff
362
- -SomeInteractor.call(*args)
363
- +SomeInteractorJob.perform_async(*args)
346
+ - SomeInteractor.call(*args)
347
+ + class SomeInteractorJob
348
+ + include Sidekiq::Job
349
+ +
350
+ + def perform(*args)
351
+ + SomeInteractor.call(*args)
352
+ + end
353
+ + end
354
+ +
355
+ + SomeInteractorJob.perform_async(*args)
364
356
  ```
365
357
 
366
- ```ruby
367
- # after
368
- class SomeInteractor
369
- include Interactify
370
-
371
- def call
372
- # ...
373
- end
374
- end
358
+ #### after
359
+ ```diff
360
+ - SomeInteractor.call!(*args)
361
+ + SomeInteractor::Async.call!(*args)
375
362
  ```
376
363
 
377
364
  No need to manually create a job class or handle the perform/call impedance mismatch
378
365
 
379
- ```diff
380
- -SomeInteractor.call!(*args)
381
- +SomeInteractor::Async.call!(*args)
382
- ```
383
-
384
366
  This also makes it easy to add cron jobs to run interactors. As any interactor can be asyncified.
385
367
  By using it's internal Async class.
386
368
 
387
369
  N.B. as your class is now executing asynchronously you can no longer rely on its promises later on in the chain.
388
370
 
389
-
390
-
391
371
  ## FAQs
392
372
  - This is ugly isn't it?
393
373
 
@@ -403,73 +383,33 @@ class OuterOrganizer
403
383
  )
404
384
  end
405
385
  ```
386
+ 1. Do you find the syntax of OuterOrganizer ugly?
406
387
 
407
- Yes I agree. It's early days and I'm open to syntax improvement ideas. This is really about it being conceptually less ugly than the alternative, which is to jump around between lots of files. In the existing alternative to using this gem the ugliness is not within each individual file, but within the overall hidden architecture and the hunting process of jumping around in complex interactor chains. We can't see that ugliness but we probably experience it. If you don't feel or experience that ugliness then this gem may not be the right fit for you.
388
+ While the syntax might seem unconventional initially, its conceptual elegance lies in streamlining complex interactor chains. Traditional methods often involve navigating through multiple files, creating a hidden and cumbersome architecture. This gem aims to alleviate that by centralizing operations, making the overall process more intuitive.
408
389
 
390
+ 2. Is this compatible with interactor/interactor-contracts?
409
391
 
410
- - Is this interactor/interactor-contracts compatible?
411
- Yes and we use them as dependencies. It's possible we'd drop those dependencies in the future but unlikely. I think it's highly likely we'd retain compatibility.
392
+ Yes, it's fully compatible. We currently use these as dependencies. While there's a possibility of future changes, maintaining this compatibility is a priority.
412
393
 
394
+ 3. Why not suggest enhancements to the interactor or interactor-contracts gems?
413
395
 
414
- - Why not propose changes to the interactor or interactor-contracts gem?
415
- Honestly, I think both are great and why we've built on top of them.
416
- I presume they'd object to such an extensive opinionated change, and I think that would be the right decision too.
417
- If this becomes more stable, less coupled to Rails, there's interest, and things we can provide upstream I'd be happy to propose changes to those gems.
418
-
419
- - Isn't this all just syntactic sugar?
420
- Yes, but it's sugar that makes the code easier to read and understand.
421
-
422
- - Is it really easier to parse this new DSL/syntax than POROs?
423
- That's subjective, but I think so. The benefit is you have fewer extraneous files patching over a common problem in interactors.
424
-
425
- - But it gets really verbose and complex!
426
- Again this is subjective, but if you've worked with apps with hundred or thousands of interactors, you'll have encountered these problems.
427
- I think when we work with interactors we're in one of two modes.
428
- Hunting to find the interactor we need to change, or working on the interactor we need to change.
429
- This makes the first step much easier.
430
- The second step has always been a great experience with interactors.
431
-
432
- - I prefer Service Objects
433
- If you're not heavily invested into interactors this may not be for you.
434
- I love the chaining interactors provide.
435
- I love the contracts.
436
- I love the simplicity of the interface.
437
- I love the way they can be composed.
438
- I love the way they can be tested.
439
- When I've used service objects, I've found them to be more complex to test and compose.
440
- I can't see a clean way that using service objects to compose interactors could work well without losing some of the aforementioned benefits.
441
-
442
- ### TODO
443
- We want to add support for explicitly specifying promises in organizers.
444
-
445
- The benefit here is on clarifying the contract between organizers and interactors.
446
-
447
- This is another variation of the "interactors themselves are great but their coordination and finding where things happen is hard in large applications".
448
-
449
- By adding promise notation to organizers, we can signal to the reader that 'here in this part of the chain is the thing you are looking for'.
450
-
451
- A writer of an organizer may expect LoadOrder to promise :order, but for the reader, it's not quite as explicit.
452
- The expected syntax will be
453
-
454
- ```ruby
455
- organize \
456
- LoadOrder.promising(:order),
457
- TakePayment.promising(:payment_transaction)
458
- ```
396
+ These gems are excellent in their own right, which is why we've built upon them. Proposing such extensive changes might not align with their current philosophy. However, if our approach proves stable and garners interest, we're open to discussing potential contributions to these gems.
459
397
 
460
- This will be validated at test time against the interactors promises.
398
+ 4. Is this just syntactic sugar?
461
399
 
400
+ It's more than that. This approach enhances readability and comprehension of the code. It simplifies the structure, making it easier to navigate and maintain.
462
401
 
463
- ## Development
402
+ 5. Is the new DSL/syntax easier to understand than plain old Ruby objects (POROs)?
464
403
 
465
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
404
+ This is subjective, but we believe it is. It reduces the need for numerous files addressing common interactor issues, thereby streamlining the workflow.
466
405
 
467
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
406
+ 6. Doesn't this approach become verbose and complex in large applications?
468
407
 
469
- ## Contributing
408
+ While it may appear so, this method shines in large-scale applications with numerous interactors. It simplifies locating and modifying the necessary interactors, which is often a cumbersome process.
470
409
 
471
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/interactify.
410
+ 7. What if I prefer using Service Objects?
472
411
 
412
+ That's completely valid. Service Objects have their merits, but this gem is particularly useful for those deeply engaged with interactors. It capitalizes on the chaining, contracts, simplicity, composability, and testability that interactors offer. Combining Service Objects with interactors might not retain these advantages as effectively.
473
413
  ## License
474
414
 
475
415
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_RETRY: "1"
@@ -0,0 +1,16 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+
7
+ group :development do
8
+ gem "bundler", "~> 2.0"
9
+ end
10
+
11
+ group :test do
12
+ gem "simplecov", require: false
13
+ gem "rspec", "~> 3.0"
14
+ end
15
+
16
+ gemspec path: "../"
@@ -0,0 +1,127 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ interactify (0.3.0.pre.alpha.2)
5
+ activesupport (>= 6.0.0)
6
+ interactor
7
+ interactor-contracts
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (7.1.2)
13
+ base64
14
+ bigdecimal
15
+ concurrent-ruby (~> 1.0, >= 1.0.2)
16
+ connection_pool (>= 2.2.5)
17
+ drb
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ mutex_m
21
+ tzinfo (~> 2.0)
22
+ appraisal (2.5.0)
23
+ bundler
24
+ rake
25
+ thor (>= 0.14.0)
26
+ base64 (0.2.0)
27
+ bigdecimal (3.1.5)
28
+ concurrent-ruby (1.2.2)
29
+ connection_pool (2.4.1)
30
+ debug (1.9.1)
31
+ irb (~> 1.10)
32
+ reline (>= 0.3.8)
33
+ diff-lcs (1.5.0)
34
+ docile (1.4.0)
35
+ drb (2.2.0)
36
+ ruby2_keywords
37
+ dry-configurable (1.0.1)
38
+ dry-core (~> 1.0, < 2)
39
+ zeitwerk (~> 2.6)
40
+ dry-core (1.0.0)
41
+ concurrent-ruby (~> 1.0)
42
+ zeitwerk (~> 2.6)
43
+ dry-inflector (1.0.0)
44
+ dry-initializer (3.1.1)
45
+ dry-logic (1.5.0)
46
+ concurrent-ruby (~> 1.0)
47
+ dry-core (~> 1.0, < 2)
48
+ zeitwerk (~> 2.6)
49
+ dry-schema (1.13.3)
50
+ concurrent-ruby (~> 1.0)
51
+ dry-configurable (~> 1.0, >= 1.0.1)
52
+ dry-core (~> 1.0, < 2)
53
+ dry-initializer (~> 3.0)
54
+ dry-logic (>= 1.4, < 2)
55
+ dry-types (>= 1.7, < 2)
56
+ zeitwerk (~> 2.6)
57
+ dry-types (1.7.1)
58
+ concurrent-ruby (~> 1.0)
59
+ dry-core (~> 1.0)
60
+ dry-inflector (~> 1.0)
61
+ dry-logic (~> 1.4)
62
+ zeitwerk (~> 2.6)
63
+ dry-validation (1.10.0)
64
+ concurrent-ruby (~> 1.0)
65
+ dry-core (~> 1.0, < 2)
66
+ dry-initializer (~> 3.0)
67
+ dry-schema (>= 1.12, < 2)
68
+ zeitwerk (~> 2.6)
69
+ i18n (1.14.1)
70
+ concurrent-ruby (~> 1.0)
71
+ interactor (3.1.2)
72
+ interactor-contracts (0.3.0)
73
+ dry-validation (~> 1.0)
74
+ interactor (~> 3)
75
+ io-console (0.7.1)
76
+ irb (1.11.0)
77
+ rdoc
78
+ reline (>= 0.3.8)
79
+ minitest (5.20.0)
80
+ mutex_m (0.2.0)
81
+ psych (5.1.2)
82
+ stringio
83
+ rake (13.1.0)
84
+ rdoc (6.6.2)
85
+ psych (>= 4.0.0)
86
+ reline (0.4.1)
87
+ io-console (~> 0.5)
88
+ rspec (3.12.0)
89
+ rspec-core (~> 3.12.0)
90
+ rspec-expectations (~> 3.12.0)
91
+ rspec-mocks (~> 3.12.0)
92
+ rspec-core (3.12.2)
93
+ rspec-support (~> 3.12.0)
94
+ rspec-expectations (3.12.3)
95
+ diff-lcs (>= 1.2.0, < 2.0)
96
+ rspec-support (~> 3.12.0)
97
+ rspec-mocks (3.12.6)
98
+ diff-lcs (>= 1.2.0, < 2.0)
99
+ rspec-support (~> 3.12.0)
100
+ rspec-support (3.12.1)
101
+ ruby2_keywords (0.0.5)
102
+ simplecov (0.22.0)
103
+ docile (~> 1.1)
104
+ simplecov-html (~> 0.11)
105
+ simplecov_json_formatter (~> 0.1)
106
+ simplecov-html (0.12.3)
107
+ simplecov_json_formatter (0.1.4)
108
+ stringio (3.1.0)
109
+ thor (1.3.0)
110
+ tzinfo (2.0.6)
111
+ concurrent-ruby (~> 1.0)
112
+ zeitwerk (2.6.12)
113
+
114
+ PLATFORMS
115
+ ruby
116
+
117
+ DEPENDENCIES
118
+ appraisal
119
+ bundler (~> 2.0)
120
+ debug
121
+ interactify!
122
+ rake (~> 13.0)
123
+ rspec (~> 3.0)
124
+ simplecov
125
+
126
+ BUNDLED WITH
127
+ 2.4.22
@@ -0,0 +1,14 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "simplecov", require: false
6
+ gem "rake", "~> 13.0"
7
+ gem "railties", "6"
8
+ gem "sidekiq", "7"
9
+
10
+ group :test do
11
+ gem "rspec", "~> 3.0"
12
+ end
13
+
14
+ gemspec path: "../"