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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0499472f115bf5da195d99bdd428ba149cdfdd9aaafa9fcc3142bed9666912c8'
4
- data.tar.gz: c5f216790f1eff9d99e779c6e2ca1560f02add2714e7e49286467bdf0a49d34a
3
+ metadata.gz: ecfee3b816360cda72ebf826ecd5f17301eafad003962b860711e8195b7f6ee5
4
+ data.tar.gz: 80500186030dab2c7ad0cbf37c1dbff0c067a88b90cbb3422c3f2da121f1c45b
5
5
  SHA512:
6
- metadata.gz: e37bb04b1113d2f9dd82736382d3616bccd46e279be3a588bbdb113a34a6788d289d4baf4e2d7c4eaed2340cd4db3b2ea0e4950c278ad6f3435ce6530b7e1e21
7
- data.tar.gz: 498f891d7209f9942a121db3d0ee7ae63f7e07399d4a0503ae2880acbf24df4795cdfdd1a75f4f2273a61268db251cf1f565aeb62fcfd6ee4435ddfdf19c6e15
6
+ metadata.gz: 950e811e86bcc7d34882ab1a1a00ef9e9d82bfada7cdf98dab119e7df76e4101c8c6b6418f13d05c99dc831149c8e7263c558b2bd642efa9ff80ea137d887d6e
7
+ data.tar.gz: f395398d33a331e996bb571ee3368aee8a0c648629b77eb9a0e30f87eab841b4dddc5897e5177fdc5a4e6fdfaaef977446f2e772764a54b663b05c03e0eebaba
data/README.md CHANGED
@@ -3,19 +3,153 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/ex_aequo.svg)](http://badge.fury.io/rb/ex_aequo)
4
4
  [![Gem Downloads](https://img.shields.io/gem/dt/ex_aequo.svg)](https://rubygems.org/gems/ex_aequo)
5
5
 
6
- # ExAequo
7
6
 
8
- gem install ex_aequo
7
+ # Some Tools
9
8
 
10
- Some Tools
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 2022 Robert Dober robert.dober@gmail.com
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ExAequo
4
4
  module Version
5
- VERSION = '0.1.4'
5
+ VERSION = '0.2.2'
6
6
  end
7
7
  end
8
8
  # SPDX-License-Identifier: Apache-2.0
data/lib/ex_aequo.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  module ExAequo
4
4
  end
5
+ require_relative 'ex_aequo/args_parser'
5
6
  require_relative 'ex_aequo/color'
6
7
  require_relative 'ex_aequo/version'
7
8
  # SPDX-License-Identifier: Apache-2.0
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.1.4
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: 2022-11-21 00:00:00.000000000 Z
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.7
75
+ rubygems_version: 3.3.26
56
76
  signing_key:
57
77
  specification_version: 4
58
78
  summary: Some tools