hanami-utils 0.0.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,167 @@
1
+ require 'hanami/utils/kernel'
2
+
3
+ module Hanami
4
+ module Utils
5
+ # A collection of loading paths.
6
+ #
7
+ # @since 0.2.0
8
+ class LoadPaths
9
+ # Initialize a new collection for the given paths
10
+ #
11
+ # @param paths [String, Pathname, Array<String>, Array<Pathname>] A single
12
+ # or a collection of objects that can be converted into a Pathname
13
+ #
14
+ # @return [Hanami::Utils::LoadPaths] self
15
+ #
16
+ # @since 0.2.0
17
+ #
18
+ # @see http://ruby-doc.org/stdlib/libdoc/pathname/rdoc/Pathname.html
19
+ # @see Hanami::Utils::Kernel.Pathname
20
+ def initialize(*paths)
21
+ @paths = Utils::Kernel.Array(paths)
22
+ end
23
+
24
+ # It specifies the policy for initialize copies of the object, when #clone
25
+ # or #dup are invoked.
26
+ #
27
+ # @api private
28
+ # @since 0.2.0
29
+ #
30
+ # @see http://ruby-doc.org/core/Object.html#method-i-clone
31
+ # @see http://ruby-doc.org/core/Object.html#method-i-dup
32
+ #
33
+ # @example
34
+ # require 'hanami/utils/load_paths'
35
+ #
36
+ # paths = Hanami::Utils::LoadPaths.new '.'
37
+ # paths2 = paths.dup
38
+ #
39
+ # paths << '..'
40
+ # paths2 << '../..'
41
+ #
42
+ # paths
43
+ # # => #<Hanami::Utils::LoadPaths:0x007f84e0cad430 @paths=[".", ".."]>
44
+ #
45
+ # paths2
46
+ # # => #<Hanami::Utils::LoadPaths:0x007faedc4ad3e0 @paths=[".", "../.."]>
47
+ def initialize_copy(original)
48
+ @paths = original.instance_variable_get(:@paths).dup
49
+ end
50
+
51
+ # Iterates thru the collection and yields the given block.
52
+ # It skips duplications and raises an error in case one of the paths
53
+ # doesn't exist.
54
+ #
55
+ # @yield [pathname] the block of code that acts on the collection
56
+ # @yieldparam pathname [Pathname]
57
+ #
58
+ # @return [void]
59
+ #
60
+ # @raise [Errno::ENOENT] if one of the paths doesn't exist
61
+ #
62
+ # @since 0.2.0
63
+ def each
64
+ @paths.each do |path|
65
+ yield realpath(path)
66
+ end
67
+ end
68
+
69
+ # Adds the given path(s).
70
+ #
71
+ # It returns self, so that multiple operations can be performed.
72
+ #
73
+ # @param paths [String, Pathname, Array<String>, Array<Pathname>] A single
74
+ # or a collection of objects that can be converted into a Pathname
75
+ #
76
+ # @return [Hanami::Utils::LoadPaths] self
77
+ #
78
+ # @raise [RuntimeError] if the object was previously frozen
79
+ #
80
+ # @since 0.2.0
81
+ #
82
+ # @see http://ruby-doc.org/stdlib/libdoc/pathname/rdoc/Pathname.html
83
+ # @see Hanami::Utils::Kernel.Pathname
84
+ # @see Hanami::Utils::LoadPaths#freeze
85
+ #
86
+ # @example Basic usage
87
+ # require 'hanami/utils/load_paths'
88
+ #
89
+ # paths = Hanami::Utils::LoadPaths.new
90
+ # paths.push '.'
91
+ # paths.push '..', '../..'
92
+ #
93
+ # @example Chainable calls
94
+ # require 'hanami/utils/load_paths'
95
+ #
96
+ # paths = Hanami::Utils::LoadPaths.new
97
+ # paths.push('.')
98
+ # .push('..', '../..')
99
+ #
100
+ # @example Shovel alias (#<<)
101
+ # require 'hanami/utils/load_paths'
102
+ #
103
+ # paths = Hanami::Utils::LoadPaths.new
104
+ # paths << '.'
105
+ # paths << ['..', '../..']
106
+ #
107
+ # @example Chainable calls with shovel alias (#<<)
108
+ # require 'hanami/utils/load_paths'
109
+ #
110
+ # paths = Hanami::Utils::LoadPaths.new
111
+ # paths << '.' << '../..'
112
+ def push(*paths)
113
+ @paths.push(*paths)
114
+ @paths = Kernel.Array(@paths)
115
+ self
116
+ end
117
+
118
+ alias_method :<<, :push
119
+
120
+ # It freezes the object by preventing further modifications.
121
+ #
122
+ # @since 0.2.0
123
+ #
124
+ # @see http://ruby-doc.org/core/Object.html#method-i-freeze
125
+ #
126
+ # @example
127
+ # require 'hanami/utils/load_paths'
128
+ #
129
+ # paths = Hanami::Utils::LoadPaths.new
130
+ # paths.freeze
131
+ #
132
+ # paths.frozen? # => true
133
+ #
134
+ # paths.push '.' # => RuntimeError
135
+ def freeze
136
+ super
137
+ @paths.freeze
138
+ end
139
+
140
+ # @since 0.6.0
141
+ # @api private
142
+ def ==(other)
143
+ case other
144
+ when self.class
145
+ other.paths == paths
146
+ else
147
+ other == paths
148
+ end
149
+ end
150
+
151
+ protected
152
+ # @since 0.6.0
153
+ # @api private
154
+ attr_reader :paths
155
+
156
+ private
157
+ # Allow subclasses to define their own policy to discover the realpath
158
+ # of the given path.
159
+ #
160
+ # @since 0.2.0
161
+ # @api private
162
+ def realpath(path)
163
+ Utils::Kernel.Pathname(path).realpath
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,146 @@
1
+ require 'hanami/utils/string'
2
+ require 'hanami/utils/kernel'
3
+
4
+ module Hanami
5
+ module Utils
6
+ # Prefixed string
7
+ #
8
+ # @since 0.1.0
9
+ class PathPrefix < Hanami::Utils::String
10
+ # Path separator
11
+ #
12
+ # @since 0.3.1
13
+ # @api private
14
+ DEFAULT_SEPARATOR = '/'.freeze
15
+
16
+ # Initialize the path prefix
17
+ #
18
+ # @param string [::String] the prefix value
19
+ # @param separator [::String] the separator used between tokens
20
+ #
21
+ # @return [PathPrefix] self
22
+ #
23
+ # @since 0.1.0
24
+ #
25
+ # @see Hanami::Utils::PathPrefix::DEFAULT_SEPARATOR
26
+ def initialize(string = nil, separator = DEFAULT_SEPARATOR)
27
+ super(string)
28
+ @separator = separator
29
+ end
30
+
31
+ # Joins self with the given token.
32
+ # It cleans up all the `separator` repetitions.
33
+ #
34
+ # @param strings [::String] the token(s) we want to join
35
+ #
36
+ # @return [Hanami::Utils::PathPrefix] the joined string
37
+ #
38
+ # @since 0.1.0
39
+ #
40
+ # @example Single string
41
+ # require 'hanami/utils/path_prefix'
42
+ #
43
+ # path_prefix = Hanami::Utils::PathPrefix.new('/posts')
44
+ # path_prefix.join('new').to_s # => "/posts/new"
45
+ # path_prefix.join('/new').to_s # => "/posts/new"
46
+ #
47
+ # path_prefix = Hanami::Utils::PathPrefix.new('posts')
48
+ # path_prefix.join('new').to_s # => "/posts/new"
49
+ # path_prefix.join('/new').to_s # => "/posts/new"
50
+ #
51
+ # @example Multiple strings
52
+ # require 'hanami/utils/path_prefix'
53
+ #
54
+ # path_prefix = Hanami::Utils::PathPrefix.new('myapp')
55
+ # path_prefix.join('/assets', 'application.js').to_s
56
+ # # => "/myapp/assets/application.js"
57
+ def join(*strings)
58
+ relative_join(strings).absolute!
59
+ end
60
+
61
+ # Joins self with the given token, without prefixing it with `separator`.
62
+ # It cleans up all the `separator` repetitions.
63
+ #
64
+ # @param strings [::String] the tokens we want to join
65
+ # @param separator [::String] the separator used between tokens
66
+ #
67
+ # @return [Hanami::Utils::PathPrefix] the joined string
68
+ #
69
+ # @raise [TypeError] if one of the argument can't be treated as a
70
+ # string
71
+ #
72
+ # @since 0.1.0
73
+ #
74
+ # @example
75
+ # require 'hanami/utils/path_prefix'
76
+ #
77
+ # path_prefix = Hanami::Utils::PathPrefix.new 'posts'
78
+ # path_prefix.relative_join('new').to_s # => 'posts/new'
79
+ # path_prefix.relative_join('new', '_').to_s # => 'posts_new'
80
+ def relative_join(strings, separator = @separator)
81
+ raise TypeError if separator.nil?
82
+ prefix = @string.gsub(@separator, separator)
83
+ result = [prefix, strings]
84
+ result.flatten!
85
+ result.compact!
86
+ result.reject! {|string| string == separator }
87
+
88
+ self.class.new(
89
+ result.join(separator), separator
90
+ ).relative!
91
+ end
92
+
93
+ protected
94
+
95
+ # Modifies the path prefix to have a prepended separator.
96
+ #
97
+ # @return [self]
98
+ #
99
+ # @since 0.3.1
100
+ # @api private
101
+ #
102
+ # @see #absolute
103
+ def absolute!
104
+ @string.prepend(separator) unless absolute?
105
+
106
+ self
107
+ end
108
+
109
+ # Returns whether the path prefix starts with its separator.
110
+ #
111
+ # @return [TrueClass,FalseClass]
112
+ #
113
+ # @since 0.3.1
114
+ # @api private
115
+ #
116
+ # @example
117
+ # require 'hanami/utils/path_prefix'
118
+ #
119
+ # Hanami::Utils::PathPrefix.new('/posts').absolute? #=> true
120
+ # Hanami::Utils::PathPrefix.new('posts').absolute? #=> false
121
+ def absolute?
122
+ @string.start_with?(separator)
123
+ end
124
+
125
+ # Modifies the path prefix to remove the leading separator.
126
+ #
127
+ # @return [self]
128
+ #
129
+ # @since 0.3.1
130
+ # @api private
131
+ #
132
+ # @see #relative
133
+ def relative!
134
+ @string.gsub!(%r{(?<!:)#{ separator * 2 }}, separator)
135
+ @string.sub!(%r{\A#{ separator }}, '')
136
+
137
+ self
138
+ end
139
+
140
+ private
141
+ # @since 0.1.0
142
+ # @api private
143
+ attr_reader :separator
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,419 @@
1
+ require 'hanami/utils/inflector'
2
+
3
+ module Hanami
4
+ module Utils
5
+ # String on steroids
6
+ #
7
+ # @since 0.1.0
8
+ class String
9
+ # Empty string for #classify
10
+ #
11
+ # @since 0.6.0
12
+ # @api private
13
+ EMPTY_STRING = ''.freeze
14
+
15
+ # Separator between Ruby namespaces
16
+ #
17
+ # @since 0.1.0
18
+ # @api private
19
+ NAMESPACE_SEPARATOR = '::'.freeze
20
+
21
+ # Separator for #classify
22
+ #
23
+ # @since 0.3.0
24
+ # @api private
25
+ CLASSIFY_SEPARATOR = '_'.freeze
26
+
27
+ # Regexp for #tokenize
28
+ #
29
+ # @since 0.3.0
30
+ # @api private
31
+ TOKENIZE_REGEXP = /\((.*)\)/
32
+
33
+ # Separator for #tokenize
34
+ #
35
+ # @since 0.3.0
36
+ # @api private
37
+ TOKENIZE_SEPARATOR = '|'.freeze
38
+
39
+ # Separator for #underscore
40
+ #
41
+ # @since 0.3.0
42
+ # @api private
43
+ UNDERSCORE_SEPARATOR = '/'.freeze
44
+
45
+ # gsub second parameter used in #underscore
46
+ #
47
+ # @since 0.3.0
48
+ # @api private
49
+ UNDERSCORE_DIVISION_TARGET = '\1_\2'.freeze
50
+
51
+ # Separator for #titleize
52
+ #
53
+ # @since 0.4.0
54
+ # @api private
55
+ TITLEIZE_SEPARATOR = ' '.freeze
56
+
57
+ # Separator for #capitalize
58
+ #
59
+ # @since 0.5.2
60
+ # @api private
61
+ CAPITALIZE_SEPARATOR = ' '.freeze
62
+
63
+ # Separator for #dasherize
64
+ #
65
+ # @since 0.4.0
66
+ # @api private
67
+ DASHERIZE_SEPARATOR = '-'.freeze
68
+
69
+ # Regexp for #classify
70
+ #
71
+ # @since 0.3.4
72
+ # @api private
73
+ CLASSIFY_WORD_SEPARATOR = /#{CLASSIFY_SEPARATOR}|#{NAMESPACE_SEPARATOR}|#{UNDERSCORE_SEPARATOR}|#{DASHERIZE_SEPARATOR}/
74
+
75
+ # Initialize the string
76
+ #
77
+ # @param string [::String, Symbol] the value we want to initialize
78
+ #
79
+ # @return [String] self
80
+ #
81
+ # @since 0.1.0
82
+ def initialize(string)
83
+ @string = string.to_s
84
+ end
85
+
86
+ # Return a titleized version of the string
87
+ #
88
+ # @return [Hanami::Utils::String] the transformed string
89
+ #
90
+ # @since 0.4.0
91
+ #
92
+ # @example
93
+ # require 'hanami/utils/string'
94
+ #
95
+ # string = Hanami::Utils::String.new 'hanami utils'
96
+ # string.titleize # => "Hanami Utils"
97
+ def titleize
98
+ self.class.new underscore.split(CLASSIFY_SEPARATOR).map(&:capitalize).join(TITLEIZE_SEPARATOR)
99
+ end
100
+
101
+ # Return a capitalized version of the string
102
+ #
103
+ # @return [Hanami::Utils::String] the transformed string
104
+ #
105
+ # @since 0.5.2
106
+ #
107
+ # @example
108
+ # require 'hanami/utils/string'
109
+ #
110
+ # string = Hanami::Utils::String.new 'hanami'
111
+ # string.capitalize # => "Hanami"
112
+ #
113
+ # string = Hanami::Utils::String.new 'hanami utils'
114
+ # string.capitalize # => "Hanami utils"
115
+ #
116
+ # string = Hanami::Utils::String.new 'Hanami Utils'
117
+ # string.capitalize # => "Hanami utils"
118
+ #
119
+ # string = Hanami::Utils::String.new 'hanami_utils'
120
+ # string.capitalize # => "Hanami utils"
121
+ #
122
+ # string = Hanami::Utils::String.new 'hanami-utils'
123
+ # string.capitalize # => "Hanami utils"
124
+ def capitalize
125
+ head, *tail = underscore.split(CLASSIFY_SEPARATOR)
126
+
127
+ self.class.new(
128
+ tail.unshift(head.capitalize).join(CAPITALIZE_SEPARATOR)
129
+ )
130
+ end
131
+
132
+ # Return a CamelCase version of the string
133
+ #
134
+ # @return [String] the transformed string
135
+ #
136
+ # @since 0.1.0
137
+ #
138
+ # @example
139
+ # require 'hanami/utils/string'
140
+ #
141
+ # string = Hanami::Utils::String.new 'hanami_utils'
142
+ # string.classify # => 'HanamiUtils'
143
+ def classify
144
+ words = split(CLASSIFY_WORD_SEPARATOR).map!(&:capitalize)
145
+ delimiters = scan(CLASSIFY_WORD_SEPARATOR)
146
+
147
+ delimiters.map! do |delimiter|
148
+ delimiter == CLASSIFY_SEPARATOR ? EMPTY_STRING : NAMESPACE_SEPARATOR
149
+ end
150
+
151
+ self.class.new words.zip(delimiters).join
152
+ end
153
+
154
+ # Return a downcased and underscore separated version of the string
155
+ #
156
+ # Revised version of `ActiveSupport::Inflector.underscore` implementation
157
+ # @see https://github.com/rails/rails/blob/feaa6e2048fe86bcf07e967d6e47b865e42e055b/activesupport/lib/active_support/inflector/methods.rb#L90
158
+ #
159
+ # @return [String] the transformed string
160
+ #
161
+ # @since 0.1.0
162
+ #
163
+ # @example
164
+ # require 'hanami/utils/string'
165
+ #
166
+ # string = Hanami::Utils::String.new 'HanamiUtils'
167
+ # string.underscore # => 'hanami_utils'
168
+ def underscore
169
+ new_string = gsub(NAMESPACE_SEPARATOR, UNDERSCORE_SEPARATOR)
170
+ new_string.gsub!(/([A-Z\d]+)([A-Z][a-z])/, UNDERSCORE_DIVISION_TARGET)
171
+ new_string.gsub!(/([a-z\d])([A-Z])/, UNDERSCORE_DIVISION_TARGET)
172
+ new_string.gsub!(/[[:space:]]|\-/, UNDERSCORE_DIVISION_TARGET)
173
+ new_string.downcase!
174
+ self.class.new new_string
175
+ end
176
+
177
+ # Return a downcased and dash separated version of the string
178
+ #
179
+ # @return [Hanami::Utils::String] the transformed string
180
+ #
181
+ # @since 0.4.0
182
+ #
183
+ # @example
184
+ # require 'hanami/utils/string'
185
+ #
186
+ # string = Hanami::Utils::String.new 'Hanami Utils'
187
+ # string.dasherize # => 'hanami-utils'
188
+ #
189
+ # string = Hanami::Utils::String.new 'hanami_utils'
190
+ # string.dasherize # => 'hanami-utils'
191
+ #
192
+ # string = Hanami::Utils::String.new 'HanamiUtils'
193
+ # string.dasherize # => "hanami-utils"
194
+ def dasherize
195
+ self.class.new underscore.split(CLASSIFY_SEPARATOR).join(DASHERIZE_SEPARATOR)
196
+ end
197
+
198
+ # Return the string without the Ruby namespace of the class
199
+ #
200
+ # @return [String] the transformed string
201
+ #
202
+ # @since 0.1.0
203
+ #
204
+ # @example
205
+ # require 'hanami/utils/string'
206
+ #
207
+ # string = Hanami::Utils::String.new 'Hanami::Utils::String'
208
+ # string.demodulize # => 'String'
209
+ #
210
+ # string = Hanami::Utils::String.new 'String'
211
+ # string.demodulize # => 'String'
212
+ def demodulize
213
+ self.class.new split(NAMESPACE_SEPARATOR).last
214
+ end
215
+
216
+ # Return the top level namespace name
217
+ #
218
+ # @return [String] the transformed string
219
+ #
220
+ # @since 0.1.2
221
+ #
222
+ # @example
223
+ # require 'hanami/utils/string'
224
+ #
225
+ # string = Hanami::Utils::String.new 'Hanami::Utils::String'
226
+ # string.namespace # => 'Hanami'
227
+ #
228
+ # string = Hanami::Utils::String.new 'String'
229
+ # string.namespace # => 'String'
230
+ def namespace
231
+ self.class.new split(NAMESPACE_SEPARATOR).first
232
+ end
233
+
234
+ # It iterates through the tokens and calls the given block.
235
+ # A token is a substring wrapped by `()` and separated by `|`.
236
+ #
237
+ # @yield the block that is called for each token.
238
+ #
239
+ # @return [void]
240
+ #
241
+ # @since 0.1.0
242
+ #
243
+ # @example
244
+ # require 'hanami/utils/string'
245
+ #
246
+ # string = Hanami::Utils::String.new 'Hanami::(Utils|App)'
247
+ # string.tokenize do |token|
248
+ # puts token
249
+ # end
250
+ #
251
+ # # =>
252
+ # 'Hanami::Utils'
253
+ # 'Hanami::App'
254
+ def tokenize
255
+ if match = TOKENIZE_REGEXP.match(@string)
256
+ pre, post = match.pre_match, match.post_match
257
+ tokens = match[1].split(TOKENIZE_SEPARATOR)
258
+ tokens.each do |token|
259
+ yield(self.class.new("#{pre}#{token}#{post}"))
260
+ end
261
+ else
262
+ yield(self.class.new(@string))
263
+ end
264
+
265
+ nil
266
+ end
267
+
268
+ # Return a pluralized version of self.
269
+ #
270
+ # @return [Hanami::Utils::String] the pluralized string.
271
+ #
272
+ # @api private
273
+ # @since 0.4.1
274
+ #
275
+ # @see Hanami::Utils::Inflector
276
+ def pluralize
277
+ self.class.new Inflector.pluralize(self)
278
+ end
279
+
280
+ # Return a singularized version of self.
281
+ #
282
+ # @return [Hanami::Utils::String] the singularized string.
283
+ #
284
+ # @api private
285
+ # @since 0.4.1
286
+ #
287
+ # @see Hanami::Utils::Inflector
288
+ def singularize
289
+ self.class.new Inflector.singularize(self)
290
+ end
291
+
292
+ # Returns the hash of the internal string
293
+ #
294
+ # @return [Fixnum]
295
+ #
296
+ # @since 0.3.0
297
+ def hash
298
+ @string.hash
299
+ end
300
+
301
+ # Returns a string representation
302
+ #
303
+ # @return [String]
304
+ #
305
+ # @since 0.3.0
306
+ def to_s
307
+ @string
308
+ end
309
+
310
+ alias_method :to_str, :to_s
311
+
312
+ # Equality
313
+ #
314
+ # @return [TrueClass,FalseClass]
315
+ #
316
+ # @since 0.3.0
317
+ def ==(other)
318
+ to_s == other
319
+ end
320
+
321
+ alias_method :eql?, :==
322
+
323
+ # Split the string with the given pattern
324
+ #
325
+ # @return [Array<String>]
326
+ #
327
+ # @see http://www.ruby-doc.org/core/String.html#method-i-split
328
+ #
329
+ # @since 0.3.0
330
+ def split(pattern, limit = 0)
331
+ @string.split(pattern, limit)
332
+ end
333
+
334
+ # Replace the given pattern with the given replacement
335
+ #
336
+ # @return [String,nil]
337
+ #
338
+ # @see http://www.ruby-doc.org/core/String.html#method-i-gsub
339
+ #
340
+ # @since 0.3.0
341
+ def gsub(pattern, replacement = nil, &blk)
342
+ if block_given?
343
+ @string.gsub(pattern, &blk)
344
+ else
345
+ @string.gsub(pattern, replacement)
346
+ end
347
+ end
348
+
349
+ # Both forms iterate through str, matching the pattern
350
+ #
351
+ # @return [String,nil]
352
+ #
353
+ # @see http://www.ruby-doc.org/core/String.html#method-i-scan
354
+ #
355
+ # @since 0.6.0
356
+ def scan(pattern, &blk)
357
+ @string.scan(pattern, &blk)
358
+ end
359
+
360
+ # Replace the rightmost match of <tt>pattern</tt> with <tt>replacement</tt>
361
+ #
362
+ # If the pattern cannot be matched, it returns the original string.
363
+ #
364
+ # This method does NOT mutate the original string.
365
+ #
366
+ # @param pattern [Regexp, String] the pattern to find
367
+ # @param replacement [String, Hanami::Utils::String] the string to replace
368
+ #
369
+ # @return [Hanami::Utils::String] the replaced string
370
+ #
371
+ # @since 0.6.0
372
+ #
373
+ # @example
374
+ # require 'hanami/utils/string'
375
+ #
376
+ # string = Hanami::Utils::String.new('authors/books/index')
377
+ # result = string.rsub(/\//, '#')
378
+ #
379
+ # puts string
380
+ # # => #<Hanami::Utils::String:0x007fdb41233ad8 @string="authors/books/index">
381
+ #
382
+ # puts result
383
+ # # => #<Hanami::Utils::String:0x007fdb41232ed0 @string="authors/books#index">
384
+ def rsub(pattern, replacement)
385
+ if i = rindex(pattern)
386
+ s = @string.dup
387
+ s[i] = replacement
388
+ self.class.new s
389
+ else
390
+ self
391
+ end
392
+ end
393
+
394
+ # Override Ruby's method_missing in order to provide ::String interface
395
+ #
396
+ # @api private
397
+ # @since 0.3.0
398
+ #
399
+ # @raise [NoMethodError] If doesn't respond to the given method
400
+ def method_missing(m, *args, &blk)
401
+ if respond_to?(m)
402
+ s = @string.__send__(m, *args, &blk)
403
+ s = self.class.new(s) if s.is_a?(::String)
404
+ s
405
+ else
406
+ raise NoMethodError.new(%(undefined method `#{ m }' for "#{ @string }":#{ self.class }))
407
+ end
408
+ end
409
+
410
+ # Override Ruby's respond_to_missing? in order to support ::String interface
411
+ #
412
+ # @api private
413
+ # @since 0.3.0
414
+ def respond_to_missing?(m, include_private=false)
415
+ @string.respond_to?(m, include_private)
416
+ end
417
+ end
418
+ end
419
+ end