lotus-utils 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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: