lab42_options 0.4.0 → 0.5.3

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: d7cc89febbade4730d1c7112704d5655af6d9cd5
4
- data.tar.gz: 5de219eff28bf69199cdb5c1c59ef157eed0efd9
3
+ metadata.gz: 591fe82bbd4733ef27af5cbf07d0dce041b4a40c
4
+ data.tar.gz: 2de9e059429417fe2dabe852aa306d318df8d721
5
5
  SHA512:
6
- metadata.gz: 1dc5104a71fbe2ca6572fdebb57b88d22215086fb2f8d69b020f49fc9d52f75aeae2c012d590ba20096dc48da311b69eb6478600f079b40b6992fcb9e5c3ce67
7
- data.tar.gz: 3fdaf3fcd7413022e98b1e322c4ded53296bc0005547a49aa492dac0a12ad9bf41e91f27a9cb922633ec60b9ff547c6b2ddab9995dcae2de7e3e0c0952396bdd
6
+ metadata.gz: 56f563ae9f2a64a2f9644143708f3048dc9bd3caa53347e7681217a07e5a967fb8a8e0c482de5de44adb40216bd6be7fac5a0feef0566babf53b20903c9199b5
7
+ data.tar.gz: 94b1247f81b98ab1dfaf6dfed7274c68b3b8e181368ab9ae42d8b120cfd996079a3abee26532c5119a1a90af67afee31e5fab730ddcb4652c66e482afecb13a7
data/README.md CHANGED
@@ -2,82 +2,4 @@
2
2
 
3
3
  ## Options
4
4
 
5
- Lets us specify command line options with the same syntax as ruby parameters
6
-
7
- ```
8
- my_shiny_gem "hello" "world" :verbose answer: 42
9
- ```
10
-
11
- Will yield
12
-
13
- ```ruby
14
- require 'lab42/options'
15
-
16
- options = Lab42::Options.new.parse ARGV
17
- options.args # --> %W{ hello world }
18
- options.first # --> "hello"
19
- options[:verbose] # --> true
20
- options.verbose # --> true
21
- options[:answer] # --> "42"
22
- options.answer # --> "42"
23
- ```
24
-
25
- ## Multiple values
26
-
27
- When providing the same key many times the options object will become an array
28
-
29
- ```
30
- my_even_shiner_gem 42 tag: cool :mixed tag: hot mixed: pickels
31
- ```
32
-
33
- Will yield
34
-
35
- ```ruby
36
-
37
- options.args # --> %W{42}
38
- options[:tag] # --> %W{cool hot}
39
- options.mixed # --> [true, "pickels"]
40
- ```
41
-
42
- ## Required and Default Arguments
43
-
44
- ```ruby
45
- Lab42::Options.new greeting: "hello", target: :required
46
- ```
47
-
48
- will parse as follows:
49
-
50
- ```sh
51
- greet target: "world" # target = "world", greeting = "hello"
52
- greet target: "world" greeting: "cheerio" # target = "world", greeting = "cheerio"
53
- greet greeting: "howdy" # error missing required argument :target"
54
- ```
55
-
56
- ## Reading from yaml files
57
-
58
- ```ruby
59
- options = Lab42::Options.new greeting: "hello", target: :required
60
- options.read_from "./options.yml"
61
- # or
62
- options.read_from :load # read from file indicated by load: <file>
63
- # or
64
- options.read_from load: "./.default_options.yml" # read from file indicated by load: <file>
65
- # defaulting to "./.default_options.yml"
66
- ```
67
-
68
- Although this allows for nested parameters, defaults and requirements for deeper levels are not implemented right now (and are maybe not in the scope of a parameter parser).
69
-
70
- Existence of the yaml file is not reenforced either but that might be a good enhancement for the near
71
- future.
72
-
73
-
74
- ## Missing Features
75
-
76
- * Help Message Generation
77
-
78
- https://github.com/RobertDober/lab42_options/issues/3
79
-
80
- * Typed Arguments
81
-
82
- https://github.com/RobertDober/lab42_options/issues/4
83
-
5
+ Please see the demos for a description [here](https://github.com/RobertDober/lab42_options/tree/master/demo)
data/lib/lab42/options.rb CHANGED
@@ -1,19 +1,55 @@
1
1
  require 'ostruct'
2
+ require 'lab42/core/fn'
3
+ require 'lab42/core/kernel'
4
+
2
5
  require_relative './options/parser'
3
6
  require_relative './options/forwarder'
7
+ require_relative './options/validator'
8
+ require_relative './options/error_issuer'
4
9
 
5
10
  module Lab42
6
11
  class Options
7
- attr_reader :yaml_file
12
+ attr_reader :args, :strict_mode, :yaml_file
13
+
14
+ def define_help txt
15
+ @defined_help_text = txt
16
+ self
17
+ end
18
+
19
+ def defaults
20
+ @__defaults__ ||=
21
+ @registered.select{|_,v| v != :required}
22
+ end
23
+
24
+ def define_help_for opt, txt=nil, &blk
25
+ @help_text_for_option[opt] = txt||blk.(defaults.fetch(opt))
26
+ self
27
+ end
28
+
29
+ def get_help_text
30
+ (
31
+ [ @defined_help_text ] +
32
+ required_options.map do | ro |
33
+ "#{ro}: #{@help_text_for_option.fetch(ro, :required)}"
34
+ end +
35
+ defaults.map do | d,v |
36
+ "#{d}: defaults to #{@help_text_for_option.fetch(d,v.inspect)}"
37
+ end
38
+ ).compact.join("\n")
39
+ end
8
40
 
9
41
  def parse *args
10
42
  args = args.first if Array === args.first
43
+ @args = args
11
44
  @parsed = Lab42::Options::Parser.new.parse( self, args )
45
+
46
+ return if help_asked?
47
+
48
+ validate!
49
+
12
50
  set_defaults
13
- check_required
14
- issue_errors!
15
- result = OpenStruct.new @parsed
16
- result.forwarding_to :kwds
51
+
52
+ OpenStruct.new( @parsed ).forwarding_to :kwds
17
53
  end
18
54
 
19
55
  def read_from file_sym_or_hash
@@ -27,30 +63,65 @@ module Lab42
27
63
  end
28
64
  end
29
65
 
66
+ def strict(new_mode=:errors)
67
+ @strict_mode = new_mode
68
+ self
69
+ end
70
+
71
+ def strict?; !!strict_mode end
72
+
73
+ def warning_mode?
74
+ strict? && /warnings\z/ === strict_mode
75
+ end
76
+
77
+ def error_mode?
78
+ strict? && !warning_mode?
79
+ end
30
80
  private
31
81
  def initialize options={}
32
82
  @registered = {}
33
- @errors = []
83
+ @help_text_for_option = {}
84
+ @strict_mode = :errors
34
85
  options.each do | k, v |
35
86
  register_option k, v
36
87
  end
37
88
  end
38
89
 
39
- def check_required
40
- required_options.each do |ro|
41
- @errors << "Required option #{ro} was not provided" unless @parsed[:kwds].to_h.has_key? ro
42
- end
43
- end
90
+ # @spurious.each do | err |
91
+ # $stderr.puts "invalid parameter #{err.inspect}"
92
+ # end
93
+ # else
94
+ # raise ArgumentError, "invalid parameters: #{@spurious.map(&sendmsg(:inspect)).join(", ")}"
95
+ # end
44
96
 
45
- def defaults
46
- @__defaults__ =
47
- @registered.select{|_,v| v != :required}
97
+
98
+ def help_asked?
99
+ %w{-h --help :help}.any?( &@parsed[:to_a].fn.include? )
48
100
  end
49
101
 
50
- def issue_errors!
51
- return if @errors.empty?
102
+ # @spurious.each do | err |
103
+ # $stderr.puts "invalid parameter #{err.inspect}"
104
+ # end
105
+ # else
106
+ # raise ArgumentError, "invalid parameters: #{@spurious.map(&sendmsg(:inspect)).join(", ")}"
107
+ # end
108
+
109
+ def issue_errors validator
110
+ errors = "validator.missing"
52
111
  raise ArgumentError, @errors.join("\n")
53
112
  end
113
+
114
+ # @spurious.each do | err |
115
+ # $stderr.puts "invalid parameter #{err.inspect}"
116
+ # end
117
+ # else
118
+ # raise ArgumentError, "invalid parameters: #{@spurious.map(&sendmsg(:inspect)).join(", ")}"
119
+ # end
120
+
121
+ def issue_warnings validator
122
+ get_spurious
123
+
124
+ end
54
125
  def register_option k, v
55
126
  @registered[k] = v
56
127
  end
@@ -74,5 +145,23 @@ module Lab42
74
145
  @parsed[:kwds][k] = dv unless @parsed[:kwds].to_h.has_key? k
75
146
  end
76
147
  end
148
+ def validate!
149
+ validator = Validator.new( @registered )
150
+ validator.validate @parsed[:kwds].to_h
151
+ return if validator.valid?
152
+ issuer = ErrorIssuer.new self, validator
153
+ issuer.handle_errors!
154
+ end
155
+ end
156
+
157
+ class PermissiveOptions < Options
158
+ def strict
159
+ raise NoMethodError, "strict message not understood in this object #{self}"
160
+ end
161
+ private
162
+ def initialize *args, **kwds
163
+ super(*args, **kwds)
164
+ @strict_mode = false
165
+ end
77
166
  end
78
167
  end
@@ -0,0 +1,18 @@
1
+ require_relative 'hash_helper'
2
+ module Lab42
3
+ class Options
4
+ Identity = ->(x){x}
5
+
6
+ module ArrayHelpers
7
+
8
+ def counts
9
+ group_by(&Identity).map_values(&:size)
10
+ end
11
+
12
+ def flag_count
13
+ count{|x| x==true}
14
+ end
15
+
16
+ end # module ArrayHelpers
17
+ end # class Options
18
+ end # module Lab42
@@ -0,0 +1,5 @@
1
+ require_relative '../options'
2
+
3
+ Options = Lab42::Options
4
+ PermissiveOptions = Lab42::PermissiveOptions
5
+ ParameterError = Lab42::Options::ParameterError
@@ -0,0 +1,4 @@
1
+ # This will die as soon as it goes into lab42_core
2
+ module Lab42
3
+ Negate = -> (x){!x}
4
+ end # module Lab42
@@ -0,0 +1,14 @@
1
+ module Lab42
2
+ class Options
3
+ module DefaultHelpers
4
+
5
+ def counts
6
+ { self => 1 }
7
+ end
8
+ def flag_count
9
+ self == true ? 1 : 0
10
+ end
11
+
12
+ end # module DefaultHelpers
13
+ end # class Options
14
+ end # module Lab42
@@ -0,0 +1,60 @@
1
+ module Lab42
2
+ class Options
3
+ ParameterError = Class.new ArgumentError
4
+ class ErrorIssuer
5
+ Symbolize = -> (s){ s.to_sym.inspect }
6
+
7
+ attr_reader :options, :validator
8
+
9
+ def handle_errors!
10
+
11
+ if options.error_mode?
12
+ raise_errors_for_missing true
13
+ elsif options.warning_mode?
14
+ warn_for_spurious
15
+ raise_errors_for_missing
16
+ else
17
+ raise_errors_for_missing
18
+ end
19
+ end
20
+
21
+ private
22
+ def format_error_message sp, missing
23
+ spm = if sp.empty?
24
+ nil
25
+ else
26
+ "unspecified parameters: #{sp.map(Symbolize).join(", ")}"
27
+ end
28
+ mm = if missing.empty?
29
+ nil
30
+ else
31
+ "missing required parameters: #{missing.map(Symbolize).join(", ")}"
32
+ end
33
+ [spm, mm].compact.join "\n"
34
+ end
35
+
36
+ def get_spurious_errors
37
+ validator.spurious.keys
38
+ end
39
+
40
+ def initialize options, validator
41
+ @options = options
42
+ @validator = validator
43
+ end
44
+
45
+ def raise_errors_for_missing with_spurious_errors=false
46
+ spurious_errors = with_spurious_errors ? get_spurious_errors : []
47
+ missing_errors = validator.missing.keys
48
+
49
+ return if spurious_errors.empty? && missing_errors.empty?
50
+ raise ParameterError, format_error_message( spurious_errors, missing_errors )
51
+ end
52
+
53
+ def warn_for_spurious
54
+ validator.spurious.each do | k, v |
55
+ $stderr.puts "unspecified parameter passed: #{k}: #{v.inspect}"
56
+ end
57
+ end
58
+ end # class ErrorIssuer
59
+ end # class Options
60
+ end # module Lab42
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ def map_values bhv=nil, &blk
3
+ bhv ||= blk
4
+ inject self.class.new do | h, (k,v) |
5
+ h.merge( k => bhv.(v) )
6
+ end
7
+ end
8
+ end
@@ -1,20 +1,39 @@
1
1
  require 'yaml'
2
+
3
+ require_relative 'array_helpers'
4
+ require_relative 'default_helpers'
5
+
2
6
  module Lab42
3
7
  class Options
4
8
  class Parser
5
9
  attr_accessor :data, :defaults, :kwds, :positionals, :yaml_file
6
- def parse options, args
7
- self.yaml_file = options.yaml_file
10
+
11
+ def parse option, args
12
+ self.yaml_file = option.yaml_file
8
13
  self.data = {to_a: args}
9
- self.kwds = {}
10
- self.positionals = []
11
14
  parse_all args
12
15
  # read_yaml file might need the args parsed
13
- defaults = read_yaml_file
14
- data.merge kwds: OpenStruct.new(defaults.merge(kwds)), args: positionals
16
+ defaults = read_yaml_file || option.defaults
17
+ merged = defaults.merge kwds
18
+ merged = extend_values merged
19
+ result = data.merge kwds: OpenStruct.new( merged ), args: positionals
20
+ check_for_errors option, args if option.strict_mode
21
+ result
15
22
  end
16
23
 
24
+ def errors; @errors.dup end
25
+
17
26
  private
27
+ def initialize
28
+ @errors = []
29
+ self.kwds = {}
30
+ self.positionals = []
31
+ end
32
+
33
+ def check_for_errors options, args
34
+
35
+ end
36
+
18
37
  def convert_hash hs
19
38
  return hs unless Hash === hs
20
39
  hs.keys.inject Hash.new do |h, k|
@@ -26,6 +45,16 @@ module Lab42
26
45
  end
27
46
  end
28
47
 
48
+ def extend_values kwds
49
+ kwds.map_values do | val|
50
+ if Array === val
51
+ val.extend Lab42::Options::ArrayHelpers
52
+ else
53
+ val.extend Lab42::Options::DefaultHelpers rescue val
54
+ end
55
+ end
56
+ end
57
+
29
58
  def parse_all args
30
59
  e = (args[0..-1] || []).enum_for :each
31
60
  loop do
@@ -43,7 +72,7 @@ module Lab42
43
72
  def read_yaml_file
44
73
  read_yaml_file!
45
74
  rescue Errno::ENOENT
46
- {}
75
+ nil
47
76
  end
48
77
 
49
78
  def read_yaml_file!
@@ -54,7 +83,7 @@ module Lab42
54
83
  when String
55
84
  convert_hash YAML.load File.read yaml_file
56
85
  else
57
- {}
86
+ nil
58
87
  end
59
88
  end
60
89
 
@@ -0,0 +1,35 @@
1
+ require 'set'
2
+ require 'forwarder'
3
+ require_relative './core_extension'
4
+
5
+ module Lab42
6
+ class Options
7
+ class Validator
8
+ attr_reader :missing, :spurious
9
+ extend Forwarder
10
+
11
+ forward :missing?, to: :missing, as: :empty?, after: Lab42::Negate
12
+ forward :spurious?, to: :spurious, as: :empty?, after: Lab42::Negate
13
+
14
+ def valid?
15
+ missing.empty? && spurious.empty?
16
+ end
17
+
18
+ def validate parsed
19
+ parsed.each do | key, value |
20
+ @spurious.update key => value unless @allowed.include? key
21
+ @missing.delete key if @missing.include? key
22
+ end
23
+ end
24
+
25
+ private
26
+ def initialize registered
27
+ @spurious = {}
28
+ @missing = Hash[
29
+ *registered.select{ |_,v| v == :required }.map{ |k,| [k => true] }.flatten
30
+ ]
31
+ @allowed = Set.new registered.keys
32
+ end
33
+ end # class Validator
34
+ end # class Options
35
+ end # module Lab42
@@ -1,5 +1,5 @@
1
1
  module Lab42
2
2
  class Options
3
- VERSION = "0.4.0"
3
+ VERSION = "0.5.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,55 +1,174 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lab42_options
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-23 00:00:00.000000000 Z
11
+ date: 2014-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lab42_core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.7
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.7
27
+ - !ruby/object:Gem::Dependency
28
+ name: forwarder2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.0
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: pry
15
43
  requirement: !ruby/object:Gem::Requirement
16
44
  requirements:
17
- - - ~>
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-nav
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
18
60
  - !ruby/object:Gem::Version
19
- version: 0.9.12
61
+ version: '0.2'
20
62
  type: :development
21
63
  prerelease: false
22
64
  version_requirements: !ruby/object:Gem::Requirement
23
65
  requirements:
24
- - - ~>
66
+ - - "~>"
25
67
  - !ruby/object:Gem::Version
26
- version: 0.9.12
68
+ version: '0.2'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: rspec
29
71
  requirement: !ruby/object:Gem::Requirement
30
72
  requirements:
31
- - - ~>
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: qed
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.9'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.9'
97
+ - !ruby/object:Gem::Dependency
98
+ name: ae
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.8'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.8'
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.1'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.1'
125
+ - !ruby/object:Gem::Dependency
126
+ name: mocha
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
32
130
  - !ruby/object:Gem::Version
33
- version: 2.13.0
131
+ version: '1.1'
34
132
  type: :development
35
133
  prerelease: false
36
134
  version_requirements: !ruby/object:Gem::Requirement
37
135
  requirements:
38
- - - ~>
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.1'
139
+ - !ruby/object:Gem::Dependency
140
+ name: test-unit
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
39
144
  - !ruby/object:Gem::Version
40
- version: 2.13.0
145
+ version: '2.1'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '2.1'
41
153
  description: Specify command line arguments with Ruby Syntax
42
154
  email: robert.dober@gmail.com
43
155
  executables: []
44
156
  extensions: []
45
157
  extra_rdoc_files: []
46
158
  files:
159
+ - LICENSE
160
+ - README.md
47
161
  - lib/lab42/options.rb
48
- - lib/lab42/options/parser.rb
162
+ - lib/lab42/options/array_helpers.rb
163
+ - lib/lab42/options/auto_import.rb
164
+ - lib/lab42/options/core_extension.rb
165
+ - lib/lab42/options/default_helpers.rb
166
+ - lib/lab42/options/error_issuer.rb
49
167
  - lib/lab42/options/forwarder.rb
168
+ - lib/lab42/options/hash_helper.rb
169
+ - lib/lab42/options/parser.rb
170
+ - lib/lab42/options/validator.rb
50
171
  - lib/lab42/options/version.rb
51
- - LICENSE
52
- - README.md
53
172
  homepage: https://github.com/RobertDober/lab42_options
54
173
  licenses:
55
174
  - MIT
@@ -60,17 +179,17 @@ require_paths:
60
179
  - lib
61
180
  required_ruby_version: !ruby/object:Gem::Requirement
62
181
  requirements:
63
- - - '>='
182
+ - - ">="
64
183
  - !ruby/object:Gem::Version
65
184
  version: 2.0.0
66
185
  required_rubygems_version: !ruby/object:Gem::Requirement
67
186
  requirements:
68
- - - '>='
187
+ - - ">="
69
188
  - !ruby/object:Gem::Version
70
189
  version: '0'
71
190
  requirements: []
72
191
  rubyforge_project:
73
- rubygems_version: 2.0.3
192
+ rubygems_version: 2.2.2
74
193
  signing_key:
75
194
  specification_version: 4
76
195
  summary: Command Lines the Ruby Way