hanami-utils 2.0.0.alpha1 → 2.0.0

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.
@@ -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}/ # 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
@@ -2,9 +2,10 @@
2
2
 
3
3
  module Hanami
4
4
  module Utils
5
- # Defines the version
5
+ # The current hanami-utils version.
6
6
  #
7
7
  # @since 0.1.0
8
- VERSION = "2.0.0.alpha1"
8
+ # @api public
9
+ VERSION = "2.0.0"
9
10
  end
10
11
  end
data/lib/hanami/utils.rb CHANGED
@@ -23,7 +23,7 @@ module Hanami
23
23
 
24
24
  # Checks if the current VM is JRuby
25
25
  #
26
- # @return [TrueClass,FalseClass] return if the VM is JRuby or not
26
+ # @return [TrueClass,FalseClass] info whether the VM is JRuby or not
27
27
  #
28
28
  # @since 0.3.1
29
29
  # @api private
@@ -33,7 +33,7 @@ module Hanami
33
33
 
34
34
  # Checks if the current VM is Rubinius
35
35
  #
36
- # @return [TrueClass,FalseClass] return if the VM is Rubinius or not
36
+ # @return [TrueClass,FalseClass] info whether the VM is Rubinius or not
37
37
  #
38
38
  # @since 0.3.1
39
39
  # @api private
@@ -41,7 +41,7 @@ module Hanami
41
41
  RUBY_ENGINE == HANAMI_RUBINIUS
42
42
  end
43
43
 
44
- # Recursively require Ruby files under the given directory.
44
+ # Recursively requires Ruby files under the given directory.
45
45
  #
46
46
  # If the directory is relative, it implies it's the path from current directory.
47
47
  # If the directory is absolute, it uses as it is.
@@ -71,7 +71,7 @@ module Hanami
71
71
  # @since 1.0.0
72
72
  # @api private
73
73
  def self.for_each_file_in(directory, &blk)
74
- directory = directory.to_s.gsub(%r{(\/|\\)}, File::SEPARATOR)
74
+ directory = directory.to_s.gsub(%r{(/|\\)}, File::SEPARATOR)
75
75
  directory = Pathname.new(Dir.pwd).join(directory).to_s
76
76
  directory = File.join(directory, "**", "*.rb") unless directory =~ /(\*\*)/
77
77