globs 0.0.2 → 0.0.3

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