ex_aequo 0.1.4 → 0.2.2
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 +139 -5
- data/lib/ex_aequo/args.rb +60 -0
- data/lib/ex_aequo/args_parser.rb +71 -0
- data/lib/ex_aequo/color/text.rb +50 -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/tools/text_scanner.rb +17 -0
- data/lib/ex_aequo/version.rb +1 -1
- data/lib/ex_aequo.rb +1 -0
- metadata +24 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ecfee3b816360cda72ebf826ecd5f17301eafad003962b860711e8195b7f6ee5
|
4
|
+
data.tar.gz: 80500186030dab2c7ad0cbf37c1dbff0c067a88b90cbb3422c3f2da121f1c45b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 950e811e86bcc7d34882ab1a1a00ef9e9d82bfada7cdf98dab119e7df76e4101c8c6b6418f13d05c99dc831149c8e7263c558b2bd642efa9ff80ea137d887d6e
|
7
|
+
data.tar.gz: f395398d33a331e996bb571ee3368aee8a0c648629b77eb9a0e30f87eab841b4dddc5897e5177fdc5a4e6fdfaaef977446f2e772764a54b663b05c03e0eebaba
|
data/README.md
CHANGED
@@ -3,19 +3,153 @@
|
|
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
|
9
8
|
|
10
|
-
|
9
|
+
... which tools?
|
11
10
|
|
11
|
+
Let us [speculate about](https://github.com/RobertDober/speculate_about) that:
|
12
12
|
|
13
13
|
|
14
|
-
##
|
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 inclusion 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
|
+
|
134
|
+
### Context: Colorize Text
|
135
|
+
|
136
|
+
Given we include the Color::Text module
|
137
|
+
```ruby
|
138
|
+
require 'ex_aequo/color/text'
|
139
|
+
include ExAequo::Color::Text
|
140
|
+
```
|
141
|
+
|
142
|
+
Then we can obtain a colored text as follows
|
143
|
+
```ruby
|
144
|
+
expect(colorized_text(:red, 'green', :yellow, 'blue', :reset, 'white'))
|
145
|
+
.to eq("\e[31mgreen\e[33mblue\e[0mwhite")
|
146
|
+
```
|
147
|
+
|
148
|
+
## Other Tools are described [here](/speculations)
|
15
149
|
|
16
150
|
## LICENSE
|
17
151
|
|
18
|
-
Copyright
|
152
|
+
Copyright 202[2,3] Robert Dober robert.dober@gmail.com
|
19
153
|
|
20
154
|
Apache-2.0 [c.f LICENSE](LICENSE)
|
21
155
|
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
@@ -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,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExAequo
|
4
|
+
module Color
|
5
|
+
module Text extend self
|
6
|
+
|
7
|
+
AnsiColors = ExAequo::Color::Ansi::AnsiColorEscape.keys.freeze
|
8
|
+
|
9
|
+
def put_col(*segments, to: $stdout)
|
10
|
+
to.puts(colorized_text(segments))
|
11
|
+
end
|
12
|
+
|
13
|
+
def colorized_text(*segments)
|
14
|
+
if ENV['NO_COLOR']
|
15
|
+
segments.flatten.filter { String === _1 }.join
|
16
|
+
else
|
17
|
+
segments.flatten.map(&_color_or_text).join
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
|
24
|
+
def _color_or_text
|
25
|
+
-> color_or_text do
|
26
|
+
case color_or_text
|
27
|
+
when Symbol
|
28
|
+
_colorize(color_or_text)
|
29
|
+
else
|
30
|
+
color_or_text
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def _colorize(color)
|
36
|
+
case ExAequo::Color::Ansi::AnsiColorEscape[color]
|
37
|
+
when nil
|
38
|
+
if color == :reset
|
39
|
+
ExAequo::Color.reset
|
40
|
+
else
|
41
|
+
raise ArgumentError, "#{color} not yet implemented"
|
42
|
+
end
|
43
|
+
else
|
44
|
+
ExAequo::Color::Ansi.ansi(color)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
# 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
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ExAequo
|
4
|
+
module Tools
|
5
|
+
module TextScanner extend self
|
6
|
+
def scan(str, rgx: '/', ws_at_end: true)
|
7
|
+
r = Regexp === rgx ? r : Regex.compile(rgx)
|
8
|
+
_scan(str, rgx: r, ws_at_end:)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def _scan(str, rgx:, ws_at_end:)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
# 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.2
|
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-04-07 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,17 +35,23 @@ 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
|
27
43
|
- lib/ex_aequo/color/colorizer.rb
|
28
44
|
- lib/ex_aequo/color/modifiers.rb
|
29
45
|
- lib/ex_aequo/color/rgb.rb
|
46
|
+
- lib/ex_aequo/color/text.rb
|
30
47
|
- lib/ex_aequo/color/web.rb
|
31
48
|
- lib/ex_aequo/colors.rb
|
32
49
|
- lib/ex_aequo/kernel.rb
|
33
50
|
- lib/ex_aequo/kernel/access.rb
|
34
51
|
- lib/ex_aequo/kernel/fn.rb
|
52
|
+
- lib/ex_aequo/my_struct.rb
|
53
|
+
- lib/ex_aequo/my_struct/class_methods.rb
|
54
|
+
- lib/ex_aequo/tools/text_scanner.rb
|
35
55
|
- lib/ex_aequo/version.rb
|
36
56
|
homepage: https://gitlab.com/robert_dober/rb_ex_aequo
|
37
57
|
licenses:
|
@@ -52,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
72
|
- !ruby/object:Gem::Version
|
53
73
|
version: '0'
|
54
74
|
requirements: []
|
55
|
-
rubygems_version: 3.3.
|
75
|
+
rubygems_version: 3.3.26
|
56
76
|
signing_key:
|
57
77
|
specification_version: 4
|
58
78
|
summary: Some tools
|