simple_command_dispatcher 3.0.4 → 4.1.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 +173 -59
- data/Gemfile +3 -6
- data/Gemfile.lock +77 -70
- data/Jenkinsfile +2 -2
- data/README.md +312 -224
- data/Rakefile +0 -8
- data/lib/core_ext/kernel.rb +22 -0
- data/lib/simple_command_dispatcher/commands/command_callable.rb +52 -0
- data/lib/simple_command_dispatcher/commands/errors.rb +44 -0
- data/lib/simple_command_dispatcher/commands/utils.rb +20 -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 +70 -121
- data/simple_command_dispatcher.gemspec +9 -10
- metadata +26 -42
- 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
@@ -1,135 +1,84 @@
|
|
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
|
-
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
5
|
+
require 'core_ext/kernel'
|
6
|
+
require 'simple_command_dispatcher/commands/command_callable'
|
7
|
+
require 'simple_command_dispatcher/configuration'
|
8
|
+
require 'simple_command_dispatcher/errors'
|
9
|
+
require 'simple_command_dispatcher/services/command_service'
|
10
|
+
require 'simple_command_dispatcher/version'
|
16
11
|
|
17
|
-
module
|
18
|
-
# Provides a way to call
|
19
|
-
#
|
20
|
-
# For information about the simple_command gem, visit {https://rubygems.org/gems/simple_command}
|
12
|
+
module SimpleCommandDispatcher
|
13
|
+
# Provides a way to call your custom commands dynamically.
|
21
14
|
#
|
22
|
-
|
23
|
-
|
24
|
-
|
15
|
+
class << self
|
16
|
+
# Calls a *Command* given the command name, the namespace (modules) the command belongs to
|
17
|
+
# and the (request) parameters to pass to the command.
|
18
|
+
#
|
19
|
+
# @param command [Symbol, String] the name of the Command to call.
|
20
|
+
#
|
21
|
+
# @param command_namespace [Hash, Array] the ruby modules that qualify the Command to call.
|
22
|
+
# When passing a Hash, the Hash keys serve as documentation only.
|
23
|
+
# For example, ['Api', 'AppName', 'V1'] and { :api :Api, app_name: :AppName, api_version: :V1 }
|
24
|
+
# will both produce 'Api::AppName::V1', this string will be prepended to the command to form the Command
|
25
|
+
# to call (e.g. 'Api::AppName::V1::MySimpleCommand' = Api::AppName::V1::MySimpleCommand.call(*request_params)).
|
26
|
+
#
|
27
|
+
# @param request_params [Hash, Array, Object] the parameters to pass to the call method of the Command. This
|
28
|
+
# parameter is simply passed through to the call method of the Command. Hash parameters are passed as
|
29
|
+
# keyword arguments, Array parameters are passed as positional arguments, and other objects are passed
|
30
|
+
# as a single argument.
|
31
|
+
#
|
32
|
+
# @return [Object] the Object returned as a result of calling the Command#call method.
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
#
|
36
|
+
# # Below call equates to the following:
|
37
|
+
# # Api::Carz4Rent::V1::Authenticate.call({ email: 'sam@gmail.com', password: 'AskM3!' })
|
38
|
+
# SimpleCommandDispatcher.call(command: :Authenticate,
|
39
|
+
# command_namespace: { api: :Api, app_name: :Carz4Rent, api_version: :V1 },
|
40
|
+
# request_params: { email: 'sam@gmail.com', password: 'AskM3!' } ) # => Command result
|
41
|
+
#
|
42
|
+
# # Below equates to the following: Api::Carz4Rent::V2::Authenticate.call('sam@gmail.com', 'AskM3!')
|
43
|
+
# SimpleCommandDispatcher.call(command: :Authenticate,
|
44
|
+
# command_namespace: ['Api', 'Carz4Rent', 'V2'],
|
45
|
+
# request_params: ['sam@gmail.com', 'AskM3!']) # => Command result
|
46
|
+
#
|
47
|
+
# # Below equates to the following:
|
48
|
+
# # Api::Auth::JazzMeUp::V1::Authenticate.call('jazz_me@gmail.com', 'JazzM3!')
|
49
|
+
# SimpleCommandDispatcher.call(command: :Authenticate,
|
50
|
+
# command_namespace: ['Api::Auth::JazzMeUp', :V1],
|
51
|
+
# request_params: ['jazz_me@gmail.com', 'JazzM3!']) # => Command result
|
52
|
+
#
|
53
|
+
def call(command:, command_namespace: {}, request_params: nil)
|
54
|
+
# Create a constantized class from our command and command_namespace...
|
55
|
+
constantized_class_object = Services::CommandService.new(command:, command_namespace:).to_class
|
56
|
+
validate_command!(constantized_class_object)
|
25
57
|
|
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)
|
58
|
+
# We know we have a valid command class object if we get here. All we need to do is call the .call
|
59
|
+
# class method, pass the request_params arguments depending on the request_params data type, and
|
60
|
+
# return the results.
|
73
61
|
|
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
|
62
|
+
call_command(constantized_class_object:, request_params:)
|
63
|
+
end
|
92
64
|
|
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
|
65
|
+
private
|
107
66
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
def simple_command?(klass_constant)
|
118
|
-
klass_constant.eigenclass.included_modules.include? SimpleCommand::ClassMethods
|
67
|
+
def call_command(constantized_class_object:, request_params:)
|
68
|
+
if request_params.is_a?(Hash)
|
69
|
+
constantized_class_object.call(**request_params)
|
70
|
+
elsif request_params.is_a?(Array)
|
71
|
+
constantized_class_object.call(*request_params)
|
72
|
+
elsif request_params.present?
|
73
|
+
constantized_class_object.call(request_params)
|
74
|
+
else
|
75
|
+
constantized_class_object.call
|
119
76
|
end
|
77
|
+
end
|
120
78
|
|
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.")
|
79
|
+
def validate_command!(constantized_class_object)
|
80
|
+
unless constantized_class_object.eigenclass.public_method_defined?(:call)
|
81
|
+
raise Errors::RequiredClassMethodMissingError, constantized_class_object
|
133
82
|
end
|
134
83
|
end
|
135
84
|
end
|
@@ -6,16 +6,15 @@ require 'simple_command_dispatcher/version'
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'simple_command_dispatcher'
|
9
|
-
spec.version =
|
9
|
+
spec.version = SimpleCommandDispatcher::VERSION
|
10
10
|
spec.authors = ['Gene M. Angelo, Jr.']
|
11
11
|
spec.email = ['public.gma@gmail.com']
|
12
12
|
|
13
|
-
spec.summary = '
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
based on the API version. simple_command_dispatcher allows you to execute either command with one line of code dynamically.'.gsub(/\s+/, ' ')
|
13
|
+
spec.summary = 'Dynamic command execution for Rails applications using convention over configuration - automatically maps request routes to command classes.'
|
14
|
+
spec.description = 'A lightweight Ruby gem that enables Rails applications to dynamically execute command objects using convention over configuration. ' \
|
15
|
+
'Automatically transforms request paths into Ruby class constants, allowing controllers to dispatch commands based on routes and parameters. ' \
|
16
|
+
'Features the optional CommandCallable module for standardized command interfaces with built-in success/failure tracking and error handling. ' \
|
17
|
+
'Perfect for clean, maintainable Rails APIs with RESTful route-to-command mapping. Only depends on ActiveSupport for reliable camelization.'
|
19
18
|
spec.homepage = 'https://github.com/gangelo/simple_command_dispatcher'
|
20
19
|
spec.license = 'MIT'
|
21
20
|
|
@@ -35,7 +34,7 @@ Gem::Specification.new do |spec|
|
|
35
34
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
35
|
spec.require_paths = ['lib']
|
37
36
|
|
38
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 3.
|
39
|
-
|
40
|
-
spec.add_runtime_dependency '
|
37
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.3', '< 4.0')
|
38
|
+
|
39
|
+
spec.add_runtime_dependency 'activesupport', '>= 7.0.8', '< 9.0'
|
41
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_command_dispatcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gene M. Angelo, Jr.
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activesupport
|
@@ -19,7 +18,7 @@ dependencies:
|
|
19
18
|
version: 7.0.8
|
20
19
|
- - "<"
|
21
20
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
21
|
+
version: '9.0'
|
23
22
|
type: :runtime
|
24
23
|
prerelease: false
|
25
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,33 +28,14 @@ dependencies:
|
|
29
28
|
version: 7.0.8
|
30
29
|
- - "<"
|
31
30
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
version: 1.0.1
|
43
|
-
type: :runtime
|
44
|
-
prerelease: false
|
45
|
-
version_requirements: !ruby/object:Gem::Requirement
|
46
|
-
requirements:
|
47
|
-
- - "~>"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '1.0'
|
50
|
-
- - ">="
|
51
|
-
- !ruby/object:Gem::Version
|
52
|
-
version: 1.0.1
|
53
|
-
description: 'Within a services API (rails-api for instance), you may have a need
|
54
|
-
to execute different simple_commands or your own custom commands (service objects)
|
55
|
-
based on one or more factors: multiple application, API version, user type, user
|
56
|
-
credentials, etc. For example, your service API may need to execute either Api::Auth::V1::AuthenticateCommand.call(...)
|
57
|
-
or Api::Auth::V2::AuthenticateCommand.call(...) based on the API version. simple_command_dispatcher
|
58
|
-
allows you to execute either command with one line of code dynamically.'
|
31
|
+
version: '9.0'
|
32
|
+
description: A lightweight Ruby gem that enables Rails applications to dynamically
|
33
|
+
execute command objects using convention over configuration. Automatically transforms
|
34
|
+
request paths into Ruby class constants, allowing controllers to dispatch commands
|
35
|
+
based on routes and parameters. Features the optional CommandCallable module for
|
36
|
+
standardized command interfaces with built-in success/failure tracking and error
|
37
|
+
handling. Perfect for clean, maintainable Rails APIs with RESTful route-to-command
|
38
|
+
mapping. Only depends on ActiveSupport for reliable camelization.
|
59
39
|
email:
|
60
40
|
- public.gma@gmail.com
|
61
41
|
executables: []
|
@@ -80,19 +60,25 @@ files:
|
|
80
60
|
- Rakefile
|
81
61
|
- bin/console
|
82
62
|
- bin/setup
|
83
|
-
- lib/
|
63
|
+
- lib/core_ext/kernel.rb
|
84
64
|
- lib/simple_command_dispatcher.rb
|
65
|
+
- lib/simple_command_dispatcher/commands/command_callable.rb
|
66
|
+
- lib/simple_command_dispatcher/commands/errors.rb
|
67
|
+
- lib/simple_command_dispatcher/commands/utils.rb
|
85
68
|
- lib/simple_command_dispatcher/configuration.rb
|
86
|
-
- lib/simple_command_dispatcher/
|
87
|
-
- lib/simple_command_dispatcher/
|
69
|
+
- lib/simple_command_dispatcher/errors.rb
|
70
|
+
- lib/simple_command_dispatcher/errors/invalid_class_constant_error.rb
|
71
|
+
- lib/simple_command_dispatcher/errors/required_class_method_missing_error.rb
|
72
|
+
- lib/simple_command_dispatcher/helpers/camelize.rb
|
73
|
+
- lib/simple_command_dispatcher/helpers/trim_all.rb
|
74
|
+
- lib/simple_command_dispatcher/services/command_namespace_service.rb
|
75
|
+
- lib/simple_command_dispatcher/services/command_service.rb
|
88
76
|
- lib/simple_command_dispatcher/version.rb
|
89
|
-
- lib/tasks/simple_command_dispatcher_sandbox.rake
|
90
77
|
- simple_command_dispatcher.gemspec
|
91
78
|
homepage: https://github.com/gangelo/simple_command_dispatcher
|
92
79
|
licenses:
|
93
80
|
- MIT
|
94
81
|
metadata: {}
|
95
|
-
post_install_message:
|
96
82
|
rdoc_options: []
|
97
83
|
require_paths:
|
98
84
|
- lib
|
@@ -100,7 +86,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
100
86
|
requirements:
|
101
87
|
- - ">="
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: 3.
|
89
|
+
version: '3.3'
|
104
90
|
- - "<"
|
105
91
|
- !ruby/object:Gem::Version
|
106
92
|
version: '4.0'
|
@@ -110,10 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
96
|
- !ruby/object:Gem::Version
|
111
97
|
version: '0'
|
112
98
|
requirements: []
|
113
|
-
rubygems_version: 3.
|
114
|
-
signing_key:
|
99
|
+
rubygems_version: 3.6.8
|
115
100
|
specification_version: 4
|
116
|
-
summary:
|
117
|
-
|
118
|
-
Ideal for rails-api.
|
101
|
+
summary: Dynamic command execution for Rails applications using convention over configuration
|
102
|
+
- automatically maps request routes to command classes.
|
119
103
|
test_files: []
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'configuration'
|
4
|
-
|
5
|
-
module SimpleCommand
|
6
|
-
module Dispatcher
|
7
|
-
class << self
|
8
|
-
attr_writer :configuration
|
9
|
-
end
|
10
|
-
|
11
|
-
# Returns the application configuration object.
|
12
|
-
#
|
13
|
-
# @return [Configuration] the application Configuration object.
|
14
|
-
def self.configuration
|
15
|
-
@configuration ||= Configuration.new
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.configure
|
19
|
-
yield(configuration)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,251 +0,0 @@
|
|
1
|
-
# rubocop:disable Style/OptionHash
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require_relative '../core_extensions/string'
|
5
|
-
|
6
|
-
module SimpleCommand
|
7
|
-
# Handles class and module transformations.
|
8
|
-
module KlassTransform
|
9
|
-
# Returns a constantized class (as a Class constant), given the klass and klass_modules.
|
10
|
-
#
|
11
|
-
# @param klass [Symbol or String] the class name.
|
12
|
-
# @param klass_modules [Hash, Array or String] the modules klass belongs to.
|
13
|
-
# @param options [Hash] the options that determine how klass_modules is transformed.
|
14
|
-
# @option options [Boolean] :camelize (false) determines whether or not both klass and klass_modules
|
15
|
-
# should be camelized.
|
16
|
-
# @option options [Boolean] :titleize (false) determines whether or not both klass and klass_modules
|
17
|
-
# should be titleized.
|
18
|
-
# @option options [Boolean] :class_titleize (false) determines whether or not klass names should be
|
19
|
-
# titleized.
|
20
|
-
# @option options [Boolean] :class_camelized (false) determines whether or not klass names should be
|
21
|
-
# camelized.
|
22
|
-
# @option options [Boolean] :module_titleize (false) determines whether or not klass_modules names
|
23
|
-
# should be titleized.
|
24
|
-
# @option options [Boolean] :module_camelized (false) determines whether or not klass_modules names
|
25
|
-
# should be camelized.
|
26
|
-
#
|
27
|
-
# @return [Class] the class constant. Can be used to call ClassConstant.constantize.
|
28
|
-
#
|
29
|
-
# @raise [NameError] if the constantized class string cannot be constantized; that is, if it is not
|
30
|
-
# a valid class constant.
|
31
|
-
#
|
32
|
-
# @example
|
33
|
-
#
|
34
|
-
# to_constantized_class("Authenticate", "Api") # => Api::Authenticate
|
35
|
-
# to_constantized_class(:Authenticate, [:Api, :AppName, :V1]) # => Api::AppName::V1::Authenticate
|
36
|
-
# to_constantized_class(:Authenticate, { :api :Api, app_name: :AppName, api_version: :V2 })
|
37
|
-
# # => Api::AppName::V2::Authenticate
|
38
|
-
# to_constantized_class("authenticate", { :api :api, app_name: :app_name, api_version: :v1 },
|
39
|
-
# { class_titleize: true, module_titleize: true }) # => Api::AppName::V1::Authenticate
|
40
|
-
#
|
41
|
-
def to_constantized_class(klass, klass_modules = [], options = {})
|
42
|
-
constantized_class_string = to_constantized_class_string(klass, klass_modules, options)
|
43
|
-
|
44
|
-
begin
|
45
|
-
constantized_class_string.constantize
|
46
|
-
rescue StandardError
|
47
|
-
raise NameError, "\"#{constantized_class_string}\" is not a valid class constant."
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Returns a fully-qualified constantized class (as a string), given the klass and klass_modules.
|
52
|
-
#
|
53
|
-
# @param [Symbol or String] klass the class name.
|
54
|
-
# @param [Hash, Array or String] klass_modules the modules klass belongs to.
|
55
|
-
# @param [Hash] options the options that determine how klass_modules is transformed.
|
56
|
-
# @option options [Boolean] :class_titleize (false) Determines whether or not klass should be
|
57
|
-
# titleized.
|
58
|
-
# @option options [Boolean] :module_titleize (false) Determines whether or not klass_modules
|
59
|
-
# should be titleized.
|
60
|
-
#
|
61
|
-
# @return [String] the fully qualified class, which includes module(s) and class name.
|
62
|
-
#
|
63
|
-
# @example
|
64
|
-
#
|
65
|
-
# to_constantized_class_string("Authenticate", "Api") # => "Api::Authenticate"
|
66
|
-
# to_constantized_class_string(:Authenticate, [:Api, :AppName, :V1]) # => "Api::AppName::V1::Authenticate"
|
67
|
-
# to_constantized_class_string(:Authenticate, { :api :Api, app_name: :AppName, api_version: :V2 })
|
68
|
-
# # => "Api::AppName::V2::Authenticate"
|
69
|
-
# to_constantized_class_string("authenticate", { :api :api, app_name: :app_name, api_version: :v1 },
|
70
|
-
# { class_titleize: true, module_titleize: true }) # => "Api::AppName::V1::Authenticate"
|
71
|
-
#
|
72
|
-
def to_constantized_class_string(klass, klass_modules = [], options = {})
|
73
|
-
options = ensure_options(options)
|
74
|
-
klass_modules = to_modules_string(klass_modules, options)
|
75
|
-
klass_string = to_class_string(klass, options)
|
76
|
-
"#{klass_modules}#{klass_string}"
|
77
|
-
end
|
78
|
-
|
79
|
-
# Returns a string of modules that can be subsequently prepended to a class, to create a constantized class.
|
80
|
-
#
|
81
|
-
# @param [Hash, Array or String] klass_modules the modules a class belongs to.
|
82
|
-
# @param [Hash] options the options that determine how klass_modules is transformed.
|
83
|
-
# @option options [Boolean] :module_titleize (false) Determines whether or not klass_modules should be titleized.
|
84
|
-
#
|
85
|
-
# @return [String] a string of modules that can be subsequently prepended to a class, to create a
|
86
|
-
# constantized class.
|
87
|
-
#
|
88
|
-
# @raise [ArgumentError] if the klass_modules is not of type String, Hash or Array.
|
89
|
-
#
|
90
|
-
# @example
|
91
|
-
#
|
92
|
-
# to_modules_string("Api") # => "Api::"
|
93
|
-
# to_modules_string([:Api, :AppName, :V1]) # => "Api::AppName::V1::"
|
94
|
-
# to_modules_string({ :api :Api, app_name: :AppName, api_version: :V1 }) # => "Api::AppName::V1::"
|
95
|
-
# to_modules_string({ :api :api, app_name: :app_name, api_version: :v1 }, { module_titleize: true })
|
96
|
-
# # => "Api::AppName::V1::"
|
97
|
-
#
|
98
|
-
def to_modules_string(klass_modules = [], options = {}) # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
99
|
-
klass_modules = validate_klass_modules(klass_modules)
|
100
|
-
|
101
|
-
options = ensure_options(options)
|
102
|
-
|
103
|
-
klass_modules_string = ''
|
104
|
-
unless klass_modules.empty?
|
105
|
-
case klass_modules
|
106
|
-
when String
|
107
|
-
klass_modules_string = klass_modules
|
108
|
-
when Array
|
109
|
-
klass_modules_string = klass_modules.join('::').to_s
|
110
|
-
when Hash
|
111
|
-
klass_modules_string = ''
|
112
|
-
klass_modules.to_a.each_with_index.map do |value, index|
|
113
|
-
klass_modules_string = index.zero? ? value[1].to_s : "#{klass_modules_string}::#{value[1]}"
|
114
|
-
end
|
115
|
-
else
|
116
|
-
raise ArgumentError, 'Class modules is not a String, Hash or Array.'
|
117
|
-
end
|
118
|
-
klass_modules_string = klass_modules_string.split('::').map(&:titleize).join('::') if options[:module_titleize]
|
119
|
-
klass_modules_string = camelize(klass_modules_string) if options[:module_camelize]
|
120
|
-
klass_modules_string = klass_modules_string.trim_all
|
121
|
-
klass_modules_string = "#{klass_modules_string}::" unless klass_modules_string.empty?
|
122
|
-
end
|
123
|
-
|
124
|
-
klass_modules_string
|
125
|
-
end
|
126
|
-
|
127
|
-
# Returns the klass as a string after transformations have been applied.
|
128
|
-
#
|
129
|
-
# @param [Symbol or String] klass the class name to be transformed.
|
130
|
-
# @param [Hash] options the options that determine how klass will be transformed.
|
131
|
-
# @option options [Boolean] :class_titleize (false) Determines whether or not klass should be titleized.
|
132
|
-
#
|
133
|
-
# @return [String] the transformed class as a string.
|
134
|
-
#
|
135
|
-
# @example
|
136
|
-
#
|
137
|
-
# to_class_string("MyClass") # => "MyClass"
|
138
|
-
# to_class_string("myClass", { class_titleize: true }) # => "MyClass"
|
139
|
-
# to_class_string(:MyClass) # => "MyClass"
|
140
|
-
# to_class_string(:myClass, { class_titleize: true }) # => "MyClass"
|
141
|
-
#
|
142
|
-
def to_class_string(klass, options = {})
|
143
|
-
klass = validate_klass(klass, options)
|
144
|
-
klass = klass.titleize if options[:class_titleize]
|
145
|
-
klass = camelize(klass) if options[:class_camelize]
|
146
|
-
klass
|
147
|
-
end
|
148
|
-
|
149
|
-
# Transforms a route into a module string
|
150
|
-
#
|
151
|
-
# @return [String] the camelized token.
|
152
|
-
#
|
153
|
-
# @example
|
154
|
-
#
|
155
|
-
# camelize("/api/app/auth/v1") # => "Api::App::Auth::V1"
|
156
|
-
# camelize("/api/app_name/auth/v1") # => "Api::AppName::Auth::V1"
|
157
|
-
#
|
158
|
-
def camelize(token)
|
159
|
-
raise ArgumentError, 'Token is not a String' unless token.instance_of? String
|
160
|
-
|
161
|
-
token.titlecase.camelize.sub(/^:*/, '').trim_all unless token.empty?
|
162
|
-
end
|
163
|
-
|
164
|
-
private
|
165
|
-
|
166
|
-
# @!visibility public
|
167
|
-
#
|
168
|
-
# Ensures options are initialized and valid before accessing them.
|
169
|
-
#
|
170
|
-
# @param [Hash] options the options that determine how processing and transformations will be handled.
|
171
|
-
# @option options [Boolean] :camelize (false) determines whether or not both class and module names
|
172
|
-
# should be camelized.
|
173
|
-
# @option options [Boolean] :titleize (false) determines whether or not both class and module names
|
174
|
-
# should be titleized.
|
175
|
-
# @option options [Boolean] :class_titleize (false) determines whether or not class names should be titleized.
|
176
|
-
# @option options [Boolean] :module_titleize (false) determines whether or not module names should be titleized.
|
177
|
-
# @option options [Boolean] :class_camelized (false) determines whether or not class names should be camelized.
|
178
|
-
# @option options [Boolean] :module_camelized (false) determines whether or not module names should be camelized.
|
179
|
-
#
|
180
|
-
# @return [Hash] the initialized, validated options.
|
181
|
-
#
|
182
|
-
def ensure_options(options)
|
183
|
-
options = {} unless options.instance_of? Hash
|
184
|
-
options = { camelize: false, titleize: false, class_titleize: false, module_titleize: false,
|
185
|
-
class_camelize: false, module_camelize: false }.merge(options)
|
186
|
-
|
187
|
-
options[:class_camelize] = options[:module_camelize] = true if options[:camelize]
|
188
|
-
|
189
|
-
options[:class_titleize] = options[:module_titleize] = true if options[:titleize]
|
190
|
-
|
191
|
-
options
|
192
|
-
end
|
193
|
-
|
194
|
-
# @!visibility public
|
195
|
-
#
|
196
|
-
# Validates klass and returns klass as a string after all blanks have been removed using klass.gsub(/\s+/, "").
|
197
|
-
#
|
198
|
-
# @param [Symbol or String] klass the class name to be validated. klass cannot be empty?
|
199
|
-
#
|
200
|
-
# @return [String] the validated class as a string with blanks removed.
|
201
|
-
#
|
202
|
-
# @raise [ArgumentError] if the klass is empty? or not of type String or Symbol.
|
203
|
-
#
|
204
|
-
# @example
|
205
|
-
#
|
206
|
-
# validate_klass(" My Class ") # => "MyClass"
|
207
|
-
# validate_klass(:MyClass) # => "MyClass"
|
208
|
-
#
|
209
|
-
def validate_klass(klass, _options)
|
210
|
-
unless klass.is_a?(Symbol) || klass.is_a?(String)
|
211
|
-
raise ArgumentError,
|
212
|
-
'Class is not a String or Symbol. Class must equal the class name of the ' \
|
213
|
-
'SimpleCommand or Command to call in the form of a String or Symbol.'
|
214
|
-
end
|
215
|
-
|
216
|
-
klass = klass.to_s.strip
|
217
|
-
|
218
|
-
raise ArgumentError, 'Class is empty?' if klass.empty?
|
219
|
-
|
220
|
-
klass
|
221
|
-
end
|
222
|
-
|
223
|
-
# @!visibility public
|
224
|
-
#
|
225
|
-
# Validates and returns klass_modules.
|
226
|
-
#
|
227
|
-
# @param [Symbol, Array or String] klass_modules the module(s) to be validated.
|
228
|
-
#
|
229
|
-
# @return [Symbol, Array or String] the validated module(s).
|
230
|
-
#
|
231
|
-
# @raise [ArgumentError] if the klass_modules is not of type String, Hash or Array.
|
232
|
-
#
|
233
|
-
# @example
|
234
|
-
#
|
235
|
-
# validate_modules(" Module ") # => " Module "
|
236
|
-
# validate_modules(:Module) # => "Module"
|
237
|
-
# validate_module("ModuleA::ModuleB") # => "ModuleA::ModuleB"
|
238
|
-
#
|
239
|
-
def validate_klass_modules(klass_modules)
|
240
|
-
return {} if klass_modules.nil? || (klass_modules.respond_to?(:empty?) && klass_modules.empty?)
|
241
|
-
|
242
|
-
if !klass_modules.instance_of?(String) && !klass_modules.instance_of?(Hash) && !klass_modules.instance_of?(Array)
|
243
|
-
raise ArgumentError, 'Class modules is not a String, Hash or Array.'
|
244
|
-
end
|
245
|
-
|
246
|
-
klass_modules
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
# rubocop:enable Style/OptionHash
|