acclaim 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,44 +1,3 @@
1
- this_dir = File.expand_path('..', __FILE__)
2
- gem_dir = File.join this_dir, 'gem'
3
- spec_file = File.join this_dir, 'acclaim.gemspec'
1
+ require 'rookie'
4
2
 
5
- spec = Gem::Specification.load spec_file
6
-
7
- task :mkdir do
8
- FileUtils.mkdir_p gem_dir
9
- end
10
-
11
- task :gem => :mkdir do
12
- gem_file = File.join this_dir, Gem::Builder.new(spec).build
13
- FileUtils.mv gem_file, gem_dir
14
- end
15
-
16
- namespace :gem do
17
-
18
- task :build => :gem
19
-
20
- gem_file = File.join gem_dir, "#{spec.name}-#{spec.version}.gem"
21
-
22
- task :push => :gem do
23
- sh "gem push #{gem_file}"
24
- end
25
-
26
- task :install => :gem do
27
- sh "gem install #{gem_file}"
28
- end
29
-
30
- task :uninstall do
31
- sh "gem uninstall #{spec.name}"
32
- end
33
-
34
- task :clean do
35
- FileUtils.rm_rf gem_dir
36
- end
37
-
38
- end
39
-
40
- task :clean => 'gem:clean'
41
-
42
- task :setup => [ 'gem:install', :clean ]
43
-
44
- task :default => :setup
3
+ Rookie::Tasks.new 'acclaim.gemspec'
@@ -17,5 +17,6 @@ Gem::Specification.new('acclaim') do |gem|
17
17
  gem.files = `git ls-files`.split "\n"
18
18
 
19
19
  gem.add_development_dependency 'rspec'
20
+ gem.add_development_dependency 'rookie'
20
21
 
21
22
  end
@@ -64,25 +64,27 @@ module Acclaim
64
64
  end
65
65
 
66
66
  # Adds an option to this command.
67
- def option(*args)
68
- options << Option.new(*args)
67
+ def option(*args, &block)
68
+ options << Option.new(*args, &block)
69
69
  end
70
70
 
71
71
  alias :opt :option
72
72
 
73
73
  # The block which is executed when this command is called. It is given 2
74
- # parameters; the first is an Options instance which can be queried for
75
- # settings information; the second is the remaining command line.
74
+ # parameters; the first is an Option::Values instance which can be queried
75
+ # for settings information; the second is the remaining command line.
76
76
  def action(&block)
77
77
  @action = block
78
78
  end
79
79
 
80
80
  alias :when_called :action
81
81
 
82
+ # Adds help subcommand and options to this command.
82
83
  def help(opts = {})
83
84
  subcommands << Help.create(self, opts)
84
85
  end
85
86
 
87
+ # Adds help subcommand and options to this command.
86
88
  def version(version_string, opts = {})
87
89
  subcommands << Version.create(self, version_string, opts)
88
90
  end
@@ -92,50 +94,43 @@ module Acclaim
92
94
  Option::Parser.new(args, options).parse!
93
95
  end
94
96
 
95
- # Invokes this command with a fresh set of options.
97
+ # Invokes this command with a fresh set of option values.
96
98
  def run(*args)
97
99
  invoke Option::Values.new, args
98
100
  rescue Option::Parser::Error => e
99
101
  puts e.message
100
102
  end
101
103
 
102
- # Parses the argument array. If the first element of the argument array
103
- # corresponds to a subcommand, it will be invoked with said array and
104
- # with this command's parsed options. This command will be executed
105
- # otherwise.
104
+ # Parses the argument array. The argument array will be searched for
105
+ # subcommands; if one is found, it will be invoked, if not, this command
106
+ # will be executed. A subcommand may be anywhere in the array as long as
107
+ # it is before an argument separator, which is tipically a double dash
108
+ # (<tt>--<\tt>) and may be omitted.
106
109
  def invoke(opts, args = [])
107
110
  opts.merge! parse_options!(args)
108
111
  handle_special_options! opts, args
109
- arg_separator = args.find do |arg|
110
- arg =~ Option::Parser::Regexp::ARGUMENT_SEPARATOR
111
- end
112
- separator_index = args.index arg_separator
113
- subcommands.find do |subcommand|
114
- index = args.index subcommand.line
115
- # If we have the subcommand AND the separator, then we have it if the
116
- # subcommand is before the separator.
117
- index and (not separator_index or index < separator_index)
118
- end.tap do |subcommand|
119
- if subcommand
120
- args.delete subcommand.line
121
- subcommand.invoke(opts, args)
122
- else
123
- execute(opts, args)
124
- end
112
+ if subcommand = find_subcommand_in(separated args)
113
+ args.delete subcommand.line
114
+ subcommand.invoke(opts, args)
115
+ else
116
+ execute(opts, args)
125
117
  end
126
118
  end
127
119
 
128
- # Calls this command's action block with the given options and arguments.
120
+ # Calls this command's action block with the given option values and
121
+ # arguments.
129
122
  def execute(opts, args)
130
123
  @action.call opts, args
131
124
  end
132
125
 
133
126
  alias :call :execute
134
127
 
128
+ # True if this is a top-level command.
135
129
  def root?
136
130
  superclass == Acclaim::Command
137
131
  end
138
132
 
133
+ # Finds the root of the command hierarchy.
139
134
  def root
140
135
  command = self
141
136
  command = command.superclass until command.root?
@@ -150,6 +145,22 @@ module Acclaim
150
145
  const_get(:Version).execute opts, args if opts.acclaim_version?
151
146
  end
152
147
 
148
+ # Attempts to find a subcommand of this command in the given argument
149
+ # array. If a subcommand is found, it is returned, if not, nil is
150
+ # returned.
151
+ def find_subcommand_in(args)
152
+ subcommands.find do |subcommand|
153
+ args.include? subcommand.line
154
+ end
155
+ end
156
+
157
+ # Finds the argument separator and returns an array containing all the
158
+ # elements before it. If a separator is not present, the original array
159
+ # is returned.
160
+ def separated(args)
161
+ args.take_while { |arg| arg !~ Option::Parser::Regexp::ARGUMENT_SEPARATOR }
162
+ end
163
+
153
164
  end
154
165
 
155
166
  # Add the class methods to the subclass and add it to this command's list of
@@ -1,23 +1,32 @@
1
1
  require 'acclaim/option/arity'
2
2
  require 'acclaim/option/parser/regexp'
3
+ require 'acclaim/option/type'
3
4
 
4
5
  module Acclaim
5
6
 
6
7
  # Represents a command-line option.
7
8
  class Option
8
9
 
9
- attr_accessor :key, :names, :description, :type, :default
10
+ attr_accessor :key, :names, :description, :type, :default, :handler
10
11
 
11
- def initialize(key, *args)
12
+ def initialize(key, *args, &block)
12
13
  options = args.last.is_a?(Hash) ? args.pop : {}
14
+ matches = args.select { |arg| arg.is_a? String }.group_by do |arg|
15
+ arg =~ Parser::Regexp::SWITCH ? true : false
16
+ end
17
+ klass = args.find { |arg| arg.is_a? Class }
13
18
  self.key = key
14
- self.names = args.find_all { |arg| arg =~ Parser::Regexp::SWITCH }
15
- self.description = args.find { |arg| arg !~ Parser::Regexp::SWITCH }
16
- self.type = args.find { |arg| arg.is_a? Class }
19
+ self.names = matches.fetch true, []
20
+ self.description = matches.fetch(false, []).first
17
21
  self.arity = options[:arity]
18
22
  self.default = options[:default]
19
23
  self.required = options[:required]
20
- yield self if block_given?
24
+ self.type = klass || String
25
+ self.handler = block
26
+ end
27
+
28
+ def convert_parameters(*args)
29
+ args.map { |arg| Type[type].call arg }
21
30
  end
22
31
 
23
32
  def =~(str)
@@ -73,39 +73,19 @@ module Acclaim
73
73
  def parse_values!
74
74
  Values.new.tap do |options_instance|
75
75
  options.each do |option|
76
- key = option.key.to_sym
77
- options_instance[key] = option.default
78
- args = argv.find_all { |arg| option =~ arg }
79
- if args.any?
76
+ key = option.key
77
+ options_instance[key] = option.default unless options_instance[key]
78
+ switches = argv.find_all { |switch| option =~ switch }
79
+ if switches.any?
80
80
  if option.flag?
81
- options_instance[key] = true
81
+ set_option_value option, options_instance
82
82
  else
83
- arity = option.arity
84
- args.each do |arg|
85
- arg_index = argv.index arg
86
- len = if arity.bound?
87
- arg_index + arity.total
88
- else
89
- argv.length - 1
90
- end
91
- params = argv[arg_index + 1, len]
92
- values = []
93
- params.each do |param|
94
- case param
95
- when nil, SWITCH, ARGUMENT_SEPARATOR then break
96
- else
97
- break if arity.bound? and values.count >= arity.total
98
- values << param
99
- end
100
- end
101
- count = values.count
102
- Error.raise_wrong_arg_number count, *option.arity if count < arity.required
103
- value = if arity.total == 1 then values.first else values end
104
- options_instance[key] = value unless values.empty?
105
- values.each { |value| argv.delete value }
83
+ switches.each do |switch|
84
+ params = extract_parameters_of! option, switch
85
+ argv.delete switch
86
+ set_option_value option, options_instance, params
106
87
  end
107
88
  end
108
- args.each { |arg| argv.delete arg }
109
89
  else
110
90
  Error.raise_missing_arg(option.names.join ' | ') if option.required?
111
91
  end
@@ -113,6 +93,44 @@ module Acclaim
113
93
  end
114
94
  end
115
95
 
96
+ def extract_parameters_of!(option, switch)
97
+ arity = option.arity
98
+ switch_index = argv.index switch
99
+ len = if arity.bound?
100
+ switch_index + arity.total
101
+ else
102
+ argv.length - 1
103
+ end
104
+ params = argv[switch_index + 1, len]
105
+ values = []
106
+ params.each do |param|
107
+ case param
108
+ when nil, SWITCH, ARGUMENT_SEPARATOR then break
109
+ else
110
+ break if arity.bound? and values.count >= arity.total
111
+ values << param
112
+ end
113
+ end
114
+ count = values.count
115
+ Error.raise_wrong_arg_number count, *arity if count < arity.required
116
+ values.each { |value| argv.delete value }
117
+ end
118
+
119
+ def set_option_value(option, values, params = [])
120
+ params = option.convert_parameters *params
121
+ if handler = option.handler
122
+ if option.flag? then handler.call values
123
+ else handler.call values, params end
124
+ else
125
+ key = option.key.to_sym
126
+ if option.flag? then values[key] = true
127
+ else
128
+ value = option.arity.total == 1 ? params.first : params
129
+ values[key] = value unless params.empty?
130
+ end
131
+ end
132
+ end
133
+
116
134
  end
117
135
  end
118
136
  end
@@ -0,0 +1,55 @@
1
+ module Acclaim
2
+ class Option
3
+
4
+ # Associates a class with a handler block.
5
+ module Type
6
+
7
+ instance_eval do
8
+
9
+ # Yields class, proc pairs if a block was given. Returns an enumerator
10
+ # otherwise.
11
+ def each(&block)
12
+ table.each &block
13
+ end
14
+
15
+ # Returns all registered classes.
16
+ def all
17
+ table.keys
18
+ end
19
+
20
+ alias registered all
21
+
22
+ # Registers a handler for a class.
23
+ def register(klass, &block)
24
+ table[klass] = block
25
+ end
26
+
27
+ alias add_handler_for register
28
+ alias accept register
29
+
30
+ # Returns the handler for the given class.
31
+ def handler_for(klass)
32
+ table[klass]
33
+ end
34
+
35
+ alias [] handler_for
36
+
37
+ private
38
+
39
+ # The hash used to associate classes with their handlers.
40
+ def table
41
+ @table ||= {}
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end
50
+
51
+ require 'acclaim/option/type/date'
52
+ require 'acclaim/option/type/date_time'
53
+ require 'acclaim/option/type/string'
54
+ require 'acclaim/option/type/time'
55
+ require 'acclaim/option/type/uri'
@@ -0,0 +1,21 @@
1
+ require 'acclaim/option/type'
2
+ require 'date'
3
+
4
+ module Acclaim
5
+ class Option
6
+ module Type
7
+
8
+ # Handles dates given as arguments in the command line.
9
+ module Date
10
+
11
+ def self.handle(str)
12
+ ::Date.parse str
13
+ end
14
+
15
+ end
16
+
17
+ self.accept ::Date, &Date.method(:handle)
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'acclaim/option/type'
2
+ require 'date'
3
+
4
+ module Acclaim
5
+ class Option
6
+ module Type
7
+
8
+ # Handles dates and times given as arguments in the command line.
9
+ module DateTime
10
+
11
+ def self.handle(str)
12
+ ::DateTime.parse str
13
+ end
14
+
15
+ end
16
+
17
+ self.accept ::DateTime, &DateTime.method(:handle)
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'acclaim/option/type'
2
+ require 'time'
3
+
4
+ module Acclaim
5
+ class Option
6
+ module Type
7
+
8
+ # Handles strings given as arguments in the command line.
9
+ module String
10
+
11
+ def self.handle(str)
12
+ str.to_s
13
+ end
14
+
15
+ end
16
+
17
+ self.accept ::String, &String.method(:handle)
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'acclaim/option/type'
2
+ require 'time'
3
+
4
+ module Acclaim
5
+ class Option
6
+ module Type
7
+
8
+ # Handles times given as arguments in the command line.
9
+ module Time
10
+
11
+ def self.handle(str)
12
+ ::Time.parse str
13
+ end
14
+
15
+ end
16
+
17
+ self.accept ::Time, &Time.method(:handle)
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'acclaim/option/type'
2
+ require 'uri'
3
+
4
+ module Acclaim
5
+ class Option
6
+ module Type
7
+
8
+ # Handles URIs given as arguments in the command line.
9
+ module URI
10
+
11
+ def self.handle(str)
12
+ ::URI.parse str
13
+ end
14
+
15
+ end
16
+
17
+ self.accept ::URI, &URI.method(:handle)
18
+
19
+ end
20
+ end
21
+ end
@@ -3,7 +3,7 @@ module Acclaim
3
3
 
4
4
  MAJOR = 0
5
5
  MINOR = 0
6
- PATCH = 3
6
+ PATCH = 4
7
7
  BUILD = nil
8
8
 
9
9
  STRING = [ MAJOR, MINOR, PATCH, BUILD ].compact.join '.'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acclaim
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-19 00:00:00.000000000 Z
12
+ date: 2011-12-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &13323500 !ruby/object:Gem::Requirement
16
+ requirement: &13170100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,18 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *13323500
24
+ version_requirements: *13170100
25
+ - !ruby/object:Gem::Dependency
26
+ name: rookie
27
+ requirement: &13169620 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *13169620
25
36
  description: Command-line option parser and command interface.
26
37
  email: matheus.a.m.moreira@gmail.com
27
38
  executables: []
@@ -43,6 +54,12 @@ files:
43
54
  - lib/acclaim/option/arity.rb
44
55
  - lib/acclaim/option/parser.rb
45
56
  - lib/acclaim/option/parser/regexp.rb
57
+ - lib/acclaim/option/type.rb
58
+ - lib/acclaim/option/type/date.rb
59
+ - lib/acclaim/option/type/date_time.rb
60
+ - lib/acclaim/option/type/string.rb
61
+ - lib/acclaim/option/type/time.rb
62
+ - lib/acclaim/option/type/uri.rb
46
63
  - lib/acclaim/option/values.rb
47
64
  - lib/acclaim/version.rb
48
65
  - spec/acclaim/option/arity_spec.rb