simple_scripting 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 59993d0d200b77aa1deb2aadda05560b427cee00
4
- data.tar.gz: 1baeb57678e05f4fac32cf8d34afb42b175d5b08
3
+ metadata.gz: f48ef28748f34f32e008d951c4b4d7471e5cbe4b
4
+ data.tar.gz: e6cd1cd8132eb9df036e0b5aff06ab0f8e2dd1ec
5
5
  SHA512:
6
- metadata.gz: a06e70451f2b78d8833edaa7649c7ac9130920aca97b2cd9ec171ca3db30a370a65fd15a120a05256f29289bc3e66cad8202c8a8e0f4ea3c04c47ce7ba09d0a3
7
- data.tar.gz: 52cf1615fa2ca5b6f8c1147176ba3859cccc856b613fba1a178d21185bd826f9005cd1d6d88d6fd602e3d8646c267dab4d2a7321357a3c7fe8563c0869821363
6
+ metadata.gz: fa2f35c9e810e06a1e29125650d071de636e4bb80ce3d9010a8eb74e006646f0a786cdab9311a33e0fb77d33d541c55724a78bb9ffb607a199db952dc7f28819
7
+ data.tar.gz: 4e2bdc8da33f0aeede34d91dffeff61f2e420ab633d45cd408519f60becd026d35d13f0139af655c035074792626abae88e8041b1b95bf83e0b5f376f928a946
data/.gitignore CHANGED
@@ -1 +1,2 @@
1
1
  .byebug_history
2
+ coverage/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/Gemfile CHANGED
@@ -8,4 +8,5 @@ end
8
8
 
9
9
  group :test do
10
10
  gem 'rspec', '~> 3.6'
11
+ gem 'coveralls', '~> 0.8.21', require: false
11
12
  end
data/Gemfile.lock CHANGED
@@ -1,7 +1,15 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
+ coveralls (0.8.21)
5
+ json (>= 1.8, < 3)
6
+ simplecov (~> 0.14.1)
7
+ term-ansicolor (~> 1.3)
8
+ thor (~> 0.19.4)
9
+ tins (~> 1.6)
4
10
  diff-lcs (1.3)
11
+ docile (1.1.5)
12
+ json (2.1.0)
5
13
  parseconfig (1.0.8)
6
14
  rake (12.0.0)
7
15
  rspec (3.6.0)
@@ -17,11 +25,21 @@ GEM
17
25
  diff-lcs (>= 1.2.0, < 2.0)
18
26
  rspec-support (~> 3.6.0)
19
27
  rspec-support (3.6.0)
28
+ simplecov (0.14.1)
29
+ docile (~> 1.1.0)
30
+ json (>= 1.8, < 3)
31
+ simplecov-html (~> 0.10.0)
32
+ simplecov-html (0.10.2)
33
+ term-ansicolor (1.6.0)
34
+ tins (~> 1.0)
35
+ thor (0.19.4)
36
+ tins (1.15.0)
20
37
 
21
38
  PLATFORMS
22
39
  ruby
23
40
 
24
41
  DEPENDENCIES
42
+ coveralls (~> 0.8.21)
25
43
  parseconfig (~> 1.0)
26
44
  rake (~> 12.0)
27
45
  rspec (~> 3.6)
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
+ [![Gem Version][GV img]](https://rubygems.org/gems/simple_scripting)
1
2
  [![Build Status][BS img]](https://travis-ci.org/saveriomiroddi/simple_scripting)
3
+ [![Dependency Status][DS img]](https://gemnasium.com/saveriomiroddi/simple_scripting)
4
+ [![Code Climate][CC img]](https://codeclimate.com/github/saveriomiroddi/simple_scripting)
5
+ [![Coverage Status][CS img]](https://coveralls.io/r/saveriomiroddi/simple_scripting)
2
6
 
3
7
  # SimpleScripting
4
8
 
@@ -23,7 +27,7 @@ This is a definition example:
23
27
  'schedule',
24
28
  '[weeks]',
25
29
  long_help: 'This is the long help! It can span multiple lines.'
26
- )
30
+ ) || exit
27
31
 
28
32
  which:
29
33
 
@@ -33,7 +37,7 @@ which:
33
37
  - optionally accepts the `weeks` argument,
34
38
  - automatically adds the `-h` and `--help` switches,
35
39
  - prints all the options and the long help if the help is invoked,
36
- - prints the help and exits if invalid parameters are passed (eg. too many).
40
+ - exits with a descriptive error if invalid parameters are passed.
37
41
 
38
42
  This is a sample result:
39
43
 
@@ -90,8 +94,12 @@ This is the workflow and functionality offered by `SS::C`:
90
94
 
91
95
  The purpose of encryption in this library is just to avoid displaying passwords in plaintext; it's not considered safe against attacks.
92
96
 
93
- [BS img]: https://travis-ci.org/saveriomiroddi/simple_scripting.svg?branch=master
94
-
95
97
  ## Help
96
98
 
97
99
  See the [wiki](https://github.com/saveriomiroddi/simple_scripting/wiki) for additional help.
100
+
101
+ [GV img]: https://badge.fury.io/rb/simple_scripting.png
102
+ [BS img]: https://travis-ci.org/saveriomiroddi/simple_scripting.svg?branch=master
103
+ [DS img]: https://gemnasium.com/saveriomiroddi/simple_scripting.png
104
+ [CC img]: https://codeclimate.com/github/saveriomiroddi/simple_scripting.png
105
+ [CS img]: https://coveralls.io/repos/saveriomiroddi/simple_scripting/badge.png?branch=master
@@ -4,129 +4,200 @@ module SimpleScripting
4
4
 
5
5
  module Argv
6
6
 
7
+ class ExitError < StandardError; end
8
+
7
9
  extend self
8
10
 
9
- def decode(*params_definition, arguments: ARGV, long_help: nil, output: $stdout, command: nil)
10
- # If the param is a Hash, we have multiple commands. We check and if the command is correct,
11
- # recursively call the function with the specific parameters.
11
+ def decode(*params_definition, arguments: ARGV, long_help: nil, output: $stdout)
12
+ # WATCH OUT! @long_help can also be set in :decode_command!. See issue #17.
12
13
  #
14
+ @long_help = long_help
15
+ @output = output
16
+
13
17
  if params_definition.first.is_a?(Hash)
14
- command = arguments.shift
15
- commands_definition = params_definition.first
18
+ decode_command!(params_definition, arguments)
19
+ else
20
+ decode_arguments!(params_definition, arguments)
21
+ end
22
+ rescue ExitError
23
+ # return nil, to be used with the 'decode(...) || exit' pattern
24
+ ensure
25
+ # Clean up the instance variables.
26
+ #
27
+ # There is a balance to strike between instance variables, and local variables
28
+ # passed around. One of the options, which is this case, is to set and instance
29
+ # variables only these two, which are constant.
16
30
 
17
- if command == '-h' || command == '--help'
18
- print_optparse_commands_help(commands_definition, output, false)
19
- output == $stdout ? exit : return
20
- end
31
+ @long_help = nil
32
+ @output = nil
33
+ end
21
34
 
22
- command_params_definition = commands_definition[command]
35
+ private
23
36
 
24
- if command_params_definition.nil?
25
- print_optparse_commands_help(commands_definition, output, true)
26
- output == $stdout ? exit : return
27
- else
28
- return [command, decode(*command_params_definition, arguments: arguments, output: output, command: command)]
37
+ # MAIN CASES ###########################################
38
+
39
+ # Input params_definition for a non-nested case:
40
+ #
41
+ # [{"command1"=>["arg1", {:long_help=>"This is the long help."}], "command2"=>["arg2"]}]
42
+ #
43
+ def decode_command!(params_definition, arguments, commands_stack=[])
44
+ commands_definition = params_definition.first
45
+
46
+ # Set the `command` variable only after; in the case where we print the help, this variable
47
+ # must be unset.
48
+ #
49
+ command_for_check = arguments.shift
50
+
51
+ if command_for_check == '-h' || command_for_check == '--help'
52
+ print_optparse_commands_help(nil, commands_definition)
53
+ end
54
+
55
+ command = command_for_check
56
+ command_params_definition = commands_definition[command]
57
+
58
+ case command_params_definition
59
+ when nil
60
+ print_optparse_commands_help(command, commands_definition)
61
+ when Hash
62
+ commands_stack << command
63
+
64
+ # Nested case! Decode recursively
65
+ #
66
+ decode_command!([command_params_definition], arguments, commands_stack)
67
+ else
68
+ commands_stack << command
69
+
70
+ if command_params_definition.last.is_a?(Hash)
71
+ internal_params = command_params_definition.pop # only long_help is here, if present
72
+ @long_help = internal_params.delete(:long_help)
29
73
  end
74
+
75
+ [
76
+ command,
77
+ decode_arguments!(command_params_definition, arguments, commands_stack),
78
+ ]
30
79
  end
80
+ end
31
81
 
82
+ def decode_arguments!(params_definition, arguments, commands_stack=[])
32
83
  result = {}
33
- parser_opts_ref = nil # not available outside the block
84
+ parser_opts_copy = nil # not available outside the block
34
85
  args = {} # { 'name' => mandatory? }
35
86
 
36
87
  OptionParser.new do |parser_opts|
37
88
  params_definition.each do |param_definition|
38
89
  case param_definition
39
90
  when Array
40
- if param_definition[1] && param_definition[1].start_with?('--')
41
- key = param_definition[1].split(' ')[0][2 .. -1].gsub('-', '_').to_sym
42
- else
43
- key = param_definition[0][1 .. -1].to_sym
44
- end
45
-
46
- parser_opts.on(*param_definition) do |value|
47
- result[key] = value || true
48
- end
91
+ process_option_definition!(param_definition, parser_opts, result)
49
92
  when String
50
- if param_definition.start_with?('[')
51
- arg_name = param_definition[1 .. -2].to_sym
52
-
53
- args[arg_name] = false
54
- else
55
- arg_name = param_definition.to_sym
56
-
57
- args[arg_name] = true
58
- end
93
+ process_argument_definition!(param_definition, args)
59
94
  else
60
95
  raise "Unrecognized value: #{param_definition}"
61
96
  end
62
97
  end
63
98
 
64
99
  parser_opts.on('-h', '--help', 'Help') do
65
- print_optparse_help(parser_opts, args, long_help, output, command: command)
66
- output == $stdout ? exit : return
100
+ print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
67
101
  end
68
102
 
69
- parser_opts_ref = parser_opts
103
+ parser_opts_copy = parser_opts
70
104
  end.parse!(arguments)
71
105
 
72
106
  first_arg_name = args.keys.first.to_s
73
107
 
74
- # Varargs
75
108
  if first_arg_name.start_with?('*')
76
- # Mandatory?
77
- if args.fetch(first_arg_name.to_sym)
78
- if arguments.empty?
79
- print_optparse_help(parser_opts_ref, args, long_help, output, command: command)
80
- output == $stdout ? exit : return
81
- else
82
- name = args.keys.first[1..-1].to_sym
109
+ process_varargs!(arguments, result, commands_stack, args, parser_opts_copy)
110
+ else
111
+ process_regular_argument!(arguments, result, commands_stack, args, parser_opts_copy)
112
+ end
83
113
 
84
- result[name] = arguments
85
- end
86
- # Optional
114
+ result
115
+ end
116
+
117
+ # DEFINITIONS PROCESSING ###############################
118
+
119
+ def process_option_definition!(param_definition, parser_opts, result)
120
+ if param_definition[1] && param_definition[1].start_with?('--')
121
+ key = param_definition[1].split(' ')[0][2 .. -1].tr('-', '_').to_sym
122
+ else
123
+ key = param_definition[0][1 .. -1].to_sym
124
+ end
125
+
126
+ parser_opts.on(*param_definition) do |value|
127
+ result[key] = value || true
128
+ end
129
+ end
130
+
131
+ def process_argument_definition!(param_definition, args)
132
+ if param_definition.start_with?('[')
133
+ arg_name = param_definition[1 .. -2].to_sym
134
+
135
+ args[arg_name] = false
136
+ else
137
+ arg_name = param_definition.to_sym
138
+
139
+ args[arg_name] = true
140
+ end
141
+ end
142
+
143
+ def process_varargs!(arguments, result, commands_stack, args, parser_opts_copy)
144
+ first_arg_name = args.keys.first.to_s
145
+
146
+ # Mandatory argument
147
+ if args.fetch(first_arg_name.to_sym)
148
+ if arguments.empty?
149
+ print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
87
150
  else
88
151
  name = args.keys.first[1..-1].to_sym
89
152
 
90
153
  result[name] = arguments
91
154
  end
155
+ # Optional
92
156
  else
93
- min_args_size = args.count { |name, mandatory| mandatory }
157
+ name = args.keys.first[1..-1].to_sym
94
158
 
95
- case arguments.size
96
- when (min_args_size .. args.size)
97
- arguments.zip(args) do |value, (name, mandatory)|
98
- result[name] = value
99
- end
100
- else
101
- print_optparse_help(parser_opts_ref, args, long_help, output, command: command)
102
- output == $stdout ? exit : return
103
- end
159
+ result[name] = arguments
104
160
  end
161
+ end
105
162
 
106
- result
163
+ def process_regular_argument!(arguments, result, commands_stack, args, parser_opts_copy)
164
+ min_args_size = args.count { |_, mandatory| mandatory }
165
+
166
+ case arguments.size
167
+ when (min_args_size .. args.size)
168
+ arguments.zip(args) do |value, (name, _)|
169
+ result[name] = value
170
+ end
171
+ else
172
+ print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
173
+ end
107
174
  end
108
175
 
109
- private
176
+ # HELP #################################################
177
+
178
+ def print_optparse_commands_help(command, commands_definition)
179
+ @output.print "Invalid command. " if command
180
+ @output.puts "Valid commands:", "", " " + commands_definition.keys.join(', ')
110
181
 
111
- def print_optparse_commands_help(commands_definition, output, is_error)
112
- output.print "Invalid command. " if is_error
113
- output.puts "Valid commands:", "", " " + commands_definition.keys.join(', ')
182
+ raise ExitError
114
183
  end
115
184
 
116
- def print_optparse_help(parser_opts, args, long_help, output, command: nil)
117
- parser_opts_help = parser_opts.to_s
185
+ def print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
186
+ parser_opts_help = parser_opts_copy.to_s
118
187
 
119
- if command
120
- parser_opts_help = parser_opts_help.sub!(/(\[options\])/, "#{command} \\1")
188
+ if commands_stack.size > 0
189
+ parser_opts_help = parser_opts_help.sub!('[options]', commands_stack.join(' ') + ' [options]')
121
190
  end
122
191
 
123
192
  if args.size > 0
124
193
  args_display = args.map { |name, mandatory| mandatory ? "<#{ name }>" : "[<#{ name }>]" }.join(' ')
125
- parser_opts_help = parser_opts_help.sub!(/^(Usage: .*)/, "\\1 #{args_display}")
194
+ parser_opts_help = parser_opts_help.sub!(/^(Usage: .*)/) { |text| "#{text} #{args_display}" }
126
195
  end
127
196
 
128
- output.puts parser_opts_help
129
- output.puts "", long_help if long_help
197
+ @output.puts parser_opts_help
198
+ @output.puts "", @long_help if @long_help
199
+
200
+ raise ExitError
130
201
  end
131
202
 
132
203
  end
@@ -1,5 +1,5 @@
1
1
  module SimpleScripting
2
2
 
3
- VERSION = "0.9.1"
3
+ VERSION = "0.9.2"
4
4
 
5
5
  end
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.add_development_dependency "rake", "~> 12.0"
22
22
  s.add_development_dependency "rspec", "~> 3.6"
23
+ s.add_development_dependency 'coveralls', "~> 0.8.21"
23
24
 
24
25
  s.files = `git ls-files`.split("\n")
25
26
  s.test_files = `git ls-files -- spec/*`.split("\n")
@@ -26,7 +26,7 @@ describe SimpleScripting::Argv do
26
26
  it 'should implement the help' do
27
27
  decoder_params.last[:arguments] = ['-h']
28
28
 
29
- described_class.decode(*decoder_params)
29
+ return_value = described_class.decode(*decoder_params)
30
30
 
31
31
  expected_output = %Q{\
32
32
  Usage: rspec [options] <mandatory> [<optional>]
@@ -42,6 +42,7 @@ This is the long help!
42
42
  }
43
43
 
44
44
  expect(output_buffer.string).to eql(expected_output)
45
+ expect(return_value).to be(nil)
45
46
  end
46
47
 
47
48
  it "should implement basic switches and arguments (all set)" do
@@ -145,13 +146,14 @@ This is the long help!
145
146
 
146
147
  end
147
148
 
148
- describe 'Multiple commands' do
149
+ describe 'Commands' do
149
150
 
150
151
  describe 'regular case' do
151
152
 
152
153
  let(:decoder_params) {{
153
154
  'command1' => [
154
- 'arg1'
155
+ 'arg1',
156
+ long_help: 'This is the long help.'
155
157
  ],
156
158
  'command2' => [
157
159
  'arg2'
@@ -205,6 +207,8 @@ $a = true
205
207
  expected_output = %Q{\
206
208
  Usage: rspec command1 [options] <arg1>
207
209
  -h, --help Help
210
+
211
+ This is the long help.
208
212
  }
209
213
 
210
214
  expect(output_buffer.string).to eql(expected_output)
@@ -212,6 +216,54 @@ Usage: rspec command1 [options] <arg1>
212
216
 
213
217
  end
214
218
 
219
+ describe 'Nested commands' do
220
+
221
+ let(:decoder_params) {{
222
+ 'command1' => {
223
+ 'nested1a' => [
224
+ 'arg1',
225
+ long_help: 'nested1a long help.'
226
+ ],
227
+ 'nested1b' => [
228
+ 'arg1b'
229
+ ],
230
+ },
231
+ 'command2' => [
232
+ 'arg2'
233
+ ],
234
+ output: output_buffer
235
+ }}
236
+
237
+ it 'should print the command1 help' do
238
+ decoder_params[:arguments] = ['command1', '-h']
239
+
240
+ actual_result = described_class.decode(decoder_params)
241
+
242
+ expected_output = "\
243
+ Valid commands:
244
+
245
+ nested1a, nested1b
246
+ "
247
+
248
+ expect(output_buffer.string).to eql(expected_output)
249
+ end
250
+
251
+ it 'should print the nested1a help, and long help' do
252
+ decoder_params[:arguments] = ['command1', 'nested1a', '-h']
253
+
254
+ actual_result = described_class.decode(decoder_params)
255
+
256
+ expected_output = "\
257
+ Usage: rspec command1 nested1a [options] <arg1>
258
+ -h, --help Help
259
+
260
+ nested1a long help.
261
+ "
262
+
263
+ expect(output_buffer.string).to eql(expected_output)
264
+ end
265
+ end
266
+
215
267
  describe 'No argv case' do
216
268
 
217
269
  let(:decoder_params) {{
@@ -0,0 +1,51 @@
1
+ # Coverage setup
2
+
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+
6
+ # This file was generated by the `rspec --init` command. Conventionally, all
7
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
8
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
9
+ # this file to always be loaded, without a need to explicitly require it in any
10
+ # files.
11
+ #
12
+ # Given that it is always loaded, you are encouraged to keep this file as
13
+ # light-weight as possible. Requiring heavyweight dependencies from this file
14
+ # will add to the boot time of your test suite on EVERY test run, even for an
15
+ # individual file that may not need all of that loaded. Instead, consider making
16
+ # a separate helper file that requires the additional dependencies and performs
17
+ # the additional setup, and require it from the spec files that actually need
18
+ # it.
19
+ #
20
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
+ RSpec.configure do |config|
22
+ # rspec-expectations config goes here. You can use an alternate
23
+ # assertion/expectation library such as wrong or the stdlib/minitest
24
+ # assertions if you prefer.
25
+ config.expect_with :rspec do |expectations|
26
+ # This option will default to `true` in RSpec 4. It makes the `description`
27
+ # and `failure_message` of custom matchers include text for helper methods
28
+ # defined using `chain`, e.g.:
29
+ # be_bigger_than(2).and_smaller_than(4).description
30
+ # # => "be bigger than 2 and smaller than 4"
31
+ # ...rather than:
32
+ # # => "be bigger than 2"
33
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
34
+ end
35
+
36
+ # rspec-mocks config goes here. You can use an alternate test double
37
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
38
+ config.mock_with :rspec do |mocks|
39
+ # Prevents you from mocking or stubbing a method that does not exist on
40
+ # a real object. This is generally recommended, and will default to
41
+ # `true` in RSpec 4.
42
+ mocks.verify_partial_doubles = true
43
+ end
44
+
45
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
46
+ # have no way to turn it off -- the option exists only for backwards
47
+ # compatibility in RSpec 3). It causes shared context metadata to be
48
+ # inherited by the metadata hash of host groups and examples, rather than
49
+ # triggering implicit auto-inclusion in groups with matching metadata.
50
+ config.shared_context_metadata_behavior = :apply_to_host_groups
51
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saverio Miroddi
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: coveralls
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.8.21
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.8.21
55
69
  description: Simplifies options parsing and configuration loading.
56
70
  email:
57
71
  - saverio.pub2@gmail.com
@@ -60,6 +74,7 @@ extensions: []
60
74
  extra_rdoc_files: []
61
75
  files:
62
76
  - ".gitignore"
77
+ - ".rspec"
63
78
  - ".travis.yml"
64
79
  - Gemfile
65
80
  - Gemfile.lock
@@ -74,6 +89,7 @@ files:
74
89
  - spec/simple_scripting/argv_spec.rb
75
90
  - spec/simple_scripting/configuration/value_spec.rb
76
91
  - spec/simple_scripting/configuration_spec.rb
92
+ - spec/spec_helper.rb
77
93
  homepage: https://github.com/saveriomiroddi/simple_scripting
78
94
  licenses:
79
95
  - GPL-3.0
@@ -94,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
110
  version: '0'
95
111
  requirements: []
96
112
  rubyforge_project:
97
- rubygems_version: 2.5.2
113
+ rubygems_version: 2.6.13
98
114
  signing_key:
99
115
  specification_version: 4
100
116
  summary: Library for simplifying some typical scripting functionalities.
@@ -102,3 +118,4 @@ test_files:
102
118
  - spec/simple_scripting/argv_spec.rb
103
119
  - spec/simple_scripting/configuration/value_spec.rb
104
120
  - spec/simple_scripting/configuration_spec.rb
121
+ - spec/spec_helper.rb