ex_aequo 0.1.4 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +127 -4
- data/lib/ex_aequo/args.rb +60 -0
- data/lib/ex_aequo/args_parser.rb +71 -0
- data/lib/ex_aequo/my_struct/class_methods.rb +19 -0
- data/lib/ex_aequo/my_struct.rb +32 -0
- data/lib/ex_aequo/version.rb +1 -1
- data/lib/ex_aequo.rb +1 -0
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2aaf57df3f78e7ecdd11fdf189a6dbd0942a68d9ec41213713583bebd533c5dd
|
4
|
+
data.tar.gz: 799c05c0d0b4e4f20d736de98017c960e40fc370fabbfce0d3fe5827499cdafa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4ee1ab2d221d3fe055f3e9a4d27c76fef3237af11718a3fef986702f5a08511cb43a30a439fd3e896b8a86d785691d3aab01327dbbca8d54026f6c87292b390
|
7
|
+
data.tar.gz: e038d29057424cf6f2f45139562c060b76221eca5fe6c55eaf3ae0bb0bf58b9b68666122e11d5eedf8aff396c241b1e11f1fa87892ee710e85dd02cdfd883812
|
data/README.md
CHANGED
@@ -3,15 +3,138 @@
|
|
3
3
|
[](http://badge.fury.io/rb/ex_aequo)
|
4
4
|
[](https://rubygems.org/gems/ex_aequo)
|
5
5
|
|
6
|
-
# ExAequo
|
7
6
|
|
8
|
-
|
7
|
+
# Some Tools
|
8
|
+
|
9
|
+
... which tools?
|
10
|
+
|
11
|
+
Let us [speculate about](https://github.com/RobertDober/speculate_about) that:
|
12
|
+
|
13
|
+
|
14
|
+
## Context: MyStruct
|
15
|
+
|
16
|
+
All the goodies of `OpenStruct` without the badies:
|
17
|
+
|
18
|
+
- Immutable
|
19
|
+
- Deconstructable (→ _Patternmatchable_)
|
20
|
+
- _Hashy_ Interface (`fetch`, `merge`, `slice`...)
|
21
|
+
|
22
|
+
Given an instance of `MyStruct` constructed from a `Hash`
|
23
|
+
```ruby
|
24
|
+
require 'ex_aequo/my_struct'
|
25
|
+
let(:from_hash) { MyStruct.new(a: 1, b: 2) }
|
26
|
+
```
|
27
|
+
And an instance constructed by a reducer
|
28
|
+
```ruby
|
29
|
+
let(:incremented) { MyStruct.from_reduce(%i[a b c d e], 0) { _1.succ } }
|
30
|
+
```
|
31
|
+
Then they can be accessed like a `Hash` or `OpenStruct`
|
32
|
+
```ruby
|
33
|
+
expect(from_hash[:a]).to eq(1)
|
34
|
+
expect(from_hash.fetch(:b)).to eq(2)
|
35
|
+
expect(incremented[:e]).to eq(5)
|
36
|
+
```
|
37
|
+
|
38
|
+
And they are immutable
|
39
|
+
```ruby
|
40
|
+
expect { from_hash[:c] = 3 }
|
41
|
+
.to raise_error(MyStruct::ImmutableError, 'cannot change values with []= in immutable instance of MyStruct')
|
42
|
+
```
|
43
|
+
|
44
|
+
And _mutable_ operations just create new objects
|
45
|
+
```ruby
|
46
|
+
expect(from_hash.merge(c: 3)).to eq(MyStruct.new(a: 1, b: 2, c: 3))
|
47
|
+
expect(from_hash.to_h).to eq(a: 1, b: 2)
|
48
|
+
```
|
49
|
+
|
50
|
+
And the _hashy_ methods observer the same interface
|
51
|
+
```ruby
|
52
|
+
expect { from_hash.fetch(:c) }
|
53
|
+
.to raise_error(KeyError, 'key :c not found in #<MyStruct a=1, b=2> and no default given to #fetch')
|
54
|
+
|
55
|
+
expect(from_hash.fetch(:c, 42)).to eq(42)
|
56
|
+
expect(from_hash.fetch(:c) { 43 }).to eq(43)
|
57
|
+
|
58
|
+
expect(incremented.slice(:a, :c, :e)).to eq(MyStruct.new(a: 1, c: 3, e: 5))
|
59
|
+
```
|
60
|
+
|
61
|
+
## Context: ArgsParser and Args
|
62
|
+
|
63
|
+
An expressive, yet simple argument parser that takes full advantage of Ruby 3
|
64
|
+
and returns a modern Args struct (with modern I mean MyStruct) as a result.
|
65
|
+
|
66
|
+
Given a simple example like this on
|
67
|
+
```ruby
|
68
|
+
let(:simple_parser) do
|
69
|
+
ExAequo::ArgsParser.new(
|
70
|
+
allowed: %w[alpha: beta: :help :verbose],
|
71
|
+
aliases: {h: :help, v: :verbose, a: :alpha}
|
72
|
+
)
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
Then, as northing is required we can successfully parse empty arguments
|
77
|
+
```ruby
|
78
|
+
result = simple_parser.parse([])
|
79
|
+
|
80
|
+
expect(result).to be_ok
|
81
|
+
expect(result.missing).to be_empty
|
82
|
+
expect(result.superflous).to be_empty
|
83
|
+
expect(result.positionals).to be_empty
|
84
|
+
expect(result.keywords).to eq(MyStruct.new)
|
85
|
+
```
|
86
|
+
|
87
|
+
And, we can also provide allowed values (N.B. ruby sytnax is default)
|
88
|
+
```ruby
|
89
|
+
result = simple_parser.parse(%w[alpha: 42 43])
|
90
|
+
|
91
|
+
expect(result).to be_ok
|
92
|
+
expect(result.missing).to be_empty
|
93
|
+
expect(result.superflous).to be_empty
|
94
|
+
expect(result.positionals).to eq(%w[43])
|
95
|
+
expect(result.keywords).to eq(MyStruct.new(alpha: '42'))
|
96
|
+
```
|
97
|
+
|
98
|
+
## Context: Colors
|
99
|
+
|
100
|
+
Given the includion of the `Color` module
|
101
|
+
```ruby
|
102
|
+
include ExAequo::Color
|
103
|
+
```
|
104
|
+
|
105
|
+
|
106
|
+
Then we can create ansi colored strings
|
107
|
+
```ruby
|
108
|
+
expect(ansi(:red)).to eq("\e[31m")
|
109
|
+
```
|
110
|
+
|
111
|
+
Or we can create 256 color strings
|
112
|
+
```ruby
|
113
|
+
expect(ansi256(100)).to eq("\e[38;5;100m")
|
114
|
+
```
|
115
|
+
|
116
|
+
Or we can use RGB colors
|
117
|
+
```ruby
|
118
|
+
expect(rgb(96,32,16)).to eq("\e[38;2;96;32;16m")
|
119
|
+
expect(rgb('#602010')).to eq("\e[38;2;96;32;16m")
|
120
|
+
```
|
121
|
+
|
122
|
+
### Context: Colorize Helper
|
123
|
+
|
124
|
+
And
|
125
|
+
```ruby
|
126
|
+
expect(colorize('red', ansi: :green)).to eq("\e[32mred\e[0m")
|
127
|
+
```
|
128
|
+
|
129
|
+
Or we can omit the reset
|
130
|
+
```ruby
|
131
|
+
expect(colorize('red', ansi: :green, reset: false)).to eq("\e[32mred")
|
132
|
+
```
|
133
|
+
|
9
134
|
|
10
|
-
Some Tools
|
11
135
|
|
12
136
|
|
13
137
|
|
14
|
-
##
|
15
138
|
|
16
139
|
## LICENSE
|
17
140
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'my_struct'
|
4
|
+
|
5
|
+
module ExAequo
|
6
|
+
class Args
|
7
|
+
|
8
|
+
attr_reader :missing, :positionals, :superflous
|
9
|
+
|
10
|
+
def add_illegal_kwd(kwd)
|
11
|
+
errors << ["Illegal kwd #{kwd}"]
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_kwd(kwd, value)
|
15
|
+
# Constraint Checks would go here
|
16
|
+
@keywords[kwd.to_sym] = value
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_positional(arg)
|
21
|
+
positionals << arg
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def errors
|
26
|
+
@__errors__ ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def keywords
|
30
|
+
MyStruct.new(@keywords)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ok?
|
34
|
+
missing.empty? && errors.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_flag(flag)
|
38
|
+
@keywords[flag.to_sym] = true
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :aliases, :allowed, :required
|
45
|
+
|
46
|
+
def initialize(aliases: [], allowed: [], required: [])
|
47
|
+
@required = required
|
48
|
+
@allowed = allowed
|
49
|
+
@required = required
|
50
|
+
|
51
|
+
@bad_syntax = false
|
52
|
+
@keywords = {}
|
53
|
+
@missing = []
|
54
|
+
@positionals = []
|
55
|
+
@superflous = []
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'args'
|
4
|
+
|
5
|
+
module ExAequo
|
6
|
+
class ArgsParser
|
7
|
+
|
8
|
+
FlagRgx = %r{\A : .*}x.freeze
|
9
|
+
KwdRgx = %r{.* : \z}x.freeze
|
10
|
+
def parse(args)
|
11
|
+
result = Args.new(aliases:, allowed:, required:)
|
12
|
+
args.inject([state, result], &_parse).last
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :aliases, :allowed, :posix, :required, :state
|
18
|
+
|
19
|
+
def initialize(aliases: [], allowed: [], posix: false, required: [])
|
20
|
+
@aliases = aliases
|
21
|
+
@allowed = allowed
|
22
|
+
@posix = posix
|
23
|
+
@required = required
|
24
|
+
|
25
|
+
@state = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def _parse
|
29
|
+
->((state, result), arg) do
|
30
|
+
case state
|
31
|
+
in nil
|
32
|
+
_parse_nil(result, arg)
|
33
|
+
in kwd
|
34
|
+
_parse_kwd(kwd, result, arg)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def _parse_new_flag(flag, result)
|
40
|
+
if allowed.include?(flag)
|
41
|
+
[nil, result.set_flag(flag[1..-1])]
|
42
|
+
else
|
43
|
+
[nil, result.add_illegal_kwd(flag)]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def _parse_new_kwd(kwd, result)
|
48
|
+
if allowed.include?(kwd)
|
49
|
+
[kwd[0...-1], result]
|
50
|
+
else
|
51
|
+
[nil, result.add_illegal_kwd(kwd)]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def _parse_kwd(kwd, result, arg)
|
56
|
+
[nil, result.add_kwd(kwd, arg)]
|
57
|
+
end
|
58
|
+
|
59
|
+
def _parse_nil(result, arg)
|
60
|
+
case arg
|
61
|
+
when FlagRgx
|
62
|
+
_parse_new_flag(arg, result)
|
63
|
+
when KwdRgx
|
64
|
+
_parse_new_kwd(arg, result)
|
65
|
+
else
|
66
|
+
[nil, result.add_positional(arg)]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MyStruct < OpenStruct
|
4
|
+
module ClassMethods
|
5
|
+
def from_reduce(keys, acc, &reducer)
|
6
|
+
new(**_from_reduce(keys, acc, &reducer))
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def _from_reduce(keys, acc, &reducer)
|
12
|
+
keys.map do |key|
|
13
|
+
reducer.(acc, key) => acc
|
14
|
+
[key, acc]
|
15
|
+
end.to_h
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
# SPDX-License-Identifier: Apache-2.0
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'my_struct/class_methods'
|
4
|
+
require 'forwarder'
|
5
|
+
class MyStruct < OpenStruct
|
6
|
+
class ImmutableError < RuntimeError; end
|
7
|
+
|
8
|
+
extend ClassMethods
|
9
|
+
extend Forwarder
|
10
|
+
|
11
|
+
# forward :fetch, to: :to_h
|
12
|
+
|
13
|
+
def[]=(*)
|
14
|
+
raise ImmutableError, "cannot change values with []= in immutable instance of #{self.class}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def fetch(*args, &blk)
|
18
|
+
to_h.fetch(*args, &blk)
|
19
|
+
rescue KeyError
|
20
|
+
raise KeyError,
|
21
|
+
"key #{args.first.inspect} not found in #{self} and no default given to #fetch"
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge(with)
|
25
|
+
self.class.new(to_h.merge(with.to_h))
|
26
|
+
end
|
27
|
+
|
28
|
+
def slice(*args)
|
29
|
+
self.class.new(to_h.slice(*args.flatten))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# SPDX-License-Identifier: Apache-2.0
|
data/lib/ex_aequo/version.rb
CHANGED
data/lib/ex_aequo.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ex_aequo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
11
|
+
date: 2023-03-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: forwarder3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.0
|
13
27
|
description: 'Some tools
|
14
28
|
|
15
29
|
'
|
@@ -21,6 +35,8 @@ files:
|
|
21
35
|
- LICENSE
|
22
36
|
- README.md
|
23
37
|
- lib/ex_aequo.rb
|
38
|
+
- lib/ex_aequo/args.rb
|
39
|
+
- lib/ex_aequo/args_parser.rb
|
24
40
|
- lib/ex_aequo/color.rb
|
25
41
|
- lib/ex_aequo/color/ansi.rb
|
26
42
|
- lib/ex_aequo/color/ansi256.rb
|
@@ -32,6 +48,8 @@ files:
|
|
32
48
|
- lib/ex_aequo/kernel.rb
|
33
49
|
- lib/ex_aequo/kernel/access.rb
|
34
50
|
- lib/ex_aequo/kernel/fn.rb
|
51
|
+
- lib/ex_aequo/my_struct.rb
|
52
|
+
- lib/ex_aequo/my_struct/class_methods.rb
|
35
53
|
- lib/ex_aequo/version.rb
|
36
54
|
homepage: https://gitlab.com/robert_dober/rb_ex_aequo
|
37
55
|
licenses:
|
@@ -52,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
70
|
- !ruby/object:Gem::Version
|
53
71
|
version: '0'
|
54
72
|
requirements: []
|
55
|
-
rubygems_version: 3.3.
|
73
|
+
rubygems_version: 3.3.26
|
56
74
|
signing_key:
|
57
75
|
specification_version: 4
|
58
76
|
summary: Some tools
|