cliqr 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +31 -3
- data/Rakefile +1 -1
- data/lib/cliqr.rb +28 -3
- data/lib/cliqr/cli/builder.rb +11 -13
- data/lib/cliqr/cli/command.rb +17 -0
- data/lib/cliqr/cli/command_runner_factory.rb +66 -0
- data/lib/cliqr/cli/config.rb +144 -24
- data/lib/cliqr/cli/interface.rb +51 -0
- data/lib/cliqr/cli/router.rb +11 -0
- data/lib/cliqr/cli/validator.rb +17 -0
- data/lib/cliqr/dsl.rb +24 -26
- data/lib/cliqr/error.rb +35 -0
- data/lib/cliqr/version.rb +1 -1
- data/spec/config/config_finalize_spec.rb +14 -0
- data/spec/config/config_validator_spec.rb +40 -0
- data/spec/dsl/interface_spec.rb +69 -3
- data/spec/executor/command_runner_spec.rb +25 -0
- data/spec/executor/fixtures/always_error_command.rb +8 -0
- data/spec/executor/fixtures/test_command.rb +8 -0
- data/spec/executor/router_spec.rb +25 -0
- metadata +18 -4
- data/spec/dsl/interface_builder_spec.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b1c3a47c4045454a48b832cc443ea735fe538ae
|
4
|
+
data.tar.gz: 4b919b535387086386da176acc4a470b8cf597e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88d7eeb4ca572c3dfb8715611b2f3c5355ef9a5f5cee2c40497d485bc190c1869e57d8e4780f9507de91ad41fd15cebd44066bb28afd0ff68f6c081d67ecd1df
|
7
|
+
data.tar.gz: bf965708a3fbb48c021875fd87dfb51f417fe3f73c2e85d8053e5ec449942b0ffb62461ede244f2b48297880acc0aa7160bc1c0db23c510f128a3e61a969588b
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,23 @@
|
|
1
1
|
|
2
|
+
0.0.4 / 2015-05-11
|
3
|
+
==================
|
4
|
+
|
5
|
+
* bring document coverage to 100%
|
6
|
+
* improve documentation
|
7
|
+
* fix minor style error in rake tasks
|
8
|
+
* don't execute specs twice
|
9
|
+
* add support for command options
|
10
|
+
* improve dsl framework by completely hiding dsl methods
|
11
|
+
* add method_missing to proxy all dsl methods
|
12
|
+
* add example for router in readme
|
13
|
+
* ignore document files
|
14
|
+
* add command router
|
15
|
+
* use a template to build usage
|
16
|
+
* move builder spec in its own folder
|
17
|
+
* add command usage section
|
18
|
+
* change usage information builder
|
19
|
+
* show total downloads in readme
|
20
|
+
|
2
21
|
0.0.3 / 2015-05-08
|
3
22
|
==================
|
4
23
|
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
[](http://inch-ci.org/github/anshulverma/cliqr)
|
8
8
|
|
9
9
|
[](https://rubygems.org/gems/cliqr)
|
10
|
-
[](https://rubygems.org/gems/cliqr)
|
11
11
|
|
12
12
|
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc/generate-toc again -->
|
13
13
|
**Table of Contents**
|
@@ -15,7 +15,7 @@
|
|
15
15
|
- [cliqr](#cliqr)
|
16
16
|
- [Summary](#summary)
|
17
17
|
- [Examples](#examples)
|
18
|
-
- [Simple CLI app
|
18
|
+
- [Simple CLI app example](#simple-cli-app-example)
|
19
19
|
- [Installation](#installation)
|
20
20
|
- [Building](#building)
|
21
21
|
- [Contributing](#contributing)
|
@@ -38,15 +38,43 @@ line application. Features include:
|
|
38
38
|
The DSL provides several helper methods to build interfaces of different
|
39
39
|
styles. Here are some examples.
|
40
40
|
|
41
|
-
### Simple CLI app
|
41
|
+
### Simple CLI app example
|
42
42
|
|
43
43
|
Here is a simple hello-world example for using Cliqr.
|
44
44
|
|
45
45
|
``` ruby
|
46
|
+
require 'cliqr'
|
47
|
+
|
48
|
+
# a custom command handler
|
49
|
+
class MyCommandHandler < Cliqr.command
|
50
|
+
def execute
|
51
|
+
puts 'executing my awesome command'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
46
55
|
cli = Cliqr.interface do
|
47
56
|
basename 'my-command'
|
57
|
+
description 'this is an awesome command...try it out'
|
58
|
+
handler MyCommandHandler
|
59
|
+
|
60
|
+
option 'an-option' do
|
61
|
+
short 'a'
|
62
|
+
description 'this is a option'
|
63
|
+
end
|
48
64
|
end
|
65
|
+
|
49
66
|
puts cli.usage
|
67
|
+
#> my-command -- this is an awesome command...try it out
|
68
|
+
#>
|
69
|
+
#> USAGE:
|
70
|
+
#> my-command
|
71
|
+
#>
|
72
|
+
#> Available options:
|
73
|
+
#>
|
74
|
+
#> --an-option, -a : this is a option
|
75
|
+
|
76
|
+
cli.execute
|
77
|
+
#> executing my awesome command
|
50
78
|
```
|
51
79
|
|
52
80
|
This should print
|
data/Rakefile
CHANGED
@@ -17,7 +17,7 @@ task :coverage do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
desc 'default rake task'
|
20
|
-
task default: [:clean, :
|
20
|
+
task default: [:clean, :coverage, :rubocop, :verify_measurements, :yardstick_measure]
|
21
21
|
|
22
22
|
desc 'run CI tasks'
|
23
23
|
task :ci do
|
data/lib/cliqr.rb
CHANGED
@@ -5,19 +5,44 @@ require 'cliqr/error'
|
|
5
5
|
|
6
6
|
require 'cliqr/cli/config'
|
7
7
|
require 'cliqr/cli/builder'
|
8
|
+
require 'cliqr/cli/command'
|
8
9
|
|
9
10
|
# Top level namespace for the Cliqr gem
|
11
|
+
#
|
12
|
+
# @api public
|
10
13
|
module Cliqr
|
11
14
|
class << self
|
12
|
-
#
|
13
|
-
#
|
15
|
+
# Start building a cli interface
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# Cliqr.interface do
|
19
|
+
# basename 'my-command' # name of the command
|
20
|
+
# description 'command description in a few words' # long description
|
21
|
+
# handler MyCommandHandler # command's handler class
|
22
|
+
#
|
23
|
+
# option
|
24
|
+
# end
|
14
25
|
#
|
15
26
|
# @return [Cliqr::CLI]
|
16
27
|
#
|
17
28
|
# @api public
|
18
29
|
def interface(&block)
|
19
30
|
config = CLI::Config.build(&block)
|
20
|
-
CLI::Builder.new(config)
|
31
|
+
CLI::Builder.new(config).build
|
32
|
+
end
|
33
|
+
|
34
|
+
# All cliqr commands should extend from this. Here is an example:
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# class MyCommand < Cliqr.command
|
38
|
+
# def execute
|
39
|
+
# # execute the command
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# @return [CLI::Command]
|
44
|
+
def command
|
45
|
+
CLI::Command
|
21
46
|
end
|
22
47
|
end
|
23
48
|
end
|
data/lib/cliqr/cli/builder.rb
CHANGED
@@ -1,32 +1,30 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'cliqr/cli/validator'
|
4
|
+
require 'cliqr/cli/interface'
|
4
5
|
|
5
6
|
module Cliqr
|
6
7
|
module CLI
|
7
8
|
# Builds usage information from [CLI::Config]
|
9
|
+
#
|
10
|
+
# @api private
|
8
11
|
class Builder
|
9
12
|
# Start building a command line interface
|
10
13
|
#
|
11
|
-
# @param [
|
12
|
-
#
|
13
|
-
# CLI::Validator)
|
14
|
+
# @param [Cliqr::CLI::Config] config the configuration options for the
|
15
|
+
# interface (validated using CLI::Validator)
|
14
16
|
#
|
15
17
|
# @return [Cliqr::CLI::Builder]
|
16
18
|
def initialize(config)
|
17
|
-
|
18
|
-
@basename = config[:basename]
|
19
|
+
@config = config
|
19
20
|
end
|
20
21
|
|
21
|
-
#
|
22
|
+
# Validate and build a cli interface based on the configuration options
|
22
23
|
#
|
23
|
-
# @return [
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
<<-EOS
|
28
|
-
USAGE: #{@basename}
|
29
|
-
EOS
|
24
|
+
# @return [Cliqr::CLI::Interface]
|
25
|
+
def build
|
26
|
+
CLI::Validator.validate @config
|
27
|
+
Interface.new(@config)
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Cliqr
|
4
|
+
module CLI
|
5
|
+
# Base class for all commands to extend from
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class Command
|
9
|
+
# Execute the command
|
10
|
+
#
|
11
|
+
# @return [Integer] Exit status of the command execution
|
12
|
+
def execute
|
13
|
+
0
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module Cliqr
|
6
|
+
module CLI
|
7
|
+
# Factory class to get instance of CommandRunner based on input options
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class CommandRunnerFactory
|
11
|
+
# Get a instance of command runner based on options
|
12
|
+
#
|
13
|
+
# @param [Hash] options Used to build a command runner instance
|
14
|
+
#
|
15
|
+
# @return [Cliqr::CLI::StandardCommandRunner] If default output is require from command
|
16
|
+
# @return [Cliqr::CLI::BufferedCommandRunner] If command's output needs to be buffered
|
17
|
+
def self.get(**options)
|
18
|
+
case options[:output]
|
19
|
+
when :default
|
20
|
+
StandardCommandRunner.new
|
21
|
+
when :buffer
|
22
|
+
BufferedCommandRunner.new
|
23
|
+
else
|
24
|
+
fail Cliqr::Error::UnknownCommandRunnerException,
|
25
|
+
'cannot find a command runner for the given options'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# A standard implementation for command runner used for most commands
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
class StandardCommandRunner
|
34
|
+
# simply execute the command handler
|
35
|
+
#
|
36
|
+
# @return [Integer] Exit status of the command
|
37
|
+
def run
|
38
|
+
yield
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Used to buffer command output
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
class BufferedCommandRunner
|
46
|
+
# Run the command handler but redirect stdout to a buffer
|
47
|
+
#
|
48
|
+
# @return [Hash] The hash contains :stdout, :stderr and :status of the command
|
49
|
+
def run
|
50
|
+
old_stdout = $stdout
|
51
|
+
old_stderr = $stderr
|
52
|
+
$stdout = StringIO.new('', 'w')
|
53
|
+
$stderr = StringIO.new('', 'w')
|
54
|
+
yield
|
55
|
+
{
|
56
|
+
:stdout => $stdout.string,
|
57
|
+
:stderr => $stderr.string,
|
58
|
+
:status => 0
|
59
|
+
}
|
60
|
+
ensure
|
61
|
+
$stdout = old_stdout
|
62
|
+
$stderr = old_stderr
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/cliqr/cli/config.rb
CHANGED
@@ -3,46 +3,166 @@
|
|
3
3
|
require 'cliqr/dsl'
|
4
4
|
|
5
5
|
module Cliqr
|
6
|
+
# A extension for CLI module to group all config classes
|
6
7
|
module CLI
|
8
|
+
# A value to initialize configuration attributes with
|
9
|
+
UNSET = Object.new
|
10
|
+
|
7
11
|
# The configuration setting to build a cli application with its own dsl
|
12
|
+
#
|
13
|
+
# @api private
|
8
14
|
class Config
|
9
15
|
extend Cliqr::DSL
|
10
16
|
|
17
|
+
# Base name of the command
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
attr_accessor :basename
|
21
|
+
|
22
|
+
# Description for the base command
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
attr_accessor :description
|
26
|
+
|
27
|
+
# Command handler for the base command
|
28
|
+
#
|
29
|
+
# @return [Class]
|
30
|
+
attr_accessor :handler
|
31
|
+
|
32
|
+
# Array of options applied to the base command
|
33
|
+
#
|
34
|
+
# @return [Array<OptionConfig>]
|
35
|
+
attr_accessor :options
|
36
|
+
|
37
|
+
# New config instance with all attributes set as UNSET
|
11
38
|
def initialize
|
12
|
-
@
|
39
|
+
@basename = UNSET
|
40
|
+
@description = UNSET
|
41
|
+
@handler = UNSET
|
42
|
+
@options = UNSET
|
43
|
+
end
|
44
|
+
|
45
|
+
# Finalize config by adding default values for unset values
|
46
|
+
#
|
47
|
+
# @return [Cliqr::CLI::Config]
|
48
|
+
def finalize
|
49
|
+
@basename = '' if @basename == UNSET
|
50
|
+
@description = '' if @description == UNSET
|
51
|
+
@handler = nil if @handler == UNSET
|
52
|
+
@options = [] if @options == UNSET
|
53
|
+
|
54
|
+
self
|
13
55
|
end
|
14
56
|
|
15
57
|
# Set value for a config option
|
16
58
|
#
|
17
|
-
# @param name
|
18
|
-
# name of the config parameter
|
59
|
+
# @param [Symbol] name Name of the config parameter
|
19
60
|
#
|
20
|
-
# @param value
|
21
|
-
# value for the config parameter
|
61
|
+
# @param [Object] value Value for the config parameter
|
22
62
|
#
|
23
|
-
# @
|
24
|
-
|
25
|
-
|
63
|
+
# @param [Funciton] block Function which populates configuration for a sub-attribute
|
64
|
+
#
|
65
|
+
# @return [Object] If setting a attribute's value
|
66
|
+
# @return [Cliqr::CLI::OptionConfig] If adding a new option
|
67
|
+
def set_config(name, value, &block)
|
68
|
+
case name
|
69
|
+
when :option
|
70
|
+
handle_option value, &block # value is the long name for the option
|
71
|
+
else
|
72
|
+
handle_config name, value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Set value for config option without evaluating a block
|
79
|
+
#
|
80
|
+
# @param [Symbol] name Name of the config option
|
81
|
+
# @param [Object] value Value for the config option
|
82
|
+
#
|
83
|
+
# @return [Object] Value that was assigned to attribute
|
84
|
+
def handle_config(name, value)
|
85
|
+
public_send("#{name}=", value)
|
86
|
+
value
|
87
|
+
end
|
88
|
+
|
89
|
+
# Add a new option for the command
|
90
|
+
#
|
91
|
+
# @param [Symbol] name Long name of the option
|
92
|
+
#
|
93
|
+
# @param [Function] block Populate the option's config in this funciton block
|
94
|
+
#
|
95
|
+
# @return [Cliqr::CLI::OptionConfig] Newly created option's config
|
96
|
+
def handle_option(name, &block)
|
97
|
+
option_config = OptionConfig.build(&block)
|
98
|
+
option_config.name = name
|
99
|
+
@options = [] if @options == UNSET
|
100
|
+
@options.push option_config
|
101
|
+
option_config
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Config attributes for a command's option
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
class OptionConfig
|
109
|
+
extend Cliqr::DSL
|
110
|
+
|
111
|
+
# Long option name
|
112
|
+
#
|
113
|
+
# @return [String]
|
114
|
+
attr_accessor :name
|
115
|
+
|
116
|
+
# Optional short name for the option
|
117
|
+
#
|
118
|
+
# @return [String]
|
119
|
+
attr_accessor :short
|
120
|
+
|
121
|
+
# A description string for the option
|
122
|
+
#
|
123
|
+
# @return [String]
|
124
|
+
attr_accessor :description
|
125
|
+
|
126
|
+
# Initialize a new config instance for an option with UNSET attribute values
|
127
|
+
def initialize
|
128
|
+
@name = UNSET
|
129
|
+
@short = UNSET
|
130
|
+
@description = UNSET
|
26
131
|
end
|
27
132
|
|
28
|
-
# Finalize config by adding default values for unset values
|
133
|
+
# Finalize option's config by adding default values for unset values
|
29
134
|
#
|
30
|
-
# @return [
|
135
|
+
# @return [Cliqr::CLI::OptionConfig]
|
31
136
|
def finalize
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
137
|
+
@name = '' if @name == UNSET
|
138
|
+
@short = '' if @short == UNSET
|
139
|
+
@description = '' if @description == UNSET
|
140
|
+
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
# Set value for command option's attribute
|
145
|
+
#
|
146
|
+
# @param [Symbol] name Name of the attribute
|
147
|
+
#
|
148
|
+
# @param [Object] value Value for the attribute
|
149
|
+
#
|
150
|
+
# @return [Object] Value that was set for the attribute
|
151
|
+
def set_config(name, value)
|
152
|
+
handle_option_config name, value
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
# Set value for config option without evaluating a block
|
158
|
+
#
|
159
|
+
# @param [Symbol] name Name of the config option
|
160
|
+
# @param [Object] value Value for the config option
|
161
|
+
#
|
162
|
+
# @return [Object]
|
163
|
+
def handle_option_config(name, value)
|
164
|
+
public_send("#{name}=", value)
|
165
|
+
value
|
46
166
|
end
|
47
167
|
end
|
48
168
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'cliqr/error'
|
4
|
+
|
5
|
+
require 'cliqr/cli/router'
|
6
|
+
require 'cliqr/cli/command_runner_factory'
|
7
|
+
|
8
|
+
module Cliqr
|
9
|
+
module CLI
|
10
|
+
# A CLI interface instance which is the entry point for all CLI commands.
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
class Interface
|
14
|
+
# Create a new interface instance with a config
|
15
|
+
#
|
16
|
+
# @param [Cliqr::CLI::Config] config Config used to create this interface
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get usage information of this command line interface instance
|
22
|
+
#
|
23
|
+
# @return [String] Defines usage of this interface
|
24
|
+
def usage
|
25
|
+
template_file_path = File.expand_path('../../../../templates/usage.erb', __FILE__)
|
26
|
+
template = ERB.new(File.new(template_file_path).read, nil, '%')
|
27
|
+
result = template.result(@config.instance_eval { binding })
|
28
|
+
|
29
|
+
# remove multiple newlines from the end of usage
|
30
|
+
"#{result.strip}\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
# Execute a command
|
34
|
+
#
|
35
|
+
# @param [Symbol] output Either :default or :buffer
|
36
|
+
#
|
37
|
+
# @return [Integer] Exit code of the command execution
|
38
|
+
def execute(output: :default)
|
39
|
+
handler = @config.handler.new
|
40
|
+
begin
|
41
|
+
runner = CommandRunnerFactory.get(output: output)
|
42
|
+
runner.run do
|
43
|
+
handler.execute
|
44
|
+
end
|
45
|
+
rescue StandardError => e
|
46
|
+
raise Cliqr::Error::CommandRuntimeException.new "command '#{@config.basename}' failed", e
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/cliqr/cli/validator.rb
CHANGED
@@ -5,9 +5,26 @@ require 'cliqr/error'
|
|
5
5
|
module Cliqr
|
6
6
|
module CLI
|
7
7
|
# Validator for the command line interface configuration
|
8
|
+
#
|
9
|
+
# @api private
|
8
10
|
class Validator
|
11
|
+
# Validates the config to make sure all the options are correctly set
|
12
|
+
#
|
13
|
+
# @param [Cliqr::CLI::Config] config Settings for building command line interface
|
14
|
+
#
|
15
|
+
# @return [Cliqr::CLI::Config] Validated config object
|
9
16
|
def self.validate(config)
|
10
17
|
fail Cliqr::Error::ConfigNotFound, 'config is nil' if config.nil?
|
18
|
+
fail Cliqr::Error::BasenameNotDefined, 'basename is not defined' if config.basename.empty?
|
19
|
+
|
20
|
+
fail Cliqr::Error::HandlerNotDefined, 'command handler not defined' if config.handler.nil?
|
21
|
+
fail Cliqr::Error::InvalidCommandHandler,
|
22
|
+
'command handler must extend from Cliqr::CLI::Command' unless config.handler < Command
|
23
|
+
|
24
|
+
fail Cliqr::Error::OptionsNotDefinedException,
|
25
|
+
'options cannot be nil' if config.options.nil?
|
26
|
+
|
27
|
+
config
|
11
28
|
end
|
12
29
|
end
|
13
30
|
end
|
data/lib/cliqr/dsl.rb
CHANGED
@@ -6,56 +6,54 @@ module Cliqr
|
|
6
6
|
# Used to separate all dsl methods in a separate block, thus allowing
|
7
7
|
# separation of concerns between non-dsl methods with dsl methods which
|
8
8
|
# improves maintainability.
|
9
|
+
#
|
10
|
+
# @api private
|
9
11
|
module DSL
|
10
12
|
# Entry point for invoking dsl methods
|
11
13
|
#
|
12
|
-
# @param args Arguments to be used to build the DSL instance
|
14
|
+
# @param [Hash] args Arguments to be used to build the DSL instance
|
13
15
|
#
|
14
|
-
# @param block The block to evaluate the DSL
|
16
|
+
# @param [Function] block The block to evaluate the DSL
|
15
17
|
#
|
16
18
|
# @return [Cliqr::DSL]
|
17
19
|
def build(*args, &block)
|
18
20
|
base = new(*args)
|
19
21
|
if block_given?
|
20
|
-
|
21
|
-
delegator = delegator_klass.new(base)
|
22
|
+
delegator = DSLDelegator.new(base)
|
22
23
|
delegator.instance_eval(&block)
|
23
24
|
end
|
24
25
|
base.finalize
|
26
|
+
base
|
25
27
|
end
|
26
28
|
|
27
29
|
# Delegates all DSL methods to the base class. Can be used to keep DSL
|
28
|
-
# methods separate from non-dsl methods.
|
29
|
-
#
|
30
|
-
# @param block Block containing all dsl methods
|
31
|
-
#
|
32
|
-
# Allows separating dsl methods as:
|
30
|
+
# methods separate from non-dsl methods. All implementing subclasses will
|
31
|
+
# have to implement a set_config method as described below
|
33
32
|
#
|
34
33
|
# class MyDSLClass
|
35
34
|
# extends Cliqr::DSL
|
36
35
|
#
|
37
|
-
# def
|
38
|
-
#
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# # ... other non-dsl methods ...
|
42
|
-
#
|
43
|
-
# dsl do
|
44
|
-
# def value(value)
|
45
|
-
# set_value value
|
46
|
-
# end
|
36
|
+
# def set_config(name, value, &block)
|
37
|
+
# # handle config value for attribute "name"
|
47
38
|
# end
|
48
|
-
#
|
49
39
|
# end
|
50
40
|
#
|
51
41
|
# This will be invoked as:
|
52
42
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
43
|
+
# MyDSLClass.build do
|
44
|
+
# attribute 'some-value'
|
45
|
+
# end
|
46
|
+
class DSLDelegator < SimpleDelegator
|
47
|
+
# All dsl methods are handled dynamically by proxying through #set_config
|
48
|
+
#
|
49
|
+
# @param [Symbol] name Name of the method
|
50
|
+
# @param [Array] args Method arguments
|
51
|
+
# @param [Function] block A function to evaluate in the context of the method's arguments
|
52
|
+
#
|
53
|
+
# @return [Object] The return value of the proxied method
|
54
|
+
def method_missing(name, *args, &block)
|
55
|
+
__getobj__.set_config name, args[0], &block
|
56
|
+
end
|
59
57
|
end
|
60
58
|
end
|
61
59
|
end
|
data/lib/cliqr/error.rb
CHANGED
@@ -2,6 +2,41 @@
|
|
2
2
|
|
3
3
|
module Cliqr
|
4
4
|
module Error
|
5
|
+
# Base error class that others error types extend from
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class CliqrError < StandardError
|
9
|
+
# Set up the error to wrap another error's trace
|
10
|
+
def initialize(error_message, e = nil)
|
11
|
+
super e
|
12
|
+
|
13
|
+
# Preserve the original exception's data if provided
|
14
|
+
return unless e && e.is_a?(Exception)
|
15
|
+
|
16
|
+
set_backtrace e.backtrace
|
17
|
+
message.prepend "#{error_message}\n\nCause:\n#{e.class}: "
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Raised when the config parameter is nil
|
5
22
|
class ConfigNotFound < StandardError; end
|
23
|
+
|
24
|
+
# Raised when basename is not defined
|
25
|
+
class BasenameNotDefined < StandardError; end
|
26
|
+
|
27
|
+
# Raised when a command handler is not defined
|
28
|
+
class HandlerNotDefined < StandardError; end
|
29
|
+
|
30
|
+
# Raised if command handler does not extend from Cliqr::CLI::Command
|
31
|
+
class InvalidCommandHandler < StandardError; end
|
32
|
+
|
33
|
+
# Encapsulates the error that gets thrown during a command execution
|
34
|
+
class CommandRuntimeException < CliqrError; end
|
35
|
+
|
36
|
+
# Error to signify that a command's runner is not available
|
37
|
+
class UnknownCommandRunnerException < CliqrError; end
|
38
|
+
|
39
|
+
# Raised if config's option array is nil
|
40
|
+
class OptionsNotDefinedException < CliqrError; end
|
6
41
|
end
|
7
42
|
end
|
data/lib/cliqr/version.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Cliqr::CLI::Config do
|
6
|
+
it 'sets proper defaults for unset values' do
|
7
|
+
config = Cliqr::CLI::Config.new
|
8
|
+
config.finalize
|
9
|
+
expect(config.basename).to eq('')
|
10
|
+
expect(config.description).to eq('')
|
11
|
+
expect(config.handler).to eq(nil)
|
12
|
+
expect(config.options).to eq([])
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Cliqr::CLI::Validator do
|
6
|
+
it 'does not allow empty config' do
|
7
|
+
expect { Cliqr::CLI::Builder.new(nil).build }.to raise_error(Cliqr::Error::ConfigNotFound)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'does not allow empty basename' do
|
11
|
+
config = Cliqr::CLI::Config.new
|
12
|
+
config.basename = ''
|
13
|
+
config.finalize
|
14
|
+
expect { Cliqr::CLI::Builder.new(config).build }.to raise_error(Cliqr::Error::BasenameNotDefined)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not allow command handler to be null' do
|
18
|
+
config = Cliqr::CLI::Config.new
|
19
|
+
config.basename = 'my-command'
|
20
|
+
config.finalize
|
21
|
+
expect { Cliqr::CLI::Builder.new(config).build }.to raise_error(Cliqr::Error::HandlerNotDefined)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'only accepts command handler that extend from Cliqr::CLI::Command' do
|
25
|
+
config = Cliqr::CLI::Config.new
|
26
|
+
config.basename = 'my-command'
|
27
|
+
config.handler = Object
|
28
|
+
config.finalize
|
29
|
+
expect { Cliqr::CLI::Builder.new(config).build }.to raise_error(Cliqr::Error::InvalidCommandHandler)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'expects that config options should not be nil' do
|
33
|
+
config = Cliqr::CLI::Config.new
|
34
|
+
config.basename = 'my-command'
|
35
|
+
config.handler = TestCommand
|
36
|
+
config.options = nil
|
37
|
+
config.finalize
|
38
|
+
expect { Cliqr::CLI::Builder.new(config).build }.to raise_error(Cliqr::Error::OptionsNotDefinedException)
|
39
|
+
end
|
40
|
+
end
|
data/spec/dsl/interface_spec.rb
CHANGED
@@ -2,14 +2,80 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
|
5
|
+
require 'executor/fixtures/test_command'
|
6
|
+
|
7
|
+
describe Cliqr::CLI::Interface do
|
6
8
|
it 'builds a base command with name' do
|
7
9
|
cli = Cliqr.interface do
|
8
|
-
basename '
|
10
|
+
basename 'my-command'
|
11
|
+
description 'a command used to test cliqr'
|
12
|
+
handler TestCommand
|
13
|
+
end
|
14
|
+
|
15
|
+
expect(cli.usage).to eq <<-EOS
|
16
|
+
my-command -- a command used to test cliqr
|
17
|
+
|
18
|
+
USAGE:
|
19
|
+
my-command
|
20
|
+
EOS
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'only makes basename and handler to be required' do
|
24
|
+
cli = Cliqr.interface do
|
25
|
+
basename 'my-command'
|
26
|
+
handler TestCommand
|
9
27
|
end
|
10
28
|
|
11
29
|
expect(cli.usage).to eq <<-EOS
|
12
|
-
|
30
|
+
my-command
|
31
|
+
|
32
|
+
USAGE:
|
33
|
+
my-command
|
13
34
|
EOS
|
14
35
|
end
|
36
|
+
|
37
|
+
it 'allows options for a command' do
|
38
|
+
cli = Cliqr.interface do
|
39
|
+
basename 'my-command'
|
40
|
+
description 'a command used to test cliqr'
|
41
|
+
handler TestCommand
|
42
|
+
|
43
|
+
option 'option-1' do
|
44
|
+
short 'p'
|
45
|
+
description 'a nice option to have'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
expect(cli.usage).to eq <<-EOS
|
50
|
+
my-command -- a command used to test cliqr
|
51
|
+
|
52
|
+
USAGE:
|
53
|
+
my-command [options]
|
54
|
+
|
55
|
+
Available options:
|
56
|
+
|
57
|
+
--option-1, -p : a nice option to have
|
58
|
+
EOS
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'allows command options to optionally have description and short name' do
|
62
|
+
cli = Cliqr.interface do
|
63
|
+
basename 'my-command'
|
64
|
+
description 'a command used to test cliqr'
|
65
|
+
handler TestCommand
|
66
|
+
|
67
|
+
option 'option-1'
|
68
|
+
end
|
69
|
+
|
70
|
+
expect(cli.usage).to eq <<-EOS
|
71
|
+
my-command -- a command used to test cliqr
|
72
|
+
|
73
|
+
USAGE:
|
74
|
+
my-command [options]
|
75
|
+
|
76
|
+
Available options:
|
77
|
+
|
78
|
+
--option-1
|
79
|
+
EOS
|
80
|
+
end
|
15
81
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Cliqr::CLI::CommandRunnerFactory do
|
6
|
+
it 'returns standard runner for default output' do
|
7
|
+
runner = Cliqr::CLI::CommandRunnerFactory.get(output: :default)
|
8
|
+
expect(runner).to be_kind_of(Cliqr::CLI::StandardCommandRunner)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns buffered runner for buffer output' do
|
12
|
+
runner = Cliqr::CLI::CommandRunnerFactory.get(output: :buffer)
|
13
|
+
expect(runner).to be_kind_of(Cliqr::CLI::BufferedCommandRunner)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'throws error for default output type' do
|
17
|
+
expect { Cliqr::CLI::CommandRunnerFactory.get(output: :unknown) }.to(
|
18
|
+
raise_error(be_kind_of(Cliqr::Error::UnknownCommandRunnerException))
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'returns code 0 for default command runner' do
|
23
|
+
expect(Cliqr.command.new.execute).to eq(0)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
require 'executor/fixtures/test_command'
|
6
|
+
require 'executor/fixtures/always_error_command'
|
7
|
+
|
8
|
+
describe Cliqr::CLI::Router do
|
9
|
+
it 'routes base command with no arguments' do
|
10
|
+
cli = Cliqr.interface do
|
11
|
+
basename 'my-command'
|
12
|
+
handler TestCommand
|
13
|
+
end
|
14
|
+
result = cli.execute output: :buffer
|
15
|
+
expect(result[:stdout]).to eq "test command executed\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'handles error appropriately' do
|
19
|
+
cli = Cliqr.interface do
|
20
|
+
basename 'my-command'
|
21
|
+
handler AlwaysErrorCommand
|
22
|
+
end
|
23
|
+
expect { cli.execute }.to raise_error(Cliqr::Error::CommandRuntimeException)
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cliqr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anshul Verma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: log4r
|
@@ -56,13 +56,22 @@ files:
|
|
56
56
|
- Rakefile
|
57
57
|
- lib/cliqr.rb
|
58
58
|
- lib/cliqr/cli/builder.rb
|
59
|
+
- lib/cliqr/cli/command.rb
|
60
|
+
- lib/cliqr/cli/command_runner_factory.rb
|
59
61
|
- lib/cliqr/cli/config.rb
|
62
|
+
- lib/cliqr/cli/interface.rb
|
63
|
+
- lib/cliqr/cli/router.rb
|
60
64
|
- lib/cliqr/cli/validator.rb
|
61
65
|
- lib/cliqr/dsl.rb
|
62
66
|
- lib/cliqr/error.rb
|
63
67
|
- lib/cliqr/version.rb
|
64
|
-
- spec/
|
68
|
+
- spec/config/config_finalize_spec.rb
|
69
|
+
- spec/config/config_validator_spec.rb
|
65
70
|
- spec/dsl/interface_spec.rb
|
71
|
+
- spec/executor/command_runner_spec.rb
|
72
|
+
- spec/executor/fixtures/always_error_command.rb
|
73
|
+
- spec/executor/fixtures/test_command.rb
|
74
|
+
- spec/executor/router_spec.rb
|
66
75
|
- spec/spec_helper.rb
|
67
76
|
homepage: https://github.com/anshulverma/cliqr
|
68
77
|
licenses:
|
@@ -89,7 +98,12 @@ signing_key:
|
|
89
98
|
specification_version: 4
|
90
99
|
summary: A framework and DSL for defining CLI interface
|
91
100
|
test_files:
|
92
|
-
- spec/
|
101
|
+
- spec/config/config_finalize_spec.rb
|
102
|
+
- spec/config/config_validator_spec.rb
|
93
103
|
- spec/dsl/interface_spec.rb
|
104
|
+
- spec/executor/command_runner_spec.rb
|
105
|
+
- spec/executor/fixtures/always_error_command.rb
|
106
|
+
- spec/executor/fixtures/test_command.rb
|
107
|
+
- spec/executor/router_spec.rb
|
94
108
|
- spec/spec_helper.rb
|
95
109
|
has_rdoc:
|