lite-command 2.1.2 → 3.0.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.
@@ -1,28 +1,31 @@
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
6
8
 
7
9
  def self.inherited(base)
8
- base.include Lite::Command::Internals::Context
9
- base.include Lite::Command::Internals::Call
10
- base.include Lite::Command::Internals::Execute
11
- base.include Lite::Command::Internals::Fault
12
- base.include Lite::Command::Internals::Result
13
-
14
- base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
- # eg: Users::ResetPassword::Fault < Lite::Command::Fault
16
- #{base}::Fault = Class.new(Lite::Command::Fault)
17
-
18
- # eg: Users::ResetPassword::Noop < Users::ResetPassword::Fault
19
- #{base}::Noop = Class.new(#{base}::Fault)
20
- #{base}::Invalid = Class.new(#{base}::Fault)
21
- #{base}::Failure = Class.new(#{base}::Fault)
22
- #{base}::Error = Class.new(#{base}::Fault)
23
- RUBY
24
-
25
10
  super
11
+
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.2"
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,10 +37,10 @@ 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"
43
- spec.add_development_dependency "fasterer"
44
44
  spec.add_development_dependency "generator_spec"
45
45
  spec.add_development_dependency "rake"
46
46
  spec.add_development_dependency "rspec"
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lite-command
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.2
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
13
  - !ruby/object:Gem::Dependency
14
- name: ostruct
14
+ name: activemodel
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,13 +25,13 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: ostruct
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :development
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: fasterer
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -171,7 +171,6 @@ executables: []
171
171
  extensions: []
172
172
  extra_rdoc_files: []
173
173
  files:
174
- - ".fasterer.yml"
175
174
  - ".gitattributes"
176
175
  - ".github/workflows/ci.yml"
177
176
  - ".gitignore"
@@ -187,21 +186,22 @@ files:
187
186
  - _config.yml
188
187
  - bin/console
189
188
  - bin/setup
189
+ - lib/generators/lite/command/install_generator.rb
190
+ - lib/generators/lite/command/templates/install.rb
190
191
  - lib/generators/rails/USAGE
191
192
  - lib/generators/rails/command_generator.rb
192
193
  - lib/generators/rails/templates/command.rb.tt
193
194
  - lib/lite/command.rb
194
- - lib/lite/command/attribute.rb
195
- - lib/lite/command/attribute_validator.rb
196
195
  - lib/lite/command/base.rb
196
+ - lib/lite/command/configuration.rb
197
197
  - lib/lite/command/context.rb
198
198
  - lib/lite/command/fault.rb
199
199
  - lib/lite/command/fault_streamer.rb
200
- - lib/lite/command/internals/call.rb
201
- - lib/lite/command/internals/context.rb
202
- - lib/lite/command/internals/execute.rb
203
- - lib/lite/command/internals/fault.rb
204
- - 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
205
205
  - lib/lite/command/sequence.rb
206
206
  - lib/lite/command/step.rb
207
207
  - lib/lite/command/utils.rb
data/.fasterer.yml DELETED
@@ -1,19 +0,0 @@
1
- speedups:
2
- block_vs_symbol_to_proc: true
3
- each_with_index_vs_while: false
4
- fetch_with_argument_vs_block: true
5
- for_loop_vs_each: true
6
- getter_vs_attr_reader: true
7
- gsub_vs_tr: true
8
- hash_merge_bang_vs_hash_brackets: true
9
- keys_each_vs_each_key: true
10
- map_flatten_vs_flat_map: true
11
- module_eval: true
12
- proc_call_vs_yield: true
13
- rescue_vs_respond_to: true
14
- reverse_each_vs_reverse_each: true
15
- select_first_vs_detect: true
16
- select_last_vs_reverse_detect: true
17
- setter_vs_attr_writer: true
18
- shuffle_first_vs_sample: true
19
- sort_vs_sort_by: true
@@ -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 if r.nil? || r == true
88
- return 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