ex_aequo 0.1.4 → 0.2.2

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