simple_scripting 0.9.4 → 0.10.0

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: fb9a4aca2b7495bf0ce6236f6052bcc7c55c2624
4
- data.tar.gz: 5e4c2c1cdc733a142955b1462546ef7ab0535e07
3
+ metadata.gz: bb1844c9f2da48418b9fc91c3b4026c7fd1a1fbf
4
+ data.tar.gz: 5a30040d716b0e544027261f5bbe6fba834d75af
5
5
  SHA512:
6
- metadata.gz: 6c96951fcb96d49f0e4b4e512788b8f45c860d28d1dba22be29023db869f44bebf87b188458e8f264fc23503f104e0bd53f50bbe9b3df6efeec880c4c944b836
7
- data.tar.gz: 2f8f5c74c08f01e5a5ac020df7bd3011b21e2c0ebfc6282a7fc6e6a0d347ffd87acf4d59513dccba9a3fdf8c646229f9b455aceae55d4b14293bd88f0a0c9a90
6
+ metadata.gz: 39c51fd57bb624894fbb9b0adbfc731d365c2fd36d7b6e9e0a17d2e06fc36ba2f78c014f007b5dd360079daf449beb1ed433d694d827d15f17e664bf10bfdd0e
7
+ data.tar.gz: 6013724a8eb488ed73b2c8918096af8fc4c37a70e5e04aedcf66abd228412ec19606716b9a80a5b4ec829a99f4b071fe0978079ba35db9e023c58a4ea8265190
data/.simplecov ADDED
@@ -0,0 +1,8 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
5
+
6
+ SimpleCov.start do
7
+ add_filter '/spec/'
8
+ end
data/.travis.yml CHANGED
@@ -1,9 +1,6 @@
1
1
  language: ruby
2
+ cache: bundler
2
3
  rvm:
3
- - 2.2
4
4
  - 2.3
5
5
  - 2.4
6
6
  - 2.5
7
- # Bundler+Rubygems issue; see https://github.com/travis-ci/travis-ci/issues/8978
8
- before_install:
9
- - gem update --system
data/Gemfile CHANGED
@@ -1,12 +1,12 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'parseconfig', '~> 1.0'
4
-
5
- group :development do
6
- gem 'rake', '~> 12.0'
7
- end
3
+ gemspec
8
4
 
9
5
  group :test do
10
- gem 'rspec', '~> 3.6'
11
6
  gem 'coveralls', '~> 0.8.21', require: false
12
7
  end
8
+
9
+ group :tools do
10
+ gem 'byebug', '~> 10.0.2'
11
+ gem 'rubocop', '= 0.58.1'
12
+ end
data/Gemfile.lock CHANGED
@@ -1,6 +1,14 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simple_scripting (0.9.4)
5
+ parseconfig (~> 1.0)
6
+
1
7
  GEM
2
8
  remote: https://rubygems.org/
3
9
  specs:
10
+ ast (2.4.0)
11
+ byebug (10.0.2)
4
12
  coveralls (0.8.21)
5
13
  json (>= 1.8, < 3)
6
14
  simplecov (~> 0.14.1)
@@ -9,8 +17,14 @@ GEM
9
17
  tins (~> 1.6)
10
18
  diff-lcs (1.3)
11
19
  docile (1.1.5)
20
+ jaro_winkler (1.5.1)
12
21
  json (2.1.0)
22
+ parallel (1.12.1)
13
23
  parseconfig (1.0.8)
24
+ parser (2.5.1.2)
25
+ ast (~> 2.4.0)
26
+ powerpack (0.1.2)
27
+ rainbow (3.0.0)
14
28
  rake (12.0.0)
15
29
  rspec (3.6.0)
16
30
  rspec-core (~> 3.6.0)
@@ -25,6 +39,15 @@ GEM
25
39
  diff-lcs (>= 1.2.0, < 2.0)
26
40
  rspec-support (~> 3.6.0)
27
41
  rspec-support (3.6.0)
42
+ rubocop (0.58.1)
43
+ jaro_winkler (~> 1.5.1)
44
+ parallel (~> 1.10)
45
+ parser (>= 2.5, != 2.5.1.1)
46
+ powerpack (~> 0.1)
47
+ rainbow (>= 2.2.2, < 4.0)
48
+ ruby-progressbar (~> 1.7)
49
+ unicode-display_width (~> 1.0, >= 1.0.1)
50
+ ruby-progressbar (1.9.0)
28
51
  simplecov (0.14.1)
29
52
  docile (~> 1.1.0)
30
53
  json (>= 1.8, < 3)
@@ -34,15 +57,18 @@ GEM
34
57
  tins (~> 1.0)
35
58
  thor (0.19.4)
36
59
  tins (1.15.0)
60
+ unicode-display_width (1.4.0)
37
61
 
38
62
  PLATFORMS
39
63
  ruby
40
64
 
41
65
  DEPENDENCIES
66
+ byebug (~> 10.0.2)
42
67
  coveralls (~> 0.8.21)
43
- parseconfig (~> 1.0)
44
68
  rake (~> 12.0)
45
69
  rspec (~> 3.6)
70
+ rubocop (= 0.58.1)
71
+ simple_scripting!
46
72
 
47
73
  BUNDLED WITH
48
- 1.11.2
74
+ 1.16.1
data/README.md CHANGED
@@ -1,21 +1,93 @@
1
1
  [![Gem Version][GV img]](https://rubygems.org/gems/simple_scripting)
2
2
  [![Build Status][BS img]](https://travis-ci.org/saveriomiroddi/simple_scripting)
3
- [![Dependency Status][DS img]](https://gemnasium.com/saveriomiroddi/simple_scripting)
4
3
  [![Code Climate][CC img]](https://codeclimate.com/github/saveriomiroddi/simple_scripting)
5
4
  [![Coverage Status][CS img]](https://coveralls.io/r/saveriomiroddi/simple_scripting)
6
5
 
7
6
  # SimpleScripting
8
7
 
9
- `SS` is a library composed of two modules (`Argv` and `Configuration`) which simplify two common scripting tasks:
8
+ `SimpleScripting` is a library composed of three modules (`TabCompletion`, `Argv` and `Configuration`) that simplify three common scripting tasks:
10
9
 
10
+ - writing autocompletion scripts
11
11
  - implementing the commandline options parsing (and the related help)
12
12
  - loading and decoding the configuration for the script/application
13
13
 
14
- `SS` is an interesting (and useful) exercise in design, aimed at finding the simplest and most expressive data/structures which accomplish the given task(s). For this reason, the library can be useful for people who frequently write small scripts (eg. devops or nerds).
14
+ `SimpleScripting` is an interesting (and useful) exercise in design, aimed at finding the simplest and most expressive data/structures that accomplish the given task(s). For this reason, the library can be useful for people who frequently write small scripts (eg. devops or nerds).
15
+
16
+ ## SimpleScripting::TabCompletion
17
+
18
+ `TabCompletion` makes trivial to define tab-completion for terminal commands on Linux/Mac systems; it's so easy that an example is much simpler than an explanation.
19
+
20
+ ### Example
21
+
22
+ Suppose we have the command:
23
+
24
+ ```sh
25
+ open_project [-e|--with-editor EDITOR] <project_name>
26
+ ```
27
+
28
+ We want to add tab completion both for the option and the project name. Easy!!
29
+
30
+ Install the gem (`simple_scripting`), then create this class (`/my/completion_scripts/open_project_completion.rb`):
31
+
32
+ ```ruby
33
+ #!/usr/bin/env ruby
34
+
35
+ require 'simple_scripting/tab_completion'
36
+
37
+ class OpenProjectTabCompletion
38
+ SYSTEM_EDITORS = `update-alternatives --list editor`.split("\n").map { |filename| File.basename(filename) }
39
+
40
+ def with_editor(prefix, suffix, context)
41
+ SYSTEM_EDITORS.grep /^#{prefix}/
42
+ end
43
+
44
+ def project_name(prefix, suffix, context)
45
+ Dir["/my/home/my_projects/#{prefix}*"]
46
+ end
47
+ end
48
+
49
+ if __FILE__ == $PROGRAM_NAME
50
+ completion_definition = [
51
+ ["-e", "--with-editor EDITOR"],
52
+ 'project_name'
53
+ ]
54
+
55
+ SimpleScripting::TabCompletion.new(completion_definition).complete(OpenProjectTabCompletion.new)
56
+ end
57
+ ```
58
+
59
+ then chmod and register it:
60
+
61
+ ```sh
62
+ $ chmod +x /my/completion_scripts/open_project_completion.rb
63
+ $ complete -C /my/completion_scripts/open_project_completion.rb -o default open_project
64
+ ```
65
+
66
+ Done!
67
+
68
+ Now type the following, and get:
69
+
70
+ ```sh
71
+ $ open_project g<tab> # lists: "geet", "gitlab-ce", "gnome-terminal"
72
+ $ open_project --with-editor v # lists: "vim.basic", "vim.tiny"
73
+ $ open_project --wi<tab> # autocompletes "--with-editor"; this is built-in!
74
+ ```
75
+
76
+ Happy completion!
77
+
78
+ ### Supported shells
79
+
80
+ TabCompletion supports Bash, and Zsh with bashcompinit.
81
+
82
+ Note that a recent version of Zsh is required - the Ubuntu 16.04 standard version has a bug that breaks bash-compatible completion.
83
+
84
+ ### More complex use cases
85
+
86
+ For a description of the more complex use cases, including edge cases and error handling, see the [wiki](https://github.com/saveriomiroddi/simple_scripting/wiki/SimpleScripting::TabCompletion-Guide).
15
87
 
16
88
  ## SimpleScripting::Argv
17
89
 
18
- `SS::A` is a module which acts as frontend to the standard Option Parser library (`optparse`), giving a very convenient format for specifying the arguments. `SS::A` also generates the help.
90
+ `Argv` is a module which acts as frontend to the standard Option Parser library (`optparse`), giving a very convenient format for specifying the arguments. `Argv` also generates the help.
19
91
 
20
92
  This is a definition example:
21
93
 
@@ -76,7 +148,7 @@ For the guide, see the [wiki page](https://github.com/saveriomiroddi/simple_scri
76
148
 
77
149
  ## SimpleScripting::Configuration
78
150
 
79
- `SS::C` is a module which acts as frontend to the ParseConfig gem (`parseconfig`), giving compact access to the configuration and its values, and adding a few helpers for common tasks.
151
+ `Configuration` is a module which acts as frontend to the ParseConfig gem (`parseconfig`), giving compact access to the configuration and its values, and adding a few helpers for common tasks.
80
152
 
81
153
  Say one writes a script (`foo_my_bar.rb`), with a corresponding (`$HOME/.foo_my_bar`) configuration, which contains:
82
154
 
@@ -88,7 +160,7 @@ Say one writes a script (`foo_my_bar.rb`), with a corresponding (`$HOME/.foo_my_
88
160
  [a_group]
89
161
  group_key=baz
90
162
 
91
- This is the workflow and functionality offered by `SS::C`:
163
+ This is the workflow and functionality offered by `Configuration`:
92
164
 
93
165
  require 'simple_scripting/configuration'
94
166
 
@@ -115,6 +187,5 @@ See the [wiki](https://github.com/saveriomiroddi/simple_scripting/wiki) for addi
115
187
 
116
188
  [GV img]: https://badge.fury.io/rb/simple_scripting.png
117
189
  [BS img]: https://travis-ci.org/saveriomiroddi/simple_scripting.svg?branch=master
118
- [DS img]: https://gemnasium.com/saveriomiroddi/simple_scripting.png
119
190
  [CC img]: https://codeclimate.com/github/saveriomiroddi/simple_scripting.png
120
191
  [CS img]: https://coveralls.io/repos/saveriomiroddi/simple_scripting/badge.png?branch=master
@@ -4,32 +4,57 @@ module SimpleScripting
4
4
 
5
5
  module Argv
6
6
 
7
- class ExitError < StandardError; end
7
+ # The fact that the following errors don't descend from OptionParser::InvalidOption is somewhat
8
+ # annoying, however, there should be no practical problem.
9
+ #
10
+ class InvalidCommand < StandardError; end
11
+ class ArgumentError < StandardError; end
12
+
13
+ class ExitWithCommandsHelpPrinting < Struct.new(:commands_definition)
14
+ # Note that :long_help is not used.
15
+ def print_help(output, long_help)
16
+ output.puts "Valid commands:", "", " " + commands_definition.keys.join(', ')
17
+ end
18
+ end
19
+
20
+ class ExitWithArgumentsHelpPrinting < Struct.new(:commands_stack, :args, :parser_opts_copy)
21
+ def print_help(output, long_help)
22
+ parser_opts_help = parser_opts_copy.to_s
23
+
24
+ if commands_stack.size > 0
25
+ parser_opts_help = parser_opts_help.sub!('[options]', commands_stack.join(' ') + ' [options]')
26
+ end
27
+
28
+ if args.size > 0
29
+ args_display = args.map { |name, mandatory| mandatory ? "<#{ name }>" : "[<#{ name }>]" }.join(' ')
30
+ parser_opts_help = parser_opts_help.sub!(/^(Usage: .*)/) { |text| "#{text} #{args_display}" }
31
+ end
32
+
33
+ output.puts parser_opts_help
34
+ output.puts "", long_help if long_help
35
+ end
36
+ end
8
37
 
9
38
  extend self
10
39
 
11
- def decode(*params_definition, arguments: ARGV, long_help: nil, output: $stdout)
40
+ def decode(*params_definition, arguments: ARGV, long_help: nil, auto_help: true, output: $stdout)
12
41
  # WATCH OUT! @long_help can also be set in :decode_command!. See issue #17.
13
42
  #
14
43
  @long_help = long_help
15
- @output = output
16
44
 
17
- if params_definition.first.is_a?(Hash)
18
- decode_command!(params_definition, arguments)
19
- else
20
- decode_arguments!(params_definition, arguments)
45
+ exit_data = catch(:exit) do
46
+ if params_definition.first.is_a?(Hash)
47
+ return decode_command!(params_definition, arguments, auto_help)
48
+ else
49
+ return decode_arguments!(params_definition, arguments, auto_help)
50
+ end
21
51
  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.
30
52
 
53
+ exit_data.print_help(output, @long_help)
54
+
55
+ nil # to be used with the 'decode(...) || exit' pattern
56
+ ensure
31
57
  @long_help = nil
32
- @output = nil
33
58
  end
34
59
 
35
60
  private
@@ -40,7 +65,7 @@ module SimpleScripting
40
65
  #
41
66
  # [{"command1"=>["arg1", {:long_help=>"This is the long help."}], "command2"=>["arg2"]}]
42
67
  #
43
- def decode_command!(params_definition, arguments, commands_stack=[])
68
+ def decode_command!(params_definition, arguments, auto_help, commands_stack=[])
44
69
  commands_definition = params_definition.first
45
70
 
46
71
  # Set the `command` variable only after; in the case where we print the help, this variable
@@ -48,8 +73,19 @@ module SimpleScripting
48
73
  #
49
74
  command_for_check = arguments.shift
50
75
 
76
+ # Note that `--help` is baked into OptParse, so without a workaround, we need to always include
77
+ # it.
78
+ #
51
79
  if command_for_check == '-h' || command_for_check == '--help'
52
- print_optparse_commands_help(nil, commands_definition)
80
+ if auto_help
81
+ throw :exit, ExitWithCommandsHelpPrinting.new(commands_definition)
82
+ else
83
+ # This is tricky. Since the common behavior of `--help` is to trigger an unconditional
84
+ # help, it's not clear what to do with other tokens. For simplicity, we just return
85
+ # this flag.
86
+ #
87
+ return { help: true }
88
+ end
53
89
  end
54
90
 
55
91
  command = command_for_check
@@ -57,13 +93,13 @@ module SimpleScripting
57
93
 
58
94
  case command_params_definition
59
95
  when nil
60
- print_optparse_commands_help(command, commands_definition)
96
+ raise InvalidCommand.new("Invalid command: #{command}")
61
97
  when Hash
62
98
  commands_stack << command
63
99
 
64
100
  # Nested case! Decode recursively
65
101
  #
66
- decode_command!([command_params_definition], arguments, commands_stack)
102
+ decode_command!([command_params_definition], arguments, auto_help, commands_stack)
67
103
  else
68
104
  commands_stack << command
69
105
 
@@ -74,12 +110,12 @@ module SimpleScripting
74
110
 
75
111
  [
76
112
  compose_returned_commands(commands_stack),
77
- decode_arguments!(command_params_definition, arguments, commands_stack),
113
+ decode_arguments!(command_params_definition, arguments, auto_help, commands_stack),
78
114
  ]
79
115
  end
80
116
  end
81
117
 
82
- def decode_arguments!(params_definition, arguments, commands_stack=[])
118
+ def decode_arguments!(params_definition, arguments, auto_help, commands_stack=[])
83
119
  result = {}
84
120
  parser_opts_copy = nil # not available outside the block
85
121
  args = {} # { 'name' => mandatory? }
@@ -92,12 +128,24 @@ module SimpleScripting
92
128
  when String
93
129
  process_argument_definition!(param_definition, args)
94
130
  else
131
+ # This is an error in the params definition, so it doesn't follow the user error/help
132
+ # workflow.
133
+ #
95
134
  raise "Unrecognized value: #{param_definition}"
96
135
  end
97
136
  end
98
137
 
138
+ # See --help note in :decode_command!.
139
+ #
99
140
  parser_opts.on('-h', '--help', 'Help') do
100
- print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
141
+ if auto_help
142
+ throw :exit, ExitWithArgumentsHelpPrinting.new(commands_stack, args, parser_opts_copy)
143
+ else
144
+ # Needs to be better handled. When help is required, generally, it trumps the
145
+ # correctness of the rest of the options/arguments.
146
+ #
147
+ result[:help] = true
148
+ end
101
149
  end
102
150
 
103
151
  parser_opts_copy = parser_opts
@@ -146,7 +194,7 @@ module SimpleScripting
146
194
  # Mandatory argument
147
195
  if args.fetch(first_arg_name.to_sym)
148
196
  if arguments.empty?
149
- print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
197
+ raise ArgumentError.new("Missing mandatory argument(s)")
150
198
  else
151
199
  name = args.keys.first[1..-1].to_sym
152
200
 
@@ -163,43 +211,17 @@ module SimpleScripting
163
211
  def process_regular_argument!(arguments, result, commands_stack, args, parser_opts_copy)
164
212
  min_args_size = args.count { |_, mandatory| mandatory }
165
213
 
166
- case arguments.size
167
- when (min_args_size .. args.size)
214
+ if arguments.size < min_args_size
215
+ raise ArgumentError.new("Missing mandatory argument(s)")
216
+ elsif arguments.size > args.size
217
+ raise ArgumentError.new("Too many arguments")
218
+ else
168
219
  arguments.zip(args) do |value, (name, _)|
169
220
  result[name] = value
170
221
  end
171
- else
172
- print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
173
222
  end
174
223
  end
175
224
 
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(', ')
181
-
182
- raise ExitError
183
- end
184
-
185
- def print_optparse_arguments_help(commands_stack, args, parser_opts_copy)
186
- parser_opts_help = parser_opts_copy.to_s
187
-
188
- if commands_stack.size > 0
189
- parser_opts_help = parser_opts_help.sub!('[options]', commands_stack.join(' ') + ' [options]')
190
- end
191
-
192
- if args.size > 0
193
- args_display = args.map { |name, mandatory| mandatory ? "<#{ name }>" : "[<#{ name }>]" }.join(' ')
194
- parser_opts_help = parser_opts_help.sub!(/^(Usage: .*)/) { |text| "#{text} #{args_display}" }
195
- end
196
-
197
- @output.puts parser_opts_help
198
- @output.puts "", @long_help if @long_help
199
-
200
- raise ExitError
201
- end
202
-
203
225
  # HELPERS ##############################################
204
226
 
205
227
  def compose_returned_commands(commands_stack)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'tab_completion/commandline_processor'
4
+
5
+ module SimpleScripting
6
+
7
+ # The naming of each of the the commandline units is not standard, therefore we establish the
8
+ # following arbitrary, but consistent, naming:
9
+ #
10
+ # executable --option option_parameter argument
11
+ #
12
+ # The commandline is divided into words, as Bash would split them. All the words, except the
13
+ # executable, compose an array that we call `argv` (as Ruby would do).
14
+ #
15
+ # We define each {option name => value} or {argument name => value} as `pair`.
16
+ #
17
+ # In the context of a pair, each pair is composed of a `key` and a `value`.
18
+ #
19
+ class TabCompletion
20
+
21
+ def initialize(switches_definition, output: $stdout)
22
+ @switches_definition = switches_definition
23
+ @output = output
24
+ end
25
+
26
+ # Currently, any completion suffix is ignored and stripped.
27
+ #
28
+ def complete(execution_target, source_commandline=ENV.fetch('COMP_LINE'), cursor_position=ENV.fetch('COMP_POINT').to_i)
29
+ commandline_processor = CommandlineProcessor.process_commandline(source_commandline, cursor_position, @switches_definition)
30
+
31
+ if commandline_processor.completing_an_option?
32
+ complete_option(commandline_processor, execution_target)
33
+ elsif commandline_processor.parsing_error?
34
+ return
35
+ else # completing_a_value?
36
+ complete_value(commandline_processor, execution_target)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ #############################################
43
+ # Completion!
44
+ #############################################
45
+
46
+ def complete_option(commandline_processor, execution_target)
47
+ all_switches = @switches_definition.select { |definition| definition.is_a?(Array) }.map { |definition| definition[1][/^--\S+/] }
48
+
49
+ matching_switches = all_switches.select { |switch| switch.start_with?(commandline_processor.completing_word_prefix) }
50
+
51
+ output_entries(matching_switches)
52
+ end
53
+
54
+ def complete_value(commandline_processor, execution_target)
55
+ key, value_prefix, value_suffix, other_pairs = commandline_processor.parsed_pairs
56
+
57
+ selected_entries = execution_target.send(key, value_prefix, value_suffix, other_pairs)
58
+
59
+ output_entries(selected_entries)
60
+ end
61
+
62
+ #############################################
63
+ # Helpers
64
+ #############################################
65
+
66
+ def output_entries(entries)
67
+ @output.print entries.join("\n")
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+
5
+ require_relative '../argv'
6
+
7
+ module SimpleScripting
8
+
9
+ class TabCompletion
10
+
11
+ class CommandlineProcessor < Struct.new(:processed_argv, :cursor_marker, :switches_definition)
12
+
13
+ # Arbitrary; can be anything (except an empty string).
14
+ BASE_CURSOR_MARKER = "<tab>"
15
+
16
+ OPTIONS_TERMINATOR = "--"
17
+ LONG_OPTIONS_PREFIX = "--"
18
+
19
+ def self.process_commandline(source_commandline, cursor_position, switches_definition)
20
+ # An input string with infinite "<tabN>" substrings will cause an infinite cycle (hehe).
21
+ 0.upto(Float::INFINITY) do |i|
22
+ cursor_marker = BASE_CURSOR_MARKER.sub(">", "#{i}>")
23
+
24
+ if !source_commandline.include?(cursor_marker)
25
+ commandline_with_marker = source_commandline[0...cursor_position] + cursor_marker + source_commandline[cursor_position..-1].to_s
26
+
27
+ # Remove the executable.
28
+ processed_argv = Shellwords.split(commandline_with_marker)[1..-1]
29
+
30
+ return new(processed_argv, cursor_marker, switches_definition)
31
+ end
32
+ end
33
+ end
34
+
35
+ # We're abstracted from the commandline, with this exception. This is because while an option
36
+ # is being completed, the decoder would not recognize the key.
37
+ #
38
+ def completing_an_option?
39
+ processed_argv[marked_word_position].start_with?(LONG_OPTIONS_PREFIX) && marked_word_position < options_terminator_position
40
+ end
41
+
42
+ def parsing_error?
43
+ parse_argv.nil?
44
+ end
45
+
46
+ def completing_word_prefix
47
+ word = processed_argv[marked_word_position]
48
+
49
+ # Regex alternative: [/\A(.*?)#{cursor_marker}/m, 1]
50
+ word[0, word.index(cursor_marker)]
51
+ end
52
+
53
+ # Returns key, value prefix (before marker), value suffix (after marker), other_pairs
54
+ #
55
+ def parsed_pairs
56
+ parsed_pairs = parse_argv || raise("Parsing error")
57
+
58
+ key, value = parsed_pairs.detect do |_, value|
59
+ !boolean?(value) && value.include?(cursor_marker)
60
+ end
61
+
62
+ # Impossible case, unless there is a programmatic error.
63
+ #
64
+ key || raise("Guru meditation! (#{self.class}##{__method__}:#{__LINE__})")
65
+
66
+ value_prefix, value_suffix = value.split(cursor_marker)
67
+
68
+ parsed_pairs.delete(key)
69
+
70
+ [key, value_prefix || "", value_suffix || "", parsed_pairs]
71
+ end
72
+
73
+ private
74
+
75
+ def marked_word_position
76
+ processed_argv.index { |word| word.include?(cursor_marker) }
77
+ end
78
+
79
+ # Returns Float::INFINITY when there is no options terminator.
80
+ #
81
+ def options_terminator_position
82
+ processed_argv.index(OPTIONS_TERMINATOR) || Float::INFINITY
83
+ end
84
+
85
+ #############################################
86
+ # Helpers
87
+ #############################################
88
+
89
+ def parse_argv
90
+ # We need to convert all the arguments to optional, otherwise it's not possible to
91
+ # autocomplete arguments when not all the mandatory ones are not typed yet, eg:
92
+ #
93
+ # `my_command <tab>` with definition ['mand1', 'mand2']
94
+ #
95
+ adapted_switches_definition = switches_definition.dup
96
+
97
+ adapted_switches_definition.each_with_index do |definition, i|
98
+ adapted_switches_definition[i] = "[#{definition}]" if definition.is_a?(String) && !definition.start_with?('[')
99
+ end
100
+
101
+ SimpleScripting::Argv.decode(*adapted_switches_definition, arguments: processed_argv.dup, auto_help: false)
102
+ rescue Argv::InvalidCommand, Argv::ArgumentError, OptionParser::InvalidOption
103
+ # OptionParser::InvalidOption: see case "-O<tab>" in test suite.
104
+
105
+ # return nil
106
+ end
107
+
108
+ # For the lulz.
109
+ #
110
+ def boolean?(value)
111
+ !!value == value
112
+ end
113
+
114
+ end # CommandlineProcessor
115
+
116
+ end # TabCompletion
117
+
118
+ end # SimpleScripting
@@ -1,5 +1,5 @@
1
1
  module SimpleScripting
2
2
 
3
- VERSION = "0.9.4"
3
+ VERSION = "0.10.0"
4
4
 
5
5
  end
@@ -8,8 +8,9 @@ Gem::Specification.new do |s|
8
8
  s.name = "simple_scripting"
9
9
  s.version = SimpleScripting::VERSION
10
10
  s.platform = Gem::Platform::RUBY
11
+ s.required_ruby_version = '>= 2.3.0'
11
12
  s.authors = ["Saverio Miroddi"]
12
- s.date = "2018-01-26"
13
+ s.date = "2018-07-26"
13
14
  s.email = ["saverio.pub2@gmail.com"]
14
15
  s.homepage = "https://github.com/saveriomiroddi/simple_scripting"
15
16
  s.summary = "Library for simplifying some typical scripting functionalities."
@@ -20,7 +21,6 @@ Gem::Specification.new do |s|
20
21
 
21
22
  s.add_development_dependency "rake", "~> 12.0"
22
23
  s.add_development_dependency "rspec", "~> 3.6"
23
- s.add_development_dependency 'coveralls', "~> 0.8.21"
24
24
 
25
25
  s.files = `git ls-files`.split("\n")
26
26
  s.test_files = `git ls-files -- spec/*`.split("\n")
@@ -0,0 +1,55 @@
1
+ require 'stringio'
2
+
3
+ # Custom matchers for the tab completion test suite.
4
+ #
5
+ # Require :subject and :output_buffer to be defined/accessible.
6
+ #
7
+ # The matchers are simplistic (but still adequate); the (most) appropriate choice would be
8
+ # [diffable matchers](https://relishapp.com/rspec/rspec-expectations/v/3-6/docs/custom-matchers/define-diffable-matcher)
9
+ #
10
+ # Note that the semantic of the expected value is different from the standard rspec one, since we
11
+ # process it, therefore, we need to use intermediate instance variables.
12
+ #
13
+ module TabCompletionCustomRSpecMatchers
14
+
15
+ extend RSpec::Matchers::DSL
16
+
17
+ # Doesn't matter in this context.
18
+ #
19
+ PHONY_EXECUTABLE = '/path/to/executable'
20
+
21
+ matcher :complete_with do |expected_entries|
22
+ match do |symbolic_commandline_options|
23
+ commandline = "#{PHONY_EXECUTABLE} #{symbolic_commandline_options}"
24
+ cursor_position = commandline.index("<tab>")
25
+ commandline = commandline.sub("<tab>", "")
26
+
27
+ subject.complete(execution_target, commandline, cursor_position)
28
+
29
+ @actual_output = output_buffer.string
30
+ expected_output = expected_entries.join("\n")
31
+
32
+ expect(@actual_output).to eql(expected_output)
33
+ end
34
+
35
+ failure_message do |actual|
36
+ actual_entries = @actual_output.split("\n")
37
+
38
+ "#{actual} listed #{actual_entries} instead of #{expected}"
39
+ end
40
+ end
41
+
42
+ matcher :not_complete do
43
+ match do |symbolic_commandline_options|
44
+ expect(symbolic_commandline_options).to complete_with([])
45
+ end
46
+
47
+ failure_message do |actual|
48
+ @actual_output = output_buffer.string
49
+ actual_entries = @actual_output.split("\n")
50
+
51
+ "#{actual} listed #{actual_entries} instead of no entries"
52
+ end
53
+ end
54
+
55
+ end # TabCompletionCustomRSpecMatchers
@@ -23,27 +23,59 @@ describe SimpleScripting::Argv do
23
23
  output: output_buffer,
24
24
  ]}
25
25
 
26
- it 'should implement the help' do
27
- decoder_params.last[:arguments] = ['-h']
28
-
29
- return_value = described_class.decode(*decoder_params)
30
-
31
- expected_output = %Q{\
32
- Usage: rspec [options] <mandatory> [<optional>]
33
- -a
34
- -b "-b" description
35
- -c, --c-switch
36
- -d, --d-switch "-d" description
37
- -e, --e-switch VALUE
38
- -f, --f-switch VALUE "-f" description
39
- -h, --help Help
40
-
41
- This is the long help!
42
- }
43
-
44
- expect(output_buffer.string).to eql(expected_output)
45
- expect(return_value).to be(nil)
46
- end
26
+ context 'help' do
27
+
28
+ it 'should print help automatically by default' do
29
+ decoder_params.last[:arguments] = ['-h']
30
+
31
+ return_value = described_class.decode(*decoder_params)
32
+
33
+ expected_output = <<~OUTPUT
34
+ Usage: rspec [options] <mandatory> [<optional>]
35
+ -a
36
+ -b "-b" description
37
+ -c, --c-switch
38
+ -d, --d-switch "-d" description
39
+ -e, --e-switch VALUE
40
+ -f, --f-switch VALUE "-f" description
41
+ -h, --help Help
42
+
43
+ This is the long help!
44
+ OUTPUT
45
+
46
+ expect(output_buffer.string).to eql(expected_output)
47
+ expect(return_value).to be(nil)
48
+ end
49
+
50
+ it 'should not interpret the --help argument, and not print the help, on auto_help: false' do
51
+ decoder_params.last.merge!(
52
+ arguments: ['--help', 'm_arg'],
53
+ auto_help: false
54
+ )
55
+
56
+ actual_result = described_class.decode(*decoder_params)
57
+
58
+ expected_result = {
59
+ help: true,
60
+ mandatory: 'm_arg',
61
+ }
62
+
63
+ expect(output_buffer.string).to eql('')
64
+ expect(actual_result).to eql(expected_result)
65
+ end
66
+
67
+ it "should check all the options/arguments when --help is passed, raising an error when they're not correct" do
68
+ decoder_params.last.merge!(
69
+ arguments: ['--help'],
70
+ auto_help: false
71
+ )
72
+
73
+ decoding = -> { described_class.decode(*decoder_params) }
74
+
75
+ expect(decoding).to raise_error(SimpleScripting::Argv::ArgumentError, "Missing mandatory argument(s)")
76
+ end
77
+
78
+ end # context 'help'
47
79
 
48
80
  it "should implement basic switches and arguments (all set)" do
49
81
  decoder_params.last[:arguments] = ['-a', '-b', '-c', '-d', '-ev_swt', '-fv_swt', 'm_arg', 'o_arg']
@@ -76,7 +108,62 @@ This is the long help!
76
108
  expect(actual_result).to eql(expected_result)
77
109
  end
78
110
 
79
- end
111
+ context "multiple optional arguments" do
112
+
113
+ let(:decoder_params) {[
114
+ '[optional1]',
115
+ '[optional2]',
116
+ output: output_buffer,
117
+ ]}
118
+
119
+ it "should correctly decode a single argument passed" do
120
+ decoder_params.last[:arguments] = ['o_arg1']
121
+
122
+ actual_result = described_class.decode(*decoder_params)
123
+
124
+ expected_result = {
125
+ optional1: 'o_arg1',
126
+ }
127
+
128
+ expect(actual_result).to eql(expected_result)
129
+ end
130
+
131
+ it "should correctly decode all arguments passed" do
132
+ decoder_params.last[:arguments] = ['o_arg1', 'o_arg2']
133
+
134
+ actual_result = described_class.decode(*decoder_params)
135
+
136
+ expected_result = {
137
+ optional1: 'o_arg1',
138
+ optional2: 'o_arg2',
139
+ }
140
+
141
+ expect(actual_result).to eql(expected_result)
142
+ end
143
+
144
+ end
145
+
146
+ context "error handling" do
147
+
148
+ it "should raise an error when mandatory arguments are missing" do
149
+ decoder_params.last[:arguments] = []
150
+
151
+ decoding = -> { described_class.decode(*decoder_params) }
152
+
153
+ expect(decoding).to raise_error(SimpleScripting::Argv::ArgumentError, "Missing mandatory argument(s)")
154
+ end
155
+
156
+ it "should print an error and the help when there are too many arguments" do
157
+ decoder_params.last[:arguments] = ['arg1', 'arg2', 'excessive_arg']
158
+
159
+ decoding = -> { described_class.decode(*decoder_params) }
160
+
161
+ expect(decoding).to raise_error(SimpleScripting::Argv::ArgumentError, "Too many arguments")
162
+ end
163
+
164
+ end # context "error handling"
165
+
166
+ end # describe 'Basic functionality'
80
167
 
81
168
  describe 'Varargs' do
82
169
 
@@ -99,17 +186,19 @@ This is the long help!
99
186
  expect(actual_result).to eql(expected_result)
100
187
  end
101
188
 
102
- it "should exit when they are not specified" do
103
- decoder_params.last[:arguments] = []
189
+ context "error handling" do
104
190
 
105
- actual_result = described_class.decode(*decoder_params)
191
+ it "should exit when they are not specified" do
192
+ decoder_params.last[:arguments] = []
106
193
 
107
- expected_result = nil
194
+ decoding = -> { described_class.decode(*decoder_params) }
108
195
 
109
- expect(actual_result).to eql(expected_result)
110
- end
196
+ expect(decoding).to raise_error(SimpleScripting::Argv::ArgumentError, "Missing mandatory argument(s)")
197
+ end
111
198
 
112
- end
199
+ end # context "error handling"
200
+
201
+ end # describe '(mandatory)'
113
202
 
114
203
  describe '(optional)' do
115
204
 
@@ -142,9 +231,9 @@ This is the long help!
142
231
  expect(actual_result).to eql(expected_result)
143
232
  end
144
233
 
145
- end
234
+ end # describe '(optional)'
146
235
 
147
- end
236
+ end # describe 'Varargs'
148
237
 
149
238
  describe 'Commands' do
150
239
 
@@ -171,50 +260,86 @@ This is the long help!
171
260
  expect(actual_result).to eql(expected_result)
172
261
  end
173
262
 
174
- it 'print a message on wrong command' do
175
- decoder_params[:arguments] = ['pizza']
263
+ context "error handling" do
176
264
 
177
- described_class.decode(decoder_params)
265
+ it "should raise an error on invalid command" do
266
+ decoder_params[:arguments] = ['pizza']
178
267
 
179
- expected_output = %Q{\
180
- Invalid command. Valid commands:
268
+ decoding = -> { described_class.decode(decoder_params) }
181
269
 
182
- command1, command2
183
- }
270
+ expect(decoding).to raise_error(SimpleScripting::Argv::InvalidCommand, "Invalid command: pizza")
271
+ end
184
272
 
185
- expect(output_buffer.string).to eql(expected_output)
186
- end
273
+ end # context "error handling"
187
274
 
188
- it 'should implement the commands help' do
189
- decoder_params[:arguments] = ['-h']
275
+ context "help" do
190
276
 
191
- described_class.decode(decoder_params)
277
+ it 'should implement the commands help' do
278
+ decoder_params[:arguments] = ['-h']
192
279
 
193
- expected_output = %Q{\
194
- Valid commands:
280
+ described_class.decode(decoder_params)
195
281
 
196
- command1, command2
197
- }
282
+ expected_output = <<~OUTPUT
283
+ Valid commands:
198
284
 
199
- expect(output_buffer.string).to eql(expected_output)
200
- end
285
+ command1, command2
286
+ OUTPUT
201
287
 
202
- it "should display the command given command's help" do
203
- decoder_params[:arguments] = ['command1', '-h']
204
- $a = true
205
- described_class.decode(decoder_params)
288
+ expect(output_buffer.string).to eql(expected_output)
289
+ end
206
290
 
207
- expected_output = %Q{\
208
- Usage: rspec command1 [options] <arg1>
209
- -h, --help Help
291
+ it "should display the command given command's help" do
292
+ decoder_params[:arguments] = ['command1', '-h']
210
293
 
211
- This is the long help.
212
- }
294
+ described_class.decode(decoder_params)
213
295
 
214
- expect(output_buffer.string).to eql(expected_output)
215
- end
296
+ expected_output = <<~OUTPUT
297
+ Usage: rspec command1 [options] <arg1>
298
+ -h, --help Help
216
299
 
217
- end
300
+ This is the long help.
301
+ OUTPUT
302
+
303
+ expect(output_buffer.string).to eql(expected_output)
304
+ end
305
+
306
+ context 'auto_help: false' do
307
+
308
+ it 'should not interpret the --help argument, and not print the help' do
309
+ decoder_params.merge!(
310
+ arguments: ['-h'],
311
+ auto_help: false,
312
+ )
313
+
314
+ actual_result = described_class.decode(decoder_params)
315
+
316
+ expected_result = {
317
+ help: true,
318
+ }
319
+
320
+ expect(actual_result).to eql(expected_result)
321
+ end
322
+
323
+ it 'should ignore and not return all the other arguments' do
324
+ decoder_params.merge!(
325
+ arguments: ['-h', 'pizza'],
326
+ auto_help: false,
327
+ )
328
+
329
+ actual_result = described_class.decode(decoder_params)
330
+
331
+ expected_result = {
332
+ help: true,
333
+ }
334
+
335
+ expect(actual_result).to eql(expected_result)
336
+ end
337
+
338
+ end # context 'auto_help: false'
339
+
340
+ end # context 'help'
341
+
342
+ end # describe 'regular case'
218
343
 
219
344
  describe 'Nested commands' do
220
345
 
@@ -259,11 +384,11 @@ This is the long help.
259
384
 
260
385
  actual_result = described_class.decode(decoder_params)
261
386
 
262
- expected_output = "\
263
- Valid commands:
387
+ expected_output = <<~OUTPUT
388
+ Valid commands:
264
389
 
265
- nested1a, nested1b
266
- "
390
+ nested1a, nested1b
391
+ OUTPUT
267
392
 
268
393
  expect(output_buffer.string).to eql(expected_output)
269
394
  end
@@ -273,33 +398,35 @@ Valid commands:
273
398
 
274
399
  actual_result = described_class.decode(decoder_params)
275
400
 
276
- expected_output = "\
277
- Usage: rspec command1 nested1a [options] <arg1>
278
- -h, --help Help
401
+ expected_output = <<~OUTPUT
402
+ Usage: rspec command1 nested1a [options] <arg1>
403
+ -h, --help Help
279
404
 
280
- nested1a long help.
281
- "
405
+ nested1a long help.
406
+ OUTPUT
282
407
 
283
408
  expect(output_buffer.string).to eql(expected_output)
284
409
  end
285
- end
410
+ end # describe 'Nested commands'
286
411
 
287
- describe 'No argv case' do
412
+ end # describe 'Commands'
288
413
 
289
- let(:decoder_params) {{
290
- output: output_buffer,
291
- }}
414
+ # Special case.
415
+ #
416
+ describe 'No definitions given' do
292
417
 
293
- it 'should avoided options being interpreted as definitions' do
294
- decoder_params[:arguments] = ['pizza']
418
+ let(:decoder_params) {{
419
+ output: output_buffer,
420
+ }}
295
421
 
296
- actual_result = described_class.decode(decoder_params)
422
+ it 'should avoid options being interpreted as definitions' do
423
+ decoder_params[:arguments] = ['pizza']
297
424
 
298
- expect(actual_result).to be(nil)
299
- end
425
+ decoding = -> { described_class.decode(decoder_params) }
300
426
 
427
+ expect(decoding).to raise_error(SimpleScripting::Argv::ArgumentError, "Too many arguments")
301
428
  end
302
429
 
303
- end
430
+ end # describe 'No definitions given'
304
431
 
305
432
  end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/simple_scripting/tab_completion.rb'
4
+
5
+ describe SimpleScripting::TabCompletion do
6
+
7
+ include TabCompletionCustomRSpecMatchers
8
+
9
+ let(:output_buffer) {
10
+ StringIO.new
11
+ }
12
+
13
+ let(:switches_definition) {
14
+ [
15
+ ["-o", "--opt1 ARG"],
16
+ ["-O", "--opt2"],
17
+ "arg1", # this and the following are internally converted to optional, as
18
+ "arg2", # according to the Argv spec, without brackets they are mandatory.
19
+ ]
20
+ }
21
+
22
+ let(:execution_target) {
23
+ # Simplistic implementation. In real world, regex are not to be used this way, for multiple
24
+ # reasons.
25
+ #
26
+ Class.new do
27
+ def opt1(prefix, suffix, context)
28
+ %w(opt1v1 _opt1v2).select { |entry| entry =~ /^#{prefix}#{suffix}/ }
29
+ end
30
+
31
+ def arg1(prefix, suffix, context)
32
+ # A value starting with space is valid.
33
+ #
34
+ ['arg1v1', 'arg1v2', '_arg1v3', ' _argv1spc'].select { |entry| entry =~ /^#{prefix}#{suffix}/ }
35
+ end
36
+
37
+ def arg2(prefix, suffix, context)
38
+ # A value starting with minus is valid.
39
+ #
40
+ %w(arg2v1 arg2v2 --arg2v3).select { |entry| entry =~ /^#{prefix}#{suffix}/ }
41
+ end
42
+ end.new
43
+ }
44
+
45
+ subject { described_class.new(switches_definition, output: output_buffer) }
46
+
47
+ context "with a correct configuration" do
48
+
49
+ context "standard cases" do
50
+
51
+ # Note that the conversion of mandatory to optional argument is defined by most of the cases.
52
+ #
53
+ STANDARD_CASES = {
54
+ "<tab>" => ["arg1v1", "arg1v2", "_arg1v3", " _argv1spc"],
55
+ "a<tab>" => %w(arg1v1 arg1v2),
56
+ "--opt2 <tab>" => ["arg1v1", "arg1v2", "_arg1v3", " _argv1spc"],
57
+ "-- <tab>" => ["arg1v1", "arg1v2", "_arg1v3", " _argv1spc"],
58
+
59
+ "a <tab>" => %w(arg2v1 arg2v2 --arg2v3),
60
+ "a -- --<tab>" => %w(--arg2v3),
61
+ "-- --aaa <tab>" => %w(arg2v1 arg2v2 --arg2v3),
62
+
63
+ "--<tab>" => %w(--opt1 --opt2),
64
+ "--<tab> a" => %w(--opt1 --opt2),
65
+ "--<tab> -- a" => %w(--opt1 --opt2),
66
+ "--<tab> -- b" => %w(--opt1 --opt2),
67
+ "--<tab> --xyz" => %w(--opt1 --opt2),
68
+ "--opt1 <tab> a" => %w(opt1v1 _opt1v2),
69
+ "--opt1 o<tab> a" => %w(opt1v1),
70
+
71
+ "-o<tab>" => %w(opt1v1 _opt1v2),
72
+ "-o <tab>" => %w(opt1v1 _opt1v2),
73
+ "-o -O <tab>" => ["arg1v1", "arg1v2", "_arg1v3", " _argv1spc"],
74
+ "-O <tab>" => ["arg1v1", "arg1v2", "_arg1v3", " _argv1spc"],
75
+ }
76
+
77
+ STANDARD_CASES.each do |symbolic_commandline_options, expected_entries|
78
+ it "should output the entries for #{symbolic_commandline_options.inspect}" do
79
+ expect(symbolic_commandline_options).to complete_with(expected_entries)
80
+ end
81
+ end
82
+
83
+ end # context "standard cases"
84
+
85
+ context "suffix management" do
86
+
87
+ SUFFIX_CASES = {
88
+ "arg1<tab>v" => %w(arg1v1 arg1v2), # the execution target of the test suite doesn't
89
+ "arg1<tab>x" => %w(), # ignore the suffix; programmer-defined
90
+
91
+ "--o<tab>p" => %w(--opt1 --opt2), # options ignore the suffix (like bash); can't be
92
+ "--o<tab>x" => %w(--opt1 --opt2), # currently changed by the programmer.
93
+ }
94
+
95
+ SUFFIX_CASES.each do |symbolic_commandline_options, expected_entries|
96
+ it "should output the entries for #{symbolic_commandline_options.inspect}, ignoring the suffix" do
97
+ expect(symbolic_commandline_options).to complete_with(expected_entries)
98
+ end
99
+ end
100
+
101
+ end # context "suffix management"
102
+
103
+ context "escaped cases" do
104
+
105
+ ESCAPED_CASES = {
106
+ "\ <tab>" => [" _argv1spc"],
107
+ '\-<tab>' => %w(), # this is the result of typing `command "\-<tab>`
108
+ 'a \-<tab>' => %w(--arg2v3),
109
+ }
110
+
111
+ ESCAPED_CASES.each do |symbolic_commandline_options, _|
112
+ it "should output the entries for #{symbolic_commandline_options.inspect}"
113
+ end
114
+
115
+ end # context "escaped cases"
116
+
117
+ it "should support multiple values for an option"
118
+
119
+ it "should keep parsing also when --help is passed" do
120
+ expect("--help a<tab>").to complete_with(%w(arg1v1 arg1v2))
121
+ end
122
+
123
+ end # context "with a correct configuration"
124
+
125
+ context "with an incorrect configuration" do
126
+
127
+ INCORRECT_CASES = [
128
+ "a b <tab>", # too many args
129
+ "-O<tab>", # no values for this option
130
+ ]
131
+
132
+ INCORRECT_CASES.each do |symbolic_commandline_options|
133
+ it "should not output any entries for #{symbolic_commandline_options.inspect}" do
134
+ expect(symbolic_commandline_options).to not_complete
135
+ end
136
+ end
137
+
138
+ end # context "with an incorrect configuration"
139
+
140
+ end # describe SimpleScripting::TabCompletion
data/spec/spec_helper.rb CHANGED
@@ -49,3 +49,5 @@ RSpec.configure do |config|
49
49
  # triggering implicit auto-inclusion in groups with matching metadata.
50
50
  config.shared_context_metadata_behavior = :apply_to_host_groups
51
51
  end
52
+
53
+ require_relative 'helpers/tab_completion_custom_rspec_matchers'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_scripting
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Saverio Miroddi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-26 00:00:00.000000000 Z
11
+ date: 2018-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parseconfig
@@ -52,20 +52,6 @@ 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
69
55
  description: Simplifies options parsing and configuration loading.
70
56
  email:
71
57
  - saverio.pub2@gmail.com
@@ -75,6 +61,7 @@ extra_rdoc_files: []
75
61
  files:
76
62
  - ".gitignore"
77
63
  - ".rspec"
64
+ - ".simplecov"
78
65
  - ".travis.yml"
79
66
  - Gemfile
80
67
  - Gemfile.lock
@@ -84,11 +71,15 @@ files:
84
71
  - lib/simple_scripting/argv.rb
85
72
  - lib/simple_scripting/configuration.rb
86
73
  - lib/simple_scripting/configuration/value.rb
74
+ - lib/simple_scripting/tab_completion.rb
75
+ - lib/simple_scripting/tab_completion/commandline_processor.rb
87
76
  - lib/simple_scripting/version.rb
88
77
  - simple_scripting.gemspec
78
+ - spec/helpers/tab_completion_custom_rspec_matchers.rb
89
79
  - spec/simple_scripting/argv_spec.rb
90
80
  - spec/simple_scripting/configuration/value_spec.rb
91
81
  - spec/simple_scripting/configuration_spec.rb
82
+ - spec/simple_scripting/tab_completion_spec.rb
92
83
  - spec/spec_helper.rb
93
84
  homepage: https://github.com/saveriomiroddi/simple_scripting
94
85
  licenses:
@@ -102,7 +93,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
93
  requirements:
103
94
  - - ">="
104
95
  - !ruby/object:Gem::Version
105
- version: '0'
96
+ version: 2.3.0
106
97
  required_rubygems_version: !ruby/object:Gem::Requirement
107
98
  requirements:
108
99
  - - ">="
@@ -110,12 +101,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
101
  version: '0'
111
102
  requirements: []
112
103
  rubyforge_project:
113
- rubygems_version: 2.6.14
104
+ rubygems_version: 2.6.13
114
105
  signing_key:
115
106
  specification_version: 4
116
107
  summary: Library for simplifying some typical scripting functionalities.
117
108
  test_files:
109
+ - spec/helpers/tab_completion_custom_rspec_matchers.rb
118
110
  - spec/simple_scripting/argv_spec.rb
119
111
  - spec/simple_scripting/configuration/value_spec.rb
120
112
  - spec/simple_scripting/configuration_spec.rb
113
+ - spec/simple_scripting/tab_completion_spec.rb
121
114
  - spec/spec_helper.rb