lotus-utils 0.5.2 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75c83ed0bc5b3b7e00e70735e023117f38ef9d67
4
- data.tar.gz: 17cd9f71360e17c3adddadf260c38fc8b27622b1
3
+ metadata.gz: c9cbb89b047bc9d6da31d644120a6314aae15e13
4
+ data.tar.gz: ec85522be16a248f320b93fe5ef0911f7de63853
5
5
  SHA512:
6
- metadata.gz: 1d25381aa757c98d2549d8619a8ba7525f50d9a752b6473d8e329d8eabb7ada701ceecefae9b301136bdb71b2d836f1a6ee4ba8a0cdeaf71edb66ca127f127e9
7
- data.tar.gz: cd21b6ea4701af1949fedad3f91b3d11579de79abef45bc6b4a7c66e8814ecaec88ab765bb0afce36b5804279e9d595124cc5998fdeeb0a47ab62ae3e0849846
6
+ metadata.gz: 529c56324efb450ead039cf3f1747e101d729a9dd8cef9876273acde10cdce5298a1f8a7c14d7136667e1fc9b2d713ec3a878fa6a96ae57a70c67d5c317f76b2
7
+ data.tar.gz: cc3c30a6aa45031df28cfc0c5b533ce8e3c9fc5d0b81e9893ac71ee845f15118901cc20b8c6b8baf0c24d33732dd8570ff4fa8aa3afc0bc776ca9673a3c40cd2
data/CHANGELOG.md CHANGED
@@ -1,6 +1,22 @@
1
1
  # Lotus::Utils
2
2
  Ruby core extentions and class utilities for Lotus
3
3
 
4
+ ## v0.6.0 - 2016-01-12
5
+ ### Added
6
+ - [Luca Guidi] Official support for Ruby 2.3
7
+ - [Luca Guidi] Custom inflections
8
+ - [Luca Guidi] Introduced `Lotus::Utils::Duplicable` as a safe dup logic for Ruby types
9
+ - [Luca Guidi] Added `Lotus::Utils::String#rsub` replace rightmost occurrence
10
+
11
+ ### Fixed
12
+ - [Luca Guidi] Fix `Lotus::Utils::PathPrefix#join` and `#relative_join` by rejecting arguments that are equal to the separator
13
+ - [Karim Kiatlottiavi] Fix `Encoding::UndefinedConversionError` in `Lotus::Utils::Escape.encode`
14
+
15
+ ### Changed
16
+ - [Luca Guidi] Deprecate Ruby 2.0 and 2.1
17
+ - [Luca Guidi] Removed `Lotus::Utils::Callbacks#add` in favor of `#append`
18
+ - [Luca Guidi] Removed pattern support for `Utils::Class.load!` (eg. `Articles(Controller|::Controller)`)
19
+
4
20
  ## v0.5.2 - 2015-09-30
5
21
  ### Added
6
22
  - [Luca Guidi] Added `Lotus::Utils::String#capitalize`
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2014-2015 Luca Guidi
1
+ Copyright © 2014-2016 Luca Guidi
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  # Lotus::Utils
2
3
 
3
4
  Ruby core extentions and class utilities for [Lotus](http://lotusrb.org)
@@ -43,9 +44,78 @@ Or install it yourself as:
43
44
  ## Usage
44
45
 
45
46
  __Lotus::Utils__ is designed to enhance Ruby's code and stdlib.
46
- By default this gem doesn't load any code, you must require what you need.
47
+ **By default this gem doesn't load any code, you must require what you need.**
48
+
49
+ ## Features
50
+
51
+ ### Lotus::Interactor
52
+
53
+ Standardized Service Object with small interface and rich returning result. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Interactor)]
54
+
55
+ ### Lotus::Logger
56
+
57
+ Enhanced version of Ruby's `Logger`. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Logger)]
58
+
59
+ ### Lotus::Utils::Attributes
60
+
61
+ Set of attributes with indifferent access. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Attributes)]
62
+
63
+ ### Lotus::Utils::BasicObject
64
+
65
+ Enhanced version of Ruby's `BasicObject`. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/BasicObject)]
66
+
67
+ ### Lotus::Utils::Callbacks
68
+
69
+ Callbacks to decorate methods with `before` and `after` logic. It supports polymorphic callbacks (methods and procs). [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Callbacks)]
70
+
71
+ ### Lotus::Utils::Class
72
+
73
+ Load classes from strings. It also supports namespaces. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Class)]
74
+
75
+ ### Lotus::Utils::ClassAttribute
76
+
77
+ Inheritable class attributes. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/ClassAttribute)]
78
+
79
+ ### Lotus::Utils::Deprecation
80
+
81
+ Deprecate Lotus features. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Deprecation)]
82
+
83
+ ### Lotus::Utils::Duplicable
84
+
85
+ Safe `#dup` logic for Ruby objects. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Deprecation)]
86
+
87
+
88
+ ### Lotus::Utils::Escape
89
+
90
+ Safe and fast escape for URLs, HTML content and attributes. Based on OWASP/ESAPI code. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Escape)]
91
+
92
+ ### Lotus::Utils::Hash
93
+
94
+ Enhanced version of Ruby's `Hash`. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Hash)]
95
+
96
+ ### Lotus::Utils::IO
97
+
98
+ Silence Ruby warnings. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/IO)]
99
+
100
+ ### Lotus::Utils::Inflector
101
+
102
+ Complete and customizable english inflections (pluralization and singularization). [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Inflector)]
103
+
104
+ ### Lotus::Utils::Kernel
105
+
106
+ Type coercions for most common Ruby types. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/Kernel)]
107
+
108
+ ### Lotus::Utils::LoadPaths
109
+
110
+ Manage directories where to find Ruby source code or web static assets. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/LoadPaths)]
111
+
112
+ ### Lotus::Utils::PathPrefix
113
+
114
+ Safe logic to manage relative URLs. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/PathPrefix)]
115
+
116
+ ### Lotus::Utils::String
47
117
 
48
- Please read the documentation of each module.
118
+ Enhanced version of Ruby's `String`. [[API doc](http://www.rubydoc.info/gems/lotus-utils/Lotus/Utils/String)]
49
119
 
50
120
  ## Versioning
51
121
 
@@ -61,4 +131,4 @@ __Lotus::Utils__ uses [Semantic Versioning 2.0.0](http://semver.org)
61
131
 
62
132
  ## Copyright
63
133
 
64
- Copyright © 2014-2015 Luca Guidi – Released under MIT License
134
+ Copyright © 2014-2016 Luca Guidi – Released under MIT License
@@ -27,7 +27,7 @@ module Lotus
27
27
  # @example
28
28
  # require 'lotus/utils/attributes'
29
29
  #
30
- # attributes = Lotus::Utils::Attributes.new(a: 1, b: { 2 => [3, 4] } })
30
+ # attributes = Lotus::Utils::Attributes.new(a: 1, b: { 2 => [3, 4] })
31
31
  # attributes.to_h # => { "a" => 1, "b" => { "2" => [3, 4] } }
32
32
  def initialize(hash = {})
33
33
  @attributes = Utils::Hash.new(hash, &nil).stringify!
@@ -1,5 +1,3 @@
1
- require 'lotus/utils/deprecation'
2
-
3
1
  module Lotus
4
2
  module Utils
5
3
  # Before and After callbacks
@@ -60,16 +58,6 @@ module Lotus
60
58
  @chain.uniq!
61
59
  end
62
60
 
63
- # @since 0.1.0
64
- # @deprecated Use Lotus::Utils::Callbacks::Chain#append as it has the
65
- # same effect, but it's more consistent with the new API.
66
- #
67
- # @see Lotus::Utils::Callbacks::Chain#append
68
- def add(*callbacks, &blk)
69
- Utils::Deprecation.new("Lotus::Utils::Callbacks::Chain#add is deprecated, use #append instead.")
70
- append(*callbacks, &blk)
71
- end
72
-
73
61
  # Prepends the given callbacks to the beginning of the chain.
74
62
  #
75
63
  # @param callbacks [Array] one or multiple callbacks to add
@@ -133,7 +121,7 @@ module Lotus
133
121
  # params = Hash[id: 23]
134
122
  #
135
123
  # chain = Lotus::Utils::Callbacks::Chain.new
136
- # chain.add :authenticate!, :set_article
124
+ # chain.append :authenticate!, :set_article
137
125
  #
138
126
  # chain.run(action, params)
139
127
  #
@@ -143,11 +131,11 @@ module Lotus
143
131
  #
144
132
  # chain = Lotus::Utils::Callbacks::Chain.new
145
133
  #
146
- # chain.add do
134
+ # chain.append do
147
135
  # # some authentication logic
148
136
  # end
149
137
  #
150
- # chain.add do |params|
138
+ # chain.append do |params|
151
139
  # # some other logic that requires `params`
152
140
  # end
153
141
  #
@@ -174,7 +162,7 @@ module Lotus
174
162
  #
175
163
  # chain.frozen? # => true
176
164
  #
177
- # chain.add :authenticate! # => RuntimeError
165
+ # chain.append :authenticate! # => RuntimeError
178
166
  def freeze
179
167
  super
180
168
  @chain.freeze
@@ -1,5 +1,4 @@
1
1
  require 'lotus/utils/string'
2
- require 'lotus/utils/deprecation'
3
2
 
4
3
  module Lotus
5
4
  module Utils
@@ -39,14 +38,7 @@ module Lotus
39
38
  # # with missing constant
40
39
  # Lotus::Utils::Class.load!('Unknown') # => raises NameError
41
40
  def self.load!(name, namespace = Object)
42
- name = name.to_s
43
-
44
- if name.match(/\|/)
45
- Utils::Deprecation.new("Using Lotus::Utils::Class.load! with a pattern is deprecated, please use Lotus::Utils::Class.load_from_pattern!: #{ name }, #{ namespace }")
46
- return load_from_pattern!(name, namespace)
47
- end
48
-
49
- namespace.const_get(name)
41
+ namespace.const_get(name.to_s)
50
42
  end
51
43
 
52
44
  # Loads a class from the given pattern name and namespace
@@ -75,17 +67,17 @@ module Lotus
75
67
  # end
76
68
  #
77
69
  # # basic usage
78
- # Lotus::Utils::Class.load!('App::Service') # => App::Service
70
+ # Lotus::Utils::Class.load_from_pattern!('App::Service') # => App::Service
79
71
  #
80
72
  # # with explicit namespace
81
- # Lotus::Utils::Class.load!('Service', App) # => App::Service
73
+ # Lotus::Utils::Class.load_from_pattern!('Service', App) # => App::Service
82
74
  #
83
75
  # # with pattern
84
- # Lotus::Utils::Class.load!('App::Service(::Endpoint|Endpoint)') # => App::Service::Endpoint
85
- # Lotus::Utils::Class.load!('App::Service(Endpoint|::Endpoint)') # => App::ServiceEndpoint
76
+ # Lotus::Utils::Class.load_from_pattern!('App::Service(::Endpoint|Endpoint)') # => App::Service::Endpoint
77
+ # Lotus::Utils::Class.load_from_pattern!('App::Service(Endpoint|::Endpoint)') # => App::ServiceEndpoint
86
78
  #
87
79
  # # with missing constant
88
- # Lotus::Utils::Class.load!('Unknown') # => raises NameError
80
+ # Lotus::Utils::Class.load_from_pattern!('Unknown') # => raises NameError
89
81
  def self.load_from_pattern!(pattern, namespace = Object)
90
82
  String.new(pattern).tokenize do |token|
91
83
  begin
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'lotus/utils/duplicable'
2
3
 
3
4
  module Lotus
4
5
  module Utils
@@ -74,7 +75,7 @@ module Lotus
74
75
  def inherited(subclass)
75
76
  class_attributes.each do |attr|
76
77
  value = send(attr)
77
- value = value.dup rescue value
78
+ value = Duplicable.dup(value)
78
79
  subclass.class_attribute attr
79
80
  subclass.send("#{attr}=", value)
80
81
  end
@@ -0,0 +1,80 @@
1
+ module Lotus
2
+ module Utils
3
+ # Safe dup logic
4
+ #
5
+ # @since 0.6.0
6
+ module Duplicable
7
+ # Duplicates the given value.
8
+ #
9
+ # It accepts a block to customize the logic.
10
+ #
11
+ # The following types aren't duped:
12
+ #
13
+ # * <tt>NilClass</tt>
14
+ # * <tt>FalseClass</tt>
15
+ # * <tt>TrueClass</tt>
16
+ # * <tt>Symbol</tt>
17
+ # * <tt>Numeric</tt>
18
+ #
19
+ # All the other types are duped via <tt>#dup</tt>
20
+ #
21
+ # @param value [Object] the value to duplicate
22
+ # @param blk [Proc] the optional block to customize the logic
23
+ #
24
+ # @return [Object] the duped value
25
+ #
26
+ # @since 0.6.0
27
+ #
28
+ # @example Basic Usage With Types That Can't Be Duped
29
+ # require 'lotus/utils/duplicable'
30
+ #
31
+ # object = 23
32
+ # puts object.object_id # => 47
33
+ #
34
+ # result = Lotus::Utils::Duplicable.dup(object)
35
+ #
36
+ # puts result # => 23
37
+ # puts result.object_id # => 47 - Same object, because numbers can't be duped
38
+ #
39
+ # @example Basic Usage With Types That Can Be Duped
40
+ # require 'lotus/utils/duplicable'
41
+ #
42
+ # object = "hello"
43
+ # puts object.object_id # => 70172661782360
44
+ #
45
+ # result = Lotus::Utils::Duplicable.dup(object)
46
+ #
47
+ # puts result # => "hello"
48
+ # puts result.object_id # => 70172671467020 – Different object
49
+ #
50
+ # @example Custom Logic
51
+ # require 'lotus/utils/duplicable'
52
+ # require 'lotus/utils/hash'
53
+ #
54
+ # hash = { a: 1 }
55
+ # puts hash.object_id # => 70207105061680
56
+ #
57
+ # result = Lotus::Utils::Duplicable.dup(hash) do |value|
58
+ # case value
59
+ # when Lotus::Utils::Hash
60
+ # value.deep_dup
61
+ # when ::Hash
62
+ # Lotus::Utils::Hash.new(value).deep_dup.to_h
63
+ # end
64
+ # end
65
+ #
66
+ # puts result # => "{:a=>1}"
67
+ # puts result.object_id # => 70207105185500 – Different object
68
+ def self.dup(value, &blk)
69
+ case value
70
+ when NilClass, FalseClass, TrueClass, Symbol, Numeric
71
+ value
72
+ when v = blk && blk.call(value)
73
+ v
74
+ else
75
+ value.dup
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -528,7 +528,10 @@ module Lotus
528
528
  # @since 0.4.0
529
529
  # @api private
530
530
  def self.encode(input)
531
- input.to_s.encode(Encoding::UTF_8)
531
+ return '' if input.nil?
532
+ input.encode(Encoding::UTF_8)
533
+ rescue Encoding::UndefinedConversionError
534
+ input.dup.force_encoding(Encoding::UTF_8)
532
535
  end
533
536
 
534
537
  # Encode the given UTF-8 char.
@@ -1,8 +1,24 @@
1
+ require 'lotus/utils/duplicable'
2
+
1
3
  module Lotus
2
4
  module Utils
3
5
  # Hash on steroids
4
6
  # @since 0.1.0
5
7
  class Hash
8
+ # @since 0.6.0
9
+ # @api private
10
+ #
11
+ # @see Lotus::Utils::Hash#deep_dup
12
+ # @see Lotus::Utils::Duplicable
13
+ DUPLICATE_LOGIC = Proc.new do |value|
14
+ case value
15
+ when Hash
16
+ value.deep_dup
17
+ when ::Hash
18
+ Hash.new(value).deep_dup.to_h
19
+ end
20
+ end.freeze
21
+
6
22
  # Initialize the hash
7
23
  #
8
24
  # @param hash [#to_h] the value we want to use to initialize this instance
@@ -145,7 +161,7 @@ module Lotus
145
161
  # duped['u_hash'].class # => Lotus::Utils::Hash
146
162
  def deep_dup
147
163
  Hash.new.tap do |result|
148
- @hash.each {|k, v| result[k] = duplicate(v) }
164
+ @hash.each {|k, v| result[k] = Duplicable.dup(v, &DUPLICATE_LOGIC) }
149
165
  end
150
166
  end
151
167
 
@@ -278,22 +294,6 @@ module Lotus
278
294
  def respond_to_missing?(m, include_private=false)
279
295
  @hash.respond_to?(m, include_private)
280
296
  end
281
-
282
- private
283
- # @api private
284
- # @since 0.3.1
285
- def duplicate(value)
286
- case value
287
- when NilClass, FalseClass, TrueClass, Symbol, Numeric
288
- value
289
- when Hash
290
- value.deep_dup
291
- when ::Hash
292
- Hash.new(value).deep_dup.to_h
293
- else
294
- value.dup
295
- end
296
- end
297
297
  end
298
298
  end
299
299
  end
@@ -1,30 +1,36 @@
1
+ require 'lotus/utils/class_attribute'
2
+
1
3
  module Lotus
2
4
  module Utils
3
5
  # String inflector
4
6
  #
5
7
  # @since 0.4.1
6
- # @api private
7
8
  module Inflector
8
- # Rule for irregular plural
9
+ # Rules for irregular plurals
9
10
  #
10
- # @since 0.4.1
11
+ # @since 0.6.0
11
12
  # @api private
12
- class IrregularRule
13
- # @since 0.4.1
13
+ class IrregularRules
14
+ # @since 0.6.0
14
15
  # @api private
15
16
  def initialize(rules)
16
17
  @rules = rules
17
- @rules.freeze
18
18
  end
19
19
 
20
- # @since 0.4.1
20
+ # @since 0.6.0
21
+ # @api private
22
+ def add(key, value)
23
+ @rules[key.downcase] = value.downcase
24
+ end
25
+
26
+ # @since 0.6.0
21
27
  # @api private
22
28
  def ===(other)
23
29
  key = other.downcase
24
30
  @rules.key?(key) || @rules.value?(key)
25
31
  end
26
32
 
27
- # @since 0.4.1
33
+ # @since 0.6.0
28
34
  # @api private
29
35
  def apply(string)
30
36
  key = string.downcase
@@ -34,30 +40,6 @@ module Lotus
34
40
  end
35
41
  end
36
42
 
37
- # Rule for irregular plural, that uses a suffix.
38
- #
39
- # @since 0.4.1
40
- # @api private
41
- class SuffixRule < IrregularRule
42
- def initialize(matcher, replacement, rules)
43
- super(rules)
44
- @matcher = matcher
45
- @replacement = replacement
46
- end
47
-
48
- # @since 0.4.1
49
- # @api private
50
- def ===(other)
51
- @rules.key?(other.downcase)
52
- end
53
-
54
- # @since 0.4.1
55
- # @api private
56
- def apply(string)
57
- string.sub(@matcher, @replacement)
58
- end
59
- end
60
-
61
43
  # Matcher for blank strings
62
44
  #
63
45
  # @since 0.4.1
@@ -80,6 +62,10 @@ module Lotus
80
62
  # @api private
81
63
  EAUX = 'eaux'.freeze
82
64
 
65
+ # @since 0.6.0
66
+ # @api private
67
+ ES = 'es'.freeze
68
+
83
69
  # @since 0.4.1
84
70
  # @api private
85
71
  F = 'f'.freeze
@@ -136,6 +122,14 @@ module Lotus
136
122
  # @api private
137
123
  MINA = 'mina'.freeze
138
124
 
125
+ # @since 0.6.0
126
+ # @api private
127
+ NA = 'na'.freeze
128
+
129
+ # @since 0.6.0
130
+ # @api private
131
+ NON = 'non'.freeze
132
+
139
133
  # @since 0.4.1
140
134
  # @api private
141
135
  O = 'o'.freeze
@@ -160,6 +154,10 @@ module Lotus
160
154
  # @api private
161
155
  SSES = 'sses'.freeze
162
156
 
157
+ # @since 0.6.0
158
+ # @api private
159
+ TA = 'ta'.freeze
160
+
163
161
  # @since 0.4.1
164
162
  # @api private
165
163
  UM = 'um'.freeze
@@ -188,66 +186,14 @@ module Lotus
188
186
  # @api private
189
187
  Y = 'y'.freeze
190
188
 
191
- # Plural rule "is" => "es"
192
- #
193
- # @since 0.4.1
194
- # @api private
195
- PLURAL_IS_ES = SuffixRule.new( /is\z/, 'es', {
196
- 'analysis' => true,
197
- 'axis' => true,
198
- 'basis' => true,
199
- 'crisis' => true,
200
- 'diagnosis' => true,
201
- 'ellipsis' => true,
202
- 'hypothesis' => true,
203
- 'oasis' => true,
204
- 'paralysis' => true,
205
- 'parenthesis' => true,
206
- 'synopsis' => true,
207
- 'synthesis' => true,
208
- 'thesis' => true,
209
- })
189
+ include Utils::ClassAttribute
210
190
 
211
- # Plural rule "is" => "ides"
191
+ # Irregular rules for plurals
212
192
  #
213
- # @since 0.4.1
193
+ # @since 0.6.0
214
194
  # @api private
215
- PLURAL_IS_IDES = SuffixRule.new( /is\z/, 'ides', {
216
- 'clitoris' => true,
217
- 'iris' => true,
218
- })
219
-
220
- # Plural rule "f" => "s"
221
- #
222
- # @since 0.4.1
223
- # @api private
224
- PLURAL_F_S = SuffixRule.new( /\z/, 's', {
225
- 'chief' => true,
226
- 'spoof' => true,
227
- })
228
-
229
- # Plural rule "o" => "oes"
230
- #
231
- # @since 0.4.1
232
- # @api private
233
- PLURAL_O_OES = SuffixRule.new( /\z/, 'es', {
234
- 'buffalo' => true,
235
- 'domino' => true,
236
- 'echo' => true,
237
- 'embargo' => true,
238
- 'hero' => true,
239
- 'mosquito' => true,
240
- 'potato' => true,
241
- 'tomato' => true,
242
- 'torpedo' => true,
243
- 'veto' => true,
244
- })
245
-
246
- # Irregular rules
247
- #
248
- # @since 0.4.1
249
- # @api private
250
- PLURAL_IRREGULAR = IrregularRule.new({
195
+ class_attribute :plurals
196
+ self.plurals = IrregularRules.new({
251
197
  # irregular
252
198
  'cactus' => 'cacti',
253
199
  'child' => 'children',
@@ -276,96 +222,14 @@ module Lotus
276
222
  'series' => 'series',
277
223
  'sheep' => 'sheep',
278
224
  'species' => 'species',
279
- # a => ae
280
- 'alumna' => 'alumnae',
281
- 'alga' => 'algae',
282
- 'vertebra' => 'vertebrae',
283
- 'persona' => 'personae',
284
- 'antenna' => 'antennae',
285
- 'formula' => 'formulae',
286
- 'nebula' => 'nebulae',
287
- 'vita' => 'vitae',
288
- # on => a
289
- 'criterion' => 'criteria',
290
- 'perihelion' => 'perihelia',
291
- 'aphelion' => 'aphelia',
292
- 'phenomenon' => 'phenomena',
293
- 'prolegomenon' => 'prolegomena',
294
- 'noumenon' => 'noumena',
295
- 'organon' => 'organa',
296
- 'asyndeton' => 'asyndeta',
297
- 'hyperbaton' => 'hyperbata',
298
- # us => i
299
- 'alumnus' => 'alumni',
300
- 'alveolus' => 'alveoli',
301
- 'bacillus' => 'bacilli',
302
- 'bronchus' => 'bronchi',
303
- 'locus' => 'loci',
304
- 'nucleus' => 'nuclei',
305
- 'stimulus' => 'stimuli',
306
- 'meniscus' => 'menisci',
307
- 'thesaurus' => 'thesauri',
308
- # a => ata
309
- 'anathema' => 'anathemata',
310
- 'enema' => 'enemata',
311
- 'oedema' => 'oedemata',
312
- 'bema' => 'bemata',
313
- 'enigma' => 'enigmata',
314
- 'sarcoma' => 'sarcomata',
315
- 'carcinoma' => 'carcinomata',
316
- 'gumma' => 'gummata',
317
- 'schema' => 'schemata',
318
- 'charisma' => 'charismata',
319
- 'lemma' => 'lemmata',
320
- 'soma' => 'somata',
321
- 'diploma' => 'diplomata',
322
- 'lymphoma' => 'lymphomata',
323
- 'stigma' => 'stigmata',
324
- 'dogma' => 'dogmata',
325
- 'magma' => 'magmata',
326
- 'stoma' => 'stomata',
327
- 'drama' => 'dramata',
328
- 'melisma' => 'melismata',
329
- 'trauma' => 'traumata',
330
- 'edema' => 'edemata',
331
- 'miasma' => 'miasmata',
332
- # s => es
333
- 'acropolis' => 'acropolises',
334
- 'chaos' => 'chaoses',
335
- 'lens' => 'lenses',
336
- 'aegis' => 'aegises',
337
- 'cosmos' => 'cosmoses',
338
- 'mantis' => 'mantises',
339
- 'alias' => 'aliases',
340
- 'dais' => 'daises',
341
- 'marquis' => 'marquises',
342
- 'asbestos' => 'asbestoses',
343
- 'digitalis' => 'digitalises',
344
- 'metropolis' => 'metropolises',
345
- 'atlas' => 'atlases',
346
- 'epidermis' => 'epidermises',
347
- 'pathos' => 'pathoses',
348
- 'bathos' => 'bathoses',
349
- 'ethos' => 'ethoses',
350
- 'pelvis' => 'pelvises',
351
- 'bias' => 'biases',
352
- 'gas' => 'gases',
353
- 'polis' => 'polises',
354
- 'caddis' => 'caddises',
355
- 'rhinoceros' => 'rhinoceroses',
356
- 'cannabis' => 'cannabises',
357
- 'glottis' => 'glottises',
358
- 'sassafras' => 'sassafrases',
359
- 'canvas' => 'canvases',
360
- 'ibis' => 'ibises',
361
- 'trellis' => 'trellises',
362
225
  })
363
226
 
364
- # Irregular rules
227
+ # Irregular rules for singulars
365
228
  #
366
- # @since 0.4.1
229
+ # @since 0.6.0
367
230
  # @api private
368
- SINGULAR_IRREGULAR = IrregularRule.new({
231
+ class_attribute :singulars
232
+ self.singulars = IrregularRules.new({
369
233
  # irregular
370
234
  'cacti' => 'cactus',
371
235
  'children'=> 'child',
@@ -395,89 +259,74 @@ module Lotus
395
259
  'sheep' => 'sheep',
396
260
  'species' => 'species',
397
261
  'police' => 'police',
398
- # ae => a
399
- 'alumnae' => 'alumna',
400
- 'algae' => 'alga',
401
- 'vertebrae' => 'vertebra',
402
- 'personae' => 'persona',
403
- 'antennae' => 'antenna',
404
- 'formulae' => 'formula',
405
- 'nebulae' => 'nebula',
406
- 'vitae' => 'vita',
407
- # a = on
408
- 'criteria' => 'criterion',
409
- 'perihelia' => 'perihelion',
410
- 'aphelia' => 'aphelion',
411
- 'phenomena' => 'phenomenon',
412
- 'prolegomena' => 'prolegomenon',
413
- 'noumena' => 'noumenon',
414
- 'organa' => 'organon',
415
- 'asyndeta' => 'asyndeton',
416
- 'hyperbata' => 'hyperbaton',
417
- # ses => s
418
- 'acropolises' => 'acropolis',
419
- 'chaoses' => 'chaos',
420
- 'lenses' => 'lens',
421
- 'aegises' => 'aegis',
422
- 'cosmoses' => 'cosmos',
423
- 'mantises' => 'mantis',
424
- 'aliases' => 'alias',
425
- 'daises' => 'dais',
426
- 'marquises' => 'marquis',
427
- 'asbestoses' => 'asbestos',
428
- 'digitalises' => 'digitalis',
429
- 'metropolises' => 'metropolis',
430
- 'atlases' => 'atlas',
431
- 'epidermises' => 'epidermis',
432
- 'pathoses' => 'pathos',
433
- 'bathoses' => 'bathos',
434
- 'ethoses' => 'ethos',
435
- 'pelvises' => 'pelvis',
436
- 'biases' => 'bias',
437
- 'gases' => 'gas',
438
- 'polises' => 'polis',
439
- 'caddises' => 'caddis',
440
- 'rhinoceroses' => 'rhinoceros',
441
- 'cannabises' => 'cannabis',
442
- 'glottises' => 'glottis',
443
- 'sassafrases' => 'sassafras',
444
- 'canvases' => 'canvas',
445
- 'ibises' => 'ibis',
446
- 'trellises' => 'trellis',
447
262
  # fallback
448
- 'hives' => 'hive',
449
- # ices => ex
450
- "codices" => "codex",
451
- "murices" => "murex",
452
- "silices" => "silex",
453
- "apices" => "apex",
454
- "latices" => "latex",
455
- "vertices" => "vertex",
456
- "cortices" => "cortex",
457
- "pontifices" => "pontifex",
458
- "vortices" => "vortex",
459
- "indices" => "index",
460
- "simplices" => "simplex",
461
- # ices => ix
462
- "radices" => "radix",
463
- "helices" => "helix",
464
- "appendices" => "appendix",
465
- # es => is
466
- "axes" => "axis",
467
- "analyses" => "analysis",
468
- "bases" => "basis",
469
- "crises" => "crisis",
470
- "diagnoses" => "diagnosis",
471
- "ellipses" => "ellipsis",
472
- "hypotheses" => "hypothesis",
473
- "oases" => "oasis",
474
- "paralyses" => "paralysis",
475
- "parentheses" => "parenthesis",
476
- "syntheses" => "synthesis",
477
- "synopses" => "synopsis",
478
- "theses" => "thesis",
263
+ 'hives' => 'hive',
264
+ 'horses' => 'horse',
479
265
  })
480
266
 
267
+ # Block for custom inflection rules.
268
+ #
269
+ # @param [Proc] blk custom inflections
270
+ #
271
+ # @since 0.6.0
272
+ #
273
+ # @see Lotus::Utils::Inflector.exception
274
+ # @see Lotus::Utils::Inflector.uncountable
275
+ #
276
+ # @example
277
+ # require 'lotus/utils/inflector'
278
+ #
279
+ # Lotus::Utils::Inflector.inflections do
280
+ # exception 'analysis', 'analyses'
281
+ # exception 'alga', 'algae'
282
+ # uncountable 'music', 'butter'
283
+ # end
284
+ def self.inflections(&blk)
285
+ class_eval(&blk)
286
+ end
287
+
288
+ # Add a custom inflection exception
289
+ #
290
+ # @param [String] singular form
291
+ # @param [String] plural form
292
+ #
293
+ # @since 0.6.0
294
+ #
295
+ # @see Lotus::Utils::Inflector.inflections
296
+ # @see Lotus::Utils::Inflector.uncountable
297
+ #
298
+ # @example
299
+ # require 'lotus/utils/inflector'
300
+ #
301
+ # Lotus::Utils::Inflector.inflections do
302
+ # exception 'alga', 'algae'
303
+ # end
304
+ def self.exception(singular, plural)
305
+ singulars.add(plural, singular)
306
+ plurals.add(singular, plural)
307
+ end
308
+
309
+ # Add an uncountable word
310
+ #
311
+ # @param [Array<String>] words
312
+ #
313
+ # @since 0.6.0
314
+ #
315
+ # @see Lotus::Utils::Inflector.inflections
316
+ # @see Lotus::Utils::Inflector.exception
317
+ #
318
+ # @example
319
+ # require 'lotus/utils/inflector'
320
+ #
321
+ # Lotus::Utils::Inflector.inflections do
322
+ # uncountable 'music', 'art'
323
+ # end
324
+ def self.uncountable(*words)
325
+ Array(words).each do |word|
326
+ exception(word, word)
327
+ end
328
+ end
329
+
481
330
  # Pluralize the given string
482
331
  #
483
332
  # @param string [String] a string to pluralize
@@ -490,8 +339,8 @@ module Lotus
490
339
  return string if string.nil? || string.match(BLANK_STRING_MATCHER)
491
340
 
492
341
  case string
493
- when PLURAL_IRREGULAR
494
- PLURAL_IRREGULAR.apply(string)
342
+ when plurals
343
+ plurals.apply(string)
495
344
  when /\A((.*)[^aeiou])ch\z/
496
345
  $1 + CHES
497
346
  when /\A((.*)[^aeiou])y\z/
@@ -502,24 +351,24 @@ module Lotus
502
351
  $1 + EAUX
503
352
  when /\A(.*)x\z/
504
353
  $1 + XES
354
+ when /\A(.*)ma\z/
355
+ string + TA
505
356
  when /\A(.*)(um|#{ A })\z/
506
357
  $1 + A
507
358
  when /\A(.*)(ouse|#{ ICE })\z/
508
359
  $1 + ICE
509
- when PLURAL_O_OES
510
- PLURAL_O_OES.apply(string)
360
+ when /\A(buffal|domin|ech|embarg|her|mosquit|potat|tomat)#{ O }\z/i
361
+ $1 + OES
511
362
  when /\A(.*)(en|#{ INA })\z/
512
363
  $1 + INA
513
- when PLURAL_F_S
514
- PLURAL_F_S.apply(string)
515
364
  when /\A(.*)(?:([^f]))f[e]*\z/
516
365
  $1 + $2 + VES
517
366
  when /\A(.*)us\z/
518
367
  $1 + USES
519
- when PLURAL_IS_ES
520
- PLURAL_IS_ES.apply(string)
521
- when PLURAL_IS_IDES
522
- PLURAL_IS_IDES.apply(string)
368
+ when /\A(.*)non\z/
369
+ $1 + NA
370
+ when /\A((.*)[^aeiou])is\z/
371
+ $1 + ES
523
372
  when /\A(.*)ss\z/
524
373
  $1 + SSES
525
374
  when /s\z/
@@ -541,8 +390,8 @@ module Lotus
541
390
  return string if string.nil? || string.match(BLANK_STRING_MATCHER)
542
391
 
543
392
  case string
544
- when SINGULAR_IRREGULAR
545
- SINGULAR_IRREGULAR.apply(string)
393
+ when singulars
394
+ singulars.apply(string)
546
395
  when /\A.*[^aeiou]#{CHES}\z/
547
396
  string.sub(CHES, CH)
548
397
  when /\A.*[^aeiou]#{IES}\z/
@@ -573,6 +422,10 @@ module Lotus
573
422
  $1 + F
574
423
  when /\A(.*)#{I}\z/
575
424
  $1 + US
425
+ when /\A(.*)ae\z/
426
+ $1 + A
427
+ when /\A(.*)na\z/
428
+ $1 + NON
576
429
  when /\A(.*)#{A}\z/
577
430
  $1 + UM
578
431
  when /[^s]\z/
@@ -978,7 +978,7 @@ module Lotus
978
978
  raise TypeError.new "can't convert #{inspect_type_error(arg)}into Pathname"
979
979
  end
980
980
 
981
- # Coerces the argument to be a String.
981
+ # Coerces the argument to be a Symbol.
982
982
  #
983
983
  # @param arg [#to_sym] the argument
984
984
  #
@@ -18,7 +18,7 @@ module Lotus
18
18
  # @see http://ruby-doc.org/stdlib/libdoc/pathname/rdoc/Pathname.html
19
19
  # @see Lotus::Utils::Kernel.Pathname
20
20
  def initialize(*paths)
21
- @paths = Array(paths)
21
+ @paths = Utils::Kernel.Array(paths)
22
22
  end
23
23
 
24
24
  # It specifies the policy for initialize copies of the object, when #clone
@@ -61,7 +61,7 @@ module Lotus
61
61
  #
62
62
  # @since 0.2.0
63
63
  def each
64
- Utils::Kernel.Array(@paths).each do |path|
64
+ @paths.each do |path|
65
65
  yield realpath(path)
66
66
  end
67
67
  end
@@ -111,6 +111,7 @@ module Lotus
111
111
  # paths << '.' << '../..'
112
112
  def push(*paths)
113
113
  @paths.push(*paths)
114
+ @paths = Kernel.Array(@paths)
114
115
  self
115
116
  end
116
117
 
@@ -136,6 +137,22 @@ module Lotus
136
137
  @paths.freeze
137
138
  end
138
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
+
139
156
  private
140
157
  # Allow subclasses to define their own policy to discover the realpath
141
158
  # of the given path.
@@ -80,10 +80,13 @@ module Lotus
80
80
  def relative_join(strings, separator = @separator)
81
81
  raise TypeError if separator.nil?
82
82
  prefix = @string.gsub(@separator, separator)
83
+ result = [prefix, strings]
84
+ result.flatten!
85
+ result.compact!
86
+ result.reject! {|string| string == separator }
83
87
 
84
88
  self.class.new(
85
- [prefix, strings].flatten.compact.join(separator),
86
- separator
89
+ result.join(separator), separator
87
90
  ).relative!
88
91
  end
89
92
 
@@ -6,6 +6,12 @@ module Lotus
6
6
  #
7
7
  # @since 0.1.0
8
8
  class String
9
+ # Empty string for #classify
10
+ #
11
+ # @since 0.6.0
12
+ # @api private
13
+ EMPTY_STRING = ''.freeze
14
+
9
15
  # Separator between Ruby namespaces
10
16
  #
11
17
  # @since 0.1.0
@@ -135,14 +141,14 @@ module Lotus
135
141
  # string = Lotus::Utils::String.new 'lotus_utils'
136
142
  # string.classify # => 'LotusUtils'
137
143
  def classify
138
- words = split(CLASSIFY_WORD_SEPARATOR).map(&:capitalize)
144
+ words = split(CLASSIFY_WORD_SEPARATOR).map!(&:capitalize)
139
145
  delimiters = scan(CLASSIFY_WORD_SEPARATOR)
140
146
 
141
147
  delimiters.map! do |delimiter|
142
- delimiter == CLASSIFY_SEPARATOR ? nil : NAMESPACE_SEPARATOR
148
+ delimiter == CLASSIFY_SEPARATOR ? EMPTY_STRING : NAMESPACE_SEPARATOR
143
149
  end
144
150
 
145
- self.class.new words.zip(delimiters).compact.join
151
+ self.class.new words.zip(delimiters).join
146
152
  end
147
153
 
148
154
  # Return a downcased and underscore separated version of the string
@@ -340,6 +346,51 @@ module Lotus
340
346
  end
341
347
  end
342
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, Lotus::Utils::String] the string to replace
368
+ #
369
+ # @return [Lotus::Utils::String] the replaced string
370
+ #
371
+ # @since 0.6.0
372
+ #
373
+ # @example
374
+ # require 'lotus/utils/string'
375
+ #
376
+ # string = Lotus::Utils::String.new('authors/books/index')
377
+ # result = string.rsub(/\//, '#')
378
+ #
379
+ # puts string
380
+ # # => #<Lotus::Utils::String:0x007fdb41233ad8 @string="authors/books/index">
381
+ #
382
+ # puts result
383
+ # # => #<Lotus::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
+
343
394
  # Override Ruby's method_missing in order to provide ::String interface
344
395
  #
345
396
  # @api private
@@ -3,6 +3,6 @@ module Lotus
3
3
  # Defines the version
4
4
  #
5
5
  # @since 0.1.0
6
- VERSION = '0.5.2'.freeze
6
+ VERSION = '0.6.0'.freeze
7
7
  end
8
8
  end
data/lotus-utils.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
10
10
  spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
11
11
  spec.description = %q{Lotus utilities}
12
- spec.summary = %q{Ruby core extentions and Louts utilities}
12
+ spec.summary = %q{Ruby core extentions and Lotus utilities}
13
13
  spec.homepage = 'http://lotusrb.org'
14
14
  spec.license = 'MIT'
15
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lotus-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-09-30 00:00:00.000000000 Z
13
+ date: 2016-01-12 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -76,6 +76,7 @@ files:
76
76
  - lib/lotus/utils/class.rb
77
77
  - lib/lotus/utils/class_attribute.rb
78
78
  - lib/lotus/utils/deprecation.rb
79
+ - lib/lotus/utils/duplicable.rb
79
80
  - lib/lotus/utils/escape.rb
80
81
  - lib/lotus/utils/hash.rb
81
82
  - lib/lotus/utils/inflector.rb
@@ -106,9 +107,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
107
  version: '0'
107
108
  requirements: []
108
109
  rubyforge_project:
109
- rubygems_version: 2.4.5.1
110
+ rubygems_version: 2.5.1
110
111
  signing_key:
111
112
  specification_version: 4
112
- summary: Ruby core extentions and Louts utilities
113
+ summary: Ruby core extentions and Lotus utilities
113
114
  test_files: []
114
115
  has_rdoc: