gli_aziz_light 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +17 -0
  6. data/CONTRIBUTING.md +23 -0
  7. data/Gemfile +8 -0
  8. data/LICENSE.txt +201 -0
  9. data/ObjectModel.graffle +1191 -0
  10. data/README.rdoc +109 -0
  11. data/Rakefile +126 -0
  12. data/bin/gli +59 -0
  13. data/bin/report_on_rake_results +10 -0
  14. data/bin/test_all_rubies.sh +6 -0
  15. data/features/gli_executable.feature +90 -0
  16. data/features/gli_init.feature +232 -0
  17. data/features/step_definitions/gli_executable_steps.rb +18 -0
  18. data/features/step_definitions/gli_init_steps.rb +11 -0
  19. data/features/step_definitions/todo_steps.rb +88 -0
  20. data/features/support/env.rb +53 -0
  21. data/features/todo.feature +413 -0
  22. data/features/todo_legacy.feature +128 -0
  23. data/gli.cheat +95 -0
  24. data/gli.gemspec +34 -0
  25. data/gli.rdoc +73 -0
  26. data/lib/gli.rb +35 -0
  27. data/lib/gli/app.rb +286 -0
  28. data/lib/gli/app_support.rb +341 -0
  29. data/lib/gli/command.rb +171 -0
  30. data/lib/gli/command_finder.rb +41 -0
  31. data/lib/gli/command_line_option.rb +34 -0
  32. data/lib/gli/command_line_token.rb +63 -0
  33. data/lib/gli/command_support.rb +181 -0
  34. data/lib/gli/commands/compound_command.rb +42 -0
  35. data/lib/gli/commands/doc.rb +231 -0
  36. data/lib/gli/commands/help.rb +95 -0
  37. data/lib/gli/commands/help_modules/arg_name_formatter.rb +20 -0
  38. data/lib/gli/commands/help_modules/command_finder.rb +60 -0
  39. data/lib/gli/commands/help_modules/command_help_format.rb +156 -0
  40. data/lib/gli/commands/help_modules/global_help_format.rb +70 -0
  41. data/lib/gli/commands/help_modules/help_completion_format.rb +31 -0
  42. data/lib/gli/commands/help_modules/list_formatter.rb +23 -0
  43. data/lib/gli/commands/help_modules/one_line_wrapper.rb +18 -0
  44. data/lib/gli/commands/help_modules/options_formatter.rb +49 -0
  45. data/lib/gli/commands/help_modules/text_wrapper.rb +53 -0
  46. data/lib/gli/commands/help_modules/tty_only_wrapper.rb +23 -0
  47. data/lib/gli/commands/help_modules/verbatim_wrapper.rb +16 -0
  48. data/lib/gli/commands/initconfig.rb +74 -0
  49. data/lib/gli/commands/rdoc_document_listener.rb +116 -0
  50. data/lib/gli/commands/scaffold.rb +401 -0
  51. data/lib/gli/dsl.rb +226 -0
  52. data/lib/gli/exceptions.rb +71 -0
  53. data/lib/gli/flag.rb +68 -0
  54. data/lib/gli/gli_option_block_parser.rb +84 -0
  55. data/lib/gli/gli_option_parser.rb +156 -0
  56. data/lib/gli/option_parser_factory.rb +81 -0
  57. data/lib/gli/option_parsing_result.rb +21 -0
  58. data/lib/gli/options.rb +23 -0
  59. data/lib/gli/switch.rb +35 -0
  60. data/lib/gli/terminal.rb +101 -0
  61. data/lib/gli/version.rb +5 -0
  62. data/test/apps/README.md +2 -0
  63. data/test/apps/todo/Gemfile +2 -0
  64. data/test/apps/todo/README.rdoc +6 -0
  65. data/test/apps/todo/Rakefile +23 -0
  66. data/test/apps/todo/bin/todo +63 -0
  67. data/test/apps/todo/lib/todo/commands/create.rb +24 -0
  68. data/test/apps/todo/lib/todo/commands/list.rb +63 -0
  69. data/test/apps/todo/lib/todo/commands/ls.rb +47 -0
  70. data/test/apps/todo/lib/todo/commands/make.rb +52 -0
  71. data/test/apps/todo/lib/todo/version.rb +3 -0
  72. data/test/apps/todo/test/tc_nothing.rb +14 -0
  73. data/test/apps/todo/todo.gemspec +23 -0
  74. data/test/apps/todo/todo.rdoc +5 -0
  75. data/test/apps/todo_legacy/Gemfile +2 -0
  76. data/test/apps/todo_legacy/README.rdoc +6 -0
  77. data/test/apps/todo_legacy/Rakefile +23 -0
  78. data/test/apps/todo_legacy/bin/todo +61 -0
  79. data/test/apps/todo_legacy/lib/todo/commands/create.rb +24 -0
  80. data/test/apps/todo_legacy/lib/todo/commands/list.rb +63 -0
  81. data/test/apps/todo_legacy/lib/todo/commands/ls.rb +47 -0
  82. data/test/apps/todo_legacy/lib/todo/version.rb +3 -0
  83. data/test/apps/todo_legacy/test/tc_nothing.rb +14 -0
  84. data/test/apps/todo_legacy/todo.gemspec +23 -0
  85. data/test/apps/todo_legacy/todo.rdoc +5 -0
  86. data/test/apps/todo_plugins/commands/third.rb +1 -0
  87. data/test/config.yaml +10 -0
  88. data/test/fake_std_out.rb +30 -0
  89. data/test/init_simplecov.rb +8 -0
  90. data/test/option_test_helper.rb +13 -0
  91. data/test/tc_command.rb +508 -0
  92. data/test/tc_compound_command.rb +22 -0
  93. data/test/tc_doc.rb +325 -0
  94. data/test/tc_flag.rb +62 -0
  95. data/test/tc_gli.rb +773 -0
  96. data/test/tc_help.rb +387 -0
  97. data/test/tc_options.rb +43 -0
  98. data/test/tc_subcommand_parsing.rb +104 -0
  99. data/test/tc_subcommands.rb +260 -0
  100. data/test/tc_switch.rb +55 -0
  101. data/test/tc_terminal.rb +100 -0
  102. data/test/tc_verbatim_wrapper.rb +36 -0
  103. data/test/test_helper.rb +20 -0
  104. metadata +330 -0
@@ -0,0 +1,156 @@
1
+ module GLI
2
+ # Parses the command-line options using an actual +OptionParser+
3
+ class GLIOptionParser
4
+ def initialize(commands,flags,switches,accepts,default_command = nil,subcommand_option_handling_strategy=:legacy)
5
+ command_finder = CommandFinder.new(commands,default_command || "help")
6
+ @global_option_parser = GlobalOptionParser.new(OptionParserFactory.new(flags,switches,accepts),command_finder)
7
+ @accepts = accepts
8
+ @subcommand_option_handling_strategy = subcommand_option_handling_strategy
9
+ end
10
+
11
+ # Given the command-line argument array, returns an OptionParsingResult
12
+ def parse_options(args) # :nodoc:
13
+ option_parser_class = self.class.const_get("#{@subcommand_option_handling_strategy.to_s.capitalize}CommandOptionParser")
14
+ OptionParsingResult.new.tap { |parsing_result|
15
+ parsing_result.arguments = args
16
+ parsing_result = @global_option_parser.parse!(parsing_result)
17
+ parsing_result.cli_options = @global_option_parser.cli_options
18
+ option_parser_class.new(@accepts).parse!(parsing_result)
19
+ }
20
+ end
21
+
22
+ private
23
+
24
+ class GlobalOptionParser
25
+ attr_reader :cli_options
26
+
27
+ def initialize(option_parser_factory,command_finder)
28
+ @option_parser_factory = option_parser_factory
29
+ @command_finder = command_finder
30
+ end
31
+
32
+ def parse!(parsing_result)
33
+ parsing_result.arguments = GLIOptionBlockParser.new(@option_parser_factory,UnknownGlobalArgument).parse!(parsing_result.arguments)
34
+ @cli_options = { :global => @option_parser_factory.options_hash.dup, "commands" => {} }
35
+ parsing_result.global_options = @option_parser_factory.options_hash_with_defaults_set!
36
+ command_name = if parsing_result.global_options[:help]
37
+ "help"
38
+ else
39
+ parsing_result.arguments.shift
40
+ end
41
+ parsing_result.command = @command_finder.find_command(command_name)
42
+ parsing_result
43
+ end
44
+ end
45
+
46
+ class NormalCommandOptionParser
47
+ attr_accessor :cli_options
48
+
49
+ def initialize(accepts)
50
+ @accepts = accepts
51
+ end
52
+
53
+ def error_handler
54
+ lambda { |message,extra_error_context|
55
+ raise UnknownCommandArgument.new(message,extra_error_context)
56
+ }
57
+ end
58
+
59
+ def parse!(parsing_result)
60
+ parsed_command_options = {}
61
+ command = parsing_result.command
62
+ arguments = nil
63
+
64
+ loop do
65
+ option_parser_factory = OptionParserFactory.for_command(command,@accepts)
66
+ option_block_parser = CommandOptionBlockParser.new(option_parser_factory, self.error_handler)
67
+ option_block_parser.command = command
68
+ arguments = parsing_result.arguments
69
+
70
+ arguments = option_block_parser.parse!(arguments)
71
+
72
+ parsing_result.cli_options["commands"][command.name] = option_parser_factory.options_hash.dup
73
+ parsed_command_options[command] = option_parser_factory.options_hash_with_defaults_set!
74
+ command_finder = CommandFinder.new(command.commands,command.get_default_command)
75
+ next_command_name = arguments.shift
76
+
77
+ begin
78
+ command = command_finder.find_command(next_command_name)
79
+ rescue AmbiguousCommand
80
+ arguments.unshift(next_command_name)
81
+ break
82
+ rescue UnknownCommand
83
+ arguments.unshift(next_command_name)
84
+ # Although command finder could certainy know if it should use
85
+ # the default command, it has no way to put the "unknown command"
86
+ # back into the argument stack. UGH.
87
+ unless command.get_default_command.nil?
88
+ command = command_finder.find_command(command.get_default_command)
89
+ end
90
+ break
91
+ end
92
+ end
93
+ parsed_command_options[command] ||= {}
94
+ command_options = parsed_command_options[command]
95
+
96
+ this_command = command.parent
97
+ child_command_options = command_options
98
+
99
+ while this_command.kind_of?(command.class)
100
+ this_command_options = parsed_command_options[this_command] || {}
101
+ child_command_options[GLI::Command::PARENT] = this_command_options
102
+ this_command = this_command.parent
103
+ child_command_options = this_command_options
104
+ end
105
+
106
+ parsing_result.command_options = command_options
107
+ parsing_result.command = command
108
+ parsing_result.arguments = Array(arguments.compact)
109
+ parsing_result
110
+ end
111
+ end
112
+
113
+ class LegacyCommandOptionParser < NormalCommandOptionParser
114
+ def parse!(parsing_result)
115
+ command = parsing_result.command
116
+ option_parser_factory = OptionParserFactory.for_command(command,@accepts)
117
+ option_block_parser = LegacyCommandOptionBlockParser.new(option_parser_factory, self.error_handler)
118
+ option_block_parser.command = command
119
+
120
+ parsing_result.arguments = option_block_parser.parse!(parsing_result.arguments)
121
+ parsing_result.cli_options["commands"][command.name] = option_parser_factory.options_hash.dup
122
+ parsing_result.command_options = option_parser_factory.options_hash_with_defaults_set!
123
+
124
+ subcommand,args = find_subcommand(command,parsing_result.arguments)
125
+ parsing_result.command = subcommand
126
+ parsing_result.arguments = args
127
+ end
128
+
129
+ private
130
+
131
+ def find_subcommand(command,arguments)
132
+ arguments = Array(arguments)
133
+ command_name = if arguments.empty?
134
+ nil
135
+ else
136
+ arguments.first
137
+ end
138
+
139
+ default_command = command.get_default_command
140
+ finder = CommandFinder.new(command.commands,default_command.to_s)
141
+
142
+ begin
143
+ results = [finder.find_command(command_name),arguments[1..-1]]
144
+ find_subcommand(results[0],results[1])
145
+ rescue UnknownCommand, AmbiguousCommand
146
+ begin
147
+ results = [finder.find_command(default_command.to_s),arguments]
148
+ find_subcommand(results[0],results[1])
149
+ rescue UnknownCommand, AmbiguousCommand
150
+ [command,arguments]
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,81 @@
1
+ module GLI
2
+ # Factory for creating an OptionParser based on app configuration and DSL calls
3
+ class OptionParserFactory
4
+
5
+ # Create an option parser factory for a command. This has the added
6
+ # feature of setting up -h and --help on the command if those
7
+ # options aren't otherwise configured, e.g. to allow todo add --help as an
8
+ # alternate to todo help add
9
+ def self.for_command(command,accepts)
10
+ self.new(command.flags,command.switches,accepts).tap { |factory|
11
+ add_help_switches_to_command(factory.option_parser,command)
12
+ }
13
+ end
14
+
15
+ # Create an OptionParserFactory for the given
16
+ # flags, switches, and accepts
17
+ def initialize(flags,switches,accepts)
18
+ @flags = flags
19
+ @switches = switches
20
+ @options_hash = {}
21
+ @option_parser = OptionParser.new do |opts|
22
+ self.class.setup_accepts(opts,accepts)
23
+ self.class.setup_options(opts,@switches,@options_hash)
24
+ self.class.setup_options(opts,@flags,@options_hash)
25
+ end
26
+ end
27
+
28
+ attr_reader :option_parser
29
+ attr_reader :options_hash
30
+
31
+ def options_hash_with_defaults_set!
32
+ set_defaults(@flags,@options_hash)
33
+ set_defaults(@switches,@options_hash)
34
+ @options_hash
35
+ end
36
+
37
+ private
38
+
39
+ def set_defaults(options_by_name,options_hash)
40
+ options_by_name.values.each do |option|
41
+ option.names_and_aliases.each do |option_name|
42
+ [option_name,option_name.to_sym].each do |name|
43
+ options_hash[name] = option.default_value if options_hash[name].nil?
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.setup_accepts(opts,accepts)
50
+ accepts.each do |object,block|
51
+ opts.accept(object) do |arg_as_string|
52
+ block.call(arg_as_string)
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.setup_options(opts,tokens,options)
58
+ tokens.each do |ignore,token|
59
+ opts.on(*token.arguments_for_option_parser) do |arg|
60
+ token.names_and_aliases.each do |name|
61
+ options[name] = arg
62
+ options[name.to_sym] = arg
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ def self.add_help_switches_to_command(option_parser,command)
69
+ help_args = %w(-h --help).reject { |_| command.has_option?(_) }
70
+
71
+ unless help_args.empty?
72
+ help_args << "Get help for #{command.name}"
73
+ option_parser.on(*help_args) do
74
+ raise CommandException.new(nil,command,0)
75
+ end
76
+ end
77
+ end
78
+
79
+
80
+ end
81
+ end
@@ -0,0 +1,21 @@
1
+ module GLI
2
+ class OptionParsingResult
3
+ attr_accessor :global_options
4
+ attr_accessor :command
5
+ attr_accessor :command_options
6
+ attr_accessor :arguments
7
+
8
+ attr_accessor :cli_options
9
+
10
+ def convert_to_openstruct!
11
+ @global_options = Options.new(@global_options)
12
+ @command_options = Options.new(@command_options)
13
+ self
14
+ end
15
+
16
+ # Allows us to splat this object into blocks and methods expecting parameters in this order
17
+ def to_a
18
+ [@global_options,@command,@command_options,@arguments]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ require 'ostruct'
2
+
3
+ module GLI
4
+ # Subclass of +OpenStruct+ that provides hash-like methods for #[] and #[]=. Note that is is *not* a Hash.
5
+ # By using GLI::App#use_openstruct, your options will be coerced into one of these.
6
+ class Options < OpenStruct
7
+
8
+ # Return the value of an attribute
9
+ def[](k)
10
+ @table[k.to_sym]
11
+ end
12
+
13
+ # Set the value of an attribute
14
+ def[]=(k, v)
15
+ @table[k.to_sym] = v
16
+ end
17
+
18
+ def map(&block)
19
+ @table.map(&block)
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,35 @@
1
+ require 'gli/command_line_option.rb'
2
+
3
+ module GLI
4
+ # Defines a command line switch
5
+ class Switch < CommandLineOption #:nodoc:
6
+
7
+ attr_accessor :default_value
8
+ attr_reader :negatable
9
+
10
+ # Creates a new switch
11
+ #
12
+ # names - Array of symbols or strings representing the names of this switch
13
+ # options - hash of options:
14
+ # :desc - the short description
15
+ # :long_desc - the long description
16
+ # :negatable - true or false if this switch is negatable; defaults to true
17
+ # :default_value - default value if the switch is omitted
18
+ def initialize(names,options = {})
19
+ super(names,options)
20
+ @default_value = false if options[:default_value].nil?
21
+ @negatable = options[:negatable].nil? ? true : options[:negatable]
22
+ if @default_value != false && @negatable == false
23
+ raise "A switch with default #{@default_value} that isn't negatable is useless"
24
+ end
25
+ end
26
+
27
+ def arguments_for_option_parser
28
+ all_forms_a
29
+ end
30
+
31
+ def negatable?
32
+ @negatable
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,101 @@
1
+ module GLI
2
+ # Class to encapsulate stuff about the terminal. This is useful to application developers
3
+ # as a canonical means to get information about the user's current terminal configuraiton.
4
+ # GLI uses this to determine the number of columns to use when printing to the screen.
5
+ #
6
+ # To access it, use Terminal.instance. This is a singleton mostly to facilitate testing, but
7
+ # it seems reasonable enough, since there's only one terminal in effect
8
+ #
9
+ # Example:
10
+ #
11
+ # Terminal.instance.size[0] # => columns in the terminal
12
+ # Terminal.default_size = [128,24] # => change default when we can't figure it out
13
+ # raise "no ls?!?!?" unless Terminal.instance.command_exists?("ls")
14
+ #
15
+ class Terminal
16
+
17
+ @@default_size = [80,24]
18
+
19
+ # Get the default size of the terminal when we can't figure it out
20
+ #
21
+ # Returns an array of int [cols,rows]
22
+ def self.default_size
23
+ @@default_size
24
+ end
25
+
26
+ # Set the default size of the terminal to use when we can't figure it out
27
+ #
28
+ # +size+:: array of two int [cols,rows]
29
+ def self.default_size=(size)
30
+ @@default_size = size
31
+ end
32
+
33
+ # Provide access to the shared instance.
34
+ def self.instance; @@instance ||= Terminal.new; end
35
+
36
+ # Call this to cause methods to throw exceptions rather than return a sane default. You
37
+ # probably don't want to call this unless you are writing tests
38
+ def make_unsafe! #:nodoc:
39
+ @unsafe = true
40
+ end
41
+
42
+ # Returns true if the given command exists on this system
43
+ #
44
+ # +command+:: The command, as a String, to check for, without any path information.
45
+ def self.command_exists?(command)
46
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? {|dir| File.exists? File.join(dir, command) }
47
+ end
48
+
49
+ def command_exists?(command)
50
+ self.class.command_exists?(command)
51
+ end
52
+
53
+ SIZE_DETERMINERS = [
54
+ [
55
+ lambda { (ENV['COLUMNS'] =~ /^\d+$/) && (ENV['LINES'] =~ /^\d+$/) },
56
+ lambda { [ENV['COLUMNS'].to_i, ENV['LINES'].to_i] }
57
+ ],
58
+ [
59
+ lambda { (jruby? || (!STDIN.tty? && ENV['TERM'])) && command_exists?('tput') },
60
+ lambda { [run_command('tput cols').to_i, run_command('tput lines').to_i] }
61
+ ],
62
+ [
63
+ lambda { (solaris? && STDIN.tty? && command_exists?('stty')) },
64
+ lambda { run_command('stty').split("\n")[1].scan(/\d+/)[0..1].map { |size_element| size_element.to_i }.reverse }
65
+ ],
66
+ [
67
+ lambda { STDIN.tty? && command_exists?('stty') },
68
+ lambda { run_command('stty size').scan(/\d+/).map { |size_element| size_element.to_i }.reverse }
69
+ ],
70
+ [
71
+ lambda { true },
72
+ lambda { Terminal.default_size },
73
+ ],
74
+ ]
75
+
76
+ # Get the size of the current terminal.
77
+ # Ripped from hirb[https://github.com/cldwalker/hirb/blob/master/lib/hirb/util.rb]
78
+ #
79
+ # Returns an Array of size two Ints representing the terminal width and height
80
+ def size
81
+ SIZE_DETERMINERS.select { |(predicate,ignore)| predicate.call }.first[1].call
82
+ rescue Exception => ex
83
+ raise ex if @unsafe
84
+ Terminal.default_size
85
+ end
86
+
87
+ private
88
+
89
+ # Runs a command using backticks. Extracted to allow for testing
90
+ def self.run_command(command)
91
+ `#{command}`
92
+ end
93
+
94
+ # True if we are JRuby; exposed to allow for testing
95
+ def self.jruby?; RUBY_PLATFORM =~ /java/; end
96
+
97
+ # True if this is running under Solaris Sparc
98
+ def self.solaris?; RUBY_PLATFORM =~ /solaris/; end
99
+
100
+ end
101
+ end
@@ -0,0 +1,5 @@
1
+ module GLI
2
+ unless const_defined? :VERSION
3
+ VERSION = '2.8.1'
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ Special-built applications for use in tests that will hopefully reveal
2
+ various features and edge cases
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,6 @@
1
+ = todo
2
+
3
+ Describe your project here
4
+
5
+ :include:todo.rdoc
6
+
@@ -0,0 +1,23 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/rdoctask'
5
+
6
+ Rake::RDocTask.new do |rd|
7
+ rd.main = "README.rdoc"
8
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
9
+ rd.title = 'Your application title'
10
+ end
11
+
12
+ spec = eval(File.read('todo.gemspec'))
13
+
14
+ Rake::GemPackageTask.new(spec) do |pkg|
15
+ end
16
+
17
+ require 'rake/testtask'
18
+ Rake::TestTask.new do |t|
19
+ t.libs << "test"
20
+ t.test_files = FileList['test/tc_*.rb']
21
+ end
22
+
23
+ task :default => :test