hanami-utils 0.0.0 → 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +183 -0
- data/LICENSE.md +22 -0
- data/README.md +106 -7
- data/hanami-utils.gemspec +15 -13
- data/lib/hanami-utils.rb +1 -0
- data/lib/hanami/interactor.rb +497 -0
- data/lib/hanami/logger.rb +141 -0
- data/lib/hanami/utils.rb +31 -2
- data/lib/hanami/utils/attributes.rb +132 -0
- data/lib/hanami/utils/basic_object.rb +53 -0
- data/lib/hanami/utils/callbacks.rb +286 -0
- data/lib/hanami/utils/class.rb +94 -0
- data/lib/hanami/utils/class_attribute.rb +95 -0
- data/lib/hanami/utils/deprecation.rb +70 -0
- data/lib/hanami/utils/duplicable.rb +80 -0
- data/lib/hanami/utils/escape.rb +577 -0
- data/lib/hanami/utils/hash.rb +299 -0
- data/lib/hanami/utils/inflector.rb +439 -0
- data/lib/hanami/utils/io.rb +37 -0
- data/lib/hanami/utils/kernel.rb +1031 -0
- data/lib/hanami/utils/load_paths.rb +167 -0
- data/lib/hanami/utils/path_prefix.rb +146 -0
- data/lib/hanami/utils/string.rb +419 -0
- data/lib/hanami/utils/version.rb +4 -1
- metadata +51 -16
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
@@ -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
|