Class: Operations::Command

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

Overview

should they ever happen.

Defined Under Namespace

Classes: OperationFailed

Constant Summary collapse

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.



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

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

  if after.present?
    ActiveSupport::Deprecation.new.warn("Operations::Command `after:` option is deprecated and will be " \
      "removed in 1.0.0. Please use `on_success:` instead")
  end

  preconditions.push(precondition) if precondition.present?
  super(operation, preconditions: preconditions, on_success: after, **options)
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



182
183
184
185
186
187
188
189
190
# File 'lib/operations/command.rb', line 182

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

#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

#form_classObject



287
288
289
# File 'lib/operations/command.rb', line 287

def form_class
  @form_class ||= build_form_class
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

#to_hashObject



279
280
281
282
283
284
285
# File 'lib/operations/command.rb', line 279

def to_hash
  {
    **main_components_to_hash,
    **form_components_to_hash,
    configuration: configuration
  }
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