Class: Atacama::Contract
- Inherits:
-
Object
- Object
- Atacama::Contract
- Defined in:
- lib/atacama/contract.rb
Overview
This class enables a DSL for creating a contract for the initializer
Direct Known Subclasses
Definition, Step, Transaction, Transaction::Result, Values::Option, Values::Return
Constant Summary collapse
- RESERVED_KEYS =
%i[call initialize context].freeze
- Types =
Namespace alias for easier reading when defining types.
Atacama::Types
- NameInterface =
Types::Strict::Symbol.constrained(excluded_from: RESERVED_KEYS)
- ContextInterface =
Types::Strict::Hash | Types.Instance(Context)
Instance Attribute Summary collapse
-
#context ⇒ Object
readonly
Returns the value of attribute context.
Class Method Summary collapse
-
.call(context = {}) { ... } ⇒ Object
The main interface to executing contracts.
-
.inherited(subclass) ⇒ Object
Executed by the Ruby VM at subclass time.
-
.inject(injected) ⇒ Class
Inject dependencies statically in to the Contract object.
- .injected ⇒ Object
- .injected=(hash) ⇒ Object
-
.option(name, type: nil) ⇒ Object
Define an initializer value.
-
.options ⇒ Hash<String, Atacama::Parameter>
The defined options on the contract.
-
.return_type ⇒ Dry::Type?
The defined return type on the Contract.
-
.returns(type) ⇒ Object
Set the return type for the contract.
-
.validate_return(value) ⇒ Object
Execute type checking on a value for the defined return value.
Instance Method Summary collapse
-
#call ⇒ Object
abstract
The default entrypoint for all Contracts.
-
#initialize(context: {}) ⇒ Contract
constructor
A new instance of Contract.
- #inspect ⇒ Object
Constructor Details
#initialize(context: {}) ⇒ Contract
Returns a new instance of Contract.
134 135 136 137 138 139 140 141 142 |
# File 'lib/atacama/contract.rb', line 134 def initialize(context: {}, **) ContextInterface[context] # Validate the type @context = Context.new(self.class.injected).tap do |ctx| ctx.merge!(context.is_a?(Context) ? context.to_h : context) end Validator.call(options: self.class., context: @context, klass: self.class) end |
Instance Attribute Details
#context ⇒ Object (readonly)
Returns the value of attribute context.
129 130 131 |
# File 'lib/atacama/contract.rb', line 129 def context @context end |
Class Method Details
.call(context = {}) { ... } ⇒ Object
The main interface to executing contracts. Given a set of options it will check the parameter types as well as return types, if defined.
54 55 56 |
# File 'lib/atacama/contract.rb', line 54 def call(context = {}, &block) new(context: context).call(&block).tap { |result| validate_return(result) } end |
.inherited(subclass) ⇒ Object
Executed by the Ruby VM at subclass time. Ensure that all internal state is copied to the new subclass.
109 110 111 112 113 114 115 |
# File 'lib/atacama/contract.rb', line 109 def inherited(subclass) subclass.returns(return_type) .each do |name, parameter| subclass.option(name, type: parameter.type) end end |
.inject(injected) ⇒ Class
Inject dependencies statically in to the Contract object. Allows for easier composition of contracts when used in a Transaction.
67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/atacama/contract.rb', line 67 def inject(injected) Validator.call({ options: Hash[injected.keys.zip(.values_at(*injected.keys))], context: Context.new(injected), klass: self }) Class.new(self) do self.injected = injected end end |
.injected ⇒ Object
123 124 125 126 |
# File 'lib/atacama/contract.rb', line 123 def injected # Silences the VM warning about accessing uninitalized ivar defined?(@injected) ? @injected : {} end |
.injected=(hash) ⇒ Object
118 119 120 |
# File 'lib/atacama/contract.rb', line 118 def injected=(hash) @injected = Types::Strict::Hash[hash] end |
.option(name, type: nil) ⇒ Object
Define an initializer value.
31 32 33 34 35 36 |
# File 'lib/atacama/contract.rb', line 31 def option(name, type: nil) [NameInterface[name]] = Parameter.new(name: name, type: type) define_method(name) { @context[name] } define_method("#{name}?") { !!@context[name] } end |
.options ⇒ Hash<String, Atacama::Parameter>
The defined options on the contract.
103 104 105 |
# File 'lib/atacama/contract.rb', line 103 def @options ||= {} end |
.return_type ⇒ Dry::Type?
The defined return type on the Contract.
82 83 84 |
# File 'lib/atacama/contract.rb', line 82 def return_type defined?(@returns) && @returns end |
.returns(type) ⇒ Object
Set the return type for the contract. This is only validated automatically through the #call class method.
42 43 44 |
# File 'lib/atacama/contract.rb', line 42 def returns(type) # rubocop:disable Style/TrivialAccessors @returns = type end |
.validate_return(value) ⇒ Object
Execute type checking on a value for the defined return value. Useful when executing ‘new` on these objects.
92 93 94 95 96 97 98 |
# File 'lib/atacama/contract.rb', line 92 def validate_return(value) Atacama.check(return_type, value) do |e| raise ReturnTypeMismatchError, Atacama.format_exception(self, e, 'The return value was an incorrect type.', ) end end |
Instance Method Details
#call ⇒ Object
The default entrypoint for all Contracts. This is executed and type checked when called from the Class#call.
157 158 159 |
# File 'lib/atacama/contract.rb', line 157 def call self end |
#inspect ⇒ Object
145 146 147 148 149 150 151 152 |
# File 'lib/atacama/contract.rb', line 145 def inspect "#<#{self.class}:0x%x %s>" % [ object_id, self.class..keys.map do |option| "#{option}: #{context.send(option).inspect[0..20]}" end.join(' ') ] end |