apparatus 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 850cfd0da74aad4407d90cc19810eaeae86b5df42f52be53e9f6692f9c5c70cd
4
+ data.tar.gz: 72fc26c24dc5f16adcf5f34e0a04b762147f5c060a6b8289571c766b55e72fc6
5
+ SHA512:
6
+ metadata.gz: e9b68dac90b308fa4cd441b0a3d23af2623ce2f84db169d52b3fa28c7c269ea34d2df40724d5d30796457ce9ab9541cdc17ec4ee4b275ce161b5b02bcdb4dbf6
7
+ data.tar.gz: 2bbf204f185fd43855f952fac9f5b0d700d46c703a0a3243c56eaab2cad6927a9e2a4236a42446cc180c703921e4691440f381520fefc028527d8d7b37342057
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,152 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ require:
4
+ - rubocop-performance
5
+ - rubocop-rake
6
+ - rubocop-rspec
7
+
8
+ AllCops:
9
+ NewCops: enable
10
+ TargetRubyVersion: "3.2.0"
11
+ Exclude:
12
+ - "bin/**/*"
13
+
14
+ Style/StringLiterals:
15
+ Enabled: true
16
+ EnforcedStyle: double_quotes
17
+
18
+ Style/FrozenStringLiteralComment:
19
+ Enabled: false
20
+
21
+ Style/WordArray:
22
+ Enabled: true
23
+ EnforcedStyle: percent
24
+
25
+ Layout/LineLength:
26
+ Enabled: false
27
+
28
+ Style/NumericLiterals:
29
+ Strict: false
30
+
31
+ Style/Documentation:
32
+ Enabled: false
33
+
34
+ Style/PercentLiteralDelimiters:
35
+ Enabled: true
36
+ PreferredDelimiters:
37
+ default: "()"
38
+ "%": "{}"
39
+ "%i": "()"
40
+ "%w": "()"
41
+
42
+ Metrics/MethodLength:
43
+ Enabled: false
44
+
45
+ Metrics/BlockLength:
46
+ Enabled: false
47
+
48
+ Style/StringLiteralsInInterpolation:
49
+ Enabled: false
50
+
51
+ Style/RegexpLiteral:
52
+ Enabled: false
53
+
54
+ Style/AsciiComments:
55
+ Enabled: false
56
+
57
+ Metrics/ClassLength:
58
+ Enabled: false
59
+
60
+ Layout/FirstHashElementIndentation:
61
+ EnforcedStyle: consistent
62
+
63
+ Bundler/OrderedGems:
64
+ Enabled: false
65
+
66
+ Metrics/AbcSize:
67
+ Enabled: false
68
+
69
+ Metrics/CyclomaticComplexity:
70
+ Enabled: false
71
+
72
+ Style/NumericPredicate:
73
+ Enabled: false
74
+
75
+ Style/ZeroLengthPredicate:
76
+ Enabled: false
77
+
78
+ Metrics/PerceivedComplexity:
79
+ Enabled: false
80
+
81
+ Metrics/ModuleLength:
82
+ Enabled: false
83
+
84
+ Naming/AccessorMethodName:
85
+ Enabled: false
86
+
87
+ Style/FormatStringToken:
88
+ Enabled: false
89
+
90
+ Style/EvenOdd:
91
+ Enabled: true
92
+
93
+ Style/IfUnlessModifier:
94
+ Enabled: false
95
+
96
+ Metrics/ParameterLists:
97
+ Enabled: false
98
+
99
+ Naming/VariableNumber:
100
+ Enabled: false
101
+
102
+ Layout/SpaceAroundOperators:
103
+ Enabled: true
104
+ EnforcedStyleForExponentOperator: space
105
+
106
+ Lint/SuppressedException:
107
+ Enabled: false
108
+
109
+ Style/RescueStandardError:
110
+ Enabled: false
111
+
112
+ Naming/RescuedExceptionsVariableName:
113
+ Enabled: false
114
+
115
+ Style/GuardClause:
116
+ Enabled: false
117
+
118
+ Layout/BeginEndAlignment:
119
+ EnforcedStyleAlignWith: begin
120
+
121
+ Style/DoubleNegation:
122
+ Enabled: false
123
+
124
+ Lint/FormatParameterMismatch:
125
+ Enabled: false
126
+
127
+ Style/HashSyntax:
128
+ Enabled: false
129
+
130
+ Style/FetchEnvVar:
131
+ Enabled: false
132
+
133
+ Style/RedundantRegexpEscape:
134
+ Enabled: false
135
+
136
+ Style/RedundantConstantBase:
137
+ Enabled: false
138
+
139
+ Style/NegatedIf:
140
+ Enabled: false
141
+
142
+ Style/ReturnNilInPredicateMethodDefinition:
143
+ Enabled: false
144
+
145
+ RSpec/ExampleLength:
146
+ Enabled: false
147
+
148
+ RSpec/MultipleExpectations:
149
+ Enabled: false
150
+
151
+ RSpec/BeEq:
152
+ Enabled: false
data/.rubocop_todo.yml ADDED
File without changes
data/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ ## [Unreleased]
2
+
3
+ ## 0.1.2 - 2023-08-20
4
+
5
+ - Add basic tests
6
+ - Add rubocop gems
7
+ - Improve main logic (thanks tests)
8
+
9
+ ## 0.1.1 - 2023-08-14
10
+
11
+ - Add source code for the apparatus
12
+ - Create meaningful README.md
13
+
14
+ ## 0.1.0 - 2023-08-09
15
+
16
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+ gemspec
3
+
4
+ gem "bundler"
5
+ gem "rake"
6
+ gem "rspec", "~> 3.0"
7
+ gem "rubocop"
8
+ gem "rubocop-performance"
9
+ gem "rubocop-rake"
10
+ gem "rubocop-rspec"
data/Gemfile.lock ADDED
@@ -0,0 +1,80 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ apparatus (0.1.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ base64 (0.1.1)
11
+ diff-lcs (1.5.0)
12
+ json (2.6.3)
13
+ language_server-protocol (3.17.0.3)
14
+ parallel (1.23.0)
15
+ parser (3.2.2.3)
16
+ ast (~> 2.4.1)
17
+ racc
18
+ racc (1.7.1)
19
+ rainbow (3.1.1)
20
+ rake (13.0.6)
21
+ regexp_parser (2.8.1)
22
+ rexml (3.2.6)
23
+ rspec (3.12.0)
24
+ rspec-core (~> 3.12.0)
25
+ rspec-expectations (~> 3.12.0)
26
+ rspec-mocks (~> 3.12.0)
27
+ rspec-core (3.12.2)
28
+ rspec-support (~> 3.12.0)
29
+ rspec-expectations (3.12.3)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.12.0)
32
+ rspec-mocks (3.12.6)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.12.0)
35
+ rspec-support (3.12.1)
36
+ rubocop (1.56.0)
37
+ base64 (~> 0.1.1)
38
+ json (~> 2.3)
39
+ language_server-protocol (>= 3.17.0)
40
+ parallel (~> 1.10)
41
+ parser (>= 3.2.2.3)
42
+ rainbow (>= 2.2.2, < 4.0)
43
+ regexp_parser (>= 1.8, < 3.0)
44
+ rexml (>= 3.2.5, < 4.0)
45
+ rubocop-ast (>= 1.28.1, < 2.0)
46
+ ruby-progressbar (~> 1.7)
47
+ unicode-display_width (>= 2.4.0, < 3.0)
48
+ rubocop-ast (1.29.0)
49
+ parser (>= 3.2.1.0)
50
+ rubocop-capybara (2.18.0)
51
+ rubocop (~> 1.41)
52
+ rubocop-factory_bot (2.23.1)
53
+ rubocop (~> 1.33)
54
+ rubocop-performance (1.19.0)
55
+ rubocop (>= 1.7.0, < 2.0)
56
+ rubocop-ast (>= 0.4.0)
57
+ rubocop-rake (0.6.0)
58
+ rubocop (~> 1.0)
59
+ rubocop-rspec (2.23.2)
60
+ rubocop (~> 1.33)
61
+ rubocop-capybara (~> 2.17)
62
+ rubocop-factory_bot (~> 2.22)
63
+ ruby-progressbar (1.13.0)
64
+ unicode-display_width (2.4.2)
65
+
66
+ PLATFORMS
67
+ arm64-darwin-22
68
+
69
+ DEPENDENCIES
70
+ apparatus!
71
+ bundler
72
+ rake
73
+ rspec (~> 3.0)
74
+ rubocop
75
+ rubocop-performance
76
+ rubocop-rake
77
+ rubocop-rspec
78
+
79
+ BUNDLED WITH
80
+ 2.4.18
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 pirminis
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.
data/Makefile ADDED
@@ -0,0 +1,6 @@
1
+ default:
2
+ @echo "Check Makefile for tasks"
3
+
4
+ check:
5
+ bundle exec rubocop --parallel
6
+ bundle exec rake spec
data/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # What is apparatus?
2
+
3
+ Have you ever worked on code that evolves so quickly that not only it gives you anxiety, but becomes technical debt over time?
4
+
5
+ Apparatus is a very simple architectural way to solve this. It uses composition over inheritance and is a (very) simple ECS (entity-component-system) implementation.
6
+
7
+ It decouples data and logic.
8
+
9
+ ## What does it do?
10
+
11
+ It ensures your complex code parts are highly structured and easy to understand and modify:
12
+ - code is organized and tidy by default
13
+ - data is always separated from logic
14
+ - adding, removing, modifying data and logic is easy
15
+ - complete freedom how data is shaped
16
+ - very easy to to see what is being done at a glance
17
+
18
+ ## When to use it?
19
+
20
+ Only use this gem for the business logic that is very volatile:
21
+ - there are or will be a lot of changes in the business logic
22
+ - feature requests come frequently and are aplenty
23
+ - you still have no idea how to structure your complex piece of code
24
+
25
+ ## Installation
26
+
27
+ ### Using Rubygems:
28
+
29
+ ```sh
30
+ gem install apparatus
31
+ ```
32
+
33
+ ### Using Bundler:
34
+
35
+ Add the following to your Gemfile:
36
+
37
+ ```sh
38
+ gem "apparatus"
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ### Basic example
44
+
45
+ Let's build code that outputs delivery methods to the standard output. We have a imaginary cart and three shipping methods.
46
+
47
+ ```ruby
48
+ require "apparatus"
49
+
50
+ # aliases (optional)
51
+ Entity = Apparatus::Entity
52
+ System = Apparatus::System
53
+
54
+ # component classes
55
+ Type = Struct.new(:value)
56
+ Name = Struct.new(:value)
57
+ Identifier = Struct.new(:value)
58
+ Price = Struct.new(:value)
59
+ WeightInKg = Struct.new(:value)
60
+ Boolean = Struct.new(:value)
61
+
62
+ # entities
63
+ delivery = Entity.new({
64
+ type: Type.new("delivery_method"),
65
+ identifier: Identifier.new("delivery"),
66
+ name: Name.new("Delivery by courier"),
67
+ price: Price.new(3.99)
68
+ })
69
+ pickup = Entity.new({
70
+ type: Type.new("delivery_method"),
71
+ identifier: Identifier.new("pickup"),
72
+ name: Name.new("Pickup in closest store"),
73
+ price: Price.new(0.0)
74
+ })
75
+ pigeon = Entity.new({
76
+ type: Type.new("delivery_method"),
77
+ identifier: Identifier.new("pigeon"),
78
+ name: Name.new("Delivery by pigeon"),
79
+ price: Price.new(50.0)
80
+ })
81
+ cart = Entity.new({
82
+ type: Type.new("cart"),
83
+ total_price: Price.new(27.0),
84
+ total_weight: WeightInKg.new(5.0)
85
+ })
86
+
87
+ # systems
88
+ class PrintShippingMethods < System
89
+ def run
90
+ entities.each do |entity|
91
+ next if !entity.has?(:type, :price)
92
+
93
+ next if entity[:type].value.to_s != "delivery_method"
94
+
95
+ name, price, enabled = entity[:name], entity[:price], entity[:enabled]
96
+
97
+ next if enabled && !enabled.value
98
+
99
+ puts "#{name.value} (#{price.value} €)"
100
+ end
101
+ end
102
+ end
103
+
104
+ class EnableDeliveryMethods < System
105
+ def run
106
+ cart = entities.find { _1.has?(:type) && _1[:type].value.to_s == "cart" }
107
+
108
+ return if !cart
109
+
110
+ delivery_methods = entities.select do |entity|
111
+ entity.has?(:type, :identifier) &&
112
+ entity[:type].value.to_s == "delivery_method"
113
+ end
114
+
115
+ delivery_methods.each do |delivery_method|
116
+ delivery_method[:enabled] = delivery_method[:identifier].value.to_s == "pigeon" ?
117
+ Boolean.new(cart[:total_weight].value < 0.1) :
118
+ Boolean.new(true)
119
+ end
120
+ end
121
+ end
122
+
123
+ # apparatus itself
124
+ apparatus = Apparatus::Body.new
125
+
126
+ apparatus.add_entities(delivery, pickup, pigeon, cart)
127
+ apparatus.add_systems(
128
+ EnableDeliveryMethods,
129
+ PrintShippingMethods
130
+ )
131
+
132
+ # run all systems
133
+ apparatus.run
134
+ ```
135
+
136
+ If you run this code, the output is following:
137
+ ```
138
+ Delivery by courier (3.99)
139
+ Pickup in closest store (0.0)
140
+ ```
141
+
142
+ Delivery by pigeon is missing, because the cart weight is too heavy and pigeon cannot bring you (imaginary) package.
143
+
144
+ However if you comment the line `EnableDeliveryMethods,` and run the code again, the output changes to:
145
+ ```
146
+ Delivery by courier (3.99)
147
+ Pickup in closest store (0.0)
148
+ Delivery by pigeon (50.0)
149
+ ```
150
+
151
+ ## Noteworthy things about the basic example
152
+
153
+ I think you should notice some positive things about the basic example:
154
+ - Component classes are really simple classes that can be of any shape that is useful to you. I chose `Struct`, but it can be anything: custom class, String, Integer, etc.
155
+ - An entity is just a collection of components
156
+ - A system just processes entities one by one
157
+ - When apparatus is run, each system is run one by one
158
+ - Nowhere did I call a loose method on a data object: data is data and has no logic; all logic lies within systems
159
+ - I can easily disable any amount of systems if needed without breaking the apparatus
160
+ - The whole implementation is very linear and easy to read
161
+ - Code is easy to debug (just use `puts` or `byebug`)
162
+
163
+ ## What apparatus is not
164
+
165
+ This is not THE SOLUTION.
166
+
167
+ This gem is not meant to be used always, everywhere. It shines when you have suspicion that the incoming feature will be extremely volatile (business logic will change a lot or complexity will grow, there might be a lot of feature requests, etc).
168
+
169
+ ## Drawbacks in the implementation
170
+
171
+ If you check the source code of this gem, it is absurdly simple. However, with simplicity come the drawbacks:
172
+ - No magic
173
+ - No queries or cached queries
174
+ - Almost no helper methods
175
+ - If you have not encountered ECS, then apparatus might seem strange
176
+
177
+ ## Alternatives
178
+
179
+ There are alternatives that have a lot more features:
180
+ - https://github.com/guitsaru/draco
181
+ - https://github.com/ged/chione
182
+ - https://github.com/jtuttle/baku
183
+
184
+ ## Development
185
+
186
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `make check` to run the checks (rubocop + tests). You can also run `bin/console` for an interactive prompt that will allow you to experiment.
187
+
188
+ 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).
189
+
190
+ ## Contributing
191
+
192
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pirminis/apparatus.
193
+
194
+ ## License
195
+
196
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i(spec rubocop)
data/apparatus.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/apparatus/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "apparatus"
7
+ spec.version = Apparatus::VERSION
8
+ spec.authors = ["pirminis"]
9
+ spec.email = ["pirminis@gmail.com"]
10
+
11
+ spec.summary = "Take control of your business logic."
12
+ spec.description = "Systematic approach to controlling complex business logic by using entity-component-system architectural pattern."
13
+ spec.homepage = "https://github.com/pirminis/apparatus"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 3.2.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = "https://github.com/pirminis/apparatus/blob/master/CHANGELOG.md"
20
+ spec.metadata["rubygems_mfa_required"] = "true"
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (File.expand_path(f) == __FILE__) || f.start_with?(*%w(bin/ test/ spec/ features/ .git .circleci appveyor))
27
+ end
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apparatus
4
+ VERSION = "0.1.2"
5
+ end
data/lib/apparatus.rb ADDED
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "apparatus/version"
4
+
5
+ module Apparatus
6
+ class Entity
7
+ attr_accessor :components
8
+
9
+ def initialize(components = {})
10
+ @components = components
11
+ end
12
+
13
+ def [](key)
14
+ components[key]
15
+ end
16
+
17
+ def []=(key, value)
18
+ components[key] = value
19
+ end
20
+
21
+ def has?(*list)
22
+ components.keys.to_set.intersection(list).size == list.size
23
+ end
24
+ end
25
+
26
+ class System
27
+ attr_reader :apparatus, :entities
28
+
29
+ def initialize(apparatus, entities)
30
+ raise TypeError, %{Expected 'apparatus' to be Apparatus::Body, got #{apparatus.class.inspect} instead} if !apparatus.is_a?(Apparatus::Body)
31
+ raise TypeError, %{Expected 'entities' to be Array, got #{entities.class.inspect} instead} if !entities.is_a?(Array)
32
+
33
+ @apparatus = apparatus
34
+ @entities = entities
35
+ end
36
+ end
37
+
38
+ class Body
39
+ class SystemHierarchyError < StandardError; end
40
+
41
+ attr_reader :entities, :systems
42
+
43
+ def initialize
44
+ @entities = []
45
+ @systems = []
46
+ end
47
+
48
+ def add_system(system_class)
49
+ raise NoMethodError, "Expected a public instance method 'run' to be defined, but it was not" if !system_class.public_instance_methods.include?(:run)
50
+
51
+ raise SystemHierarchyError, "System is expected to extend 'Apparatus::System', but did not" if !(system_class < Apparatus::System)
52
+
53
+ systems << system_class.new(self, entities)
54
+ end
55
+
56
+ def add_systems(*list)
57
+ list.each { add_system(_1) }
58
+ end
59
+
60
+ def add_entity(entity)
61
+ entities << entity
62
+ end
63
+
64
+ def add_entities(*list)
65
+ list.each { add_entity(_1) }
66
+ end
67
+
68
+ def run
69
+ systems.each(&:run)
70
+ end
71
+ end
72
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apparatus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - pirminis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-08-20 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Systematic approach to controlling complex business logic by using entity-component-system
14
+ architectural pattern.
15
+ email:
16
+ - pirminis@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".rspec"
22
+ - ".rubocop.yml"
23
+ - ".rubocop_todo.yml"
24
+ - CHANGELOG.md
25
+ - Gemfile
26
+ - Gemfile.lock
27
+ - LICENSE.txt
28
+ - Makefile
29
+ - README.md
30
+ - Rakefile
31
+ - apparatus.gemspec
32
+ - lib/apparatus.rb
33
+ - lib/apparatus/version.rb
34
+ homepage: https://github.com/pirminis/apparatus
35
+ licenses:
36
+ - MIT
37
+ metadata:
38
+ homepage_uri: https://github.com/pirminis/apparatus
39
+ source_code_uri: https://github.com/pirminis/apparatus
40
+ changelog_uri: https://github.com/pirminis/apparatus/blob/master/CHANGELOG.md
41
+ rubygems_mfa_required: 'true'
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 3.2.0
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ requirements: []
57
+ rubygems_version: 3.4.18
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Take control of your business logic.
61
+ test_files: []