lite-command 2.1.3 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_model" unless defined?(ActiveModel)
4
+
3
5
  module Lite
4
6
  module Command
5
7
  class Base
@@ -7,22 +9,23 @@ module Lite
7
9
  def self.inherited(base)
8
10
  super
9
11
 
10
- base.include Lite::Command::Internals::Context
11
- base.include Lite::Command::Internals::Call
12
- base.include Lite::Command::Internals::Execute
13
- base.include Lite::Command::Internals::Fault
14
- base.include Lite::Command::Internals::Result
15
-
16
- base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
17
- # eg: Users::ResetPassword::Fault < Lite::Command::Fault
18
- #{base}::Fault = Class.new(Lite::Command::Fault)
19
-
20
- # eg: Users::ResetPassword::Noop < Users::ResetPassword::Fault
21
- #{base}::Noop = Class.new(#{base}::Fault)
22
- #{base}::Invalid = Class.new(#{base}::Fault)
23
- #{base}::Failure = Class.new(#{base}::Fault)
24
- #{base}::Error = Class.new(#{base}::Fault)
25
- RUBY
12
+ base.include ActiveModel::Validations
13
+
14
+ base.include Lite::Command::Internals::Attributes
15
+ base.include Lite::Command::Internals::Calls
16
+ base.include Lite::Command::Internals::Executions
17
+ base.include Lite::Command::Internals::Faults
18
+ base.include Lite::Command::Internals::Results
19
+
20
+ if Lite::Command.configuration.raise_dynamic_faults # rubocop:disable Style/GuardClause
21
+ base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
22
+ #{base}::Fault = Class.new(Lite::Command::Fault)
23
+ #{base}::Noop = Class.new(#{base}::Fault)
24
+ #{base}::Invalid = Class.new(#{base}::Fault)
25
+ #{base}::Failure = Class.new(#{base}::Fault)
26
+ #{base}::Error = Class.new(#{base}::Fault)
27
+ RUBY
28
+ end
26
29
  end
27
30
 
28
31
  attr_reader :context
@@ -30,7 +33,7 @@ module Lite
30
33
 
31
34
  def initialize(context = {})
32
35
  @context = Lite::Command::Context.build(context)
33
- Utils.hook(self, :on_pending)
36
+ Utils.try(self, :on_pending)
34
37
  end
35
38
 
36
39
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Command
5
+
6
+ class Configuration
7
+
8
+ attr_accessor :raise_dynamic_faults
9
+
10
+ def initialize
11
+ @raise_dynamic_faults = false
12
+ end
13
+
14
+ end
15
+
16
+ class << self
17
+
18
+ attr_writer :configuration
19
+
20
+ def configuration
21
+ @configuration ||= Configuration.new
22
+ end
23
+
24
+ def configure
25
+ yield(configuration)
26
+ end
27
+
28
+ def reset_configuration!
29
+ @configuration = Configuration.new
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -5,7 +5,7 @@ module Lite
5
5
 
6
6
  class Fault < StandardError
7
7
 
8
- attr_reader :caused_by, :thrown_by, :reason, :metadata
8
+ attr_reader :reason, :metadata, :caused_by, :thrown_by
9
9
 
10
10
  def initialize(**params)
11
11
  @reason = params.fetch(:reason)
@@ -16,16 +16,16 @@ module Lite
16
16
  super(reason)
17
17
  end
18
18
 
19
- def self.build(type, command, thrown_exception, dynamic: false)
20
- klass = dynamic ? command.class : Lite::Command
19
+ def self.build(type, catcher, thrower, dynamic: false)
20
+ klass = dynamic ? catcher.class : Lite::Command
21
21
  fault = klass.const_get(type.to_s)
22
22
  fault = fault.new(
23
- reason: command.reason,
24
- metadata: command.metadata,
25
- caused_by: command.caused_by || command,
26
- thrown_by: command
23
+ reason: catcher.reason,
24
+ metadata: catcher.metadata,
25
+ caused_by: catcher.caused_by,
26
+ thrown_by: catcher.thrown_by
27
27
  )
28
- fault.set_backtrace(thrown_exception.backtrace) if thrown_exception.respond_to?(:backtrace)
28
+ fault.set_backtrace(thrower.backtrace) if thrower.respond_to?(:backtrace)
29
29
  fault
30
30
  end
31
31
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lite
4
+ module Command
5
+ module Internals
6
+ module Attributes
7
+
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ def required(*attributes, from: :context, **options)
15
+ delegate(*attributes, from:)
16
+
17
+ validates_each(*attributes, **options) do |command, method_name, _attr_value|
18
+ next unless Utils.evaluate(command, options)
19
+ next if command.errors.added?(from, :undefined) || command.errors.added?(method_name, :required)
20
+
21
+ if !command.respond_to?(from, true)
22
+ command.errors.add(from, :undefined, message: "is an undefined argument")
23
+ elsif !command.send(from).respond_to?(method_name, true)
24
+ command.errors.add(method_name, :required, message: "is a required argument")
25
+ end
26
+ end
27
+ end
28
+
29
+ def optional(*attributes, from: :context, **_options)
30
+ delegate(*attributes, from:)
31
+ end
32
+
33
+ private
34
+
35
+ def delegate(*attributes, from: :context)
36
+ attributes.each do |method_name|
37
+ define_method(method_name) do
38
+ return unless respond_to?(from)
39
+
40
+ Utils.try(send(from), method_name)
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ def read_attribute_for_validation(method_name)
48
+ Utils.try(self, method_name)
49
+ rescue NameError
50
+ # Do nothing, fallback to :undefined error
51
+ end
52
+
53
+ private
54
+
55
+ def validate_context_attributes
56
+ return if errors.empty?
57
+
58
+ invalid!(errors.full_messages.join(". "), errors.messages)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+ end
@@ -13,25 +13,25 @@ module Lite
13
13
  FAULTS = (STATUSES - [SUCCESS]).freeze
14
14
 
15
15
  module Internals
16
- module Call
16
+ module Calls
17
17
 
18
18
  def self.included(base)
19
19
  base.extend ClassMethods
20
- base.class_eval do
21
- attr_reader :reason, :metadata
22
- end
20
+ base.class_eval { attr_reader :reason, :metadata }
23
21
  end
24
22
 
25
23
  module ClassMethods
26
24
 
27
25
  def call(context = {})
28
26
  instance = send(:new, context)
27
+ instance.validate
29
28
  instance.send(:execute)
30
29
  instance
31
30
  end
32
31
 
33
32
  def call!(context = {})
34
33
  instance = send(:new, context)
34
+ instance.validate
35
35
  instance.send(:execute!)
36
36
  instance
37
37
  end
@@ -58,6 +58,10 @@ module Lite
58
58
  !success? && reason?(reason)
59
59
  end
60
60
 
61
+ def bad?(reason = nil)
62
+ ![SUCCESS, NOOP].include?(status) && reason?(reason)
63
+ end
64
+
61
65
  FAULTS.each do |f|
62
66
  # eg: noop? or failure?("idk")
63
67
  define_method(:"#{f}?") do |reason = nil|
@@ -11,7 +11,7 @@ module Lite
11
11
  ].freeze
12
12
 
13
13
  module Internals
14
- module Execute
14
+ module Executions
15
15
 
16
16
  def state
17
17
  @state || PENDING
@@ -35,15 +35,16 @@ module Lite
35
35
  increment_execution_index
36
36
  assign_execution_cmd_id
37
37
  start_monotonic_time
38
- Utils.hook(self, :on_before_execution)
38
+ Utils.try(self, :on_before_validation)
39
39
  validate_context_attributes
40
+ Utils.try(self, :on_before_execution)
40
41
  executing!
41
- Utils.hook(self, :on_executing)
42
+ Utils.try(self, :on_executing)
42
43
  end
43
44
 
44
45
  def after_execution
45
46
  send(:"#{success? ? COMPLETE : INTERRUPTED}!")
46
- Utils.hook(self, :on_after_execution)
47
+ Utils.try(self, :on_after_execution)
47
48
  stop_monotonic_time
48
49
  append_execution_result
49
50
  freeze_execution_objects
@@ -57,25 +58,25 @@ module Lite
57
58
 
58
59
  def execute
59
60
  around_execution { call }
60
- Utils.hook(self, :on_success)
61
+ Utils.try(self, :on_success)
61
62
  rescue StandardError => e
62
63
  fault(e, ERROR, metadata) unless e.is_a?(Lite::Command::Fault)
63
64
  after_execution
64
- Utils.hook(self, :"on_#{status}", e)
65
+ Utils.try(self, :"on_#{status}", e)
65
66
  ensure
66
- Utils.hook(self, :"on_#{state}")
67
+ Utils.try(self, :"on_#{state}")
67
68
  end
68
69
 
69
70
  def execute!
70
71
  around_execution { call }
71
- Utils.hook(self, :on_success)
72
+ Utils.try(self, :on_success)
72
73
  rescue StandardError => e
73
74
  fault(e, ERROR, metadata) unless e.is_a?(Lite::Command::Fault)
74
75
  after_execution
75
- Utils.hook(self, :"on_#{status}", e)
76
+ Utils.try(self, :"on_#{status}", e)
76
77
  raise(e)
77
78
  else
78
- Utils.hook(self, :"on_#{state}")
79
+ Utils.try(self, :"on_#{state}")
79
80
  end
80
81
 
81
82
  end
@@ -3,18 +3,24 @@
3
3
  module Lite
4
4
  module Command
5
5
  module Internals
6
- module Fault
6
+ module Faults
7
7
 
8
- def self.included(base)
9
- base.class_eval do
10
- attr_reader :caused_by, :thrown_by
11
- end
8
+ def caused_by
9
+ return if success?
10
+
11
+ @caused_by || self
12
12
  end
13
13
 
14
14
  def caused_fault?
15
15
  caused_by == self
16
16
  end
17
17
 
18
+ def thrown_by
19
+ return if success?
20
+
21
+ @thrown_by || self
22
+ end
23
+
18
24
  def threw_fault?
19
25
  thrown_by == self
20
26
  end
@@ -32,7 +38,7 @@ module Lite
32
38
  end
33
39
 
34
40
  def raise_dynamic_faults?
35
- false
41
+ Lite::Command.configuration.raise_dynamic_faults
36
42
  end
37
43
 
38
44
  end
@@ -5,7 +5,7 @@ require "securerandom" unless defined?(SecureRandom)
5
5
  module Lite
6
6
  module Command
7
7
  module Internals
8
- module Result
8
+ module Results
9
9
 
10
10
  def index
11
11
  @index ||= context.index ||= 0
@@ -19,7 +19,7 @@ module Lite
19
19
  next unless step.run?(self)
20
20
 
21
21
  cmd = step.command.call(context)
22
- throw!(cmd) unless cmd.ok?
22
+ throw!(cmd) if cmd.bad?
23
23
  end
24
24
  end
25
25
 
@@ -12,13 +12,7 @@ module Lite
12
12
  end
13
13
 
14
14
  def run?(cmd)
15
- if options[:if]
16
- Utils.call(cmd, options[:if])
17
- elsif options[:unless]
18
- !Utils.call(cmd, options[:unless])
19
- else
20
- true
21
- end
15
+ Utils.evaluate(cmd, options)
22
16
  end
23
17
 
24
18
  end
@@ -6,16 +6,12 @@ module Lite
6
6
 
7
7
  module_function
8
8
 
9
- def try(object, method_name, *args, include_private: false)
9
+ def try(object, method_name, *args, include_private: true)
10
10
  return unless object.respond_to?(method_name, include_private)
11
11
 
12
12
  object.send(method_name, *args)
13
13
  end
14
14
 
15
- def hook(object, method_name, *args)
16
- try(object, method_name, *args, include_private: true)
17
- end
18
-
19
15
  def call(object, argument)
20
16
  if argument.is_a?(Symbol) || argument.is_a?(String)
21
17
  object.send(argument)
@@ -26,6 +22,16 @@ module Lite
26
22
  end
27
23
  end
28
24
 
25
+ def evaluate(object, options = {})
26
+ if options[:if]
27
+ call(object, options[:if])
28
+ elsif options[:unless]
29
+ !call(object, options[:unless])
30
+ else
31
+ options.fetch(:default, true)
32
+ end
33
+ end
34
+
29
35
  end
30
36
  end
31
37
  end
@@ -3,7 +3,7 @@
3
3
  module Lite
4
4
  module Command
5
5
 
6
- VERSION = "2.1.3"
6
+ VERSION = "3.0.0"
7
7
 
8
8
  end
9
9
  end
data/lib/lite/command.rb CHANGED
@@ -1,19 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "generators/rails/command_generator" if defined?(Rails::Generators)
3
+ if defined?(Rails::Generators)
4
+ require "generators/lite/command/install_generator"
5
+ require "generators/rails/command_generator"
6
+ end
4
7
 
5
8
  require "lite/command/version"
9
+ require "lite/command/configuration"
6
10
  require "lite/command/utils"
7
11
  require "lite/command/context"
8
- require "lite/command/attribute"
9
- require "lite/command/attribute_validator"
10
12
  require "lite/command/fault"
11
13
  require "lite/command/fault_streamer"
12
- require "lite/command/internals/context"
13
- require "lite/command/internals/call"
14
- require "lite/command/internals/execute"
15
- require "lite/command/internals/fault"
16
- require "lite/command/internals/result"
14
+ require "lite/command/internals/attributes"
15
+ require "lite/command/internals/calls"
16
+ require "lite/command/internals/executions"
17
+ require "lite/command/internals/faults"
18
+ require "lite/command/internals/results"
17
19
  require "lite/command/base"
18
20
  require "lite/command/step"
19
21
  require "lite/command/sequence"
data/lite-command.gemspec CHANGED
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
37
37
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
38
38
  spec.require_paths = %w[lib]
39
39
 
40
+ spec.add_dependency "activemodel"
40
41
  spec.add_dependency "ostruct"
41
42
 
42
43
  spec.add_development_dependency "bundler"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lite-command
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Juan Gomez
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-09 00:00:00.000000000 Z
11
+ date: 2024-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
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
27
  - !ruby/object:Gem::Dependency
14
28
  name: ostruct
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -172,21 +186,22 @@ files:
172
186
  - _config.yml
173
187
  - bin/console
174
188
  - bin/setup
189
+ - lib/generators/lite/command/install_generator.rb
190
+ - lib/generators/lite/command/templates/install.rb
175
191
  - lib/generators/rails/USAGE
176
192
  - lib/generators/rails/command_generator.rb
177
193
  - lib/generators/rails/templates/command.rb.tt
178
194
  - lib/lite/command.rb
179
- - lib/lite/command/attribute.rb
180
- - lib/lite/command/attribute_validator.rb
181
195
  - lib/lite/command/base.rb
196
+ - lib/lite/command/configuration.rb
182
197
  - lib/lite/command/context.rb
183
198
  - lib/lite/command/fault.rb
184
199
  - lib/lite/command/fault_streamer.rb
185
- - lib/lite/command/internals/call.rb
186
- - lib/lite/command/internals/context.rb
187
- - lib/lite/command/internals/execute.rb
188
- - lib/lite/command/internals/fault.rb
189
- - lib/lite/command/internals/result.rb
200
+ - lib/lite/command/internals/attributes.rb
201
+ - lib/lite/command/internals/calls.rb
202
+ - lib/lite/command/internals/executions.rb
203
+ - lib/lite/command/internals/faults.rb
204
+ - lib/lite/command/internals/results.rb
190
205
  - lib/lite/command/sequence.rb
191
206
  - lib/lite/command/step.rb
192
207
  - lib/lite/command/utils.rb
@@ -1,102 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lite
4
- module Command
5
- class Attribute
6
-
7
- attr_accessor :command
8
- attr_reader :method_name, :options, :errors
9
-
10
- def initialize(method_name, options)
11
- @method_name = method_name
12
- @options = options
13
- @errors = []
14
- end
15
-
16
- def from
17
- options[:from] || :context
18
- end
19
-
20
- def filled?
21
- Utils.call(command, options[:filled]) || false
22
- end
23
-
24
- def required?
25
- Utils.call(command, options[:required]) || false
26
- end
27
-
28
- def typed?
29
- options.key?(:types) && types.any?
30
- end
31
-
32
- def types
33
- @types ||= begin
34
- t = Array(Utils.call(command, options[:types]))
35
-
36
- if filled?
37
- t.uniq - [NilClass]
38
- else
39
- t | [NilClass]
40
- end
41
- end
42
- end
43
-
44
- def validate!
45
- validate_respond_attribute!
46
- return unless errors.empty?
47
-
48
- validate_required_attribute!
49
- validate_attribute_type!
50
- validate_attribute_filled!
51
- end
52
-
53
- def valid?
54
- errors.empty?
55
- end
56
-
57
- def value
58
- return @value if defined?(@value)
59
-
60
- @value = command.send(from).public_send(method_name)
61
- end
62
-
63
- private
64
-
65
- def validate_respond_attribute!
66
- return if command.respond_to?(from, true)
67
-
68
- @errors << "is not defined or an attribute"
69
- end
70
-
71
- def validate_required_attribute!
72
- return unless required?
73
- return if command.send(from).respond_to?(method_name)
74
-
75
- @errors << "#{method_name} is required"
76
- end
77
-
78
- def validate_attribute_type!
79
- return unless typed?
80
- return if types.include?(value.class)
81
-
82
- @errors << "#{method_name} type invalid"
83
- end
84
-
85
- def empty?
86
- r = Utils.try(options[:filled], :[], :empty)
87
- return false if r.nil? || r == true
88
- return false unless value.respond_to?(:empty?)
89
-
90
- value.empty?
91
- end
92
-
93
- def validate_attribute_filled!
94
- return unless filled?
95
- return unless value.nil? || empty?
96
-
97
- @errors << "#{method_name} must be filled"
98
- end
99
-
100
- end
101
- end
102
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lite
4
- module Command
5
- class AttributeValidator
6
-
7
- attr_reader :command
8
-
9
- def initialize(command)
10
- @command = command
11
- end
12
-
13
- def attributes
14
- @attributes ||=
15
- command.class.attributes.map do |_method_name, attribute|
16
- attribute.tap { |a| a.command = command }
17
- end
18
- end
19
-
20
- def errors
21
- @errors ||= attributes.each_with_object({}) do |attribute, h|
22
- next if attribute.tap(&:validate!).valid?
23
-
24
- h[attribute.from] ||= []
25
- h[attribute.from] = h[attribute.from] | attribute.errors
26
- end
27
- end
28
-
29
- def valid?
30
- attributes.empty? || errors.empty?
31
- end
32
-
33
- end
34
- end
35
- end