simple_command_dispatcher 3.0.4 → 4.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +47 -25
- data/.gitignore +3 -0
- data/.rubocop.yml +36 -41
- data/.ruby-version +1 -1
- data/CHANGELOG.md +148 -59
- data/Gemfile +3 -6
- data/Gemfile.lock +76 -69
- data/Jenkinsfile +2 -2
- data/README.md +371 -217
- data/Rakefile +0 -8
- data/lib/core_ext/kernel.rb +22 -0
- data/lib/simple_command_dispatcher/configuration.rb +37 -35
- data/lib/simple_command_dispatcher/errors/invalid_class_constant_error.rb +16 -0
- data/lib/simple_command_dispatcher/errors/required_class_method_missing_error.rb +15 -0
- data/lib/simple_command_dispatcher/errors.rb +4 -0
- data/lib/simple_command_dispatcher/helpers/camelize.rb +46 -0
- data/lib/simple_command_dispatcher/helpers/trim_all.rb +16 -0
- data/lib/simple_command_dispatcher/services/command_namespace_service.rb +60 -0
- data/lib/simple_command_dispatcher/services/command_service.rb +152 -0
- data/lib/simple_command_dispatcher/version.rb +2 -4
- data/lib/simple_command_dispatcher.rb +69 -121
- data/simple_command_dispatcher.gemspec +3 -3
- metadata +12 -31
- data/lib/core_extensions/string.rb +0 -10
- data/lib/simple_command_dispatcher/configure.rb +0 -22
- data/lib/simple_command_dispatcher/klass_transform.rb +0 -251
- data/lib/tasks/simple_command_dispatcher_sandbox.rake +0 -222
data/Rakefile
CHANGED
@@ -2,19 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'bundler/gem_tasks'
|
4
4
|
require 'rspec/core/rake_task'
|
5
|
-
require 'yard'
|
6
5
|
|
7
6
|
# Rspec
|
8
7
|
RSpec::Core::RakeTask.new(:spec)
|
9
8
|
#task default: :spec
|
10
9
|
|
11
|
-
# Yard
|
12
|
-
YARD::Rake::YardocTask.new do |t|
|
13
|
-
t.files = ['lib/**/*.rb']
|
14
|
-
t.options = ['--no-cache', '--protected', '--private']
|
15
|
-
t.stats_options = ['--list-undoc']
|
16
|
-
end
|
17
|
-
|
18
10
|
# Load our custom rake tasks.
|
19
11
|
Gem.find_files('tasks/**/*.rake').each { |path| import path }
|
20
12
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
# Returns the eigenclass (singleton class) of the current object.
|
5
|
+
# This allows classes to reference their own class-level methods and variables.
|
6
|
+
#
|
7
|
+
# @return [Class] the eigenclass of the current object
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class MyClass
|
11
|
+
# def self.test
|
12
|
+
# eigenclass
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# MyClass.test # => #<Class:MyClass>
|
16
|
+
#
|
17
|
+
def eigenclass
|
18
|
+
class << self
|
19
|
+
self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,44 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# This is the configuration for SimpleCommandDispatcher.
|
4
|
+
module SimpleCommandDispatcher
|
5
|
+
class << self
|
6
|
+
attr_reader :configuration
|
7
|
+
|
8
|
+
# Configures SimpleCommandDispatcher by yielding the configuration object to the block.
|
6
9
|
#
|
7
|
-
#
|
8
|
-
#
|
10
|
+
# @yield [Configuration] yields the configuration object to the block
|
11
|
+
# @return [Configuration] returns the configuration object
|
9
12
|
#
|
10
13
|
# @example
|
11
14
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
15
|
+
# SimpleCommandDispatcher.configure do |config|
|
16
|
+
# config.some_option = 'some value'
|
17
|
+
# end
|
18
|
+
def configure
|
19
|
+
self.configuration ||= Configuration.new
|
20
|
+
|
21
|
+
yield(configuration) if block_given?
|
22
|
+
|
23
|
+
configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_writer :configuration
|
29
|
+
end
|
30
|
+
|
31
|
+
# This class encapsulates the configuration properties for this gem and
|
32
|
+
# provides methods and attributes that allow for management of the same.
|
33
|
+
class Configuration
|
34
|
+
# TODO: Add attr_xxx here
|
35
|
+
|
36
|
+
# Initializes a new Configuration instance with default values
|
37
|
+
def initialize
|
38
|
+
reset
|
39
|
+
end
|
40
|
+
|
41
|
+
# Resets all configuration attributes to their default values
|
42
|
+
def reset
|
43
|
+
# TODO: Reset our attributes here e.g. @attr = nil
|
42
44
|
end
|
43
45
|
end
|
44
46
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCommandDispatcher
|
4
|
+
module Errors
|
5
|
+
# This error is raised when a command class constant is not found or invalid.
|
6
|
+
class InvalidClassConstantError < StandardError
|
7
|
+
# Initializes a new InvalidClassConstantError
|
8
|
+
#
|
9
|
+
# @param constantized_class_string [String] the class string that failed to constantize
|
10
|
+
# @param error_message [String] the underlying error message
|
11
|
+
def initialize(constantized_class_string, error_message)
|
12
|
+
super("\"#{constantized_class_string}\" is not a valid class constant. Error message: \"#{error_message}\".")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCommandDispatcher
|
4
|
+
module Errors
|
5
|
+
# This error is raised when a required class method is missing on the command class.
|
6
|
+
class RequiredClassMethodMissingError < StandardError
|
7
|
+
# Initializes a new RequiredClassMethodMissingError
|
8
|
+
#
|
9
|
+
# @param command_class_constant [Class] the command class that is missing the required method
|
10
|
+
def initialize(command_class_constant)
|
11
|
+
super("Class \"#{command_class_constant}\" does not respond_to? class method \"call\".")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'trim_all'
|
4
|
+
|
5
|
+
module SimpleCommandDispatcher
|
6
|
+
module Helpers
|
7
|
+
module Camelize
|
8
|
+
include TrimAll
|
9
|
+
|
10
|
+
# Transforms a RESTful route into a Ruby constant string for instantiation
|
11
|
+
#
|
12
|
+
# @param token [String] the route path to be camelized
|
13
|
+
# @return [String] the camelized constant name
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
#
|
17
|
+
# camelize("/api/users/v1") # => "Api::Users::V1"
|
18
|
+
# # Then: Api::Users::V1.new.call
|
19
|
+
#
|
20
|
+
def camelize(token)
|
21
|
+
raise ArgumentError, 'Token is not a String' unless token.instance_of? String
|
22
|
+
|
23
|
+
return if token.empty?
|
24
|
+
|
25
|
+
# For RESTful paths → Ruby constants, use Rails' proven methods
|
26
|
+
# They're fast, reliable, and handle edge cases that matter for constants
|
27
|
+
result = trim_all(token)
|
28
|
+
.gsub(%r{[/\-\.\s:]+}, '/') # Normalize separators to /
|
29
|
+
.split('/') # Split into path segments
|
30
|
+
.reject(&:empty?) # Remove empty segments
|
31
|
+
.map { |segment| segment.underscore.camelize } # Rails camelization
|
32
|
+
.join('::') # Join as Ruby namespace
|
33
|
+
|
34
|
+
result.empty? ? '' : result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Usage example:
|
41
|
+
# "/api/user_sessions/v1" → "Api::UserSessions::V1"
|
42
|
+
#
|
43
|
+
# Then in your dispatcher:
|
44
|
+
# constant_name = camelize(request.path)
|
45
|
+
# command_class = Object.const_get(constant_name)
|
46
|
+
# command_class.new.call
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SimpleCommandDispatcher
|
4
|
+
module Helpers
|
5
|
+
module TrimAll
|
6
|
+
# Removes all whitespace from the given string, including Unicode whitespace
|
7
|
+
#
|
8
|
+
# @param string [String] the string to remove whitespace from
|
9
|
+
# @return [String] the string with all whitespace removed
|
10
|
+
def trim_all(string)
|
11
|
+
# Using Unicode property \p{Space} to match all Unicode whitespace characters
|
12
|
+
string.gsub(/\p{Space}+/, '')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../helpers/camelize'
|
4
|
+
require_relative '../helpers/trim_all'
|
5
|
+
|
6
|
+
module SimpleCommandDispatcher
|
7
|
+
module Services
|
8
|
+
# Returns a string of modules that can be subsequently prepended to a class, to create a fully qualified,
|
9
|
+
# constantized class.
|
10
|
+
#
|
11
|
+
# The command_namespace is provided during initialization and can be a Hash, Array, or String.
|
12
|
+
#
|
13
|
+
# @return [String] a string of modules that can be subsequently prepended to a class, to create a
|
14
|
+
# constantized class.
|
15
|
+
#
|
16
|
+
# @raise [ArgumentError] if the command_namespace is not of type String, Hash or Array.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# to_class_modules_string("Api") # => "Api::"
|
21
|
+
# to_class_modules_string([:Api, :AppName, :V1]) # => "Api::AppName::V1::"
|
22
|
+
# to_class_modules_string({ api: :Api, app_name: :AppName, api_version: :V1 }) # => "Api::AppName::V1::"
|
23
|
+
# to_class_modules_string({ api: :api, app_name: :app_name, api_version: :v1 }, { titleize_module: true })
|
24
|
+
# # => "Api::AppName::V1::"
|
25
|
+
#
|
26
|
+
class CommandNamespaceService
|
27
|
+
include Helpers::Camelize
|
28
|
+
include Helpers::TrimAll
|
29
|
+
|
30
|
+
def initialize(command_namespace:)
|
31
|
+
@command_namespace = command_namespace
|
32
|
+
end
|
33
|
+
|
34
|
+
# Handles command module transformations from String, Hash or Array into
|
35
|
+
# a fully qualified class modules string (e.g. "A::B::C::").
|
36
|
+
def to_class_modules_string
|
37
|
+
return '' if command_namespace.blank?
|
38
|
+
|
39
|
+
class_modules_string = join_class_modules_if(command_namespace:)
|
40
|
+
class_modules_string = trim_all(camelize(class_modules_string))
|
41
|
+
"#{class_modules_string}::"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :command_namespace
|
47
|
+
|
48
|
+
def join_class_modules_if(command_namespace:)
|
49
|
+
case command_namespace
|
50
|
+
when String
|
51
|
+
command_namespace
|
52
|
+
when Array
|
53
|
+
command_namespace.join('::')
|
54
|
+
when Hash
|
55
|
+
command_namespace.values.join('::')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../errors'
|
4
|
+
require_relative '../helpers/camelize'
|
5
|
+
require_relative 'command_namespace_service'
|
6
|
+
|
7
|
+
module SimpleCommandDispatcher
|
8
|
+
module Services
|
9
|
+
# Handles class and module transformations and instantiation.
|
10
|
+
class CommandService
|
11
|
+
include Helpers::Camelize
|
12
|
+
|
13
|
+
def initialize(command:, command_namespace: {})
|
14
|
+
@command = validate_command(command:)
|
15
|
+
@command_namespace = validate_command_namespace(command_namespace:)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns a constantized class (as a Class constant), given the command and command_namespace
|
19
|
+
# that were provided during initialization.
|
20
|
+
#
|
21
|
+
# @return [Class] the class constant.
|
22
|
+
#
|
23
|
+
# @raise [Errors::InvalidClassConstantError] if the constantized class string cannot be constantized; that is,
|
24
|
+
# if it is not a valid class constant.
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
#
|
28
|
+
# to_class("Authenticate", "Api") # => Api::Authenticate
|
29
|
+
# to_class(:Authenticate, [:Api, :AppName, :V1]) # => Api::AppName::V1::Authenticate
|
30
|
+
# to_class(:Authenticate, { :api :Api, app_name: :AppName, api_version: :V2 })
|
31
|
+
# # => Api::AppName::V2::Authenticate
|
32
|
+
# to_class("authenticate", { :api :api, app_name: :app_name, api_version: :v1 },
|
33
|
+
# { titleize_class: true, titleize_module: true }) # => Api::AppName::V1::Authenticate
|
34
|
+
#
|
35
|
+
def to_class
|
36
|
+
qualified_class_string = to_qualified_class_string(command, command_namespace)
|
37
|
+
|
38
|
+
begin
|
39
|
+
qualified_class_string.constantize
|
40
|
+
rescue StandardError => e
|
41
|
+
raise Errors::InvalidClassConstantError.new(qualified_class_string, e.message)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_accessor :command, :command_namespace
|
48
|
+
|
49
|
+
# Returns a fully-qualified constantized class (as a string), given the command and command_namespace.
|
50
|
+
# Both parameters are automatically camelized/titleized during processing.
|
51
|
+
#
|
52
|
+
# @param command [Symbol, String] the class name.
|
53
|
+
# @param command_namespace [Hash, Array, String] the modules command belongs to.
|
54
|
+
#
|
55
|
+
# @return [String] the fully qualified class, which includes module(s) and class name.
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
#
|
59
|
+
# to_qualified_class_string("authenticate", "api") # => "Api::Authenticate"
|
60
|
+
# to_qualified_class_string(:Authenticate, [:Api, :AppName, :V1]) # => "Api::AppName::V1::Authenticate"
|
61
|
+
# to_qualified_class_string(:authenticate, { api: :api, app_name: :app_name, api_version: :v1 })
|
62
|
+
# # => "Api::AppName::V1::Authenticate"
|
63
|
+
#
|
64
|
+
def to_qualified_class_string(command, command_namespace)
|
65
|
+
class_modules_string = CommandNamespaceService.new(command_namespace:).to_class_modules_string
|
66
|
+
class_string = to_class_string(command:)
|
67
|
+
"#{class_modules_string}#{class_string}"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the command as a string after transformations have been applied.
|
71
|
+
# The command is automatically camelized/titleized during processing.
|
72
|
+
#
|
73
|
+
# @param command [Symbol, String] the class name to be transformed.
|
74
|
+
#
|
75
|
+
# @return [String] the transformed class as a string.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
#
|
79
|
+
# to_class_string(command: "MyClass") # => "MyClass"
|
80
|
+
# to_class_string(command: "my_class") # => "MyClass"
|
81
|
+
# to_class_string(command: :MyClass) # => "MyClass"
|
82
|
+
# to_class_string(command: :my_class) # => "MyClass"
|
83
|
+
#
|
84
|
+
def to_class_string(command:)
|
85
|
+
camelize(command)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @!visibility public
|
89
|
+
#
|
90
|
+
# Validates command and returns command as a string after all blanks have been removed using
|
91
|
+
# command.gsub(/\s+/, "").
|
92
|
+
#
|
93
|
+
# @param [Symbol or String] command the class name to be validated. command cannot be empty?
|
94
|
+
#
|
95
|
+
# @return [String] the validated class as a string with blanks removed.
|
96
|
+
#
|
97
|
+
# @raise [ArgumentError] if the command is empty? or not of type String or Symbol.
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
#
|
101
|
+
# validate_command(" My Class ") # => "MyClass"
|
102
|
+
# validate_command(:MyClass) # => "MyClass"
|
103
|
+
#
|
104
|
+
def validate_command(command:)
|
105
|
+
unless command.is_a?(Symbol) || command.is_a?(String)
|
106
|
+
raise ArgumentError,
|
107
|
+
'command is not a String or Symbol. command must equal the class name of the ' \
|
108
|
+
'command to call in the form of a String or Symbol.'
|
109
|
+
end
|
110
|
+
|
111
|
+
command = command.to_s.strip
|
112
|
+
|
113
|
+
raise ArgumentError, 'command is empty?' if command.empty?
|
114
|
+
|
115
|
+
command
|
116
|
+
end
|
117
|
+
|
118
|
+
# @!visibility public
|
119
|
+
#
|
120
|
+
# Validates and returns command_namespace.
|
121
|
+
#
|
122
|
+
# @param [Hash, Array or String] command_namespace the module(s) to be validated.
|
123
|
+
#
|
124
|
+
# @return [Hash, Array or String] the validated module(s).
|
125
|
+
#
|
126
|
+
# @raise [ArgumentError] if the command_namespace is not of type String, Hash or Array.
|
127
|
+
#
|
128
|
+
# @example
|
129
|
+
#
|
130
|
+
# validate_command_namespace(" Module ") # => " Module "
|
131
|
+
# validate_command_namespace(:Module) # => :Module
|
132
|
+
# validate_command_namespace("ModuleA::ModuleB") # => "ModuleA::ModuleB"
|
133
|
+
#
|
134
|
+
def validate_command_namespace(command_namespace:)
|
135
|
+
return {} if command_namespace.blank?
|
136
|
+
|
137
|
+
unless valid_command_namespace_type?(command_namespace:)
|
138
|
+
raise ArgumentError,
|
139
|
+
'Argument command_namespace is not a String, Hash or Array.'
|
140
|
+
end
|
141
|
+
|
142
|
+
command_namespace
|
143
|
+
end
|
144
|
+
|
145
|
+
def valid_command_namespace_type?(command_namespace:)
|
146
|
+
command_namespace.instance_of?(String) ||
|
147
|
+
command_namespace.instance_of?(Hash) ||
|
148
|
+
command_namespace.instance_of?(Array)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -1,135 +1,83 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'simple_command_dispatcher/klass_transform'
|
5
|
-
require 'simple_command'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
6
4
|
require 'active_support/core_ext/string/inflections'
|
7
|
-
require '
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
self
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
5
|
+
require 'core_ext/kernel'
|
6
|
+
require 'simple_command_dispatcher/configuration'
|
7
|
+
require 'simple_command_dispatcher/errors'
|
8
|
+
require 'simple_command_dispatcher/services/command_service'
|
9
|
+
require 'simple_command_dispatcher/version'
|
16
10
|
|
17
|
-
module
|
18
|
-
# Provides a way to call
|
19
|
-
#
|
20
|
-
# For information about the simple_command gem, visit {https://rubygems.org/gems/simple_command}
|
11
|
+
module SimpleCommandDispatcher
|
12
|
+
# Provides a way to call your custom commands dynamically.
|
21
13
|
#
|
22
|
-
|
23
|
-
|
24
|
-
|
14
|
+
class << self
|
15
|
+
# Calls a *Command* given the command name, the namespace (modules) the command belongs to
|
16
|
+
# and the (request) parameters to pass to the command.
|
17
|
+
#
|
18
|
+
# @param command [Symbol, String] the name of the Command to call.
|
19
|
+
#
|
20
|
+
# @param command_namespace [Hash, Array] the ruby modules that qualify the Command to call.
|
21
|
+
# When passing a Hash, the Hash keys serve as documentation only.
|
22
|
+
# For example, ['Api', 'AppName', 'V1'] and { :api :Api, app_name: :AppName, api_version: :V1 }
|
23
|
+
# will both produce 'Api::AppName::V1', this string will be prepended to the command to form the Command
|
24
|
+
# to call (e.g. 'Api::AppName::V1::MySimpleCommand' = Api::AppName::V1::MySimpleCommand.call(*request_params)).
|
25
|
+
#
|
26
|
+
# @param request_params [Hash, Array, Object] the parameters to pass to the call method of the Command. This
|
27
|
+
# parameter is simply passed through to the call method of the Command. Hash parameters are passed as
|
28
|
+
# keyword arguments, Array parameters are passed as positional arguments, and other objects are passed
|
29
|
+
# as a single argument.
|
30
|
+
#
|
31
|
+
# @return [Object] the Object returned as a result of calling the Command#call method.
|
32
|
+
#
|
33
|
+
# @example
|
34
|
+
#
|
35
|
+
# # Below call equates to the following:
|
36
|
+
# # Api::Carz4Rent::V1::Authenticate.call({ email: 'sam@gmail.com', password: 'AskM3!' })
|
37
|
+
# SimpleCommandDispatcher.call(command: :Authenticate,
|
38
|
+
# command_namespace: { api: :Api, app_name: :Carz4Rent, api_version: :V1 },
|
39
|
+
# request_params: { email: 'sam@gmail.com', password: 'AskM3!' } ) # => Command result
|
40
|
+
#
|
41
|
+
# # Below equates to the following: Api::Carz4Rent::V2::Authenticate.call('sam@gmail.com', 'AskM3!')
|
42
|
+
# SimpleCommandDispatcher.call(command: :Authenticate,
|
43
|
+
# command_namespace: ['Api', 'Carz4Rent', 'V2'],
|
44
|
+
# request_params: ['sam@gmail.com', 'AskM3!']) # => Command result
|
45
|
+
#
|
46
|
+
# # Below equates to the following:
|
47
|
+
# # Api::Auth::JazzMeUp::V1::Authenticate.call('jazz_me@gmail.com', 'JazzM3!')
|
48
|
+
# SimpleCommandDispatcher.call(command: :Authenticate,
|
49
|
+
# command_namespace: ['Api::Auth::JazzMeUp', :V1],
|
50
|
+
# request_params: ['jazz_me@gmail.com', 'JazzM3!']) # => Command result
|
51
|
+
#
|
52
|
+
def call(command:, command_namespace: {}, request_params: nil)
|
53
|
+
# Create a constantized class from our command and command_namespace...
|
54
|
+
constantized_class_object = Services::CommandService.new(command:, command_namespace:).to_class
|
55
|
+
validate_command!(constantized_class_object)
|
25
56
|
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# @param command [Symbol, String] the name of the SimpleCommand or Command to call.
|
30
|
-
#
|
31
|
-
# @param command_modules [Hash, Array] the ruby modules that qualify the SimpleCommand to call. When
|
32
|
-
# passing a Hash, the Hash keys serve as documentation only. For example, ['Api', 'AppName', 'V1']
|
33
|
-
# and { :api :Api, app_name: :AppName, api_version: :V1 } will both produce 'Api::AppName::V1',
|
34
|
-
# this string will be prepended to the command to form the SimpleCommand to call
|
35
|
-
# (e.g. 'Api::AppName::V1::MySimpleCommand' = Api::AppName::V1::MySimpleCommand.call(*command_parameters)).
|
36
|
-
#
|
37
|
-
# @param [Hash] options the options that determine how command and command_module are transformed.
|
38
|
-
# @option options [Boolean] :camelize (false) determines whether or not both class and module names should be
|
39
|
-
# camelized.
|
40
|
-
# @option options [Boolean] :titleize (false) determines whether or not both class and module names should be
|
41
|
-
# titleized.
|
42
|
-
# @option options [Boolean] :class_titleize (false) determines whether or not class names should be titleized.
|
43
|
-
# @option options [Boolean] :class_camelized (false) determines whether or not class names should be camelized.
|
44
|
-
# @option options [Boolean] :module_titleize (false) determines whether or not module names should be titleized.
|
45
|
-
# @option options [Boolean] :module_camelized (false) determines whether or not module names should be camelized.
|
46
|
-
#
|
47
|
-
# @param command_parameters [Array<Symbol>] the parameters to pass to the call method of the SimpleCommand.
|
48
|
-
# This parameter is simplypassed through to the call method of the SimpleCommand/Command.
|
49
|
-
#
|
50
|
-
# @return [SimpleCommand, Object] the SimpleCommand or Object returned as a result of calling the
|
51
|
-
# SimpleCommand#call method or the Command#call method respectfully.
|
52
|
-
#
|
53
|
-
# @example
|
54
|
-
#
|
55
|
-
# # Below call equates to the following:
|
56
|
-
# # Api::Carz4Rent::V1::Authenticate.call({ email: 'sam@gmail.com', password: 'AskM3!' })
|
57
|
-
# SimpleCommand::Dispatcher.call(:Authenticate,
|
58
|
-
# { api: :Api, app_name: :Carz4Rent, api_version: :V1 },
|
59
|
-
# { email: 'sam@gmail.com', password: 'AskM3!' } ) # => SimpleCommand result
|
60
|
-
#
|
61
|
-
# # Below equates to the following: Api::Carz4Rent::V2::Authenticate.call('sam@gmail.com', 'AskM3!')
|
62
|
-
# SimpleCommand::Dispatcher.call(:Authenticate,
|
63
|
-
# ['Api', 'Carz4Rent', 'V2'], 'sam@gmail.com', 'AskM3!') # => SimpleCommand result
|
64
|
-
#
|
65
|
-
# # Below equates to the following:
|
66
|
-
# # Api::Auth::JazzMeUp::V1::Authenticate.call('jazz_me@gmail.com', 'JazzM3!')
|
67
|
-
# SimpleCommand::Dispatcher.call(:Authenticate, ['Api::Auth::JazzMeUp', :V1],
|
68
|
-
# 'jazz_me@gmail.com', 'JazzM3!') # => SimpleCommand result
|
69
|
-
#
|
70
|
-
def call(command = '', command_modules = {}, options = {}, *command_parameters)
|
71
|
-
# Create a constantized class from our command and command_modules...
|
72
|
-
command_class_constant = to_constantized_class(command, command_modules, options)
|
57
|
+
# We know we have a valid command class object if we get here. All we need to do is call the .call
|
58
|
+
# class method, pass the request_params arguments depending on the request_params data type, and
|
59
|
+
# return the results.
|
73
60
|
|
74
|
-
|
75
|
-
|
76
|
-
if !SimpleCommand::Dispatcher.configuration.allow_custom_commands && !simple_command?(command_class_constant)
|
77
|
-
raise ArgumentError,
|
78
|
-
"Class \"#{command_class_constant}\" " \
|
79
|
-
'must prepend module SimpleCommand if Configuration#allow_custom_commands is true.'
|
80
|
-
end
|
81
|
-
|
82
|
-
if valid_command?(command_class_constant)
|
83
|
-
# We know we have a valid SimpleCommand; all we need to do is call #call,
|
84
|
-
# pass the command_parameter variable arguments to the call, and return the results.
|
85
|
-
run_command(command_class_constant, command_parameters)
|
86
|
-
else
|
87
|
-
raise NameError, "Class \"#{command_class_constant}\" does not respond_to? method ::call."
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
private
|
61
|
+
call_command(constantized_class_object:, request_params:)
|
62
|
+
end
|
92
63
|
|
93
|
-
|
94
|
-
# class method named ::call defined. Commands that do not have a public class method
|
95
|
-
# named ::call, are considered invalid.
|
96
|
-
#
|
97
|
-
# @param klass_constant [String] a class constant that will be validated to see whether or not the
|
98
|
-
# class is a valid command.
|
99
|
-
#
|
100
|
-
# @return [Boolean] true if klass_constant has a public class method named ::call defined,
|
101
|
-
# false otherwise.
|
102
|
-
#
|
103
|
-
# @!visibility public
|
104
|
-
def valid_command?(klass_constant)
|
105
|
-
klass_constant.eigenclass.public_method_defined?(:call)
|
106
|
-
end
|
64
|
+
private
|
107
65
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
def simple_command?(klass_constant)
|
118
|
-
klass_constant.eigenclass.included_modules.include? SimpleCommand::ClassMethods
|
66
|
+
def call_command(constantized_class_object:, request_params:)
|
67
|
+
if request_params.is_a?(Hash)
|
68
|
+
constantized_class_object.call(**request_params)
|
69
|
+
elsif request_params.is_a?(Array)
|
70
|
+
constantized_class_object.call(*request_params)
|
71
|
+
elsif request_params.present?
|
72
|
+
constantized_class_object.call(request_params)
|
73
|
+
else
|
74
|
+
constantized_class_object.call
|
119
75
|
end
|
76
|
+
end
|
120
77
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
# @param parameters [Array] an array of parameters to pass to the command that will be called.
|
125
|
-
#
|
126
|
-
# @return [Object] returns the object (if any) that results from calling the command.
|
127
|
-
#
|
128
|
-
# @!visibility public
|
129
|
-
def run_command(klass_constant, parameters)
|
130
|
-
klass_constant.call(*parameters)
|
131
|
-
# rescue NameError
|
132
|
-
# raise NameError.new("Class \"#{klass_constant}\" does not respond_to? method ::call.")
|
78
|
+
def validate_command!(constantized_class_object)
|
79
|
+
unless constantized_class_object.eigenclass.public_method_defined?(:call)
|
80
|
+
raise Errors::RequiredClassMethodMissingError, constantized_class_object
|
133
81
|
end
|
134
82
|
end
|
135
83
|
end
|