hanami-utils 2.0.0.alpha1 → 2.0.0.alpha2

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,7 +6,7 @@ module Hanami
6
6
  #
7
7
  # @since 0.9.0
8
8
  module FileList
9
- # Return an ordered list of files, consistent across operating systems
9
+ # Returns an ordered list of files, consistent across operating systems
10
10
  #
11
11
  # It has the same signature of <tt>Dir.glob</tt>, it just guarantees to
12
12
  # order the results before to return them.
@@ -14,8 +14,28 @@ module Hanami
14
14
  # @since 0.9.0
15
15
  #
16
16
  # @see https://ruby-doc.org/core/Dir.html#method-c-glob
17
+ #
18
+ # @example simple usage
19
+ # require "hanami/utils/file_list"
20
+ #
21
+ # Hanami::Utils::FileList["spec/support/fixtures/file_list/*.rb"]
22
+ # # => [
23
+ # "spec/support/fixtures/file_list/a.rb",
24
+ # "spec/support/fixtures/file_list/aa.rb",
25
+ # "spec/support/fixtures/file_list/ab.rb"
26
+ # ]
27
+ #
28
+ # @example token usage
29
+ # require "hanami/utils/file_list"
30
+ #
31
+ # Hanami::Utils::FileList["spec", "support", "fixtures", "file_list", "*.rb"]
32
+ # # => [
33
+ # "spec/support/fixtures/file_list/a.rb",
34
+ # "spec/support/fixtures/file_list/aa.rb",
35
+ # "spec/support/fixtures/file_list/ab.rb"
36
+ # ]
17
37
  def self.[](*args)
18
- Dir.glob(*args).sort!
38
+ Dir.glob(::File.join(*args)).sort!
19
39
  end
20
40
  end
21
41
  end
@@ -147,6 +147,7 @@ module Hanami
147
147
  mkdir_p(path)
148
148
 
149
149
  content = ::File.readlines(path)
150
+ content << "\n" if _append_newline?(content)
150
151
  content << "#{contents}\n"
151
152
 
152
153
  write(path, content)
@@ -307,13 +308,13 @@ module Hanami
307
308
  #
308
309
  # # class App
309
310
  # # end
310
- def self.remove_block(path, target) # rubocop:disable Metrics/AbcSize
311
+ def self.remove_block(path, target)
311
312
  content = ::File.readlines(path)
312
313
  starting = index(content, path, target)
313
314
  line = content[starting]
314
315
  size = line[/\A[[:space:]]*/].bytesize
315
316
  closing = (" " * size) + (/{/.match?(target) ? "}" : "end")
316
- ending = starting + index(content[starting..-1], path, closing)
317
+ ending = starting + index(content[starting..], path, closing)
317
318
 
318
319
  content.slice!(starting..ending)
319
320
  write(path, content)
@@ -435,6 +436,16 @@ module Hanami
435
436
  end
436
437
 
437
438
  private_class_method :line_number
439
+
440
+ # @since 1.3.6
441
+ # @api private
442
+ def self._append_newline?(content)
443
+ return false if content.empty?
444
+
445
+ !content.last.end_with?("\n")
446
+ end
447
+
448
+ private_class_method :_append_newline?
438
449
  end
439
450
  end
440
451
  end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "transproc"
3
+ require "dry-transformer"
4
4
 
5
5
  module Hanami
6
6
  module Utils
7
7
  # Hash transformations
8
8
  # @since 0.1.0
9
9
  module Hash
10
- extend Transproc::Registry
11
- import Transproc::HashTransformations
10
+ extend Dry::Transformer::Registry
11
+ import Dry::Transformer::HashTransformations
12
12
 
13
13
  # Symbolize the given hash
14
14
  #
@@ -32,7 +32,7 @@ module Hanami
32
32
  self[:symbolize_keys].call(input)
33
33
  end
34
34
 
35
- # Deep symbolize the given hash
35
+ # Performs deep symbolize on the given hash
36
36
  #
37
37
  # @param input [::Hash] the input
38
38
  #
@@ -54,7 +54,7 @@ module Hanami
54
54
  self[:deep_symbolize_keys].call(input)
55
55
  end
56
56
 
57
- # Stringify the given hash
57
+ # Stringifies the given hash
58
58
  #
59
59
  # @param input [::Hash] the input
60
60
  #
@@ -74,7 +74,7 @@ module Hanami
74
74
  self[:stringify_keys].call(input)
75
75
  end
76
76
 
77
- # Deeply stringify the given hash
77
+ # Deeply stringifies the given hash
78
78
  #
79
79
  # @param input [::Hash] the input
80
80
  #
@@ -90,31 +90,19 @@ module Hanami
90
90
  #
91
91
  # hash.class
92
92
  # # => Hash
93
- def self.deep_stringify(input) # rubocop:disable Metrics/MethodLength
94
- input.each_with_object({}) do |(key, value), output|
95
- output[key.to_s] =
96
- case value
97
- when ::Hash
98
- deep_stringify(value)
99
- when Array
100
- value.map do |item|
101
- item.is_a?(::Hash) ? deep_stringify(item) : item
102
- end
103
- else
104
- value
105
- end
106
- end
93
+ def self.deep_stringify(input)
94
+ self[:deep_stringify_keys].call(input)
107
95
  end
108
96
 
109
- # Deep duplicate hash values
97
+ # Deep duplicates hash values
110
98
  #
111
- # The output of this function is a shallow duplicate of the input.
99
+ # The output of this function is a deep duplicate of the input.
112
100
  # Any further modification on the input, won't be reflected on the output
113
101
  # and viceversa.
114
102
  #
115
103
  # @param input [::Hash] the input
116
104
  #
117
- # @return [::Hash] the shallow duplicate of input
105
+ # @return [::Hash] the deep duplicate of input
118
106
  #
119
107
  # @since 1.0.1
120
108
  #
@@ -144,17 +132,17 @@ module Hanami
144
132
  # input
145
133
  # # => {"a"=>{"b"=>{"c"=>[1,2,3,4]}}}
146
134
  def self.deep_dup(input)
147
- input.each_with_object({}) do |(k, v), result|
148
- result[k] = case v
149
- when ::Hash
150
- deep_dup(v)
151
- else
152
- v.dup
153
- end
135
+ input.transform_values do |v|
136
+ case v
137
+ when ::Hash
138
+ deep_dup(v)
139
+ else
140
+ v.dup
141
+ end
154
142
  end
155
143
  end
156
144
 
157
- # Deep serialize given object into a `Hash`
145
+ # Deep serializes given object into a `Hash`
158
146
  #
159
147
  # Please note that the returning `Hash` will use symbols as keys.
160
148
  #
@@ -178,7 +166,7 @@ module Hanami
178
166
  #
179
167
  # Hanami::Utils::Hash.deep_serialize(input)
180
168
  # # => {:foo=>"bar", :baz=>[{:hello=>"world"}]}
181
- def self.deep_serialize(input) # rubocop:disable Metrics/MethodLength
169
+ def self.deep_serialize(input)
182
170
  input.to_hash.each_with_object({}) do |(key, value), output|
183
171
  output[key.to_sym] =
184
172
  case value
@@ -43,7 +43,7 @@ module Hanami
43
43
  end
44
44
  # rubocop:enable Style/ClassVars
45
45
 
46
- # Parse the given JSON paylod
46
+ # Parses the given JSON paylod
47
47
  #
48
48
  # @param payload [String] a JSON payload
49
49
  #
@@ -9,7 +9,7 @@ require "hanami/utils"
9
9
  require "hanami/utils/string"
10
10
 
11
11
  unless defined?(Boolean)
12
- # Define top level constant Boolean, so it can be easily used by other libraries
12
+ # Defines top level constant Boolean, so it can be easily used by other libraries
13
13
  # in coercions DSLs
14
14
  #
15
15
  # @since 0.3.0
@@ -28,7 +28,7 @@ module Hanami
28
28
  # @api private
29
29
  #
30
30
  # @see Hanami::Utils::Kernel.Integer
31
- NUMERIC_MATCHER = %r{\A([\d\/\.\+iE]+|NaN|Infinity)\z}.freeze
31
+ NUMERIC_MATCHER = %r{\A([\d/.+iE]+|NaN|Infinity)\z}.freeze
32
32
 
33
33
  # @since 0.8.0
34
34
  # @api private
@@ -327,7 +327,7 @@ module Hanami
327
327
  # # big complex represented as a string
328
328
  # input = Complex(2, 3)
329
329
  # Hanami::Utils::Kernel.Integer(input) # => TypeError
330
- def self.Integer(arg) # rubocop:disable Metrics/MethodLength
330
+ def self.Integer(arg)
331
331
  super(arg)
332
332
  rescue ArgumentError, TypeError, NoMethodError
333
333
  begin
@@ -418,7 +418,6 @@ module Hanami
418
418
  # input = BasicObject.new
419
419
  # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
420
420
  #
421
- # rubocop:disable Metrics/MethodLength
422
421
  def self.BigDecimal(arg, precision = ::Float::DIG)
423
422
  case arg
424
423
  when NilClass # This is only needed by Ruby 2.6
@@ -435,7 +434,6 @@ module Hanami
435
434
  rescue NoMethodError
436
435
  raise TypeError.new "can't convert #{inspect_type_error(arg)}into BigDecimal"
437
436
  end
438
- # rubocop:enable Metrics/MethodLength
439
437
 
440
438
  # Coerces the argument to be a Float.
441
439
  #
@@ -549,7 +547,7 @@ module Hanami
549
547
  # # big complex represented as a string
550
548
  # input = Complex(2, 3)
551
549
  # Hanami::Utils::Kernel.Float(input) # => TypeError
552
- def self.Float(arg) # rubocop:disable Metrics/MethodLength
550
+ def self.Float(arg)
553
551
  super(arg)
554
552
  rescue ArgumentError, TypeError
555
553
  begin
@@ -738,7 +736,8 @@ module Hanami
738
736
  # require 'hanami/utils/kernel'
739
737
  #
740
738
  # Hanami::Utils::Kernel.DateTime(3483943)
741
- # # => Time.at(3483943).to_datetime #<DateTime: 1970-02-10T08:45:43+01:00 ((2440628j,27943s,0n),+3600s,2299161j)>
739
+ # # => Time.at(3483943).to_datetime
740
+ # # #<DateTime: 1970-02-10T08:45:43+01:00 ((2440628j,27943s,0n),+3600s,2299161j)>
742
741
  #
743
742
  # Hanami::Utils::Kernel.DateTime(DateTime.now)
744
743
  # # => #<DateTime: 2014-04-18T09:33:49+02:00 ((2456766j,27229s,690849000n),+7200s,2299161j)>
@@ -898,7 +897,7 @@ module Hanami
898
897
  # # Missing #respond_to?
899
898
  # input = BasicObject.new
900
899
  # Hanami::Utils::Kernel.Boolean(input) # => TypeError
901
- def self.Boolean(arg) # rubocop:disable Metrics/MethodLength
900
+ def self.Boolean(arg)
902
901
  case arg
903
902
  when Numeric
904
903
  arg.to_i == BOOLEAN_TRUE_INTEGER
@@ -1023,7 +1022,7 @@ module Hanami
1023
1022
  raise TypeError.new "can't convert #{inspect_type_error(arg)}into Symbol"
1024
1023
  end
1025
1024
 
1026
- # Check if the given argument is a string representation of a number
1025
+ # Checks if the given argument is a string representation of a number
1027
1026
  #
1028
1027
  # @param arg [Object] the input
1029
1028
  #
@@ -1044,11 +1043,11 @@ module Hanami
1044
1043
  # @since 0.4.3
1045
1044
  # @api private
1046
1045
  def self.inspect_type_error(arg)
1047
- (arg.respond_to?(:inspect) ? arg.inspect : arg.to_s) << " "
1048
- rescue NoMethodError => _
1046
+ "#{(arg.respond_to?(:inspect) ? arg.inspect : arg.to_s)} "
1047
+ rescue NoMethodError
1049
1048
  # missing the #respond_to? method, fall back to returning the class' name
1050
1049
  begin
1051
- arg.class.name << " instance "
1050
+ "#{arg.class.name} instance "
1052
1051
  rescue NoMethodError
1053
1052
  # missing the #class method, can't fall back to anything better than nothing
1054
1053
  # Callers will have to guess from their code
@@ -1057,7 +1056,7 @@ module Hanami
1057
1056
  end
1058
1057
 
1059
1058
  class << self
1060
- private :inspect_type_error # rubocop:disable Style/AccessModifierDeclarations
1059
+ private :inspect_type_error
1061
1060
  end
1062
1061
  end
1063
1062
  end
@@ -50,7 +50,7 @@ module Hanami
50
50
  @paths = original.instance_variable_get(:@paths).dup
51
51
  end
52
52
 
53
- # Iterates thru the collection and yields the given block.
53
+ # Iterates through the collection and yields the given block.
54
54
  # It skips duplications and raises an error in case one of the paths
55
55
  # doesn't exist.
56
56
  #
@@ -117,7 +117,7 @@ module Hanami
117
117
  self
118
118
  end
119
119
 
120
- alias << push
120
+ alias_method :<<, :push
121
121
 
122
122
  # It freezes the object by preventing further modifications.
123
123
  #
@@ -158,7 +158,7 @@ module Hanami
158
158
 
159
159
  private
160
160
 
161
- # Allow subclasses to define their own policy to discover the realpath
161
+ # Allows subclasses to define their own policy to discover the realpath
162
162
  # of the given path.
163
163
  #
164
164
  # @since 0.2.0
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/utils/string"
4
3
  require "hanami/utils/kernel"
5
4
 
6
5
  module Hanami
@@ -8,7 +7,7 @@ module Hanami
8
7
  # Prefixed string
9
8
  #
10
9
  # @since 0.1.0
11
- class PathPrefix < Hanami::Utils::String
10
+ class PathPrefix
12
11
  # Path separator
13
12
  #
14
13
  # @since 0.3.1
@@ -26,7 +25,7 @@ module Hanami
26
25
  #
27
26
  # @see Hanami::Utils::PathPrefix::DEFAULT_SEPARATOR
28
27
  def initialize(string = nil, separator = DEFAULT_SEPARATOR)
29
- super(string)
28
+ @string = string.to_s
30
29
  @separator = separator
31
30
  end
32
31
 
@@ -93,6 +92,37 @@ module Hanami
93
92
  ).relative!
94
93
  end
95
94
 
95
+ # Returns the hash of the internal string
96
+ #
97
+ # @return [Integer]
98
+ #
99
+ # @since 0.3.0
100
+ def hash
101
+ @string.hash
102
+ end
103
+
104
+ # Returns a string representation
105
+ #
106
+ # @return [::String]
107
+ #
108
+ # @since 0.3.0
109
+ def to_s
110
+ @string
111
+ end
112
+
113
+ alias_method :to_str, :to_s
114
+
115
+ # Equality
116
+ #
117
+ # @return [TrueClass,FalseClass]
118
+ #
119
+ # @since 0.3.0
120
+ def ==(other)
121
+ to_s == other
122
+ end
123
+
124
+ alias_method :eql?, :==
125
+
96
126
  protected
97
127
 
98
128
  # Modifies the path prefix to have a prepended separator.
@@ -12,7 +12,7 @@ module Hanami
12
12
  # @api private
13
13
  HASH_SEPARATOR = ","
14
14
 
15
- # Serialize input into a query string
15
+ # Serializes input into a query string
16
16
  #
17
17
  # @param input [Object] the input
18
18
  #
@@ -18,22 +18,22 @@ module Hanami
18
18
  end
19
19
  end
20
20
 
21
- # Escape codes for terminals to output strings in colors
21
+ # Escapes codes for terminals to output strings in colors
22
22
  #
23
23
  # @since 1.2.0
24
24
  # @api private
25
25
  COLORS = ::Hash[
26
- black: 30,
27
- red: 31,
28
- green: 32,
29
- yellow: 33,
30
- blue: 34,
26
+ black: 30,
27
+ red: 31,
28
+ green: 32,
29
+ yellow: 33,
30
+ blue: 34,
31
31
  magenta: 35,
32
- cyan: 36,
33
- gray: 37,
32
+ cyan: 36,
33
+ gray: 37,
34
34
  ].freeze
35
35
 
36
- # Colorize output
36
+ # Colorizes output
37
37
  # 8 colors available: black, red, green, yellow, blue, magenta, cyan, and gray
38
38
  #
39
39
  # @param input [#to_s] the string to colorize
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "transproc"
3
+ require "dry/transformer"
4
4
  require "concurrent/map"
5
5
 
6
6
  module Hanami
7
7
  module Utils
8
- # String on steroids
8
+ # String utilities
9
9
  #
10
10
  # @since 0.1.0
11
- class String # rubocop:disable Metrics/ClassLength
11
+ module String
12
12
  # Empty string for #classify
13
13
  #
14
14
  # @since 0.6.0
@@ -27,18 +27,6 @@ module Hanami
27
27
  # @api private
28
28
  CLASSIFY_SEPARATOR = "_"
29
29
 
30
- # Regexp for #tokenize
31
- #
32
- # @since 0.3.0
33
- # @api private
34
- TOKENIZE_REGEXP = /\((.*)\)/.freeze
35
-
36
- # Separator for #tokenize
37
- #
38
- # @since 0.3.0
39
- # @api private
40
- TOKENIZE_SEPARATOR = "|"
41
-
42
30
  # Separator for #underscore
43
31
  #
44
32
  # @since 0.3.0
@@ -73,16 +61,16 @@ module Hanami
73
61
  #
74
62
  # @since 0.3.4
75
63
  # @api private
76
- CLASSIFY_WORD_SEPARATOR = /#{CLASSIFY_SEPARATOR}|#{NAMESPACE_SEPARATOR}|#{UNDERSCORE_SEPARATOR}|#{DASHERIZE_SEPARATOR}/.freeze
64
+ CLASSIFY_WORD_SEPARATOR = /#{CLASSIFY_SEPARATOR}|#{NAMESPACE_SEPARATOR}|#{UNDERSCORE_SEPARATOR}|#{DASHERIZE_SEPARATOR}/.freeze # rubocop:disable Layout/LineLength
77
65
 
78
66
  @__transformations__ = Concurrent::Map.new
79
67
 
80
- extend Transproc::Registry
81
- extend Transproc::Composer
68
+ extend Dry::Transformer::Registry
82
69
 
83
- # Apply the given transformation(s) to `input`
70
+ # Applies the given transformation(s) to `input`
84
71
  #
85
- # It performs a pipeline of transformations, by applying the given functions from `Hanami::Utils::String` and `::String`.
72
+ # It performs a pipeline of transformations, by applying the given
73
+ # functions from `Hanami::Utils::String` and `::String`.
86
74
  # The transformations are applied in the given order.
87
75
  #
88
76
  # It doesn't mutate the input, unless you use destructive methods from `::String`
@@ -130,42 +118,31 @@ module Hanami
130
118
  # Hanami::Utils::String.transform("Cherry", -> { "blossom" }))
131
119
  # # => ArgumentError: wrong number of arguments (given 1, expected 0)
132
120
  #
133
- # rubocop:disable Metrics/AbcSize
134
- # rubocop:disable Metrics/MethodLength
135
121
  def self.transform(input, *transformations)
136
122
  fn = @__transformations__.fetch_or_store(transformations.hash) do
137
- compose do |fns|
138
- transformations.each do |transformation, *args|
139
- fns << if transformation.is_a?(Proc)
140
- transformation
141
- elsif contain?(transformation)
142
- self[transformation, *args]
143
- elsif input.respond_to?(transformation)
144
- t(:bind, input, ->(i) { i.public_send(transformation, *args) })
145
- else
146
- raise NoMethodError.new(%(undefined method `#{transformation.inspect}' for #{input.inspect}:#{input.class}))
147
- end
148
- end
123
+ fns = Dry::Transformer::Function.new(->(object) { object })
124
+
125
+ transformations.each do |transformation, *args|
126
+ fns = fns.compose(
127
+ if transformation.is_a?(Proc)
128
+ transformation
129
+ elsif contain?(transformation)
130
+ self[transformation, *args]
131
+ elsif input.respond_to?(transformation)
132
+ ->(i) { i.public_send(transformation, *args) }
133
+ else
134
+ raise NoMethodError.new(%(undefined method `#{transformation.inspect}' for #{input.inspect}:#{input.class})) # rubocop:disable Layout/LineLength
135
+ end
136
+ )
149
137
  end
138
+
139
+ fns
150
140
  end
151
141
 
152
142
  fn.call(input)
153
143
  end
154
- # rubocop:enable Metrics/MethodLength
155
- # rubocop:enable Metrics/AbcSize
156
144
 
157
- # Extracted from `transproc` source code
158
- #
159
- # `transproc` is Copyright 2014 by Piotr Solnica (piotr.solnica@gmail.com),
160
- # released under the MIT License
161
- #
162
- # @since 1.1.0
163
- # @api private
164
- def self.bind(value, binding, fun)
165
- binding.instance_exec(value, &fun)
166
- end
167
-
168
- # Return a titleized version of the string
145
+ # Returns a titleized version of the string
169
146
  #
170
147
  # @param input [::String] the input
171
148
  #
@@ -182,7 +159,7 @@ module Hanami
182
159
  underscore(string).split(CLASSIFY_SEPARATOR).map(&:capitalize).join(TITLEIZE_SEPARATOR)
183
160
  end
184
161
 
185
- # Return a capitalized version of the string
162
+ # Returns a capitalized version of the string
186
163
  #
187
164
  # @param input [::String] the input
188
165
  #
@@ -209,7 +186,7 @@ module Hanami
209
186
  tail.unshift(head.capitalize).join(CAPITALIZE_SEPARATOR)
210
187
  end
211
188
 
212
- # Return a CamelCase version of the string
189
+ # Returns a CamelCase version of the string
213
190
  #
214
191
  # @param input [::String] the input
215
192
  #
@@ -233,7 +210,7 @@ module Hanami
233
210
  words.zip(delimiters).join
234
211
  end
235
212
 
236
- # Return a downcased and underscore separated version of the string
213
+ # Returns a downcased and underscore separated version of the string
237
214
  #
238
215
  # Revised version of `ActiveSupport::Inflector.underscore` implementation
239
216
  # @see https://github.com/rails/rails/blob/feaa6e2048fe86bcf07e967d6e47b865e42e055b/activesupport/lib/active_support/inflector/methods.rb#L90
@@ -254,11 +231,11 @@ module Hanami
254
231
  string.gsub!(NAMESPACE_SEPARATOR, UNDERSCORE_SEPARATOR)
255
232
  string.gsub!(/([A-Z\d]+)([A-Z][a-z])/, UNDERSCORE_DIVISION_TARGET)
256
233
  string.gsub!(/([a-z\d])([A-Z])/, UNDERSCORE_DIVISION_TARGET)
257
- string.gsub!(/[[:space:]]|\-/, UNDERSCORE_DIVISION_TARGET)
234
+ string.gsub!(/[[:space:]]|-/, UNDERSCORE_DIVISION_TARGET)
258
235
  string.downcase
259
236
  end
260
237
 
261
- # Return a downcased and dash separated version of the string
238
+ # Returns a downcased and dash separated version of the string
262
239
  #
263
240
  # @param input [::String] the input
264
241
  #
@@ -279,7 +256,7 @@ module Hanami
279
256
  underscore(string).split(CLASSIFY_SEPARATOR).join(DASHERIZE_SEPARATOR)
280
257
  end
281
258
 
282
- # Return the string without the Ruby namespace of the class
259
+ # Returns the string without the Ruby namespace of the class
283
260
  #
284
261
  # @param input [::String] the input
285
262
  #
@@ -297,7 +274,7 @@ module Hanami
297
274
  ::String.new(input.to_s).split(NAMESPACE_SEPARATOR).last
298
275
  end
299
276
 
300
- # Return the top level namespace name
277
+ # Returns the top level namespace name
301
278
  #
302
279
  # @param input [::String] the input
303
280
  #
@@ -336,7 +313,7 @@ module Hanami
336
313
  # # => 'authors/books#index'
337
314
  def self.rsub(input, pattern, replacement)
338
315
  string = ::String.new(input.to_s)
339
- if i = string.rindex(pattern) # rubocop:disable Lint/AssignmentInCondition
316
+ if i = string.rindex(pattern)
340
317
  s = string.dup
341
318
  s[i] = replacement
342
319
  s
@@ -344,344 +321,6 @@ module Hanami
344
321
  string
345
322
  end
346
323
  end
347
-
348
- # Initialize the string
349
- #
350
- # @param string [::String, Symbol] the value we want to initialize
351
- #
352
- # @return [Hanami::Utils::String] self
353
- #
354
- # @since 0.1.0
355
- # @deprecated
356
- def initialize(string)
357
- @string = string.to_s
358
- end
359
-
360
- # Return a titleized version of the string
361
- #
362
- # @return [Hanami::Utils::String] the transformed string
363
- #
364
- # @since 0.4.0
365
- # @deprecated Use {Hanami::Utils::String.titleize}
366
- #
367
- # @example
368
- # require 'hanami/utils/string'
369
- #
370
- # string = Hanami::Utils::String.new 'hanami utils'
371
- # string.titleize # => "Hanami Utils"
372
- def titleize
373
- self.class.new underscore.split(CLASSIFY_SEPARATOR).map(&:capitalize).join(TITLEIZE_SEPARATOR)
374
- end
375
-
376
- # Return a capitalized version of the string
377
- #
378
- # @return [Hanami::Utils::String] the transformed string
379
- #
380
- # @since 0.5.2
381
- # @deprecated Use {Hanami::Utils::String.capitalize}
382
- #
383
- # @example
384
- # require 'hanami/utils/string'
385
- #
386
- # string = Hanami::Utils::String.new 'hanami'
387
- # string.capitalize # => "Hanami"
388
- #
389
- # string = Hanami::Utils::String.new 'hanami utils'
390
- # string.capitalize # => "Hanami utils"
391
- #
392
- # string = Hanami::Utils::String.new 'Hanami Utils'
393
- # string.capitalize # => "Hanami utils"
394
- #
395
- # string = Hanami::Utils::String.new 'hanami_utils'
396
- # string.capitalize # => "Hanami utils"
397
- #
398
- # string = Hanami::Utils::String.new 'hanami-utils'
399
- # string.capitalize # => "Hanami utils"
400
- def capitalize
401
- head, *tail = underscore.split(CLASSIFY_SEPARATOR)
402
-
403
- self.class.new(
404
- tail.unshift(head.capitalize).join(CAPITALIZE_SEPARATOR)
405
- )
406
- end
407
-
408
- # Return a CamelCase version of the string
409
- #
410
- # @return [Hanami::Utils::String] the transformed string
411
- #
412
- # @since 0.1.0
413
- # @deprecated Use {Hanami::Utils::String.classify}
414
- #
415
- # @example
416
- # require 'hanami/utils/string'
417
- #
418
- # string = Hanami::Utils::String.new 'hanami_utils'
419
- # string.classify # => 'HanamiUtils'
420
- def classify
421
- words = underscore.split(CLASSIFY_WORD_SEPARATOR).map!(&:capitalize)
422
- delimiters = underscore.scan(CLASSIFY_WORD_SEPARATOR)
423
-
424
- delimiters.map! do |delimiter|
425
- delimiter == CLASSIFY_SEPARATOR ? EMPTY_STRING : NAMESPACE_SEPARATOR
426
- end
427
-
428
- self.class.new words.zip(delimiters).join
429
- end
430
-
431
- # Return a downcased and underscore separated version of the string
432
- #
433
- # Revised version of `ActiveSupport::Inflector.underscore` implementation
434
- # @see https://github.com/rails/rails/blob/feaa6e2048fe86bcf07e967d6e47b865e42e055b/activesupport/lib/active_support/inflector/methods.rb#L90
435
- #
436
- # @return [Hanami::Utils::String] the transformed string
437
- # @deprecated Use {Hanami::Utils::String.underscore}
438
- #
439
- # @since 0.1.0
440
- #
441
- # @example
442
- # require 'hanami/utils/string'
443
- #
444
- # string = Hanami::Utils::String.new 'HanamiUtils'
445
- # string.underscore # => 'hanami_utils'
446
- def underscore
447
- new_string = gsub(NAMESPACE_SEPARATOR, UNDERSCORE_SEPARATOR)
448
- new_string.gsub!(/([A-Z\d]+)([A-Z][a-z])/, UNDERSCORE_DIVISION_TARGET)
449
- new_string.gsub!(/([a-z\d])([A-Z])/, UNDERSCORE_DIVISION_TARGET)
450
- new_string.gsub!(/[[:space:]]|\-/, UNDERSCORE_DIVISION_TARGET)
451
- new_string.downcase!
452
- self.class.new new_string
453
- end
454
-
455
- # Return a downcased and dash separated version of the string
456
- #
457
- # @return [Hanami::Utils::String] the transformed string
458
- #
459
- # @since 0.4.0
460
- # @deprecated Use {Hanami::Utils::String.dasherize}
461
- #
462
- # @example
463
- # require 'hanami/utils/string'
464
- #
465
- # string = Hanami::Utils::String.new 'Hanami Utils'
466
- # string.dasherize # => 'hanami-utils'
467
- #
468
- # string = Hanami::Utils::String.new 'hanami_utils'
469
- # string.dasherize # => 'hanami-utils'
470
- #
471
- # string = Hanami::Utils::String.new 'HanamiUtils'
472
- # string.dasherize # => "hanami-utils"
473
- def dasherize
474
- self.class.new underscore.split(CLASSIFY_SEPARATOR).join(DASHERIZE_SEPARATOR)
475
- end
476
-
477
- # Return the string without the Ruby namespace of the class
478
- #
479
- # @return [Hanami::Utils::String] the transformed string
480
- #
481
- # @since 0.1.0
482
- # @deprecated Use {Hanami::Utils::String.demodulize}
483
- #
484
- # @example
485
- # require 'hanami/utils/string'
486
- #
487
- # string = Hanami::Utils::String.new 'Hanami::Utils::String'
488
- # string.demodulize # => 'String'
489
- #
490
- # string = Hanami::Utils::String.new 'String'
491
- # string.demodulize # => 'String'
492
- def demodulize
493
- self.class.new split(NAMESPACE_SEPARATOR).last
494
- end
495
-
496
- # Return the top level namespace name
497
- #
498
- # @return [Hanami::Utils::String] the transformed string
499
- #
500
- # @since 0.1.2
501
- # @deprecated Use {Hanami::Utils::String.namespace}
502
- #
503
- # @example
504
- # require 'hanami/utils/string'
505
- #
506
- # string = Hanami::Utils::String.new 'Hanami::Utils::String'
507
- # string.namespace # => 'Hanami'
508
- #
509
- # string = Hanami::Utils::String.new 'String'
510
- # string.namespace # => 'String'
511
- def namespace
512
- self.class.new split(NAMESPACE_SEPARATOR).first
513
- end
514
-
515
- # It iterates through the tokens and calls the given block.
516
- # A token is a substring wrapped by `()` and separated by `|`.
517
- #
518
- # @yield the block that is called for each token.
519
- #
520
- # @return [void]
521
- #
522
- # @since 0.1.0
523
- # @deprecated
524
- #
525
- # @example
526
- # require 'hanami/utils/string'
527
- #
528
- # string = Hanami::Utils::String.new 'Hanami::(Utils|App)'
529
- # string.tokenize do |token|
530
- # puts token
531
- # end
532
- #
533
- # # =>
534
- # 'Hanami::Utils'
535
- # 'Hanami::App'
536
- #
537
- # rubocop:disable Metrics/MethodLength
538
- def tokenize
539
- if match = TOKENIZE_REGEXP.match(@string) # rubocop:disable Lint/AssignmentInCondition
540
- pre = match.pre_match
541
- post = match.post_match
542
- tokens = match[1].split(TOKENIZE_SEPARATOR)
543
- tokens.each do |token|
544
- yield(self.class.new("#{pre}#{token}#{post}"))
545
- end
546
- else
547
- yield(self.class.new(@string))
548
- end
549
-
550
- nil
551
- end
552
- # rubocop:enable Metrics/MethodLength
553
-
554
- # Returns the hash of the internal string
555
- #
556
- # @return [Integer]
557
- #
558
- # @since 0.3.0
559
- # @deprecated
560
- def hash
561
- @string.hash
562
- end
563
-
564
- # Returns a string representation
565
- #
566
- # @return [::String]
567
- #
568
- # @since 0.3.0
569
- # @deprecated
570
- def to_s
571
- @string
572
- end
573
-
574
- alias to_str to_s
575
-
576
- # Equality
577
- #
578
- # @return [TrueClass,FalseClass]
579
- #
580
- # @since 0.3.0
581
- # @deprecated
582
- def ==(other)
583
- to_s == other
584
- end
585
-
586
- alias eql? ==
587
-
588
- # Split the string with the given pattern
589
- #
590
- # @return [Array<::String>]
591
- #
592
- # @see http://www.ruby-doc.org/core/String.html#method-i-split
593
- #
594
- # @since 0.3.0
595
- # @deprecated
596
- def split(pattern, limit = 0)
597
- @string.split(pattern, limit)
598
- end
599
-
600
- # Replace the given pattern with the given replacement
601
- #
602
- # @return [::String]
603
- #
604
- # @see http://www.ruby-doc.org/core/String.html#method-i-gsub
605
- #
606
- # @since 0.3.0
607
- # @deprecated
608
- def gsub(pattern, replacement = nil, &blk)
609
- if block_given?
610
- @string.gsub(pattern, &blk)
611
- else
612
- @string.gsub(pattern, replacement)
613
- end
614
- end
615
-
616
- # Iterate through the string, matching the pattern.
617
- # Either return all those patterns, or pass them to the block.
618
- #
619
- # @return [Array<::String>]
620
- #
621
- # @see http://www.ruby-doc.org/core/String.html#method-i-scan
622
- #
623
- # @since 0.6.0
624
- # @deprecated
625
- def scan(pattern, &blk)
626
- @string.scan(pattern, &blk)
627
- end
628
-
629
- # Replace the rightmost match of `pattern` with `replacement`
630
- #
631
- # If the pattern cannot be matched, it returns the original string.
632
- #
633
- # This method does NOT mutate the original string.
634
- #
635
- # @param pattern [Regexp, String] the pattern to find
636
- # @param replacement [String, Hanami::Utils::String] the string to replace
637
- #
638
- # @return [Hanami::Utils::String] the replaced string
639
- #
640
- # @since 0.6.0
641
- # @deprecated Use {Hanami::Utils::String.rsub}
642
- #
643
- # @example
644
- # require 'hanami/utils/string'
645
- #
646
- # string = Hanami::Utils::String.new('authors/books/index')
647
- # result = string.rsub(/\//, '#')
648
- #
649
- # puts string
650
- # # => #<Hanami::Utils::String:0x007fdb41233ad8 @string="authors/books/index">
651
- #
652
- # puts result
653
- # # => #<Hanami::Utils::String:0x007fdb41232ed0 @string="authors/books#index">
654
- def rsub(pattern, replacement)
655
- if i = rindex(pattern) # rubocop:disable Lint/AssignmentInCondition
656
- s = @string.dup
657
- s[i] = replacement
658
- self.class.new s
659
- else
660
- self
661
- end
662
- end
663
-
664
- # Override Ruby's method_missing in order to provide ::String interface
665
- #
666
- # @api private
667
- # @since 0.3.0
668
- #
669
- # @raise [NoMethodError] If doesn't respond to the given method
670
- def method_missing(method_name, *args, &blk)
671
- raise NoMethodError.new(%(undefined method `#{method_name}' for "#{@string}":#{self.class})) unless respond_to?(method_name)
672
-
673
- s = @string.__send__(method_name, *args, &blk)
674
- s = self.class.new(s) if s.is_a?(::String)
675
- s
676
- end
677
-
678
- # Override Ruby's respond_to_missing? in order to support ::String interface
679
- #
680
- # @api private
681
- # @since 0.3.0
682
- def respond_to_missing?(method_name, include_private = false)
683
- @string.respond_to?(method_name, include_private)
684
- end
685
324
  end
686
325
  end
687
326
  end