simple_command_dispatcher 1.1.1
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.mb +32 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +210 -0
- data/Rakefile +18 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/core_extensions/string.rb +5 -0
- data/lib/simple_command_dispatcher.rb +93 -0
- data/lib/simple_command_dispatcher/klass_transform.rb +240 -0
- data/lib/simple_command_dispatcher/version.rb +5 -0
- data/lib/tasks/simple_command_dispatcher_sandbox.rake +232 -0
- data/simple_command_dispatcher.gemspec +48 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 978601fab3ba1ceb4f717b3b4d1d6fb13ea649f0
|
4
|
+
data.tar.gz: ceae85997bb18d8479845f89bf324c4d15fd4eb6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7df1d6310235fbe4ea04e82f95f7a00d40e5790ddaf8f6a4a1722cb59b642b2ba6b209231b4497ed81edfc0da9d214bb62e6bbf307b9ff415f2bfea53f7e01b6
|
7
|
+
data.tar.gz: 4272706508300aa3a2e7d0760c717e5621d4db0c73cab6736eb0a5f197c192c0b4696371bc307e8882ee52d6645691036c848a8ae761c52691bb2e9004ad8b97
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.travis.yml
ADDED
data/CHANGELOG.mb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
### Unreleased
|
2
|
+
* Upcoming features
|
3
|
+
* whitelist for allowable namespaces
|
4
|
+
|
5
|
+
### 1.1.1
|
6
|
+
* Documentation updates
|
7
|
+
* Add example code in README.md to include clarification on command namespacing, and how to autoload command classes to avoid NameError exceptions when SimpleCommand::Dispatcher.call(...) is call due to uninitialized command constants.
|
8
|
+
|
9
|
+
### 1.1.0
|
10
|
+
* Bug fix lib/core_extensions/string.rb not included in SimpleCommand::Dispatcher, causing exception.
|
11
|
+
|
12
|
+
### 1.0.0
|
13
|
+
* Limit support to ruby >= 2.2.2
|
14
|
+
|
15
|
+
### 0.2.0
|
16
|
+
* Yanked
|
17
|
+
|
18
|
+
### 0.1.3
|
19
|
+
* Documentation updates
|
20
|
+
* Add `SimpleCommand::KlassTransform` rake task sandbox to test the below listed `SimpleCommand::KlassTransform` members; run the rake tasks for usage; these rake tasks are simply a playground to see how simple_command_dispatcher transforms parameters into output when calling SimpleCommand::Dispatcher.call(...):
|
21
|
+
* `SimpleCommand::KlassTransform#to_class_string`
|
22
|
+
* $ rake to_class_string
|
23
|
+
* `SimpleCommand::KlassTransform#to_modules_string`
|
24
|
+
* $ rake to_modules_string
|
25
|
+
* `SimpleCommand::KlassTransform#to_constantized_class_string`
|
26
|
+
* $ rake to_constantized_class_string
|
27
|
+
|
28
|
+
### 0.1.2
|
29
|
+
* Documentation updates
|
30
|
+
|
31
|
+
### 0.1.1
|
32
|
+
* Documentation updates
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at web.gma@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 gangelo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
[](https://badge.fury.io/gh/gangelo%2Fsimple_command_dispatcher)
|
2
|
+
[](https://badge.fury.io/rb/simple_command_dispatcher)
|
3
|
+
|
4
|
+
# Q. simple_command_dispatcher - what is it?
|
5
|
+
# A. It's a Ruby gem!
|
6
|
+
|
7
|
+
## Overview
|
8
|
+
__simple_command_dispatcher__ (SCD) allows you to execute __simple_command__ commands in a more dynamic way. If you are not familiar with the _simple_command_ gem, check it out [here][simple-command]. SCD was written specifically with the [rails-api][rails-api] in mind; however, you can use SDC wherever you use simple_command commands.
|
9
|
+
|
10
|
+
## Example
|
11
|
+
The below example is from a `rails-api` API that uses token-based authentication and services two mobile applications, identified as *__my_app1__* and *__my_app2__*, in this example.
|
12
|
+
|
13
|
+
This example assumes the following:
|
14
|
+
|
15
|
+
* `application_controller` is a base class, inherited by all other controllers. The `#authenticate_request` method is called for every request in order to make sure the request is authorized (`before_action :authenticate_request`).
|
16
|
+
* `request.headers` will contain the authorization token to authorize all requests (`request.headers["Authorization"]`)
|
17
|
+
* This application uses the following folder structure to manage its _simple_command_ commands:
|
18
|
+
|
19
|
+

|
20
|
+
|
21
|
+
Command classes (and the modules they reside under) are named *__according to their file name and respective location within the above folder structure__*; for example, the command class defined in the `/api/my_app1/v1/authenticate_request.rb` file would be defined in this manner:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# /api/my_app1/v1/authenticate_request.rb
|
25
|
+
|
26
|
+
module Api
|
27
|
+
module MyApp1
|
28
|
+
module V1
|
29
|
+
class AuthenticateRequest
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
Likewise, the command class defined in the `/api/my_app2/v2/update_user.rb` file would be defined in this manner, and so on:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
# /api/my_app2/v2/update_user.rb
|
40
|
+
|
41
|
+
module Api
|
42
|
+
module MyApp2
|
43
|
+
module V2
|
44
|
+
class UpdateUser
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
The __routes used in this example__, conform to the following format: `"/api/[app_name]/[app_version]/[controller]"` where `[app_name]` = the _application name_,`[app_version]` = the _application version_, and `[controller]` = the _controller_; therefore, running `$ rake routes` for this example would output the following sample route information:
|
52
|
+
|
53
|
+
|
54
|
+
| Prefix | Verb | URI Pattern | Controller#Action |
|
55
|
+
|-------------:|:-------------|:------------------|:------------------|
|
56
|
+
| api_my_app1_v1_user_authenticate | POST | /api/my_app1/v1/user/authenticate(.:format) | api/my_app1/v1/authentication#create |
|
57
|
+
| api_my_app1_v2_user_authenticate | POST | /api/my_app1/v2/user/authenticate(.:format) | api/my_app1/v2/authentication#create |
|
58
|
+
| api_my_app2_v1_user_authenticate | POST | /api/my_app2/v1/user/authenticate(.:format) | api/my_app2/v1/authentication#create |
|
59
|
+
| api_my_app2_v2_user | PATCH | /api/my_app2/v2/users/:id(.:format) | api/my_app2/v2/users#update |
|
60
|
+
| | PUT | /api/my_app2/v2/users/:id(.:format) | api/my_app2/v2/users#update |
|
61
|
+
|
62
|
+
|
63
|
+
### Request Authentication Code Snippet
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# /config/initializers/simple_command_dispatcher.rb
|
67
|
+
|
68
|
+
# See: http://pothibo.com/2013/07/namespace-stuff-in-your-app-folder/
|
69
|
+
|
70
|
+
=begin
|
71
|
+
# Uncomment this code if you want to namespace your commands in the following manner, for example:
|
72
|
+
#
|
73
|
+
# class Api::MyApp1::V1::AuthenticateRequest; end
|
74
|
+
#
|
75
|
+
# As opposed to this:
|
76
|
+
#
|
77
|
+
# module Api
|
78
|
+
# module MyApp1
|
79
|
+
# module V1
|
80
|
+
# class AuthenticateRequest
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
module Helpers
|
87
|
+
def self.ensure_namespace(namespace, scope = "::")
|
88
|
+
namespace_parts = namespace.split("::")
|
89
|
+
|
90
|
+
namespace_chain = ""
|
91
|
+
|
92
|
+
namespace_parts.each { | part |
|
93
|
+
namespace_chain = (namespace_chain.empty?) ? part : "#{namespace_chain}::#{part}"
|
94
|
+
eval("module #{scope}#{namespace_chain}; end")
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
Helpers.ensure_namespace("Api::MyApp1::V1")
|
100
|
+
Helpers.ensure_namespace("Api::MyApp1::V2")
|
101
|
+
Helpers.ensure_namespace("Api::MyApp2::V1")
|
102
|
+
Helpers.ensure_namespace("Api::MyApp2::V2")
|
103
|
+
=end
|
104
|
+
|
105
|
+
# simple_command_dispatcher creates commands dynamically; therefore we need
|
106
|
+
# to make sure the namespaces and command classes are loaded before we construct and
|
107
|
+
# call them. The below code traverses the 'app/api' and all subfolders, and
|
108
|
+
# autoloads them so that we do not get any NameError exceptions due to
|
109
|
+
# uninitialized constants.
|
110
|
+
Rails.application.config.to_prepare do
|
111
|
+
path = Rails.root + "app/api"
|
112
|
+
ActiveSupport::Dependencies.autoload_paths -= [path.to_s]
|
113
|
+
|
114
|
+
reloader = ActiveSupport::FileUpdateChecker.new [], path.to_s => [:rb] do
|
115
|
+
ActiveSupport::DescendantsTracker.clear
|
116
|
+
ActiveSupport::Dependencies.clear
|
117
|
+
|
118
|
+
Dir[path + "**/*.rb"].each do |file|
|
119
|
+
ActiveSupport.require_or_load file
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
Rails.application.reloaders << reloader
|
124
|
+
ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
|
125
|
+
reloader.execute
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
# /app/controllers/application_controller.rb
|
131
|
+
|
132
|
+
require 'simple_command_dispatcher'
|
133
|
+
|
134
|
+
class ApplicationController < ActionController::API
|
135
|
+
before_action :authenticate_request
|
136
|
+
attr_reader :current_user
|
137
|
+
|
138
|
+
protected
|
139
|
+
|
140
|
+
def get_command_path
|
141
|
+
# request.env['PATH_INFO'] could return any number of paths. The important
|
142
|
+
# thing (in the case of our example), is that we get the portion of the
|
143
|
+
# path that uniquely identifies the SimpleCommand we need to call; this
|
144
|
+
# would include the application, the API version and the SimpleCommand
|
145
|
+
# name itself.
|
146
|
+
command_path = request.env['PATH_INFO'] # => "/api/[app name]/v1/[action]”
|
147
|
+
command_path = command_path.split('/').slice(0,4).join('/') # => "/api/[app name]/v1/"
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def authenticate_request
|
153
|
+
# The parameters and options we are passing to the dispatcher, wind up equating
|
154
|
+
# to the following: Api::MyApp1::V1::AuthenticateRequest.call(request.headers).
|
155
|
+
# Explaination: @param command_modules (e.g. path, "/api/my_app1/v1/"), in concert with @param
|
156
|
+
# options { camelize: true }, is transformed into "Api::MyApp1::V1" and prepended to the
|
157
|
+
# @param command, which becomes "Api::MyApp1::V1::AuthenticateRequest." This string is then
|
158
|
+
# simply constantized; #call is then executed, passing the @param command_parameters
|
159
|
+
# (e.g. request.headers, which contains ["Authorization"], out authorization token).
|
160
|
+
# Consequently, the correlation between our routes and command class module structure
|
161
|
+
# was no coincidence.
|
162
|
+
command = SimpleCommand::Dispatcher.call(:AuthenticateRequest, get_command_path, { camelize: true}, request.headers)
|
163
|
+
if command.success?
|
164
|
+
@current_user = command.result
|
165
|
+
else
|
166
|
+
render json: { error: 'Not Authorized' }, status: 401
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
|
173
|
+
## Installation
|
174
|
+
|
175
|
+
Add this line to your application's Gemfile:
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
gem 'simple_command_dispatcher'
|
179
|
+
```
|
180
|
+
|
181
|
+
And then execute:
|
182
|
+
|
183
|
+
$ bundle
|
184
|
+
|
185
|
+
Or install it yourself as:
|
186
|
+
|
187
|
+
$ gem install simple_command_dispatcher
|
188
|
+
|
189
|
+
## Usage
|
190
|
+
|
191
|
+
See the example above.
|
192
|
+
|
193
|
+
## Development
|
194
|
+
|
195
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
196
|
+
|
197
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
198
|
+
|
199
|
+
## Contributing
|
200
|
+
|
201
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/gangelo/simple_command_dispatcher. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
202
|
+
|
203
|
+
|
204
|
+
## License
|
205
|
+
|
206
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
207
|
+
|
208
|
+
[simple-command]: <https://rubygems.org/gems/simple_command>
|
209
|
+
[rails-api]: <https://rubygems.org/gems/rails-api>
|
210
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require 'yard'
|
4
|
+
|
5
|
+
# Rspec
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
task default: :spec
|
8
|
+
|
9
|
+
|
10
|
+
# Yard
|
11
|
+
YARD::Rake::YardocTask.new do |t|
|
12
|
+
t.files = ['lib/**/*.rb']
|
13
|
+
t.options = ['--no-cache', '--protected', '--private']
|
14
|
+
t.stats_options = ['--list-undoc']
|
15
|
+
end
|
16
|
+
|
17
|
+
# Load our custom rake tasks.
|
18
|
+
Gem.find_files("tasks/**/*.rake").each { | path | import path }
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "simple_command_dispatcher"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require "simple_command_dispatcher/version"
|
2
|
+
require "simple_command_dispatcher/klass_transform"
|
3
|
+
require "simple_command"
|
4
|
+
require "active_support/core_ext/string/inflections"
|
5
|
+
|
6
|
+
module Kernel
|
7
|
+
def eigenclass
|
8
|
+
class << self
|
9
|
+
self
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module SimpleCommand
|
15
|
+
|
16
|
+
# Provides a way to call SimpleCommand commands in a more dymanic manner.
|
17
|
+
#
|
18
|
+
# For information about the simple_command gem, visit https://rubygems.org/gems/simple_command
|
19
|
+
#
|
20
|
+
module Dispatcher
|
21
|
+
|
22
|
+
class << self
|
23
|
+
include SimpleCommand::KlassTransform
|
24
|
+
|
25
|
+
public
|
26
|
+
|
27
|
+
# Calls a *SimpleCommand* given the command name, the modules the command belongs to and the parameters to pass to the command.
|
28
|
+
#
|
29
|
+
# @param command [Symbol, String] the name of the SimpleCommand to call.
|
30
|
+
#
|
31
|
+
# @param command_modules [Hash, Array] the ruby modules that qualify the SimpleCommand to call. When passing a Hash, the Hash
|
32
|
+
# keys serve as documentation only. For example, ['Api', 'AppName', 'V1'] and { :api :Api, app_name: :AppName, api_version: :V1 }
|
33
|
+
# will both produce 'Api::AppName::V1', this string will be prepended to the command to form the SimpleCommand to call
|
34
|
+
# (e.g. 'Api::AppName::V1::MySimpleCommand' = Api::AppName::V1::MySimpleCommand.call(*command_parameters)).
|
35
|
+
#
|
36
|
+
# @param [Hash] options the options that determine how command and command_module are transformed.
|
37
|
+
# @option options [Boolean] :camelize (false) determines whether or not both class and module names should be camelized.
|
38
|
+
# @option options [Boolean] :titleize (false) determines whether or not both class and module names should be titleized.
|
39
|
+
# @option options [Boolean] :class_titleize (false) determines whether or not class names should be titleized.
|
40
|
+
# @option options [Boolean] :class_camelized (false) determines whether or not class names should be camelized.
|
41
|
+
# @option options [Boolean] :module_titleize (false) determines whether or not module names should be titleized.
|
42
|
+
# @option options [Boolean] :module_camelized (false) determines whether or not module names should be camelized.
|
43
|
+
#
|
44
|
+
# @param command_parameters [*] the parameters to pass to the call method of the SimpleCommand. This parameter is simply
|
45
|
+
# passed through to the call method of the SimpleCommand.
|
46
|
+
#
|
47
|
+
# @return [SimpleCommand] the SimpleCommand returned as a result from calling the SimpleCommand#call method.
|
48
|
+
#
|
49
|
+
# @example
|
50
|
+
#
|
51
|
+
# # Below call equates to the following: Api::Carz4Rent::V1::Authenticate.call({ email: 'sam@gmail.com', password: 'AskM3!' })
|
52
|
+
# SimpleCommand::Dispatcher.call(:Authenticate, { api: :Api, app_name: :Carz4Rent, api_version: :V1 },
|
53
|
+
# { email: 'sam@gmail.com', password: 'AskM3!' } ) # => SimpleCommand result
|
54
|
+
#
|
55
|
+
# # Below equates to the following: Api::Carz4Rent::V2::Authenticate.call('sam@gmail.com', 'AskM3!')
|
56
|
+
# SimpleCommand::Dispatcher.call(:Authenticate, ['Api', 'Carz4Rent', 'V2'], 'sam@gmail.com', 'AskM3!') # => SimpleCommand result
|
57
|
+
#
|
58
|
+
# # Below equates to the following: Api::Auth::JazzMeUp::V1::Authenticate.call('jazz_me@gmail.com', 'JazzM3!')
|
59
|
+
# SimpleCommand::Dispatcher.call(:Authenticate, ['Api::Auth::JazzMeUp', :V1], 'jazz_me@gmail.com', 'JazzM3!') # => SimpleCommand result
|
60
|
+
#
|
61
|
+
def call(command = "", command_modules = {}, options = {}, *command_parameters)
|
62
|
+
|
63
|
+
# Create a constantized class from our command and command_modules...
|
64
|
+
simple_command_class_constant = to_constantized_class(command, command_modules, options)
|
65
|
+
|
66
|
+
# Calling is_simple_command? returns true if the class pointed to by
|
67
|
+
# simple_command_class_constant is a valid SimpleCommand class; that is,
|
68
|
+
# if it prepends module SimpleCommand::ClassMethods.
|
69
|
+
if !is_simple_command?(simple_command_class_constant)
|
70
|
+
raise ArgumentError.new('Class does not prepend module SimpleCommand.')
|
71
|
+
end
|
72
|
+
|
73
|
+
# We know we have a valid SimpleCommand; all we need to do is call #call,
|
74
|
+
# pass the command_parameter variable arguments to the call, and return the results.
|
75
|
+
simple_command_class_constant.call(*command_parameters)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# @!visibility public
|
81
|
+
#
|
82
|
+
# Returns true or false depending on whether or not the class constant prepends module SimpleCommand::ClassMethods.
|
83
|
+
#
|
84
|
+
# @param klass_constant [String] a class constant that will be validated to see whether or not the class prepends module SimpleCommand::ClassMethods.
|
85
|
+
#
|
86
|
+
# @return [Boolean] true if klass_constant prepends Module SimpleCommand::ClassMethods, false otherwise.
|
87
|
+
#
|
88
|
+
def is_simple_command?(klass_constant)
|
89
|
+
klass_constant.eigenclass.included_modules.include? SimpleCommand::ClassMethods
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
require_relative '../core_extensions/string'
|
2
|
+
|
3
|
+
module SimpleCommand
|
4
|
+
|
5
|
+
# Handles class and module transformations.
|
6
|
+
module KlassTransform
|
7
|
+
|
8
|
+
# Returns a constantized class (as a Class constant), given the klass and klass_modules.
|
9
|
+
#
|
10
|
+
# @param klass [Symbol or String] the class name.
|
11
|
+
# @param klass_modules [Hash, Array or String] the modules klass belongs to.
|
12
|
+
# @param options [Hash] the options that determine how klass_modules is transformed.
|
13
|
+
# @option options [Boolean] :camelize (false) determines whether or not both klass and klass_modules should be camelized.
|
14
|
+
# @option options [Boolean] :titleize (false) determines whether or not both klass and klass_modules should be titleized.
|
15
|
+
# @option options [Boolean] :class_titleize (false) determines whether or not klass names should be titleized.
|
16
|
+
# @option options [Boolean] :class_camelized (false) determines whether or not klass names should be camelized.
|
17
|
+
# @option options [Boolean] :module_titleize (false) determines whether or not klass_modules names should be titleized.
|
18
|
+
# @option options [Boolean] :module_camelized (false) determines whether or not klass_modules names should be camelized.
|
19
|
+
#
|
20
|
+
# @return [Class] the class constant. Can be used to call ClassConstant.constantize.
|
21
|
+
#
|
22
|
+
# @raise [NameError] if the constantized class string cannot be constantized; that is, if it is not
|
23
|
+
# a valid class constant.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
#
|
27
|
+
# to_constantized_class("Authenticate", "Api") # => Api::Authenticate
|
28
|
+
# to_constantized_class(:Authenticate, [:Api, :AppName, :V1]) # => Api::AppName::V1::Authenticate
|
29
|
+
# to_constantized_class(:Authenticate, { :api :Api, app_name: :AppName, api_version: :V2 }) # => Api::AppName::V2::Authenticate
|
30
|
+
# to_constantized_class("authenticate", { :api :api, app_name: :app_name, api_version: :v1 }, { class_titleize: true, module_titleize: true }) # => Api::AppName::V1::Authenticate
|
31
|
+
#
|
32
|
+
def to_constantized_class(klass, klass_modules = [], options = {})
|
33
|
+
constantized_class_string = to_constantized_class_string(klass, klass_modules, options)
|
34
|
+
|
35
|
+
begin
|
36
|
+
constantized_class_string.constantize
|
37
|
+
rescue
|
38
|
+
raise NameError.new("\"#{constantized_class_string}\" is not a valid class constant.")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a fully-qualified constantized class (as a string), given the klass and klass_modules.
|
43
|
+
#
|
44
|
+
# @param [Symbol or String] klass the class name.
|
45
|
+
# @param [Hash, Array or String] klass_modules the modules klass belongs to.
|
46
|
+
# @param [Hash] options the options that determine how klass_modules is transformed.
|
47
|
+
# @option options [Boolean] :class_titleize (false) Determines whether or not klass should be titleized.
|
48
|
+
# @option options [Boolean] :module_titleize (false) Determines whether or not klass_modules should be titleized.
|
49
|
+
#
|
50
|
+
# @return [String] the fully qualified class, which includes module(s) and class name.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
#
|
54
|
+
# to_constantized_class_string("Authenticate", "Api") # => "Api::Authenticate"
|
55
|
+
# to_constantized_class_string(:Authenticate, [:Api, :AppName, :V1]) # => "Api::AppName::V1::Authenticate"
|
56
|
+
# to_constantized_class_string(:Authenticate, { :api :Api, app_name: :AppName, api_version: :V2 }) # => "Api::AppName::V2::Authenticate"
|
57
|
+
# to_constantized_class_string("authenticate", { :api :api, app_name: :app_name, api_version: :v1 }, { class_titleize: true, module_titleize: true }) # => "Api::AppName::V1::Authenticate"
|
58
|
+
#
|
59
|
+
def to_constantized_class_string(klass, klass_modules = [], options = {})
|
60
|
+
options = ensure_options(options)
|
61
|
+
klass_modules = to_modules_string(klass_modules, options)
|
62
|
+
klass_string = to_class_string(klass, options)
|
63
|
+
"#{klass_modules}#{klass_string}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns a string of modules that can be subsequently prepended to a class, to create a constantized class.
|
67
|
+
#
|
68
|
+
# @param [Hash, Array or String] klass_modules the modules a class belongs to.
|
69
|
+
# @param [Hash] options the options that determine how klass_modules is transformed.
|
70
|
+
# @option options [Boolean] :module_titleize (false) Determines whether or not klass_modules should be titleized.
|
71
|
+
#
|
72
|
+
# @return [String] a string of modules that can be subsequently prepended to a class, to create a constantized class.
|
73
|
+
#
|
74
|
+
# @raise [ArgumentError] if the klass_modules is not of type String, Hash or Array.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
#
|
78
|
+
# to_modules_string("Api") # => "Api::"
|
79
|
+
# to_modules_string([:Api, :AppName, :V1]) # => "Api::AppName::V1::"
|
80
|
+
# to_modules_string({ :api :Api, app_name: :AppName, api_version: :V1 }) # => "Api::AppName::V1::"
|
81
|
+
# to_modules_string({ :api :api, app_name: :app_name, api_version: :v1 }, { module_titleize: true }) # => "Api::AppName::V1::"
|
82
|
+
#
|
83
|
+
def to_modules_string(klass_modules = [], options = {})
|
84
|
+
klass_modules = validate_klass_modules(klass_modules)
|
85
|
+
|
86
|
+
options = ensure_options(options)
|
87
|
+
|
88
|
+
klass_modules_string = ''
|
89
|
+
if !klass_modules.empty?
|
90
|
+
case klass_modules
|
91
|
+
when String
|
92
|
+
klass_modules_string = klass_modules
|
93
|
+
when Array
|
94
|
+
klass_modules_string = "#{klass_modules.join('::')}"
|
95
|
+
when Hash
|
96
|
+
klass_modules_string = ''
|
97
|
+
klass_modules.to_a.each_with_index.map { | value, index |
|
98
|
+
klass_modules_string = index == 0 ? value[1].to_s : "#{klass_modules_string}::#{value[1]}"
|
99
|
+
}
|
100
|
+
else
|
101
|
+
raise ArgumentError.new('Class modules is not a String, Hash or Array.')
|
102
|
+
end
|
103
|
+
klass_modules_string = klass_modules_string.split('::').map(&:titleize).join('::') if options[:module_titleize]
|
104
|
+
klass_modules_string = camelize(klass_modules_string) if options[:module_camelize]
|
105
|
+
klass_modules_string = klass_modules_string.trim_all
|
106
|
+
klass_modules_string = "#{klass_modules_string}::" unless klass_modules_string.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
klass_modules_string
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns the klass as a string after transformations have been applied.
|
113
|
+
#
|
114
|
+
# @param [Symbol or String] klass the class name to be transformed.
|
115
|
+
# @param [Hash] options the options that determine how klass will be transformed.
|
116
|
+
# @option options [Boolean] :class_titleize (false) Determines whether or not klass should be titleized.
|
117
|
+
#
|
118
|
+
# @return [String] the transformed class as a string.
|
119
|
+
#
|
120
|
+
# @example
|
121
|
+
#
|
122
|
+
# to_class_string("MyClass") # => "MyClass"
|
123
|
+
# to_class_string("myClass", { class_titleize: true }) # => "MyClass"
|
124
|
+
# to_class_string(:MyClass) # => "MyClass"
|
125
|
+
# to_class_string(:myClass, { class_titleize: true }) # => "MyClass"
|
126
|
+
#
|
127
|
+
def to_class_string(klass, options = {})
|
128
|
+
klass = validate_klass(klass, options)
|
129
|
+
if options[:class_titleize]
|
130
|
+
klass = klass.titleize
|
131
|
+
end
|
132
|
+
if options[:class_camelize]
|
133
|
+
klass = camelize(klass)
|
134
|
+
end
|
135
|
+
klass
|
136
|
+
end
|
137
|
+
|
138
|
+
# Transforms a route into a module string
|
139
|
+
#
|
140
|
+
# @return [String] the camelized token.
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
#
|
144
|
+
# camelize("/api/app/auth/v1") # => "Api::App::Auth::V1"
|
145
|
+
# camelize("/api/app_name/auth/v1") # => "Api::AppName::Auth::V1"
|
146
|
+
#
|
147
|
+
def camelize(token)
|
148
|
+
if !token.instance_of? String
|
149
|
+
raise ArgumentError.new('Token is not a String')
|
150
|
+
end
|
151
|
+
token = token.titlecase.camelize.sub(/^[:]*/,"").trim_all unless token.empty?
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
# @!visibility public
|
157
|
+
#
|
158
|
+
# Ensures options are initialized and valid before accessing them.
|
159
|
+
#
|
160
|
+
# @param [Hash] options the options that determine how processing and transformations will be handled.
|
161
|
+
# @option options [Boolean] :camelize (false) determines whether or not both class and module names should be camelized.
|
162
|
+
# @option options [Boolean] :titleize (false) determines whether or not both class and module names should be titleized.
|
163
|
+
# @option options [Boolean] :class_titleize (false) determines whether or not class names should be titleized.
|
164
|
+
# @option options [Boolean] :module_titleize (false) determines whether or not module names should be titleized.
|
165
|
+
# @option options [Boolean] :class_camelized (false) determines whether or not class names should be camelized.
|
166
|
+
# @option options [Boolean] :module_camelized (false) determines whether or not module names should be camelized.
|
167
|
+
#
|
168
|
+
# @return [Hash] the initialized, validated options.
|
169
|
+
#
|
170
|
+
def ensure_options(options)
|
171
|
+
options = {} unless options.instance_of? Hash
|
172
|
+
options = { camelize: false, titleize: false, class_titleize: false, module_titleize: false, class_camelize: false, module_camelize: false}.merge(options)
|
173
|
+
|
174
|
+
if options[:camelize]
|
175
|
+
options[:class_camelize] = options[:module_camelize] = true
|
176
|
+
end
|
177
|
+
|
178
|
+
if options[:titleize]
|
179
|
+
options[:class_titleize] = options[:module_titleize] = true
|
180
|
+
end
|
181
|
+
|
182
|
+
options
|
183
|
+
end
|
184
|
+
|
185
|
+
# @!visibility public
|
186
|
+
#
|
187
|
+
# Validates klass and returns klass as a string after all blanks have been removed using klass.gsub(/\s+/, "").
|
188
|
+
#
|
189
|
+
# @param [Symbol or String] klass the class name to be validated. klass cannot be empty?
|
190
|
+
#
|
191
|
+
# @return [String] the validated class as a string with blanks removed.
|
192
|
+
#
|
193
|
+
# @raise [ArgumentError] if the klass is empty? or not of type String or Symbol.
|
194
|
+
#
|
195
|
+
# @example
|
196
|
+
#
|
197
|
+
# validate_klass(" My Class ") # => "MyClass"
|
198
|
+
# validate_klass(:MyClass) # => "MyClass"
|
199
|
+
#
|
200
|
+
def validate_klass(klass, options)
|
201
|
+
if !(klass.is_a?(Symbol) || klass.is_a?(String))
|
202
|
+
raise ArgumentError.new('Class is not a String or Symbol. Class must equal the name of the SimpleCommand to call in the form of a String or Symbol.')
|
203
|
+
end
|
204
|
+
|
205
|
+
klass = klass.to_s.strip
|
206
|
+
|
207
|
+
if klass.empty?
|
208
|
+
raise ArgumentError.new('Class is empty?')
|
209
|
+
end
|
210
|
+
|
211
|
+
klass
|
212
|
+
end
|
213
|
+
|
214
|
+
# @!visibility public
|
215
|
+
#
|
216
|
+
# Validates and returns klass_modules.
|
217
|
+
#
|
218
|
+
# @param [Symbol, Array or String] klass_modules the module(s) to be validated.
|
219
|
+
#
|
220
|
+
# @return [Symbol, Array or String] the validated module(s).
|
221
|
+
#
|
222
|
+
# @raise [ArgumentError] if the klass_modules is not of type String, Hash or Array.
|
223
|
+
#
|
224
|
+
# @example
|
225
|
+
#
|
226
|
+
# validate_modules(" Module ") # => " Module "
|
227
|
+
# validate_modules(:Module) # => "Module"
|
228
|
+
# validate_module("ModuleA::ModuleB") # => "ModuleA::ModuleB"
|
229
|
+
#
|
230
|
+
def validate_klass_modules(klass_modules)
|
231
|
+
return {} if klass_modules.nil? || (klass_modules.respond_to?(:empty?) && klass_modules.empty?)
|
232
|
+
|
233
|
+
if !klass_modules.instance_of?(String) && !klass_modules.instance_of?(Hash) && !klass_modules.instance_of?(Array)
|
234
|
+
raise ArgumentError.new('Class modules is not a String, Hash or Array.')
|
235
|
+
end
|
236
|
+
|
237
|
+
klass_modules
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'rake'
|
2
|
+
# Tasks to play with certain klass_transform module members
|
3
|
+
|
4
|
+
|
5
|
+
desc 'Test to_class_string'
|
6
|
+
task :to_class_string do
|
7
|
+
require "colorize"
|
8
|
+
require "simple_command_dispatcher"
|
9
|
+
|
10
|
+
class Test
|
11
|
+
prepend SimpleCommand::KlassTransform
|
12
|
+
end
|
13
|
+
|
14
|
+
puts "Testing SimpleCommand::KlassTransform#to_class_String...\n".cyan
|
15
|
+
puts "Enter a blank line to exit.".cyan
|
16
|
+
loop do
|
17
|
+
puts "\nUsage: \"-c [class], -o [options]\" where class = String or Symbol and options = Hash".cyan
|
18
|
+
puts "\n\t Examples:"
|
19
|
+
print "\n\t\t-c \"my_class\" -o { class_camelize: true }".cyan
|
20
|
+
print "\n\t\t-c :MyClass -o { class_camelize: false }".cyan
|
21
|
+
|
22
|
+
print "\n=> "
|
23
|
+
|
24
|
+
input = STDIN.gets.chomp
|
25
|
+
break if input.empty?
|
26
|
+
|
27
|
+
input = clean_input(input)
|
28
|
+
|
29
|
+
klass = get_option_class(input)
|
30
|
+
if klass.nil? || klass.empty?
|
31
|
+
print "=> Error: Class not a String or Symbol\n".red
|
32
|
+
next
|
33
|
+
elsif !klass.is_a?(String) && ! klass.is_a?(Symbol)
|
34
|
+
print "=> Error: Class not a String or Symbol\n".red
|
35
|
+
next
|
36
|
+
end
|
37
|
+
|
38
|
+
options_hash = get_options_hash(input)
|
39
|
+
|
40
|
+
options_hash = { class_camelize: true }.merge(options_hash || {})
|
41
|
+
|
42
|
+
puts "\nCalling to_class_string with the following parameters: class: #{klass.class}, options: #{options_hash.class}".cyan
|
43
|
+
|
44
|
+
print "\nSuccess: => #to_class_String(#{klass}, #{options_hash}) => #{Test.new.to_class_string(klass, options_hash)}\n".green
|
45
|
+
end
|
46
|
+
|
47
|
+
print
|
48
|
+
print 'Done'.yellow
|
49
|
+
print
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
desc 'Test to_modules_string'
|
54
|
+
task :to_modules_string do
|
55
|
+
require "colorize"
|
56
|
+
require "simple_command_dispatcher"
|
57
|
+
|
58
|
+
class Test
|
59
|
+
prepend SimpleCommand::KlassTransform
|
60
|
+
end
|
61
|
+
|
62
|
+
puts "Testing SimpleCommand::KlassTransform#to_modules_string...\n".cyan
|
63
|
+
puts "Enter a blank line to exit.".cyan
|
64
|
+
loop do
|
65
|
+
puts "\nUsage: \"-m [modules], -o [options]\" where modules = Hash, Array or String and options = Hash".cyan
|
66
|
+
puts "\n\t Examples:"
|
67
|
+
print "\n\t\t-m \"Module1::Module2::Module3\" -o { modules_camelize: false }".cyan
|
68
|
+
print "\n\t\t-m [:module1, :module2, :module3] -o { modules_camelize: true }".cyan
|
69
|
+
print "\n\t\t-m {api: :api, app: :crazy_buttons, version: :v1} -o { modules_camelize: true }".cyan
|
70
|
+
|
71
|
+
print "\n=> "
|
72
|
+
|
73
|
+
input = STDIN.gets.chomp
|
74
|
+
break if input.empty?
|
75
|
+
|
76
|
+
input = clean_input(input)
|
77
|
+
|
78
|
+
modules = get_option_modules(input)
|
79
|
+
p modules.class
|
80
|
+
if modules.nil? || modules.empty?
|
81
|
+
print "=> Error: Modules not a Hash, Array or String\n".red
|
82
|
+
next
|
83
|
+
elsif !modules.is_a?(Hash) && !modules.is_a?(Array) && !modules.is_a?(String)
|
84
|
+
print "=> Error: Modules not a Hash, Array or String\n".red
|
85
|
+
next
|
86
|
+
end
|
87
|
+
|
88
|
+
options_hash = get_options_hash(input)
|
89
|
+
options_hash = { module_camelize: true }.merge(options_hash || {})
|
90
|
+
|
91
|
+
puts "\nCalling to_modules_string with the following parameters: modules: #{modules}, type: #{modules.class}, options: #{options_hash}, type: #{options_hash.class}...".cyan
|
92
|
+
|
93
|
+
puts "\nSuccess: => #to_modules_string(#{modules}, #{options_hash}) => #{Test.new.to_modules_string(modules, options_hash)}\n".green
|
94
|
+
end
|
95
|
+
|
96
|
+
print
|
97
|
+
print 'Done'.yellow
|
98
|
+
print
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
#
|
104
|
+
#
|
105
|
+
#
|
106
|
+
|
107
|
+
desc 'Test to_constantized_class_string'
|
108
|
+
task :to_constantized_class_string do
|
109
|
+
require "colorize"
|
110
|
+
require "simple_command_dispatcher"
|
111
|
+
|
112
|
+
class Test
|
113
|
+
prepend SimpleCommand::KlassTransform
|
114
|
+
end
|
115
|
+
|
116
|
+
puts "Testing SimpleCommand::KlassTransform#to_constantized_class_string...\n".cyan
|
117
|
+
puts "Enter a blank line to exit.".cyan
|
118
|
+
loop do
|
119
|
+
puts "\nUsage: \"-c [class] -m [modules], -o [options]\" where class = Symbol or String, modules = Hash, Array or String and options = Hash".cyan
|
120
|
+
puts "\n\t Examples:"
|
121
|
+
print "\n\t\t-c :MyClass -m \"Module1::Module2::Module3\" -o { modules_camelize: false }".cyan
|
122
|
+
print "\n\t\t-c \"my_class\" -m [:module1, :module2, :module3] -o { modules_camelize: true }".cyan
|
123
|
+
print "\n\t\t-c :myClass -m {api: :api, app: :crazy_buttons, version: :v1} -o { modules_camelize: true }".cyan
|
124
|
+
|
125
|
+
print "\n=> "
|
126
|
+
|
127
|
+
input = STDIN.gets.chomp
|
128
|
+
break if input.empty?
|
129
|
+
|
130
|
+
input = clean_input(input)
|
131
|
+
|
132
|
+
klass = get_option_class(input)
|
133
|
+
if klass.nil? || klass.empty?
|
134
|
+
print "=> Error: Class not a String or Symbol\n".red
|
135
|
+
next
|
136
|
+
elsif !klass.is_a?(String) && ! klass.is_a?(Symbol)
|
137
|
+
print "=> Error: Class not a String or Symbol\n".red
|
138
|
+
next
|
139
|
+
end
|
140
|
+
|
141
|
+
modules = get_option_modules(input)
|
142
|
+
if modules.nil? || modules.empty?
|
143
|
+
print "=> Error: Modules not a Hash, Array or String\n".red
|
144
|
+
next
|
145
|
+
elsif !modules.is_a?(Hash) && !modules.is_a?(Array) && !modules.is_a?(String)
|
146
|
+
print "=> Error: Modules not a Hash, Array or String\n".red
|
147
|
+
next
|
148
|
+
end
|
149
|
+
|
150
|
+
options_hash = get_options_hash(input)
|
151
|
+
options_hash = { class_camelize: true, module_camelize: true }.merge(options_hash || {})
|
152
|
+
|
153
|
+
puts "\nCalling to_constantized_class_string with the following parameters: class: #{klass}, type: #{klass.class}, modules: #{modules}, type: #{modules.class}, options: #{options_hash}, type: #{options_hash.class}...".cyan
|
154
|
+
|
155
|
+
puts "\nSuccess: => #to_constantized_class_string(#{klass}, #{modules}, #{options_hash}) => #{Test.new.to_constantized_class_string(klass, modules, options_hash)}\n".green
|
156
|
+
end
|
157
|
+
|
158
|
+
print
|
159
|
+
print 'Done'.yellow
|
160
|
+
print
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
#
|
165
|
+
# Helper methods
|
166
|
+
#
|
167
|
+
|
168
|
+
def clean_input(input)
|
169
|
+
# Place a space before every dash that is not separated by a space.
|
170
|
+
input = input.gsub(/-c/, " -c ").gsub(/-m/, " -m ").gsub(/-o/, " -o ")
|
171
|
+
input = input.gsub(/"/, " \" ").gsub(/\s+/, " ").strip
|
172
|
+
|
173
|
+
puts "=> keyboard input after clean_input => #{input}".magenta
|
174
|
+
|
175
|
+
input
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_option_class(options)
|
179
|
+
if options.nil? || options.empty?
|
180
|
+
return ""
|
181
|
+
end
|
182
|
+
options_options = options[/-c\s*([:"].+?(["\s-]||$?))($|\s+|-)/m,1]
|
183
|
+
|
184
|
+
return "" if options_options.nil?
|
185
|
+
|
186
|
+
options_options = options_options.gsub(/("|')+/, "")
|
187
|
+
klass = options_options.strip.gsub(/\s+/, " ")
|
188
|
+
|
189
|
+
puts "=> class after get_option_class => #{klass}".magenta
|
190
|
+
|
191
|
+
klass
|
192
|
+
end
|
193
|
+
|
194
|
+
def get_options_hash(options)
|
195
|
+
if options.nil? || options.empty?
|
196
|
+
return {}
|
197
|
+
end
|
198
|
+
options_options = options[/-o\s*([{].+?([}\s-]$?))($|\s+|-)/m,1]
|
199
|
+
|
200
|
+
return {} if options_options.nil?
|
201
|
+
|
202
|
+
puts "=> options after get_options_hash => #{options_options}".magenta
|
203
|
+
|
204
|
+
begin
|
205
|
+
eval(options_options)
|
206
|
+
rescue
|
207
|
+
{}
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_option_modules(modules)
|
212
|
+
if modules.nil? || modules.empty?
|
213
|
+
return ""
|
214
|
+
end
|
215
|
+
|
216
|
+
extracted_modules = modules[/-m\s*([\[{"].+?(["}\]\s-]$?))($|\s+|-)/m,1]
|
217
|
+
|
218
|
+
return "" if extracted_modules.nil?
|
219
|
+
|
220
|
+
extracted_modules = extracted_modules.strip.gsub(/\s+/, " ")
|
221
|
+
|
222
|
+
puts "=> modules after get_option_modules => #{extracted_modules}".magenta
|
223
|
+
|
224
|
+
begin
|
225
|
+
eval(extracted_modules)
|
226
|
+
rescue
|
227
|
+
""
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'simple_command_dispatcher/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "simple_command_dispatcher"
|
8
|
+
spec.version = SimpleCommand::Dispatcher::VERSION
|
9
|
+
spec.authors = ["Gene M. Angelo, Jr."]
|
10
|
+
spec.email = ["public.gma@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Provides a way to dispatch simple_command ruby gem SimpleCommands (service objects) in a more dynamic manner within your service API. Ideal for rails-api.}
|
13
|
+
spec.description = %q{Within a services API (rails-api for instance), you may have a need to execute different SimpleCommands
|
14
|
+
based on one or more factors: multiple application, API version, user type, user credentials, etc. For example,
|
15
|
+
your service API may need to execute either Api::Auth::V1::AuthenticateCommand.call(...) or Api::Auth::V2::AuthenticateCommand.call(...)
|
16
|
+
based on the API version. simple_command_dispatcher allows you to make one call to execute both command dynamically.}.gsub(/\s+/,' ')
|
17
|
+
spec.homepage = "https://github.com/gangelo/simple_command_dispatcher"
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
21
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
22
|
+
#if spec.respond_to?(:metadata)
|
23
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
24
|
+
#else
|
25
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
26
|
+
# "public gem pushes."
|
27
|
+
#end
|
28
|
+
|
29
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
30
|
+
f.match(%r{^(test|spec|features)/})
|
31
|
+
end
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib"]
|
35
|
+
|
36
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
37
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
38
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
39
|
+
|
40
|
+
spec.add_development_dependency "yard"
|
41
|
+
spec.add_development_dependency "rdoc"
|
42
|
+
spec.add_development_dependency "colorize"
|
43
|
+
|
44
|
+
spec.required_ruby_version = '>= 2.2.2'
|
45
|
+
spec.add_runtime_dependency 'activesupport', '~> 5.0', '>= 5.0.0.1'
|
46
|
+
|
47
|
+
spec.add_runtime_dependency 'simple_command', '>= 0.0.9'
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_command_dispatcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gene M. Angelo, Jr.
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rdoc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: colorize
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activesupport
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5.0'
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 5.0.0.1
|
107
|
+
type: :runtime
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - "~>"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '5.0'
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 5.0.0.1
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: simple_command
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 0.0.9
|
124
|
+
type: :runtime
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 0.0.9
|
131
|
+
description: 'Within a services API (rails-api for instance), you may have a need
|
132
|
+
to execute different SimpleCommands based on one or more factors: multiple application,
|
133
|
+
API version, user type, user credentials, etc. For example, your service API may
|
134
|
+
need to execute either Api::Auth::V1::AuthenticateCommand.call(...) or Api::Auth::V2::AuthenticateCommand.call(...)
|
135
|
+
based on the API version. simple_command_dispatcher allows you to make one call
|
136
|
+
to execute both command dynamically.'
|
137
|
+
email:
|
138
|
+
- public.gma@gmail.com
|
139
|
+
executables: []
|
140
|
+
extensions: []
|
141
|
+
extra_rdoc_files: []
|
142
|
+
files:
|
143
|
+
- ".gitignore"
|
144
|
+
- ".rspec"
|
145
|
+
- ".ruby-version"
|
146
|
+
- ".travis.yml"
|
147
|
+
- CHANGELOG.mb
|
148
|
+
- CODE_OF_CONDUCT.md
|
149
|
+
- Gemfile
|
150
|
+
- LICENSE.txt
|
151
|
+
- README.md
|
152
|
+
- Rakefile
|
153
|
+
- bin/console
|
154
|
+
- bin/setup
|
155
|
+
- lib/core_extensions/string.rb
|
156
|
+
- lib/simple_command_dispatcher.rb
|
157
|
+
- lib/simple_command_dispatcher/klass_transform.rb
|
158
|
+
- lib/simple_command_dispatcher/version.rb
|
159
|
+
- lib/tasks/simple_command_dispatcher_sandbox.rake
|
160
|
+
- simple_command_dispatcher.gemspec
|
161
|
+
homepage: https://github.com/gangelo/simple_command_dispatcher
|
162
|
+
licenses:
|
163
|
+
- MIT
|
164
|
+
metadata: {}
|
165
|
+
post_install_message:
|
166
|
+
rdoc_options: []
|
167
|
+
require_paths:
|
168
|
+
- lib
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 2.2.2
|
174
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
175
|
+
requirements:
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: '0'
|
179
|
+
requirements: []
|
180
|
+
rubyforge_project:
|
181
|
+
rubygems_version: 2.6.7
|
182
|
+
signing_key:
|
183
|
+
specification_version: 4
|
184
|
+
summary: Provides a way to dispatch simple_command ruby gem SimpleCommands (service
|
185
|
+
objects) in a more dynamic manner within your service API. Ideal for rails-api.
|
186
|
+
test_files: []
|