Class: Operations::Command

Inherits:
Object
  • Object
show all
Extended by:
Dry::Initializer
Defined in:
lib/operations/command.rb

Overview

should they ever happen.

Defined Under Namespace

Classes: OperationFailed

Constant Summary collapse

UNDEFINED =
Object.new.freeze
EMPTY_HASH =
{}.freeze
COMPONENTS =
%i[contract policies idempotency preconditions operation on_success on_failure].freeze
FORM_HYDRATOR =
->(_form_class, params, **_context) { params }

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(operation, policy: UNDEFINED, policies: [UNDEFINED], precondition: nil, preconditions: [], after: [], **options) ⇒ Command

Returns a new instance of Command.



196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/operations/command.rb', line 196

def initialize(
  operation, policy: UNDEFINED, policies: [UNDEFINED],
  precondition: nil, preconditions: [], after: [], **options
)
  policies_sum = Array.wrap(policy) + policies
  result_policies = policies_sum - [UNDEFINED] unless policies_sum == [UNDEFINED, UNDEFINED]
  options[:policies] = result_policies if result_policies

  preconditions.push(precondition) if precondition.present?
  super(operation, preconditions: preconditions, on_success: after, **options)
  @form_class ||= build_form_class
end

Class Method Details

.build(operation, contract = nil, **deps) ⇒ Object

A short-cut to initialize operation by convention:

Namespace::OperationName - operation Namespace::OperationName::Contract - contract Namespace::OperationName::Policies - policies Namespace::OperationName::Preconditions - preconditions

All the dependencies are passed to every component’s initializer, so they’d be better tolerant to unknown dependencies. Luckily it is easily achievable with Dry::Initializer. This plays really well with Operations::Convenience



186
187
188
189
190
191
192
193
194
# File 'lib/operations/command.rb', line 186

def self.build(operation, contract = nil, **deps)
  options = {
    contract: (contract || operation::Contract).new(**deps),
    policies: [operation::Policy.new(**deps)]
  }
  options[:preconditions] = [operation::Precondition.new(**deps)] if operation.const_defined?(:Precondition)

  new(operation.new(**deps), **options)
end

Instance Method Details

#allowed(params = EMPTY_HASH, **context) ⇒ Object

Works the same way as ‘callable` but checks only the policy.



256
257
258
# File 'lib/operations/command.rb', line 256

def allowed(params = EMPTY_HASH, **context)
  operation_result(component(:policies).call(params.to_h, context))
end

#as_jsonObject



294
295
296
297
298
299
300
# File 'lib/operations/command.rb', line 294

def as_json(*)
  {
    **main_components_as_json,
    **form_components_as_json,
    configuration: configuration.as_json
  }
end

#call(params, **context) ⇒ Object

Executes all the components in a particular order. Returns the result on any step failure. First it validates the user input with the contract then it checks the policy and preconditions and if everything passes - executes the operation routine. The whole process always happens inside of a DB transaction.



220
221
222
# File 'lib/operations/command.rb', line 220

def call(params, **context)
  operation_result(unwrap_monad(call_monad(params.to_h, context)))
end

#call!(params, **context) ⇒ Object

Works the same way as ‘call` but raises an exception on operation failure.

Raises:



225
226
227
228
229
230
# File 'lib/operations/command.rb', line 225

def call!(params, **context)
  result = call(params, **context)
  raise OperationFailed.new(result) if result.failure?

  result
end

#callable(params = EMPTY_HASH, **context) ⇒ Object

Checks if the operation is possible to call in the current context. Performs both: policy and preconditions checks.



251
252
253
# File 'lib/operations/command.rb', line 251

def callable(params = EMPTY_HASH, **context)
  operation_result(unwrap_monad(callable_monad(component(:contract).call(params.to_h, context))))
end

#merge(**changes) ⇒ Object

Instantiates a new command with the given fields updated. Useful for defining multiple commands for a single operation body.



211
212
213
# File 'lib/operations/command.rb', line 211

def merge(**changes)
  self.class.new(operation, **self.class.dry_initializer.attributes(self), **changes)
end

#possible(params = EMPTY_HASH, **context) ⇒ Object

Works the same way as ‘callable` but checks only preconditions.



261
262
263
# File 'lib/operations/command.rb', line 261

def possible(params = EMPTY_HASH, **context)
  operation_result(component(:preconditions).call(params.to_h, context))
end

#pretty_print(pp) ⇒ Object



279
280
281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/operations/command.rb', line 279

def pretty_print(pp)
  attributes = self.class.dry_initializer.attributes(self)

  pp.object_group(self) do
    pp.seplist(attributes.keys, -> { pp.text "," }) do |name|
      pp.breakable " "
      pp.group(1) do
        pp.text name.to_s
        pp.text " = "
        pp.pp send(name)
      end
    end
  end
end

#try_call!(params, **context) ⇒ Object

Calls the operation and raises an exception in case of a failure but only if preconditions and policies have passed. This means that the exception will be raised only on contract or the operation body failure.

Raises:



236
237
238
239
240
241
# File 'lib/operations/command.rb', line 236

def try_call!(params, **context)
  result = call(params, **context)
  raise OperationFailed.new(result) if result.failure? && !result.failed_precheck?

  result
end

#valid?(*args, **kwargs) ⇒ Boolean

Returns boolean result instead of Operations::Result for validate method. True on success and false on failure.

Returns:

  • (Boolean)


275
276
277
# File 'lib/operations/command.rb', line 275

def valid?(*args, **kwargs)
  validate(*args, **kwargs).success?
end

#validate(params, **context) ⇒ Object

Checks if the operation is valid to call in the current context and parameters. Performs policy preconditions and contract checks.



245
246
247
# File 'lib/operations/command.rb', line 245

def validate(params, **context)
  operation_result(unwrap_monad(validate_monad(params.to_h, context)))
end