atacama 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +44 -0
- data/README.md +102 -0
- data/Rakefile +14 -0
- data/atacama.gemspec +31 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/atacama/contract/context.rb +16 -0
- data/lib/atacama/contract/parameter.rb +25 -0
- data/lib/atacama/contract/validator.rb +32 -0
- data/lib/atacama/contract.rb +58 -0
- data/lib/atacama/step.rb +10 -0
- data/lib/atacama/transaction/definition.rb +22 -0
- data/lib/atacama/transaction/halt_execution.rb +12 -0
- data/lib/atacama/transaction.rb +96 -0
- data/lib/atacama/values.rb +35 -0
- data/lib/atacama/version.rb +3 -0
- data/lib/atacama.rb +10 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c084ce1e412bcdd0fd5b4496b95bf06541e5fce
|
4
|
+
data.tar.gz: 3a01181b47c0e4531492e135a69fb5279993ae4e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 93fa86a443b71fa0a428855f317cb1debb6789b18fa22776592650a54689fd124b056748d088992e9c2aa2533af7f387b049b8cdf6e32191d2d2b34d87119e0e
|
7
|
+
data.tar.gz: fb995c152f720d6737091ae89b0bce3e190e959857b02b2fde32dc4f48d14fdc6aa4dc1feeec448c265ada2824946207ed327aa57f426056155f378b50f5bb3b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
atacama (0.1.0)
|
5
|
+
dry-types
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
concurrent-ruby (1.0.5)
|
11
|
+
dry-configurable (0.7.0)
|
12
|
+
concurrent-ruby (~> 1.0)
|
13
|
+
dry-container (0.6.0)
|
14
|
+
concurrent-ruby (~> 1.0)
|
15
|
+
dry-configurable (~> 0.1, >= 0.1.3)
|
16
|
+
dry-core (0.4.7)
|
17
|
+
concurrent-ruby (~> 1.0)
|
18
|
+
dry-equalizer (0.2.1)
|
19
|
+
dry-inflector (0.1.2)
|
20
|
+
dry-logic (0.4.2)
|
21
|
+
dry-container (~> 0.2, >= 0.2.6)
|
22
|
+
dry-core (~> 0.2)
|
23
|
+
dry-equalizer (~> 0.2)
|
24
|
+
dry-types (0.13.2)
|
25
|
+
concurrent-ruby (~> 1.0)
|
26
|
+
dry-container (~> 0.3)
|
27
|
+
dry-core (~> 0.4, >= 0.4.4)
|
28
|
+
dry-equalizer (~> 0.2)
|
29
|
+
dry-inflector (~> 0.1, >= 0.1.2)
|
30
|
+
dry-logic (~> 0.4, >= 0.4.2)
|
31
|
+
minitest (5.11.3)
|
32
|
+
rake (10.5.0)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
atacama!
|
39
|
+
bundler (~> 1.16)
|
40
|
+
minitest (~> 5.0)
|
41
|
+
rake (~> 10.0)
|
42
|
+
|
43
|
+
BUNDLED WITH
|
44
|
+
1.16.2
|
data/README.md
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# Atacama
|
4
|
+
|
5
|
+
[](https://travis-ci.org/fulcrumapp/atacama)
|
6
|
+
|
7
|
+
Atacama aims to attack the issue of Service Object patterns in a way that focuses on reusing logic
|
8
|
+
and ease of testability.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'atacama'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install atacama
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
The basic object is `Contract`. It enforces type contracts by utilizing `dry-types`.
|
29
|
+
|
30
|
+
```
|
31
|
+
class UserFetcher < Atacama::Contract
|
32
|
+
option :id, Types::Strict::Number.gt(0)
|
33
|
+
|
34
|
+
def call
|
35
|
+
User.find(id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
UserFetcher.call(id: 1)
|
40
|
+
```
|
41
|
+
|
42
|
+
With the use of two classes, we can compose together multiple Contracts to yield a pipeline
|
43
|
+
of changes to execute.
|
44
|
+
|
45
|
+
```
|
46
|
+
class UserFetcher < Atacama::Step
|
47
|
+
option :id, type: Types::Strict::Number.gt(0)
|
48
|
+
|
49
|
+
def call
|
50
|
+
Option(model: User.find(id))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Duration < Atacama::Step
|
55
|
+
def call
|
56
|
+
start = Time.now
|
57
|
+
yield
|
58
|
+
$redis.avg('duration', Time.now - start)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class UpdateUser < Atacama::Transformer
|
63
|
+
option :model, type: Types::Instance(User)
|
64
|
+
option :attributes, type: Types::Strict::Hash
|
65
|
+
|
66
|
+
step :duration, with: Duration do
|
67
|
+
step :find, with: UserFetcher
|
68
|
+
step :save
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def save
|
74
|
+
context.model.update_attributes(attributes)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
UpdateUser.call(id: 1, attributes: {
|
79
|
+
email: 'hello@world.com'
|
80
|
+
})
|
81
|
+
```
|
82
|
+
|
83
|
+
Any step can be mocked out without the need for a third party library. Just pass any object that
|
84
|
+
responds to `#call` in the class initializer.
|
85
|
+
|
86
|
+
```
|
87
|
+
UpdateUser.new(steps: {
|
88
|
+
save: lambda do |**|
|
89
|
+
puts "skipping save"
|
90
|
+
end
|
91
|
+
})
|
92
|
+
```
|
93
|
+
|
94
|
+
## Development
|
95
|
+
|
96
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
97
|
+
|
98
|
+
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).
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/fulcrumapp/atacama.
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << 'test'
|
6
|
+
t.libs << 'lib'
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
task default: :test
|
11
|
+
|
12
|
+
task :bash do
|
13
|
+
exec 'docker run -v `pwd`:/app:cached -it spatialnetworks/alpine bash -l -c "gem install rerun && bundle && bash"'
|
14
|
+
end
|
data/atacama.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'atacama/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'atacama'
|
9
|
+
spec.version = Atacama::VERSION
|
10
|
+
spec.authors = ['Tyler Johnston']
|
11
|
+
spec.email = ['tyler@spatialnetworks.com']
|
12
|
+
spec.license = 'MIT'
|
13
|
+
spec.summary = 'DRY Service Objects'
|
14
|
+
spec.description = 'Service objects using composable contracts'
|
15
|
+
spec.homepage = 'https://github.com/fulcrumapp/atacama'
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = 'exe'
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ['lib']
|
25
|
+
|
26
|
+
spec.add_dependency 'dry-types', '~> 0.13.2'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
29
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'atacama'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require 'irb'
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module Atacama
|
6
|
+
# Generic object store for passing values between contracts
|
7
|
+
class Context < OpenStruct
|
8
|
+
alias key? respond_to?
|
9
|
+
|
10
|
+
def merge!(hash)
|
11
|
+
hash.each do |(key, value)|
|
12
|
+
self[key] = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Atacama
|
4
|
+
# A description of the signature of the parameter being validated
|
5
|
+
class Parameter
|
6
|
+
attr_reader :name, :type
|
7
|
+
|
8
|
+
def initialize(name:, type: nil)
|
9
|
+
@name = name
|
10
|
+
@type = type
|
11
|
+
end
|
12
|
+
|
13
|
+
# Determine the validity of a value for an optionally given type. Raises a
|
14
|
+
# type error on failure.
|
15
|
+
# @raise [Atacama::TypeError]
|
16
|
+
# @returns Boolean
|
17
|
+
def valid?(value)
|
18
|
+
return true if type.nil?
|
19
|
+
type[value]
|
20
|
+
true
|
21
|
+
rescue Dry::Types::ConstraintError => error
|
22
|
+
raise TypeError, error.message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Atacama
|
4
|
+
# Validation execution class for a given set of parameters and options.
|
5
|
+
class Validator
|
6
|
+
def self.call(**context)
|
7
|
+
new(**context).call
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param options [Hash] options schema
|
11
|
+
# @param context [Atacama::Context] keyword arguments to validate
|
12
|
+
def initialize(options:, context:)
|
13
|
+
@options = options
|
14
|
+
@context = context
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
detect_invalid_types!
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :options, :context
|
24
|
+
|
25
|
+
def detect_invalid_types!
|
26
|
+
options.each do |(key, parameter)|
|
27
|
+
raise ArgumentError, "option not found: #{key}" unless context.key?(key)
|
28
|
+
parameter.valid? context[key]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-types'
|
4
|
+
|
5
|
+
require 'atacama/contract/parameter'
|
6
|
+
require 'atacama/contract/validator'
|
7
|
+
require 'atacama/contract/context'
|
8
|
+
|
9
|
+
module Atacama
|
10
|
+
# The type namespace to interact with DRY::Types
|
11
|
+
module Types
|
12
|
+
include Dry::Types.module
|
13
|
+
Boolean = Types::True | Types::False
|
14
|
+
ContextOrHash = Strict::Hash | Instance(Context)
|
15
|
+
end
|
16
|
+
|
17
|
+
# This class enables a DSL for creating a contract for the initializer
|
18
|
+
class Contract
|
19
|
+
RESERVED_KEYS = %i[call initialize context].freeze
|
20
|
+
|
21
|
+
NameInterface = Types::Strict::Symbol.constrained(excluded_from: RESERVED_KEYS)
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def options
|
25
|
+
@options ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Define an initializer value.
|
29
|
+
# @param [Symbol] name of the argument
|
30
|
+
def option(name, **kwargs)
|
31
|
+
# Validate it's a symbol and not reserved
|
32
|
+
name = NameInterface[name]
|
33
|
+
|
34
|
+
options[name] = Parameter.new(name: name, **kwargs)
|
35
|
+
|
36
|
+
define_method(name) { @context[name] }
|
37
|
+
define_method("#{name}?") { !!@context[name] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def call(context = {})
|
41
|
+
new(context: context).call
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :context
|
46
|
+
|
47
|
+
def initialize(context: {}, **)
|
48
|
+
# Validate the type
|
49
|
+
Types::ContextOrHash[context]
|
50
|
+
@context = context.is_a?(Context) ? context : Context.new(context)
|
51
|
+
Validator.call(options: self.class.options, context: @context)
|
52
|
+
end
|
53
|
+
|
54
|
+
def call
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/atacama/step.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Atacama
|
4
|
+
# Struct object holding the step definition
|
5
|
+
class Definition < Contract
|
6
|
+
option :name, type: Types::Strict::Symbol
|
7
|
+
option :with, type: Types::Any.optional
|
8
|
+
option :yielding, type: Types::Any.optional
|
9
|
+
|
10
|
+
def yielding?
|
11
|
+
!!yielding
|
12
|
+
end
|
13
|
+
|
14
|
+
def proc_invocation?
|
15
|
+
with.is_a? Proc
|
16
|
+
end
|
17
|
+
|
18
|
+
def method_invocation?
|
19
|
+
with.nil?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'atacama/contract'
|
4
|
+
require 'atacama/transaction/halt_execution'
|
5
|
+
require 'atacama/transaction/definition'
|
6
|
+
|
7
|
+
module Atacama
|
8
|
+
class Transaction < Contract
|
9
|
+
include Values::Methods
|
10
|
+
|
11
|
+
class Result < Contract
|
12
|
+
option :value, type: Types::Any
|
13
|
+
option :transaction, type: Types.Instance(Context)
|
14
|
+
end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# @returns [Array<Atacama::Transaction::Definition>]
|
18
|
+
def steps
|
19
|
+
@steps ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
# Add a step to the processing queue.
|
23
|
+
# @param name [Symbol] a unique name for a step
|
24
|
+
def step(name, **kwargs, &block)
|
25
|
+
kwargs[:yielding] = block_given? ? Class.new(self, &block) : nil
|
26
|
+
kwargs[:with] ||= nil
|
27
|
+
steps.push Definition.call(name: name, **kwargs)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(context: {}, steps: {})
|
32
|
+
super(context: context)
|
33
|
+
@overrides = steps
|
34
|
+
end
|
35
|
+
|
36
|
+
def call
|
37
|
+
value = begin
|
38
|
+
execute(self.class.steps)
|
39
|
+
rescue HaltExecution => exception
|
40
|
+
exception.value
|
41
|
+
end
|
42
|
+
|
43
|
+
raise MissingReturn, "Return value from #{self.class} missing, received: #{value.inspect}" unless value.is_a? Values::Return
|
44
|
+
|
45
|
+
Result.call(value: value.value, transaction: context)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def execute(steps)
|
51
|
+
steps.each do |step|
|
52
|
+
evaluate(step).tap do |result|
|
53
|
+
context.merge!(result.value) if result.is_a? Values::Option
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def evaluate(step)
|
59
|
+
if overridden?(step)
|
60
|
+
evaluate_override(step)
|
61
|
+
elsif step.method_invocation?
|
62
|
+
evaluate_method(step)
|
63
|
+
elsif step.proc_invocation?
|
64
|
+
evaluate_proc(step)
|
65
|
+
else
|
66
|
+
evaluate_instance(step)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def overridden?(step)
|
71
|
+
@overrides.key?(step.name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def evaluate_override(step)
|
75
|
+
callable = @overrides[step.name]
|
76
|
+
instance_exec(&callable)
|
77
|
+
end
|
78
|
+
|
79
|
+
def evaluate_method(step)
|
80
|
+
send(step.name) do
|
81
|
+
execute(step.yielding.steps)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def evaluate_proc(step)
|
86
|
+
callable = step.with
|
87
|
+
instance_exec(&callable)
|
88
|
+
end
|
89
|
+
|
90
|
+
def evaluate_instance(step)
|
91
|
+
step.with.new(context: context).call do
|
92
|
+
execute(step.yielding.steps)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'atacama/contract'
|
4
|
+
|
5
|
+
module Atacama
|
6
|
+
module Values
|
7
|
+
# Helper methods for emitting value objects inside of a Contract.
|
8
|
+
module Methods
|
9
|
+
# rubocop:disable Naming/MethodName
|
10
|
+
|
11
|
+
# This value tells the orchestrator to merge in these parameters on the
|
12
|
+
# next call.
|
13
|
+
# @param [Hash] the hash object to merge
|
14
|
+
def Option(value)
|
15
|
+
Values::Option.call(value: value)
|
16
|
+
end
|
17
|
+
|
18
|
+
def Return(value)
|
19
|
+
raise HaltExecution.new(Values::Return.call(value: value))
|
20
|
+
end
|
21
|
+
# rubocop:enable Naming/MethodName
|
22
|
+
end
|
23
|
+
|
24
|
+
# This object notifies the Transaction that a new variable needs to be
|
25
|
+
# merged in to the current context.
|
26
|
+
class Option < Contract
|
27
|
+
option :value, type: Types::Strict::Hash
|
28
|
+
end
|
29
|
+
|
30
|
+
# This object notifies the Transaction that a new
|
31
|
+
class Return < Contract
|
32
|
+
option :value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/atacama.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'atacama/version'
|
2
|
+
require 'atacama/contract'
|
3
|
+
require 'atacama/transaction'
|
4
|
+
require 'atacama/step'
|
5
|
+
|
6
|
+
module Atacama
|
7
|
+
ArgumentError = Class.new(StandardError)
|
8
|
+
TypeError = Class.new(StandardError)
|
9
|
+
MissingReturn = Class.new(StandardError)
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: atacama
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tyler Johnston
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-types
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.13.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.13.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.16'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.16'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
description: Service objects using composable contracts
|
70
|
+
email:
|
71
|
+
- tyler@spatialnetworks.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".gitignore"
|
77
|
+
- ".travis.yml"
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- atacama.gemspec
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- lib/atacama.rb
|
86
|
+
- lib/atacama/contract.rb
|
87
|
+
- lib/atacama/contract/context.rb
|
88
|
+
- lib/atacama/contract/parameter.rb
|
89
|
+
- lib/atacama/contract/validator.rb
|
90
|
+
- lib/atacama/step.rb
|
91
|
+
- lib/atacama/transaction.rb
|
92
|
+
- lib/atacama/transaction/definition.rb
|
93
|
+
- lib/atacama/transaction/halt_execution.rb
|
94
|
+
- lib/atacama/values.rb
|
95
|
+
- lib/atacama/version.rb
|
96
|
+
homepage: https://github.com/fulcrumapp/atacama
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata: {}
|
100
|
+
post_install_message:
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 2.6.11
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: DRY Service Objects
|
120
|
+
test_files: []
|