globs 0.0.2 → 0.0.3

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: 33e33b53ea61ac120c9a50e88a9eaf5f9c59843808d5c25edf9af003d3a2b42a
4
- data.tar.gz: 0f59998761ed6675653e669b99391283653fd6ac09579240d97a0d3c1da47e6d
3
+ metadata.gz: 853fae28b48ce579ade6a922bb4198169c88e156fe9bde5a789b42a57073630c
4
+ data.tar.gz: fd6666c02bb0277079692af6112c13e6b073c452cd1b2cc77c8f509ba2031fd5
5
5
  SHA512:
6
- metadata.gz: 756c66b3af815aab238b66ed95fb4c133bcf448933de922f4838e2255dca68db4c3f8f75850bacfe8cfc19f931671855638e062d09f636a55f6f5d01f8325396
7
- data.tar.gz: 915e3ddc3aaf39dbc29c9bcc24fc8b7a6ffaa1210d33b6f1f3d316cee3d116272ebbd9982ddaba0b909d94cb8e4486eb6340d0af83df3fc15803c5cec36f5d29
6
+ metadata.gz: 52b355fa6f04a5cb23a702875dd7c609304ab9728ee515a9fb0618db272a72cd6046b9d5a86b5981877424faa23c185e1b4c0f9b8ce5737cdadd5909f2a9f932
7
+ data.tar.gz: 7451f0ef91f72265744d3e424fa27659a50287eccc0bcacf0aeb102bdaff39a9ffe3704ed338a0bfb16160808cb43e5f9cd653ad52e966c4211c5b47695c1a6b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- globs (0.0.1)
4
+ globs (0.0.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/lib/globs/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Globs
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/globs.rb CHANGED
@@ -3,20 +3,32 @@ require "globs/version"
3
3
  # Container for Globs methods, currently only static as the public api
4
4
  # footprint is relatively small.
5
5
  #
6
- # It should be noted that I'll be exceptionally verbose in the comments
7
- # of this code as it's an interesting usecase for learning to combine
8
- # map, flat_map, and reduce for permutation-type generators.
9
- #
10
- # More than likely I'll write a tutorial on how the code works later and
11
- # link to it in these comments for the exceptionally curious. It'd be roughly
12
- # a 2.5 / 5 for difficulty.
13
- #
14
6
  # @author baweaver
15
7
  # @since 0.0.1
16
8
  #
17
9
  module Globs
18
10
  extend self
19
11
 
12
+ # Opening brace of a Glob expression
13
+ OPENING_BRACE = /\{/
14
+
15
+ # Closing brace of a Glob expression
16
+ CLOSING_BRACE = /\}/
17
+
18
+ # StringScanner is not 0 indexed. Offset for index.
19
+ SCANNER_INDEX_OFFSET = 1
20
+
21
+ # We don't want to include the brace in our final set, so offset the index
22
+ # to compensate
23
+ BRACE_POSITION_OFFSET = 1
24
+
25
+ # Full positional offset for the braces and scanner's non-zero index
26
+ POSITION_OFFSET = SCANNER_INDEX_OFFSET + BRACE_POSITION_OFFSET
27
+
28
+ # End of the string position, used to clarify difference between
29
+ # explicit EOS and positional offsets
30
+ END_OF_STRING = -1
31
+
20
32
  # Shorthand for `puts expand(str)` for outputting to STDOUT for
21
33
  # unix-like piping.
22
34
  #
@@ -36,6 +48,9 @@ module Globs
36
48
  #
37
49
  # @since 0.0.1
38
50
  #
51
+ # @note
52
+ # Modified to use StringScanner in 0.0.3 for more accurate tokenization
53
+ #
39
54
  # @example
40
55
  #
41
56
  # ```
@@ -43,38 +58,91 @@ module Globs
43
58
  # => ["test.a.1.com", "test.a.2.com", "test.b.1.com", "test.b.2.com"]
44
59
  # ```
45
60
  #
46
- # @note
47
- # While this _could_ be made into a tokenization type process for speed
48
- # reasons there's very little reason to do so immediately. The current
49
- # implementation is far more proof of concept than anything.
50
- #
51
- # If speed needs to happen to arise from usage, PRs are welcome to
52
- # optimize this method, but the public api should remain the same.
53
- #
54
61
  # @param string [String]
55
62
  # Glob-like string to be expanded
56
63
  #
57
64
  # @return [Array[String]]
58
65
  # All expansions of the glob-like string
59
66
  def expand(string)
60
- string
61
- .split(/\{|\}/)
62
- .map { |cs| interpret_glob_set(cs.split(/, */)) }
63
- .reduce { |sets, cs|
64
- sets.flat_map { |s| cs.map { |c| s + c } }
67
+ scanner = StringScanner.new(string)
68
+ results = ['']
69
+
70
+ until scanner.eos?
71
+ beginning = scanner.pos
72
+ start, finish = next_expression_positions!(scanner)
73
+
74
+ # There are no further expressions in the string if the start position is
75
+ # negative.
76
+ #
77
+ # Proceed to move the scanner's cursor to the end of the string and take
78
+ # the rest of the string to append to the current result items.
79
+ if start.negative?
80
+ scanner.pos = string.size
81
+
82
+ non_glob_str = string[beginning..END_OF_STRING]
83
+ expressions = ['']
84
+ else
85
+ non_glob_str = string[beginning..(start - POSITION_OFFSET)]
86
+ expressions = interpret_expression(string[start..finish])
87
+ end
88
+
89
+ resulting_expressions = expressions.map { |exp| non_glob_str + exp }
90
+
91
+ results = results.flat_map { |res|
92
+ resulting_expressions.map { |exp| res + exp }
65
93
  }
94
+ end
95
+
96
+ results
66
97
  end
67
98
 
68
- # Interprets a set of glob expressions, looking for items like ranges. When
69
- # it finds such an item, it expands the range into an array to flatten into
70
- # the set of possible values for a section.
99
+ # Finds the beginning and end of the next expression set in the string. If
100
+ # an expression set is not found, it will return END_OF_STRING for either
101
+ # of the positions being absent.
102
+ #
103
+ # @since 0.0.3
71
104
  #
72
- # @param set [Array[String]]
73
- # Unexpanded glob set
105
+ # @mutates
106
+ #
107
+ # @note
108
+ # This will mutate the given StringScanner and cause it to move its
109
+ # associated cursor to the end of the last-found expression.
110
+ #
111
+ # If an expression is not found, it will register the last known beginning
112
+ # position, as `scan_until` will simply return nil and not change the
113
+ # cursor position if something is not found.
114
+ #
115
+ # @param scanner [StringScanner]
116
+ # Current StringScanner being used to tokenize and scan the glob expression
117
+ # string
118
+ #
119
+ # @return [Array[Integer, Integer]]
120
+ # Starting and ending positions of the next expression, excluding braces
121
+ private def next_expression_positions!(scanner)
122
+ return [END_OF_STRING, END_OF_STRING] unless scanner.scan_until(OPENING_BRACE)
123
+ start = scanner.pos
124
+
125
+ return [start, END_OF_STRING] unless scanner.scan_until(CLOSING_BRACE)
126
+ finish = scanner.pos
127
+
128
+ [start, finish - POSITION_OFFSET]
129
+ end
130
+
131
+ # Interprets a glob expression to extract permutatable items from it.
132
+ #
133
+ # @since 0.0.3
134
+ #
135
+ # @param string [String]
136
+ # String to interpret as a glob expression
74
137
  #
75
138
  # @return [Array[String]]
76
- # Fully expanded glob sets
77
- private def interpret_glob_set(set)
78
- set.flat_map { |s| s.include?('..') ? Range.new(*s.split('..')).to_a : s }
139
+ # Collection of permutable strings to iterate over when constructing a
140
+ # final glob set
141
+ private def interpret_expression(string)
142
+ string
143
+ .split(/, */)
144
+ .flat_map { |exp|
145
+ exp.include?('..') ? Range.new(*exp.split('..')).to_a : exp
146
+ }
79
147
  end
80
148
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: globs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Weaver
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-12 00:00:00.000000000 Z
11
+ date: 2019-01-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler