hocon 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +4 -2
  3. data/lib/hocon.rb +2 -0
  4. data/lib/hocon/config.rb +1010 -0
  5. data/lib/hocon/config_error.rb +32 -2
  6. data/lib/hocon/config_factory.rb +46 -0
  7. data/lib/hocon/config_include_context.rb +49 -0
  8. data/lib/hocon/config_includer_file.rb +27 -0
  9. data/lib/hocon/config_list.rb +49 -0
  10. data/lib/hocon/config_mergeable.rb +74 -0
  11. data/lib/hocon/config_object.rb +144 -1
  12. data/lib/hocon/config_parse_options.rb +33 -9
  13. data/lib/hocon/config_parseable.rb +51 -0
  14. data/lib/hocon/config_render_options.rb +4 -2
  15. data/lib/hocon/config_resolve_options.rb +31 -0
  16. data/lib/hocon/config_syntax.rb +5 -2
  17. data/lib/hocon/config_util.rb +73 -0
  18. data/lib/hocon/config_value.rb +122 -0
  19. data/lib/hocon/config_value_factory.rb +66 -2
  20. data/lib/hocon/config_value_type.rb +5 -2
  21. data/lib/hocon/impl.rb +2 -0
  22. data/lib/hocon/impl/abstract_config_node.rb +29 -0
  23. data/lib/hocon/impl/abstract_config_node_value.rb +11 -0
  24. data/lib/hocon/impl/abstract_config_object.rb +148 -42
  25. data/lib/hocon/impl/abstract_config_value.rb +251 -11
  26. data/lib/hocon/impl/array_iterator.rb +19 -0
  27. data/lib/hocon/impl/config_boolean.rb +7 -1
  28. data/lib/hocon/impl/config_concatenation.rb +177 -28
  29. data/lib/hocon/impl/config_delayed_merge.rb +329 -0
  30. data/lib/hocon/impl/config_delayed_merge_object.rb +274 -0
  31. data/lib/hocon/impl/config_document_parser.rb +647 -0
  32. data/lib/hocon/impl/config_double.rb +44 -0
  33. data/lib/hocon/impl/config_impl.rb +143 -19
  34. data/lib/hocon/impl/config_impl_util.rb +18 -0
  35. data/lib/hocon/impl/config_include_kind.rb +10 -0
  36. data/lib/hocon/impl/config_int.rb +13 -1
  37. data/lib/hocon/impl/config_node_array.rb +11 -0
  38. data/lib/hocon/impl/config_node_comment.rb +19 -0
  39. data/lib/hocon/impl/config_node_complex_value.rb +54 -0
  40. data/lib/hocon/impl/config_node_concatenation.rb +11 -0
  41. data/lib/hocon/impl/config_node_field.rb +81 -0
  42. data/lib/hocon/impl/config_node_include.rb +33 -0
  43. data/lib/hocon/impl/config_node_object.rb +276 -0
  44. data/lib/hocon/impl/config_node_path.rb +48 -0
  45. data/lib/hocon/impl/config_node_root.rb +60 -0
  46. data/lib/hocon/impl/config_node_simple_value.rb +42 -0
  47. data/lib/hocon/impl/config_node_single_token.rb +17 -0
  48. data/lib/hocon/impl/config_null.rb +15 -7
  49. data/lib/hocon/impl/config_number.rb +43 -4
  50. data/lib/hocon/impl/config_parser.rb +403 -0
  51. data/lib/hocon/impl/config_reference.rb +142 -0
  52. data/lib/hocon/impl/config_string.rb +55 -7
  53. data/lib/hocon/impl/container.rb +29 -0
  54. data/lib/hocon/impl/default_transformer.rb +24 -15
  55. data/lib/hocon/impl/from_map_mode.rb +3 -1
  56. data/lib/hocon/impl/full_includer.rb +2 -0
  57. data/lib/hocon/impl/memo_key.rb +42 -0
  58. data/lib/hocon/impl/mergeable_value.rb +8 -0
  59. data/lib/hocon/impl/origin_type.rb +8 -2
  60. data/lib/hocon/impl/parseable.rb +455 -91
  61. data/lib/hocon/impl/path.rb +181 -59
  62. data/lib/hocon/impl/path_builder.rb +24 -3
  63. data/lib/hocon/impl/path_parser.rb +280 -0
  64. data/lib/hocon/impl/replaceable_merge_stack.rb +22 -0
  65. data/lib/hocon/impl/resolve_context.rb +254 -0
  66. data/lib/hocon/impl/resolve_memos.rb +21 -0
  67. data/lib/hocon/impl/resolve_result.rb +39 -0
  68. data/lib/hocon/impl/resolve_source.rb +354 -0
  69. data/lib/hocon/impl/resolve_status.rb +3 -1
  70. data/lib/hocon/impl/simple_config.rb +264 -10
  71. data/lib/hocon/impl/simple_config_document.rb +48 -0
  72. data/lib/hocon/impl/simple_config_list.rb +282 -8
  73. data/lib/hocon/impl/simple_config_object.rb +424 -88
  74. data/lib/hocon/impl/simple_config_origin.rb +263 -71
  75. data/lib/hocon/impl/simple_include_context.rb +31 -1
  76. data/lib/hocon/impl/simple_includer.rb +196 -1
  77. data/lib/hocon/impl/substitution_expression.rb +38 -0
  78. data/lib/hocon/impl/token.rb +17 -4
  79. data/lib/hocon/impl/token_type.rb +6 -2
  80. data/lib/hocon/impl/tokenizer.rb +339 -109
  81. data/lib/hocon/impl/tokens.rb +330 -79
  82. data/lib/hocon/impl/unmergeable.rb +14 -1
  83. data/lib/hocon/impl/unsupported_operation_error.rb +6 -0
  84. data/lib/hocon/impl/url.rb +37 -0
  85. data/lib/hocon/parser.rb +7 -0
  86. data/lib/hocon/parser/config_document.rb +92 -0
  87. data/lib/hocon/parser/config_document_factory.rb +36 -0
  88. data/lib/hocon/parser/config_node.rb +30 -0
  89. metadata +67 -43
  90. data/lib/hocon/impl/config_float.rb +0 -13
  91. data/lib/hocon/impl/parser.rb +0 -977
  92. data/lib/hocon/impl/properties_parser.rb +0 -83
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/impl/config_node_complex_value'
5
+
6
+ class Hocon::Impl::ConfigNodeConcatenation
7
+ include Hocon::Impl::ConfigNodeComplexValue
8
+ def new_node(nodes)
9
+ self.class.new(nodes)
10
+ end
11
+ end
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/config_error'
5
+ require 'hocon/impl/abstract_config_node'
6
+ require 'hocon/impl/abstract_config_node_value'
7
+ require 'hocon/impl/config_node_comment'
8
+ require 'hocon/impl/config_node_path'
9
+ require 'hocon/impl/config_node_single_token'
10
+ require 'hocon/impl/tokens'
11
+
12
+ class Hocon::Impl::ConfigNodeField
13
+ include Hocon::Impl::AbstractConfigNode
14
+
15
+ Tokens = Hocon::Impl::Tokens
16
+
17
+ def initialize(children)
18
+ @children = children
19
+ end
20
+
21
+ attr_reader :children
22
+
23
+ def tokens
24
+ tokens = []
25
+ @children.each do |child|
26
+ tokens += child.tokens
27
+ end
28
+ tokens
29
+ end
30
+
31
+ def replace_value(new_value)
32
+ children_copy = @children.clone
33
+ children_copy.each_with_index do |child, i|
34
+ if child.is_a?(Hocon::Impl::AbstractConfigNodeValue)
35
+ children_copy[i] = new_value
36
+ return self.class.new(children_copy)
37
+ end
38
+ end
39
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value"
40
+ end
41
+
42
+ def value
43
+ @children.each do |child|
44
+ if child.is_a?(Hocon::Impl::AbstractConfigNodeValue)
45
+ return child
46
+ end
47
+ end
48
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a value"
49
+ end
50
+
51
+ def path
52
+ @children.each do |child|
53
+ if child.is_a?(Hocon::Impl::ConfigNodePath)
54
+ return child
55
+ end
56
+ end
57
+ raise Hocon::ConfigError::ConfigBugOrBrokenError, "Field node doesn't have a path"
58
+ end
59
+
60
+ def separator
61
+ @children.each do |child|
62
+ if child.is_a?(Hocon::Impl::ConfigNodeSingleToken)
63
+ t = child.token
64
+ if t == Tokens::PLUS_EQUALS or t == Tokens::COLON or t == Tokens::EQUALS
65
+ return t
66
+ end
67
+ end
68
+ end
69
+ nil
70
+ end
71
+
72
+ def comments
73
+ comments = []
74
+ @children.each do |child|
75
+ if child.is_a?(Hocon::Impl::ConfigNodeComment)
76
+ comments << child.comment_text
77
+ end
78
+ end
79
+ comments
80
+ end
81
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/config_error'
5
+ require 'hocon/impl/abstract_config_node'
6
+ require 'hocon/impl/config_node_simple_value'
7
+
8
+ class Hocon::Impl::ConfigNodeInclude
9
+ include Hocon::Impl::AbstractConfigNode
10
+ def initialize(children, kind)
11
+ @children = children
12
+ @kind = kind
13
+ end
14
+
15
+ attr_reader :kind, :children
16
+
17
+ def tokens
18
+ tokens = []
19
+ @children.each do |child|
20
+ tokens += child.tokens
21
+ end
22
+ tokens
23
+ end
24
+
25
+ def name
26
+ @children.each do |child|
27
+ if child.is_a?(Hocon::Impl::ConfigNodeSimpleValue)
28
+ return Hocon::Impl::Tokens.value(child.token).unwrapped
29
+ end
30
+ end
31
+ nil
32
+ end
33
+ end
@@ -0,0 +1,276 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/config_syntax'
4
+ require 'hocon/impl'
5
+ require 'hocon/impl/config_node_complex_value'
6
+ require 'hocon/impl/config_node_field'
7
+ require 'hocon/impl/config_node_single_token'
8
+ require 'hocon/impl/tokens'
9
+
10
+ class Hocon::Impl::ConfigNodeObject
11
+ include Hocon::Impl::ConfigNodeComplexValue
12
+
13
+ ConfigSyntax = Hocon::ConfigSyntax
14
+ Tokens = Hocon::Impl::Tokens
15
+
16
+ def new_node(nodes)
17
+ self.class.new(nodes)
18
+ end
19
+
20
+ def has_value(desired_path)
21
+ @children.each do |node|
22
+ if node.is_a?(Hocon::Impl::ConfigNodeField)
23
+ field = node
24
+ key = field.path.value
25
+ if key == desired_path || key.starts_with(desired_path)
26
+ return true
27
+ elsif desired_path.starts_with(key)
28
+ if field.value.is_a?(self.class)
29
+ obj = field.value
30
+ remaining_path = desired_path.sub_path_to_end(key.length)
31
+ if obj.has_value(remaining_path)
32
+ return true
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ false
39
+ end
40
+
41
+ def change_value_on_path(desired_path, value, flavor)
42
+ children_copy = @children.clone
43
+ seen_non_matching = false
44
+ # Copy the value so we can change it to null but not modify the original parameter
45
+ value_copy = value
46
+ i = children_copy.size
47
+ while i >= 0 do
48
+ if children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken)
49
+ t = children_copy[i].token
50
+ # Ensure that, when we are removing settings in JSON, we don't end up with a trailing comma
51
+ if flavor.equal?(ConfigSyntax::JSON) && !seen_non_matching && t.equal?(Tokens::COMMA)
52
+ children_copy.delete_at(i)
53
+ end
54
+ i -= 1
55
+ next
56
+ elsif !children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
57
+ i -= 1
58
+ next
59
+ end
60
+ node = children_copy[i]
61
+ key = node.path.value
62
+
63
+ # Delete all multi-element paths that start with the desired path, since technically they are duplicates
64
+ if (value_copy.nil? && key == desired_path) || (key.starts_with(desired_path) && !(key == desired_path))
65
+ children_copy.delete_at(i)
66
+ # Remove any whitespace or commas after the deleted setting
67
+ j = i
68
+ while j < children_copy.size
69
+ if children_copy[j].is_a?(Hocon::Impl::ConfigNodeSingleToken)
70
+ t = children_copy[j].token
71
+ if Tokens.ignored_whitespace?(t) || t.equal?(Tokens::COMMA)
72
+ children_copy.delete_at(j)
73
+ j -= 1
74
+ else
75
+ break
76
+ end
77
+ else
78
+ break
79
+ end
80
+ j += 1
81
+ end
82
+ elsif key == desired_path
83
+ seen_non_matching = true
84
+ before = i - 1 > 0 ? children_copy[i - 1] : nil
85
+ if value.is_a?(Hocon::Impl::ConfigNodeComplexValue) && before.is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
86
+ Tokens.ignored_whitespace?(before.token)
87
+ indented_value = value.indent_text(before)
88
+ else
89
+ indented_value = value
90
+ end
91
+ children_copy[i] = node.replace_value(indented_value)
92
+ value_copy = nil
93
+ elsif desired_path.starts_with(key)
94
+ seen_non_matching = true
95
+ if node.value.is_a?(self.class)
96
+ remaining_path = desired_path.sub_path_to_end(key.length)
97
+ children_copy[i] = node.replace_value(node.value.change_value_on_path(remaining_path, value_copy, flavor))
98
+ if !value_copy.nil? && !(node == @children[i])
99
+ value_copy = nil
100
+ end
101
+ end
102
+ else
103
+ seen_non_matching = true
104
+ end
105
+ i -= 1
106
+ end
107
+ self.class.new(children_copy)
108
+ end
109
+
110
+ def set_value_on_path(desired_path, value, flavor = ConfigSyntax::CONF)
111
+ path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor)
112
+ set_value_on_path_node(path, value, flavor)
113
+ end
114
+
115
+ def set_value_on_path_node(desired_path, value, flavor)
116
+ node = change_value_on_path(desired_path.value, value, flavor)
117
+
118
+ # If the desired Path did not exist, add it
119
+ unless node.has_value(desired_path.value)
120
+ return node.add_value_on_path(desired_path, value, flavor)
121
+ end
122
+ node
123
+ end
124
+
125
+ def indentation
126
+ seen_new_line = false
127
+ indentation = []
128
+
129
+ @children.each_index do |i|
130
+ unless seen_new_line
131
+ if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(@children[i].token)
132
+ seen_new_line = true
133
+ indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_line(nil)))
134
+ end
135
+ else
136
+ if @children[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
137
+ Tokens.ignored_whitespace?(@children[i].token) &&
138
+ i + 1 < @children.size &&
139
+ (@children[i + 1].is_a?(Hocon::Impl::ConfigNodeField) || @children[i + 1].is_a?(Hocon::Impl::ConfigNodeInclude))
140
+ # Return the indentation of the first setting on its own line
141
+ indentation.push(@children[i])
142
+ return indentation
143
+ end
144
+ end
145
+ end
146
+ if indentation.empty?
147
+ indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, " ")))
148
+ return indentation
149
+ else
150
+ # Calculate the indentation of the ending curly-brace to get the indentation of the root object
151
+ last = @children[-1]
152
+ if last.is_a?(Hocon::Impl::ConfigNodeSingleToken) && last.token.equal?(Tokens::CLOSE_CURLY)
153
+ beforeLast = @children[-2]
154
+ if beforeLast.is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
155
+ Tokens.ignored_whitespace?(beforeLast.token)
156
+ indent = beforeLast.token.token_text
157
+ indent += " "
158
+ indentation.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, indent)))
159
+ return indentation
160
+ end
161
+ end
162
+ end
163
+
164
+ # The object has no curly braces and is at the root level, so don't indent
165
+ indentation
166
+ end
167
+
168
+ def add_value_on_path(desired_path, value, flavor)
169
+ path = desired_path.value
170
+ children_copy = @children.clone
171
+ indentation = indentation().clone
172
+
173
+ # If the value we're inserting is a complex value, we'll need to indent it for insertion
174
+ if value.is_a?(Hocon::Impl::ConfigNodeComplexValue)
175
+ indented_value = value.indent_text(indentation[-1])
176
+ else
177
+ indented_value = value
178
+ end
179
+ same_line = !(indentation[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
180
+ Tokens.newline?(indentation[0].token))
181
+
182
+ # If the path is of length greater than one, see if the value needs to be added further down
183
+ if path.length > 1
184
+ (0..@children.size - 1).reverse_each do |i|
185
+ unless @children[i].is_a?(Hocon::Impl::ConfigNodeField)
186
+ next
187
+ end
188
+ node = @children[i]
189
+ key = node.path.value
190
+ if path.starts_with(key) && node.value.is_a?(self.class)
191
+ remaining_path = desired_path.sub_path(key.length)
192
+ new_value = node.value
193
+ children_copy[i] = node.replace_value(new_value.add_value_on_path(remaining_path, value, flavor))
194
+ return self.class.new(children_copy)
195
+ end
196
+ end
197
+ end
198
+
199
+ # Otherwise, construct the new setting
200
+ starts_with_brace = @children[0].is_a?(Hocon::Impl::ConfigNodeSingleToken) && @children[0].token.equal?(Tokens::OPEN_CURLY)
201
+ new_nodes = []
202
+ new_nodes += indentation
203
+ new_nodes.push(desired_path.first)
204
+ new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
205
+ new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COLON))
206
+ new_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
207
+
208
+ if path.length == 1
209
+ new_nodes.push(indented_value)
210
+ else
211
+ # If the path is of length greater than one add the required new objects along the path
212
+ new_object_nodes = []
213
+ new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::OPEN_CURLY))
214
+ new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens.new_ignored_whitespace(nil, ' ')))
215
+ new_object_nodes.push(Hocon::Impl::ConfigNodeSingleToken.new(Tokens::CLOSE_CURLY))
216
+ new_object = self.class.new(new_object_nodes)
217
+ new_nodes.push(new_object.add_value_on_path(desired_path.sub_path(1), indented_value, flavor))
218
+ end
219
+
220
+ # Combine these two cases so that we only have to iterate once
221
+ if flavor.equal?(ConfigSyntax::JSON) || starts_with_brace || same_line
222
+ i = children_copy.size - 1
223
+ while i >= 0
224
+
225
+ # If we are in JSON or are adding a setting on the same line, we need to add a comma to the
226
+ # last setting
227
+ if (flavor.equal?(ConfigSyntax::JSON) || same_line) && children_copy[i].is_a?(Hocon::Impl::ConfigNodeField)
228
+ if i + 1 >= children_copy.size ||
229
+ !(children_copy[i + 1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && children_copy[i + 1].token.equal?(Tokens::COMMA))
230
+ children_copy.insert(i + 1, Hocon::Impl::ConfigNodeSingleToken.new(Tokens::COMMA))
231
+ break
232
+ end
233
+ end
234
+
235
+ # Add the value into the copy of the children map, keeping any whitespace/newlines
236
+ # before the close curly brace
237
+ if starts_with_brace && children_copy[i].is_a?(Hocon::Impl::ConfigNodeSingleToken) &&
238
+ children_copy[i].token == Tokens::CLOSE_CURLY
239
+ previous = children_copy[i - 1]
240
+ if previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(previous.token)
241
+ children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
242
+ i -= 1
243
+ elsif previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.ignored_whitespace?(previous.token)
244
+ before_previous = children_copy[i - 2]
245
+ if same_line
246
+ children_copy.insert(i - 1, Hocon::Impl::ConfigNodeField.new(new_nodes))
247
+ i -= 1
248
+ elsif before_previous.is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(before_previous.token)
249
+ children_copy.insert(i - 2, Hocon::Impl::ConfigNodeField.new(new_nodes))
250
+ i -= 2
251
+ else
252
+ children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
253
+ end
254
+ else
255
+ children_copy.insert(i, Hocon::Impl::ConfigNodeField.new(new_nodes))
256
+ end
257
+ end
258
+
259
+ i -= 1
260
+ end
261
+ end
262
+ unless starts_with_brace
263
+ if children_copy[-1].is_a?(Hocon::Impl::ConfigNodeSingleToken) && Tokens.newline?(children_copy[-1].token)
264
+ children_copy.insert(-2, Hocon::Impl::ConfigNodeField.new(new_nodes))
265
+ else
266
+ children_copy.push(Hocon::Impl::ConfigNodeField.new(new_nodes))
267
+ end
268
+ end
269
+ self.class.new(children_copy)
270
+ end
271
+
272
+ def remove_value_on_path(desired_path, flavor)
273
+ path = Hocon::Impl::PathParser.parse_path_node(desired_path, flavor).value
274
+ change_value_on_path(path, nil, flavor)
275
+ end
276
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'hocon/impl'
4
+ require 'hocon/impl/tokens'
5
+ require 'hocon/impl/abstract_config_node'
6
+
7
+ class Hocon::Impl::ConfigNodePath
8
+ include Hocon::Impl::AbstractConfigNode
9
+ Tokens = Hocon::Impl::Tokens
10
+
11
+ def initialize(path, tokens)
12
+ @path = path
13
+ @tokens = tokens
14
+ end
15
+
16
+ attr_reader :tokens
17
+
18
+ def value
19
+ @path
20
+ end
21
+
22
+ def sub_path(to_remove)
23
+ period_count = 0
24
+ tokens_copy = tokens.clone
25
+ (0..tokens_copy.size - 1).each do |i|
26
+ if Tokens.unquoted_text?(tokens_copy[i]) &&
27
+ tokens_copy[i].token_text == "."
28
+ period_count += 1
29
+ end
30
+
31
+ if period_count == to_remove
32
+ return self.class.new(@path.sub_path_to_end(to_remove), tokens_copy[i + 1..tokens_copy.size])
33
+ end
34
+ end
35
+ raise ConfigBugOrBrokenError, "Tried to remove too many elements from a Path node"
36
+ end
37
+
38
+ def first
39
+ tokens_copy = tokens.clone
40
+ (0..tokens_copy.size - 1).each do |i|
41
+ if Tokens.unquoted_text?(tokens_copy[i]) &&
42
+ tokens_copy[i].token_text == "."
43
+ return self.class.new(@path.sub_path(0, 1), tokens_copy[0, i])
44
+ end
45
+ end
46
+ self
47
+ end
48
+ end