acclaim 0.0.3 → 0.0.4

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/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