simple_command_dispatcher 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.3.1
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.0.0
5
+ before_install: gem install bundler -v 1.13.6
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
@@ -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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simple_command_dispatcher.gemspec
4
+ gemspec
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
+ [![GitHub version](https://badge.fury.io/gh/gangelo%2Fsimple_command_dispatcher.svg)](https://badge.fury.io/gh/gangelo%2Fsimple_command_dispatcher)
2
+ [![Gem Version](https://badge.fury.io/rb/simple_command_dispatcher.svg)](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
+ ![N|Solid](https://cldup.com/1UeyWzOLic.png)
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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,5 @@
1
+ class String
2
+ def trim_all
3
+ self.gsub(/\s+/, "")
4
+ end
5
+ end
@@ -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,5 @@
1
+ module SimpleCommand
2
+ module Dispatcher
3
+ VERSION = "1.1.1"
4
+ end
5
+ 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: []