substance 0.26.0.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: db4e95d7f330b7ac29bdd857017b427defc87f0ad9796fec42bc63efa3cdb485
4
+ data.tar.gz: 14b97d6b1687f5620836a7937af9046af9969149bfffc7741fb4c944e411d348
5
+ SHA512:
6
+ metadata.gz: 597f3c854d96eba40a7edea903137bf395b95c1dd1e76e58965251c1e0ca58e9e86da8e2ef0885ac4b3d7f782a7c78dd1a1cb6e18e2f1900d0dfbc51d6879322
7
+ data.tar.gz: f206aa732c3e7d3f10b5bfd0b726be2f812a8c82a2c1294dd318fc27311fd097dea82a021c10446656a5fe9c8b03ef98c9600d010a5f075719a75c258cce68a3
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Allen Rettberg
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/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Substance
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/substance`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'substance'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install substance
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ 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.
30
+
31
+ 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/substance.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/lib/substance.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "substance/version"
4
+
5
+ require_relative "substance/root_object"
6
+ require_relative "substance/attribute_object"
7
+ require_relative "substance/input_object"
8
+ require_relative "substance/input_model"
9
+ require_relative "substance/output_object"
10
+
11
+ module Substance
12
+ class Error < StandardError; end
13
+ class NotValidatedError < Error; end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "root_object"
4
+ require_relative "objects/defaults"
5
+ require_relative "objects/attributes"
6
+
7
+ module Substance
8
+ class AttributeObject < Substance::RootObject
9
+ include Substance::Objects::Defaults
10
+ include Substance::Objects::Attributes
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "input_object"
4
+
5
+ module Substance
6
+ class InputModel < InputObject
7
+ extend ActiveModel::Naming
8
+ extend ActiveModel::Translation
9
+
10
+ include ActiveModel::Conversion
11
+ include ActiveModel::Validations
12
+ include ActiveModel::Validations::Callbacks
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "attribute_object"
4
+ require_relative "objects/arguments"
5
+ require_relative "objects/options"
6
+
7
+ module Substance
8
+ class InputObject < Substance::AttributeObject
9
+ define_callbacks :initialize
10
+
11
+ include Substance::Objects::Arguments
12
+ include Substance::Objects::Options
13
+
14
+ def initialize(**input)
15
+ @input = input
16
+ run_callbacks(:initialize) do
17
+ input.each { |key, value| __send__("#{key}=".to_sym, value) }
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :input
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Arguments describe input that is required (which can be nil, unless otherwise specified).
4
+ module Substance
5
+ module Objects
6
+ module Arguments
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_arguments, instance_writer: false, default: {}
11
+ set_callback :initialize, :after do
12
+ missing_arguments = _arguments.reject do |argument, options|
13
+ options[:allow_nil] ? input.key?(argument) : !input[argument].nil?
14
+ end
15
+
16
+ missing = missing_arguments.keys
17
+
18
+ raise ArgumentError, "Missing #{"argument".pluralize(missing.length)}: #{missing.join(", ")}" if missing.any?
19
+ end
20
+ end
21
+
22
+ class_methods do
23
+ def inherited(base)
24
+ dup = _arguments.dup
25
+ base._arguments = dup.each { |k, v| dup[k] = v.dup }
26
+ super
27
+ end
28
+
29
+ private
30
+
31
+ def argument(argument, allow_nil: true)
32
+ _arguments[argument] = { allow_nil: allow_nil }
33
+ define_attribute argument
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Attributes are structured data within an object.
4
+ module Substance
5
+ module Objects
6
+ module Attributes
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_attributes, instance_writer: false, default: []
11
+ end
12
+
13
+ class_methods do
14
+ def inherited(base)
15
+ base._attributes = _attributes.dup
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def define_attribute(attribute)
22
+ _attributes << attribute
23
+ attr_accessor attribute
24
+ end
25
+ alias_method :attribute, :define_attribute
26
+ end
27
+
28
+ def to_h
29
+ _attributes.each_with_object({}) { |attr, hash| hash[attr] = public_send(attr) }
30
+ end
31
+
32
+ private
33
+
34
+ def stringable_attributes
35
+ self.class._attributes
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tablesalt/isolation"
4
+
5
+ # Defaults are used as the value when then attribute is unspecified.
6
+ module Substance
7
+ module Objects
8
+ module Defaults
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ class_attribute :_defaults, instance_writer: false, default: {}
13
+ end
14
+
15
+ class_methods do
16
+ def inherited(base)
17
+ dup = _defaults.dup
18
+ base._defaults = dup.each { |k, v| dup[k] = v.dup }
19
+ super
20
+ end
21
+
22
+ private
23
+
24
+ def define_default(attribute, static: nil, &block)
25
+ _defaults[attribute] = Value.new(static: static, &block)
26
+ end
27
+ end
28
+
29
+ class Value
30
+ include Tablesalt::Isolation
31
+
32
+ def initialize(static: nil, &block)
33
+ @value = (static.nil? && block_given?) ? block : static
34
+ end
35
+
36
+ def value
37
+ isolate(@value.respond_to?(:call) ? instance_eval(&@value) : @value)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Options describe input which may be provided to define or override default values.
4
+ module Substance
5
+ module Objects
6
+ module Options
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :_options, instance_writer: false, default: []
11
+
12
+ set_callback :initialize, :after do
13
+ _options.each do |option|
14
+ next unless _defaults.key?(option)
15
+
16
+ public_send("#{option}=".to_sym, _defaults[option].value) if public_send(option).nil?
17
+ end
18
+ end
19
+ end
20
+
21
+ class_methods do
22
+ def inherited(base)
23
+ base._options = _options.dup
24
+ super
25
+ end
26
+
27
+ private
28
+
29
+ def option(option, default: nil, &block)
30
+ _options << option
31
+ define_attribute option
32
+ define_default option, static: default, &block
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Substance
4
+ module Objects
5
+ module Output
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include AroundTheWorld
10
+
11
+ class_attribute :_outputs, instance_writer: false, default: []
12
+
13
+ delegate :_outputs, to: :class
14
+
15
+ after_validation do
16
+ next unless validated?
17
+
18
+ _outputs.each do |key|
19
+ public_send("#{key}=".to_sym, _defaults[key].value) if _defaults.key?(key) && public_send(key).nil?
20
+ end
21
+ end
22
+ end
23
+
24
+ class_methods do
25
+ def inherited(base)
26
+ base._outputs = _outputs.dup
27
+ super
28
+ end
29
+
30
+ private
31
+
32
+ def output(output, default: nil, &block)
33
+ _outputs << output
34
+ define_attribute output
35
+ define_default output, static: default, &block
36
+ ensure_validation_before output
37
+ ensure_validation_before "#{output}=".to_sym
38
+ end
39
+
40
+ def ensure_validation_before(method)
41
+ around_method method do |*args, **opts|
42
+ raise NotValidatedError unless validated?
43
+
44
+ # TODO: replace with `super(...)` when <= 2.6 support is dropped
45
+ if RUBY_VERSION < "2.7" && opts.blank?
46
+ super(*args)
47
+ else
48
+ super(*args, **opts)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def outputs
55
+ return {} if _outputs.empty?
56
+
57
+ output_struct.new(*_outputs.map(&method(:public_send)))
58
+ end
59
+
60
+ def output_struct
61
+ Struct.new(*_outputs)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Substance
4
+ module Objects
5
+ module Status
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ after_validation { self.was_validated = errors.empty? }
10
+
11
+ private
12
+
13
+ attr_accessor :was_validated
14
+ end
15
+
16
+ def validated?
17
+ was_validated.present?
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "input_model"
4
+ require_relative "objects/status"
5
+ require_relative "objects/output"
6
+
7
+ module Substance
8
+ class OutputObject < InputModel
9
+ include Objects::Status
10
+ include Objects::Output
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/callbacks"
4
+ require "short_circu_it"
5
+ require "technologic"
6
+ require "tablesalt/stringable_object"
7
+
8
+ module Substance
9
+ class RootObject
10
+ include ActiveSupport::Callbacks
11
+ include ShortCircuIt
12
+ include Technologic
13
+ include Tablesalt::StringableObject
14
+
15
+ class << self
16
+ private
17
+
18
+ def define_callbacks_with_handler(*events, handler: :on, filter: :after)
19
+ define_callbacks(*events)
20
+
21
+ events.each do |event|
22
+ define_singleton_method("#{handler}_#{event}".to_sym) do |*filters, &block|
23
+ set_callback(event, filter, *filters, &block)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rspec/custom_matchers"
4
+ require_relative "rspec/shared_examples"
5
+ require_relative "rspec/shoulda_matcher_helper"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "custom_matchers/define_argument"
4
+ require_relative "custom_matchers/define_attribute"
5
+ require_relative "custom_matchers/define_option"
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher that tests usage of `.argument`
4
+ #
5
+ # class Example < Substance::InputObject
6
+ # argument :foo
7
+ # argument :bar, allow_nil: false
8
+ # end
9
+ #
10
+ # RSpec.describe Example, type: :input_object do
11
+ # subject { described_class.new(**input) }
12
+ #
13
+ # let(:input) { {} }
14
+ #
15
+ # it { is_expected.to define_argument :foo }
16
+ # it { is_expected.to define_argument :bar, allow_nil: false }
17
+ # end
18
+
19
+ RSpec::Matchers.define :define_argument do |argument, allow_nil: true|
20
+ match { |instance| expect(instance._arguments[argument]).to eq(allow_nil: allow_nil) }
21
+ description { "define argument #{argument}" }
22
+ failure_message do
23
+ "expected #{described_class} to define argument #{argument} #{prohibit_nil_description unless allow_nil}"
24
+ end
25
+
26
+ def prohibit_nil_description
27
+ "and prohibit a nil value"
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher that tests usage of `.attribute`
4
+ #
5
+ # class Example < Substance::InputObject
6
+ # attribute :foo
7
+ # end
8
+ #
9
+ # RSpec.describe Example, type: :input_object do
10
+ # subject { described_class.new(**input) }
11
+ #
12
+ # let(:input) { {} }
13
+ #
14
+ # it { is_expected.to define_attribute :foo }
15
+ # end
16
+
17
+ RSpec::Matchers.define :define_attribute do |attribute|
18
+ match { |instance| expect(instance._attributes).to include attribute }
19
+ description { "define attribute #{attribute}" }
20
+ failure_message { "expected #{described_class} to defines attribute #{attribute}" }
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher that tests usage of `.option`
4
+ #
5
+ # class Example < Substance::InputObject
6
+ # option :foo
7
+ # option :bar, default: :baz
8
+ # option(:gaz) { :haz }
9
+ # end
10
+ #
11
+ # RSpec.describe Example, type: :input_object do
12
+ # subject { described_class.new(**input) }
13
+ #
14
+ # let(:input) { {} }
15
+ #
16
+ # it { is_expected.to define_option :foo }
17
+ # it { is_expected.to define_option :bar, default: :baz }
18
+ # it { is_expected.to define_option :gaz, default: :haz }
19
+ # end
20
+
21
+ RSpec::Matchers.define :define_option do |option, default: nil|
22
+ match do |instance|
23
+ expect(instance._defaults[option]&.value).to eq default
24
+ expect(instance._options).to include option
25
+ end
26
+ description { "define option #{option}" }
27
+ failure_message { "expected #{described_class} to define option #{option} #{for_default(default)}" }
28
+
29
+ def for_default(default)
30
+ return "without a default value" if default.nil?
31
+
32
+ "with default value #{default}"
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "shared_examples/an_input_object_with_a_class_collection_attribute"
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples_for "an input object with a class collection attribute" do |method, collection|
4
+ subject(:define) { example_input_object_class.__send__(method, value) }
5
+
6
+ let(:value) { Faker::Lorem.word.to_sym }
7
+
8
+ before do
9
+ allow(example_input_object_class).to receive(:define_default).and_call_original
10
+ allow(example_input_object_class).to receive(:define_attribute).and_call_original
11
+ end
12
+
13
+ describe "defines value" do
14
+ let(:default) { Faker::Lorem.word }
15
+
16
+ shared_examples_for "an value is defined" do
17
+ it "adds to _values" do
18
+ expect { define }.to change { example_input_object_class.public_send(collection) }.from([]).to([ value ])
19
+ end
20
+ end
21
+
22
+ context "when no block is given" do
23
+ subject(:define) { example_input_object_class.__send__(method, value, default: default) }
24
+
25
+ it_behaves_like "an value is defined"
26
+
27
+ it "defines an static default" do
28
+ define
29
+ expect(example_input_object_class).to have_received(:define_default).with(value, static: default)
30
+ end
31
+ end
32
+
33
+ context "when a block is given" do
34
+ subject(:define) { example_input_object_class.__send__(method, value, default: default, &block) }
35
+
36
+ let(:block) do
37
+ ->(_) { :block }
38
+ end
39
+
40
+ shared_examples_for "values are handed off to define_default" do
41
+ it "calls define_default" do
42
+ define
43
+ expect(example_input_object_class).to have_received(:define_default).with(value, static: default, &block)
44
+ end
45
+ end
46
+
47
+ context "with a static default" do
48
+ it_behaves_like "values are handed off to define_default"
49
+ end
50
+
51
+ context "without a static default" do
52
+ let(:default) { nil }
53
+
54
+ it_behaves_like "values are handed off to define_default"
55
+ end
56
+ end
57
+ end
58
+
59
+ it "defines an attribute" do
60
+ define
61
+ expect(example_input_object_class).to have_received(:define_attribute).with(value)
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Shoulda::Matchers::ActiveModel)
4
+ RSpec.configure do |config|
5
+ config.include(Shoulda::Matchers::ActiveModel, type: :input_object)
6
+ config.include(Shoulda::Matchers::ActiveModel, type: :input_model)
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Substance
4
+ # This constant is managed by spicerack
5
+ VERSION = "0.26.0.2"
6
+ end
metadata ADDED
@@ -0,0 +1,229 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: substance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.26.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Freshly Engineering
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: short_circu_it
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.26.0.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.26.0.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: tablesalt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 0.26.0.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 0.26.0.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: technologic
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.26.0.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 0.26.0.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: faker
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '1.8'
76
+ - - "<"
77
+ - !ruby/object:Gem::Version
78
+ version: '2.0'
79
+ type: :development
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '1.8'
86
+ - - "<"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: bundler
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 2.0.1
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: 2.0.1
117
+ - !ruby/object:Gem::Dependency
118
+ name: pry
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.10.0
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.10.0
131
+ - !ruby/object:Gem::Dependency
132
+ name: pry-nav
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 0.2.4
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: 0.2.4
145
+ - !ruby/object:Gem::Dependency
146
+ name: rake
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 12.3.3
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 12.3.3
159
+ - !ruby/object:Gem::Dependency
160
+ name: shoulda-matchers
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - '='
164
+ - !ruby/object:Gem::Version
165
+ version: 4.0.1
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '='
171
+ - !ruby/object:Gem::Version
172
+ version: 4.0.1
173
+ description: Base classes for building powerful service objects
174
+ email:
175
+ - eng@freshly.com
176
+ executables: []
177
+ extensions: []
178
+ extra_rdoc_files: []
179
+ files:
180
+ - LICENSE.txt
181
+ - README.md
182
+ - lib/substance.rb
183
+ - lib/substance/attribute_object.rb
184
+ - lib/substance/input_model.rb
185
+ - lib/substance/input_object.rb
186
+ - lib/substance/objects/arguments.rb
187
+ - lib/substance/objects/attributes.rb
188
+ - lib/substance/objects/defaults.rb
189
+ - lib/substance/objects/options.rb
190
+ - lib/substance/objects/output.rb
191
+ - lib/substance/objects/status.rb
192
+ - lib/substance/output_object.rb
193
+ - lib/substance/root_object.rb
194
+ - lib/substance/rspec.rb
195
+ - lib/substance/rspec/custom_matchers.rb
196
+ - lib/substance/rspec/custom_matchers/define_argument.rb
197
+ - lib/substance/rspec/custom_matchers/define_attribute.rb
198
+ - lib/substance/rspec/custom_matchers/define_option.rb
199
+ - lib/substance/rspec/shared_examples.rb
200
+ - lib/substance/rspec/shared_examples/an_input_object_with_a_class_collection_attribute.rb
201
+ - lib/substance/rspec/shoulda_matcher_helper.rb
202
+ - lib/substance/version.rb
203
+ homepage: https://github.com/Freshly/spicerack/blob/main/substance
204
+ licenses:
205
+ - MIT
206
+ metadata:
207
+ homepage_uri: https://github.com/Freshly/spicerack/blob/main/substance
208
+ source_code_uri: https://github.com/Freshly/spicerack/blob/main/substance
209
+ changelog_uri: https://github.com/Freshly/spicerack/blob/main/substance/CHANGELOG.md
210
+ post_install_message:
211
+ rdoc_options: []
212
+ require_paths:
213
+ - lib
214
+ required_ruby_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: 2.5.0
219
+ required_rubygems_version: !ruby/object:Gem::Requirement
220
+ requirements:
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: '0'
224
+ requirements: []
225
+ rubygems_version: 3.1.6
226
+ signing_key:
227
+ specification_version: 4
228
+ summary: Base classes for building powerful service objects
229
+ test_files: []