climate 0.2.0 → 0.3.0

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.
data/lib/climate.rb CHANGED
@@ -40,8 +40,6 @@ module Climate
40
40
  end
41
41
 
42
42
  require 'climate/errors'
43
- require 'climate/argument'
44
- require 'climate/option'
45
43
  require 'climate/parser'
46
44
  require 'climate/command'
47
45
  require 'climate/help'
@@ -14,11 +14,11 @@ module Climate
14
14
 
15
15
  # Create an instance of this command class and run it against the given
16
16
  # arguments
17
- # @param [Array<String>] arguments A list of arguments, ARGV style
17
+ # @param [Array<String>] argv A list of arguments, ARGV style
18
18
  # @param [Hash] options see {#initialize}
19
- def run(arguments, options={})
19
+ def run(argv, options={})
20
20
  begin
21
- instance = new(arguments, options)
21
+ instance = new(argv, options)
22
22
  rescue Trollop::HelpNeeded
23
23
  raise HelpNeeded.new(self)
24
24
  end
@@ -47,6 +47,13 @@ module Climate
47
47
  @name
48
48
  end
49
49
 
50
+ # because we've extended Class.name, we expose the original method
51
+ # under another name
52
+ # FIXME: surely there is a saner way of doing this?
53
+ def class_name
54
+ Class.method(:name).unbind.bind(self).call
55
+ end
56
+
50
57
  # Register this class as being a subcommand of another {Command} class
51
58
  # @param [Command] parent_class The parent we hang off of
52
59
  def subcommand_of(parent_class)
@@ -64,6 +71,15 @@ module Climate
64
71
  end
65
72
  end
66
73
 
74
+ # Call this during class definition time if you don't want any of the
75
+ # usual command line parsing to happen
76
+ def disable_parsing
77
+ @parsing_disabled = true
78
+ end
79
+
80
+ # Returns true if parsing is disabled
81
+ attr_accessor :parsing_disabled
82
+
67
83
  # Set the parent of this command
68
84
  attr_accessor :parent
69
85
 
@@ -116,16 +132,24 @@ module Climate
116
132
  # @option options [IO] :stdout stream to use as stdout, defaulting to `$stdout`
117
133
  # @option options [IO] :stderr stream to use as stderr, defaulting to `$stderr`
118
134
  # @option options [IO] :stdin stream to use as stdin, defaulting to `$stdin`
119
- def initialize(arguments, options={})
135
+ def initialize(argv, options={})
136
+ @argv = argv.clone
120
137
  @parent = options[:parent]
121
138
 
122
139
  @stdout = options[:stdout] || $stdout
123
140
  @stderr = options[:stderr] || $stderr
124
141
  @stdin = options[:stdin] || $stdin
125
142
 
126
- @arguments, @options, @leftovers = self.class.parse(arguments)
143
+ if ! self.class.parsing_disabled
144
+ @arguments, @options, @leftovers = self.class.parse(argv)
145
+ end
127
146
  end
128
147
 
148
+ # @return [Array]
149
+ # The original list of unparsed argv style arguments that were given to
150
+ # the command
151
+ attr_accessor :argv
152
+
129
153
  # @return [Hash]
130
154
  # Options that were parsed from the command line
131
155
  attr_accessor :options
@@ -156,5 +180,11 @@ module Climate
156
180
  parent.ancestor(ancestor_class)
157
181
  end
158
182
  end
183
+
184
+ # Run the command, must be implemented by all commands that are not parent
185
+ # commands (leaf commands)
186
+ def run
187
+ raise NotImplementedError, "Leaf commands must implement a run method"
188
+ end
159
189
  end
160
190
  end
data/lib/climate/help.rb CHANGED
@@ -101,7 +101,7 @@ module Climate
101
101
  end
102
102
 
103
103
  def puts(string='')
104
- string.split("\n").each do |line|
104
+ wrap(string).split("\n").each do |line|
105
105
  @output.puts((' ' * spaces) + line)
106
106
  end
107
107
  end
@@ -129,7 +129,7 @@ module Climate
129
129
 
130
130
  string.split("\n\n").map { |para|
131
131
 
132
- words = para.split(/[\n ]/)
132
+ words = para.split(/[\n ]+/)
133
133
  words[1..-1].inject([words.first]) { |m, v|
134
134
  new_last_line = m.last + " " + v
135
135
 
@@ -1,9 +1,97 @@
1
1
  module Climate
2
2
 
3
+ # Keeps the description munging code in one place
4
+ module Described
5
+ attr_reader :name
6
+
7
+ def initialize(name, description, *rest)
8
+ @name = name
9
+ @description = description
10
+ end
11
+
12
+ def description
13
+ (@description || '').gsub(/\{default\}/, default.to_s)
14
+ end
15
+ end
16
+
17
+ # Wraps the properties supplied to Trollop::Parser#opt for some OO over
18
+ # engineered sweetness
19
+ class Option
20
+ include Described
21
+ attr_reader :options
22
+
23
+ def initialize(name, description, options={})
24
+ @options = options
25
+ super
26
+ end
27
+
28
+ def type ; spec[:type] ; end
29
+ def long ; spec[:long] ; end
30
+ def short ; spec[:short] ; end
31
+ def default ; spec[:default] ; end
32
+
33
+ def optional? ; spec.has_key?(:default) ; end
34
+ def required? ; ! optional? ; end
35
+
36
+ def spec ; @specs ||= parser.specs[@name] ; end
37
+
38
+ def parser
39
+ @parser ||= Trollop::Parser.new.tap {|p| add_to(p) }
40
+ end
41
+
42
+ def long_usage
43
+ type == :flag ? "--#{long}" : "--#{long}=<#{type}>"
44
+ end
45
+
46
+ def short_usage
47
+ short && (type == :flag ? "-#{short}" : "-#{short}<#{type}>")
48
+ end
49
+
50
+ def usage(options={})
51
+ help = short_usage || long_usage
52
+
53
+ if options[:with_long] && (long_usage != help)
54
+ help = [help, long_usage].compact.join(options.fetch(:separator, '|'))
55
+ end
56
+
57
+ if optional? && !options.fetch(:hide_optional, false)
58
+ "[#{help}]"
59
+ else
60
+ help
61
+ end
62
+ end
63
+
64
+ def add_to(parser)
65
+ parser.opt(@name, @description, @options)
66
+ end
67
+ end
68
+
69
+ # argument definition is stored in these
70
+ class Argument
71
+ include Described
72
+ attr_reader :default
73
+
74
+ def initialize(name, description, options={})
75
+ super
76
+ @required = options.fetch(:required, ! options.has_key?(:default))
77
+ @multi = options.fetch(:multi, false)
78
+ @default = options.fetch(:default, nil)
79
+ end
80
+
81
+ def required? ; @required ; end
82
+ def optional? ; ! required? ; end
83
+ def multi? ; @multi ; end
84
+
85
+ def usage
86
+ string = "<#{name}>"
87
+ string += '...' if multi?
88
+ optional?? "[#{string}]" : string
89
+ end
90
+ end
91
+
3
92
  module ParsingMethods
4
93
 
5
94
  def arg(*args)
6
-
7
95
  arg = Argument.new(*args)
8
96
 
9
97
  raise DefinitionError, "can not define more arguments after a multi " +
@@ -24,27 +112,13 @@ module Climate
24
112
  end
25
113
 
26
114
  def trollop_parser
27
- parser = Trollop::Parser.new
28
-
29
- parser.stop_on @stop_on
115
+ Trollop::Parser.new.tap do |parser|
116
+ parser.stop_on @stop_on
30
117
 
31
- if cli_arguments.size > 0
32
- parser.banner ""
33
- max_length = cli_arguments.map { |h| h.name.to_s.length }.max
34
- cli_arguments.each do |argument|
35
- parser.banner(" " + argument.name.to_s.rjust(max_length) + " - #{argument.description}")
118
+ cli_options.each do |option|
119
+ option.add_to(parser)
36
120
  end
37
121
  end
38
-
39
- parser.banner ""
40
- cli_options.each do |option|
41
- option.add_to(parser)
42
- end
43
- parser
44
- end
45
-
46
- def help_banner(out=$stdout)
47
- trollop_parser.educate(out)
48
122
  end
49
123
 
50
124
  def parse_arguments(args, command=self)
@@ -69,7 +143,7 @@ module Climate
69
143
  end
70
144
 
71
145
  arg_list.zip(args).map do |argument, arg_value|
72
-
146
+ arg_value ||= argument.default
73
147
  if argument.required? && arg_value.nil?
74
148
  raise MissingArgumentError.new(argument.name, command)
75
149
  end
@@ -1,3 +1,3 @@
1
1
  module Climate
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: climate
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 2
8
+ - 3
9
9
  - 0
10
- version: 0.2.0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nick Griffiths
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-07-23 00:00:00 Z
18
+ date: 2012-08-08 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: trollop
@@ -85,9 +85,7 @@ extra_rdoc_files: []
85
85
  files:
86
86
  - lib/climate/version.rb
87
87
  - lib/climate/help/man.rb
88
- - lib/climate/argument.rb
89
88
  - lib/climate/command.rb
90
- - lib/climate/option.rb
91
89
  - lib/climate/parser.rb
92
90
  - lib/climate/errors.rb
93
91
  - lib/climate/script.rb
@@ -1,28 +0,0 @@
1
- module Climate
2
- class Argument
3
-
4
- attr_reader :name
5
- attr_reader :description
6
-
7
- def initialize(name, description, options={})
8
- @name = name
9
- @description = description
10
- @required = options.fetch(:required, true)
11
- @multi = options.fetch(:multi, false)
12
- end
13
-
14
- def required? ; @required ; end
15
- def optional? ; ! required? ; end
16
- def multi? ; @multi ; end
17
-
18
- def usage
19
- string = "<#{name}>"
20
- string += '...' if multi?
21
- optional?? "[#{string}]" : string
22
- end
23
-
24
- def formatted
25
- required?? name.to_s.upcase : "[#{name.to_s.upcase}]"
26
- end
27
- end
28
- end
@@ -1,54 +0,0 @@
1
- module Climate
2
- class Option
3
-
4
- attr_reader :name
5
- attr_reader :description
6
- attr_reader :options
7
-
8
- def initialize(name, description, options={})
9
- @name = name
10
- @description = description
11
- @options = options
12
- end
13
-
14
- def type ; spec[:type] ; end
15
- def long ; spec[:long] ; end
16
- def short ; spec[:short] ; end
17
- def default ; spec[:default] ; end
18
-
19
- def optional? ; spec.has_key?(:default) ; end
20
- def required? ; ! optional? ; end
21
-
22
- def spec ; @specs ||= parser.specs[@name] ; end
23
-
24
- def parser
25
- @parser ||= Trollop::Parser.new.tap {|p| add_to(p) }
26
- end
27
-
28
- def long_usage
29
- type == :flag ? "--#{long}" : "--#{long}=<#{type}>"
30
- end
31
-
32
- def short_usage
33
- short && (type == :flag ? "-#{short}" : "-#{short}<#{type}>")
34
- end
35
-
36
- def usage(options={})
37
- help = short_usage || long_usage
38
-
39
- if options[:with_long] && (long_usage != help)
40
- help = [help, long_usage].compact.join(options.fetch(:separator, '|'))
41
- end
42
-
43
- if optional? && !options.fetch(:hide_optional, false)
44
- "[#{help}]"
45
- else
46
- help
47
- end
48
- end
49
-
50
- def add_to(parser)
51
- parser.opt(@name, @description, @options)
52
- end
53
- end
54
- end