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.
@@ -0,0 +1,37 @@
1
+ module Hanami
2
+ module Utils
3
+ # IO utils
4
+ #
5
+ # @since 0.1.0
6
+ class IO
7
+ # Decreases the level of verbosity, during the execution of the given block.
8
+ #
9
+ # Revised version of ActiveSupport's `Kernel.with_warnings` implementation
10
+ # @see https://github.com/rails/rails/blob/v4.1.2/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L25
11
+ #
12
+ # @yield the block of code that generates warnings.
13
+ #
14
+ # @return [void]
15
+ #
16
+ # @since 0.1.0
17
+ #
18
+ # @example
19
+ # require 'hanami/utils/io'
20
+ #
21
+ # class Test
22
+ # TEST_VALUE = 'initial'
23
+ # end
24
+ #
25
+ # Hanami::Utils::IO.silence_warnings do
26
+ # Test::TEST_VALUE = 'redefined'
27
+ # end
28
+ def self.silence_warnings
29
+ old_verbose, $VERBOSE = $VERBOSE, nil
30
+ yield
31
+ ensure
32
+ $VERBOSE = old_verbose
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,1031 @@
1
+ require 'set'
2
+ require 'date'
3
+ require 'time'
4
+ require 'pathname'
5
+ require 'bigdecimal'
6
+ require 'hanami/utils'
7
+
8
+ # Define top level constant Boolean, so it can be easily used by other libraries
9
+ # in coercions DSLs
10
+ #
11
+ # @since 0.3.0
12
+ class Boolean
13
+ end unless defined?(Boolean)
14
+
15
+ module Hanami
16
+ module Utils
17
+ # Kernel utilities
18
+ # @since 0.1.1
19
+ module Kernel
20
+ # Matcher for numeric values
21
+ #
22
+ # @since 0.3.3
23
+ # @api private
24
+ #
25
+ # @see Hanami::Utils::Kernel.Integer
26
+ NUMERIC_MATCHER = /\A([\d\/\.\+iE]+|NaN|Infinity)\z/.freeze
27
+
28
+ # Coerces the argument to be an Array.
29
+ #
30
+ # It's similar to Ruby's Kernel.Array, but it applies further
31
+ # transformations:
32
+ #
33
+ # * flatten
34
+ # * compact
35
+ # * uniq
36
+ #
37
+ # @param arg [Object] the input
38
+ #
39
+ # @return [Array] the result of the coercion
40
+ #
41
+ # @since 0.1.1
42
+ #
43
+ # @see http://www.ruby-doc.org/core/Kernel.html#method-i-Array
44
+ #
45
+ # @see http://www.ruby-doc.org/core/Array.html#method-i-flatten
46
+ # @see http://www.ruby-doc.org/core/Array.html#method-i-compact
47
+ # @see http://www.ruby-doc.org/core/Array.html#method-i-uniq
48
+ #
49
+ # @example Basic Usage
50
+ # require 'hanami/utils/kernel'
51
+ #
52
+ # Hanami::Utils::Kernel.Array(nil) # => []
53
+ # Hanami::Utils::Kernel.Array(true) # => [true]
54
+ # Hanami::Utils::Kernel.Array(false) # => [false]
55
+ # Hanami::Utils::Kernel.Array(1) # => [1]
56
+ # Hanami::Utils::Kernel.Array([1]) # => [1]
57
+ # Hanami::Utils::Kernel.Array([1, [2]]) # => [1,2]
58
+ # Hanami::Utils::Kernel.Array([1, [2, nil]]) # => [1,2]
59
+ # Hanami::Utils::Kernel.Array([1, [2, nil, 1]]) # => [1,2]
60
+ #
61
+ # @example Array Interface
62
+ # require 'hanami/utils/kernel'
63
+ #
64
+ # ResultSet = Struct.new(:records) do
65
+ # def to_a
66
+ # records.to_a.sort
67
+ # end
68
+ # end
69
+ #
70
+ # Response = Struct.new(:status, :headers, :body) do
71
+ # def to_ary
72
+ # [status, headers, body]
73
+ # end
74
+ # end
75
+ #
76
+ # set = ResultSet.new([2,1,3])
77
+ # Hanami::Utils::Kernel.Array(set) # => [1,2,3]
78
+ #
79
+ # response = Response.new(200, {}, 'hello')
80
+ # Hanami::Utils::Kernel.Array(response) # => [200, {}, "hello"]
81
+ def self.Array(arg)
82
+ super(arg).dup.tap do |a|
83
+ a.flatten!
84
+ a.compact!
85
+ a.uniq!
86
+ end
87
+ end
88
+
89
+ # Coerces the argument to be a Set.
90
+ #
91
+ # @param arg [Object] the input
92
+ #
93
+ # @return [Set] the result of the coercion
94
+ #
95
+ # @raise [TypeError] if arg doesn't implement #respond_to?
96
+ #
97
+ # @since 0.1.1
98
+ #
99
+ # @example Basic Usage
100
+ # require 'hanami/utils/kernel'
101
+ #
102
+ # Hanami::Utils::Kernel.Set(nil) # => #<Set: {}>
103
+ # Hanami::Utils::Kernel.Set(true) # => #<Set: {true}>
104
+ # Hanami::Utils::Kernel.Set(false) # => #<Set: {false}>
105
+ # Hanami::Utils::Kernel.Set(1) # => #<Set: {1}>
106
+ # Hanami::Utils::Kernel.Set([1]) # => #<Set: {1}>
107
+ # Hanami::Utils::Kernel.Set([1, 1]) # => #<Set: {1}>
108
+ # Hanami::Utils::Kernel.Set([1, [2]]) # => #<Set: {1, [2]}>
109
+ # Hanami::Utils::Kernel.Set([1, [2, nil]]) # => #<Set: {1, [2, nil]}>
110
+ # Hanami::Utils::Kernel.Set({a: 1}) # => #<Set: {[:a, 1]}>
111
+ #
112
+ # @example Set Interface
113
+ # require 'securerandom'
114
+ # require 'hanami/utils/kernel'
115
+ #
116
+ # UuidSet = Class.new do
117
+ # def initialize(*uuids)
118
+ # @uuids = uuids
119
+ # end
120
+ #
121
+ # def to_set
122
+ # Set.new.tap do |set|
123
+ # @uuids.each {|uuid| set.add(uuid) }
124
+ # end
125
+ # end
126
+ # end
127
+ #
128
+ # uuids = UuidSet.new(SecureRandom.uuid)
129
+ # Hanami::Utils::Kernel.Set(uuids)
130
+ # # => #<Set: {"daa798b4-630c-4e11-b29d-92f0b1c7d075"}>
131
+ #
132
+ # @example Unchecked Exceptions
133
+ # require 'hanami/utils/kernel'
134
+ #
135
+ # Hanami::Utils::Kernel.Set(BasicObject.new) # => TypeError
136
+ def self.Set(arg)
137
+ if arg.respond_to?(:to_set)
138
+ arg.to_set
139
+ else
140
+ Set.new(::Kernel.Array(arg))
141
+ end
142
+ rescue NoMethodError
143
+ raise TypeError.new("can't convert #{inspect_type_error(arg)}into Set")
144
+ end
145
+
146
+ # Coerces the argument to be a Hash.
147
+ #
148
+ # @param arg [Object] the input
149
+ #
150
+ # @return [Hash] the result of the coercion
151
+ #
152
+ # @raise [TypeError] if arg can't be coerced
153
+ #
154
+ # @since 0.1.1
155
+ #
156
+ # @see http://www.ruby-doc.org/core/Kernel.html#method-i-Hash
157
+ #
158
+ # @example Basic Usage
159
+ # require 'hanami/utils/kernel'
160
+ #
161
+ # Hanami::Utils::Kernel.Hash(nil) # => {}
162
+ # Hanami::Utils::Kernel.Hash({a: 1}) # => { :a => 1 }
163
+ # Hanami::Utils::Kernel.Hash([[:a, 1]]) # => { :a => 1 }
164
+ # Hanami::Utils::Kernel.Hash(Set.new([[:a, 1]])) # => { :a => 1 }
165
+ #
166
+ # @example Hash Interface
167
+ # require 'hanami/utils/kernel'
168
+ #
169
+ # Room = Class.new do
170
+ # def initialize(*args)
171
+ # @args = args
172
+ # end
173
+ #
174
+ # def to_h
175
+ # Hash[*@args]
176
+ # end
177
+ # end
178
+ #
179
+ # Record = Class.new do
180
+ # def initialize(attributes = {})
181
+ # @attributes = attributes
182
+ # end
183
+ #
184
+ # def to_hash
185
+ # @attributes
186
+ # end
187
+ # end
188
+ #
189
+ # room = Room.new(:key, 123456)
190
+ # Hanami::Utils::Kernel.Hash(room) # => { :key => 123456 }
191
+ #
192
+ # record = Record.new(name: 'L')
193
+ # Hanami::Utils::Kernel.Hash(record) # => { :name => "L" }
194
+ #
195
+ # @example Unchecked Exceptions
196
+ # require 'hanami/utils/kernel'
197
+ #
198
+ # input = BasicObject.new
199
+ # Hanami::Utils::Kernel.Hash(input) # => TypeError
200
+ def self.Hash(arg)
201
+ if arg.respond_to?(:to_h)
202
+ arg.to_h
203
+ else
204
+ super(arg)
205
+ end
206
+ rescue NoMethodError
207
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Hash"
208
+ end
209
+
210
+ # Coerces the argument to be an Integer.
211
+ #
212
+ # It's similar to Ruby's Kernel.Integer, but it doesn't stop at the first
213
+ # error and raise an exception only when the argument can't be coerced.
214
+ #
215
+ # @param arg [Object] the argument
216
+ #
217
+ # @return [Fixnum] the result of the coercion
218
+ #
219
+ # @raise [TypeError] if the argument can't be coerced
220
+ #
221
+ # @since 0.1.1
222
+ #
223
+ # @see http://www.ruby-doc.org/core/Kernel.html#method-i-Integer
224
+ #
225
+ # @example Basic Usage
226
+ # require 'bigdecimal'
227
+ # require 'hanami/utils/kernel'
228
+ #
229
+ # Hanami::Utils::Kernel.Integer(1) # => 1
230
+ # Hanami::Utils::Kernel.Integer(1.2) # => 1
231
+ # Hanami::Utils::Kernel.Integer(011) # => 9
232
+ # Hanami::Utils::Kernel.Integer(0xf5) # => 245
233
+ # Hanami::Utils::Kernel.Integer("1") # => 1
234
+ # Hanami::Utils::Kernel.Integer(Rational(0.3)) # => 0
235
+ # Hanami::Utils::Kernel.Integer(Complex(0.3)) # => 0
236
+ # Hanami::Utils::Kernel.Integer(BigDecimal.new(12.00001)) # => 12
237
+ # Hanami::Utils::Kernel.Integer(176605528590345446089)
238
+ # # => 176605528590345446089
239
+ #
240
+ # Hanami::Utils::Kernel.Integer(Time.now) # => 1396947161
241
+ #
242
+ # @example Integer Interface
243
+ # require 'hanami/utils/kernel'
244
+ #
245
+ # UltimateAnswer = Struct.new(:question) do
246
+ # def to_int
247
+ # 42
248
+ # end
249
+ # end
250
+ #
251
+ # answer = UltimateAnswer.new('The Ultimate Question of Life')
252
+ # Hanami::Utils::Kernel.Integer(answer) # => 42
253
+ #
254
+ # @example Error Handling
255
+ # require 'hanami/utils/kernel'
256
+ #
257
+ # # nil
258
+ # Kernel.Integer(nil) # => TypeError
259
+ # Hanami::Utils::Kernel.Integer(nil) # => 0
260
+ #
261
+ # # float represented as a string
262
+ # Kernel.Integer("23.4") # => TypeError
263
+ # Hanami::Utils::Kernel.Integer("23.4") # => 23
264
+ #
265
+ # # rational represented as a string
266
+ # Kernel.Integer("2/3") # => TypeError
267
+ # Hanami::Utils::Kernel.Integer("2/3") # => 2
268
+ #
269
+ # # complex represented as a string
270
+ # Kernel.Integer("2.5/1") # => TypeError
271
+ # Hanami::Utils::Kernel.Integer("2.5/1") # => 2
272
+ #
273
+ # @example Unchecked Exceptions
274
+ # require 'date'
275
+ # require 'bigdecimal'
276
+ # require 'hanami/utils/kernel'
277
+ #
278
+ # # Missing #to_int and #to_i
279
+ # input = OpenStruct.new(color: 'purple')
280
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
281
+ #
282
+ # # String that doesn't represent an integer
283
+ # input = 'hello'
284
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
285
+ #
286
+ # # When true
287
+ # input = true
288
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
289
+ #
290
+ # # When false
291
+ # input = false
292
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
293
+ #
294
+ # # When Date
295
+ # input = Date.today
296
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
297
+ #
298
+ # # When DateTime
299
+ # input = DateTime.now
300
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
301
+ #
302
+ # # bigdecimal infinity
303
+ # input = BigDecimal.new("Infinity")
304
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
305
+ #
306
+ # # bigdecimal NaN
307
+ # input = BigDecimal.new("NaN")
308
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
309
+ #
310
+ # # big rational
311
+ # input = Rational(-8) ** Rational(1, 3)
312
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
313
+ #
314
+ # # big complex represented as a string
315
+ # input = Complex(2, 3)
316
+ # Hanami::Utils::Kernel.Integer(input) # => TypeError
317
+ def self.Integer(arg)
318
+ super(arg)
319
+ rescue ArgumentError, TypeError, NoMethodError
320
+ begin
321
+ case arg
322
+ when NilClass, ->(a) { a.respond_to?(:to_i) && a.to_s.match(NUMERIC_MATCHER) }
323
+ arg.to_i
324
+ else
325
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Integer"
326
+ end
327
+ rescue NoMethodError
328
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Integer"
329
+ end
330
+ rescue RangeError
331
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Integer"
332
+ end
333
+
334
+ # Coerces the argument to be a BigDecimal.
335
+ #
336
+ # @param arg [Object] the argument
337
+ #
338
+ # @return [BigDecimal] the result of the coercion
339
+ #
340
+ # @raise [TypeError] if the argument can't be coerced
341
+ #
342
+ # @since 0.3.0
343
+ #
344
+ # @see http://www.ruby-doc.org/stdlib/libdoc/bigdecimal/rdoc/BigDecimal.html
345
+ #
346
+ # @example Basic Usage
347
+ # require 'hanami/utils/kernel'
348
+ #
349
+ # Hanami::Utils::Kernel.BigDecimal(1) # => 1
350
+ # Hanami::Utils::Kernel.BigDecimal(1.2) # => 1
351
+ # Hanami::Utils::Kernel.BigDecimal(011) # => 9
352
+ # Hanami::Utils::Kernel.BigDecimal(0xf5) # => 245
353
+ # Hanami::Utils::Kernel.BigDecimal("1") # => 1
354
+ # Hanami::Utils::Kernel.BigDecimal(Rational(0.3)) # => 0.3
355
+ # Hanami::Utils::Kernel.BigDecimal(Complex(0.3)) # => 0.3
356
+ # Hanami::Utils::Kernel.BigDecimal(BigDecimal.new(12.00001)) # => 12.00001
357
+ # Hanami::Utils::Kernel.BigDecimal(176605528590345446089)
358
+ # # => 176605528590345446089
359
+ #
360
+ # @example BigDecimal Interface
361
+ # require 'hanami/utils/kernel'
362
+ #
363
+ # UltimateAnswer = Struct.new(:question) do
364
+ # def to_d
365
+ # BigDecimal.new(42)
366
+ # end
367
+ # end
368
+ #
369
+ # answer = UltimateAnswer.new('The Ultimate Question of Life')
370
+ # Hanami::Utils::Kernel.BigDecimal(answer)
371
+ # # => #<BigDecimal:7fabfd148588,'0.42E2',9(27)>
372
+ #
373
+ # @example Unchecked exceptions
374
+ # require 'hanami/utils/kernel'
375
+ #
376
+ # # When nil
377
+ # input = nil
378
+ # Hanami::Utils::Kernel.BigDecimal(nil) # => TypeError
379
+ #
380
+ # # When true
381
+ # input = true
382
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
383
+ #
384
+ # # When false
385
+ # input = false
386
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
387
+ #
388
+ # # When Date
389
+ # input = Date.today
390
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
391
+ #
392
+ # # When DateTime
393
+ # input = DateTime.now
394
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
395
+ #
396
+ # # When Time
397
+ # input = Time.now
398
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
399
+ #
400
+ # # String that doesn't represent a big decimal
401
+ # input = 'hello'
402
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
403
+ #
404
+ # # Missing #respond_to?
405
+ # input = BasicObject.new
406
+ # Hanami::Utils::Kernel.BigDecimal(input) # => TypeError
407
+ def self.BigDecimal(arg)
408
+ case arg
409
+ when ->(a) { a.respond_to?(:to_d) } then arg.to_d
410
+ when Float, Complex, Rational
411
+ BigDecimal(arg.to_s)
412
+ when ->(a) { a.to_s.match(NUMERIC_MATCHER) }
413
+ BigDecimal.new(arg)
414
+ else
415
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into BigDecimal"
416
+ end
417
+ rescue NoMethodError
418
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into BigDecimal"
419
+ end
420
+
421
+ # Coerces the argument to be a Float.
422
+ #
423
+ # It's similar to Ruby's Kernel.Float, but it doesn't stop at the first
424
+ # error and raise an exception only when the argument can't be coerced.
425
+ #
426
+ # @param arg [Object] the argument
427
+ #
428
+ # @return [Float] the result of the coercion
429
+ #
430
+ # @raise [TypeError] if the argument can't be coerced
431
+ #
432
+ # @since 0.1.1
433
+ #
434
+ # @see http://www.ruby-doc.org/core/Kernel.html#method-i-Float
435
+ #
436
+ # @example Basic Usage
437
+ # require 'bigdecimal'
438
+ # require 'hanami/utils/kernel'
439
+ #
440
+ # Hanami::Utils::Kernel.Float(1) # => 1.0
441
+ # Hanami::Utils::Kernel.Float(1.2) # => 1.2
442
+ # Hanami::Utils::Kernel.Float(011) # => 9.0
443
+ # Hanami::Utils::Kernel.Float(0xf5) # => 245.0
444
+ # Hanami::Utils::Kernel.Float("1") # => 1.0
445
+ # Hanami::Utils::Kernel.Float(Rational(0.3)) # => 0.3
446
+ # Hanami::Utils::Kernel.Float(Complex(0.3)) # => 0.3
447
+ # Hanami::Utils::Kernel.Float(BigDecimal.new(12.00001)) # => 12.00001
448
+ # Hanami::Utils::Kernel.Float(176605528590345446089)
449
+ # # => 176605528590345446089.0
450
+ #
451
+ # Hanami::Utils::Kernel.Float(Time.now) # => 397750945.515169
452
+ #
453
+ # @example Float Interface
454
+ # require 'hanami/utils/kernel'
455
+ #
456
+ # class Pi
457
+ # def to_f
458
+ # 3.14
459
+ # end
460
+ # end
461
+ #
462
+ # pi = Pi.new
463
+ # Hanami::Utils::Kernel.Float(pi) # => 3.14
464
+ #
465
+ # @example Error Handling
466
+ # require 'bigdecimal'
467
+ # require 'hanami/utils/kernel'
468
+ #
469
+ # # nil
470
+ # Kernel.Float(nil) # => TypeError
471
+ # Hanami::Utils::Kernel.Float(nil) # => 0.0
472
+ #
473
+ # # float represented as a string
474
+ # Kernel.Float("23.4") # => TypeError
475
+ # Hanami::Utils::Kernel.Float("23.4") # => 23.4
476
+ #
477
+ # # rational represented as a string
478
+ # Kernel.Float("2/3") # => TypeError
479
+ # Hanami::Utils::Kernel.Float("2/3") # => 2.0
480
+ #
481
+ # # complex represented as a string
482
+ # Kernel.Float("2.5/1") # => TypeError
483
+ # Hanami::Utils::Kernel.Float("2.5/1") # => 2.5
484
+ #
485
+ # # bigdecimal infinity
486
+ # input = BigDecimal.new("Infinity")
487
+ # Hanami::Utils::Kernel.Float(input) # => Infinity
488
+ #
489
+ # # bigdecimal NaN
490
+ # input = BigDecimal.new("NaN")
491
+ # Hanami::Utils::Kernel.Float(input) # => NaN
492
+ #
493
+ # @example Unchecked Exceptions
494
+ # require 'date'
495
+ # require 'bigdecimal'
496
+ # require 'hanami/utils/kernel'
497
+ #
498
+ # # Missing #to_f
499
+ # input = OpenStruct.new(color: 'purple')
500
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
501
+ #
502
+ # # When true
503
+ # input = true
504
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
505
+ #
506
+ # # When false
507
+ # input = false
508
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
509
+ #
510
+ # # When Date
511
+ # input = Date.today
512
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
513
+ #
514
+ # # When DateTime
515
+ # input = DateTime.now
516
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
517
+ #
518
+ # # Missing #nil?
519
+ # input = BasicObject.new
520
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
521
+ #
522
+ # # String that doesn't represent a float
523
+ # input = 'hello'
524
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
525
+ #
526
+ # # big rational
527
+ # input = Rational(-8) ** Rational(1, 3)
528
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
529
+ #
530
+ # # big complex represented as a string
531
+ # input = Complex(2, 3)
532
+ # Hanami::Utils::Kernel.Float(input) # => TypeError
533
+ def self.Float(arg)
534
+ super(arg)
535
+ rescue ArgumentError, TypeError
536
+ begin
537
+ case arg
538
+ when NilClass, ->(a) { a.respond_to?(:to_f) && a.to_s.match(NUMERIC_MATCHER) }
539
+ arg.to_f
540
+ else
541
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Float"
542
+ end
543
+ rescue NoMethodError
544
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Float"
545
+ end
546
+ rescue RangeError
547
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Float"
548
+ end
549
+
550
+ # Coerces the argument to be a String.
551
+ #
552
+ # Identical behavior of Ruby's Kernel.Array, still here because we want
553
+ # to keep the interface consistent
554
+ #
555
+ # @param arg [Object] the argument
556
+ #
557
+ # @return [String] the result of the coercion
558
+ #
559
+ # @raise [TypeError] if the argument can't be coerced
560
+ #
561
+ # @since 0.1.1
562
+ #
563
+ # @see http://www.ruby-doc.org/core/Kernel.html#method-i-String
564
+ #
565
+ # @example Basic Usage
566
+ # require 'date'
567
+ # require 'bigdecimal'
568
+ # require 'hanami/utils/kernel'
569
+ #
570
+ # Hanami::Utils::Kernel.String('') # => ""
571
+ # Hanami::Utils::Kernel.String('ciao') # => "ciao"
572
+ #
573
+ # Hanami::Utils::Kernel.String(true) # => "true"
574
+ # Hanami::Utils::Kernel.String(false) # => "false"
575
+ #
576
+ # Hanami::Utils::Kernel.String(:hanami) # => "hanami"
577
+ #
578
+ # Hanami::Utils::Kernel.String(Picture) # => "Picture" # class
579
+ # Hanami::Utils::Kernel.String(Hanami) # => "Hanami" # module
580
+ #
581
+ # Hanami::Utils::Kernel.String([]) # => "[]"
582
+ # Hanami::Utils::Kernel.String([1,2,3]) # => "[1, 2, 3]"
583
+ # Hanami::Utils::Kernel.String(%w[a b c]) # => "[\"a\", \"b\", \"c\"]"
584
+ #
585
+ # Hanami::Utils::Kernel.String({}) # => "{}"
586
+ # Hanami::Utils::Kernel.String({a: 1, 'b' => 'c'}) # => "{:a=>1, \"b\"=>\"c\"}"
587
+ #
588
+ # Hanami::Utils::Kernel.String(Date.today) # => "2014-04-11"
589
+ # Hanami::Utils::Kernel.String(DateTime.now) # => "2014-04-11T10:15:06+02:00"
590
+ # Hanami::Utils::Kernel.String(Time.now) # => "2014-04-11 10:15:53 +0200"
591
+ #
592
+ # Hanami::Utils::Kernel.String(1) # => "1"
593
+ # Hanami::Utils::Kernel.String(3.14) # => "3.14"
594
+ # Hanami::Utils::Kernel.String(013) # => "11"
595
+ # Hanami::Utils::Kernel.String(0xc0ff33) # => "12648243"
596
+ #
597
+ # Hanami::Utils::Kernel.String(Rational(-22)) # => "-22/1"
598
+ # Hanami::Utils::Kernel.String(Complex(11, 2)) # => "11+2i"
599
+ # Hanami::Utils::Kernel.String(BigDecimal.new(7944.2343, 10)) # => "0.79442343E4"
600
+ # Hanami::Utils::Kernel.String(BigDecimal.new('Infinity')) # => "Infinity"
601
+ # Hanami::Utils::Kernel.String(BigDecimal.new('NaN')) # => "Infinity"
602
+ #
603
+ # @example String interface
604
+ # require 'hanami/utils/kernel'
605
+ #
606
+ # SimpleObject = Class.new(BasicObject) do
607
+ # def to_s
608
+ # 'simple object'
609
+ # end
610
+ # end
611
+ #
612
+ # Isbn = Struct.new(:code) do
613
+ # def to_str
614
+ # code.to_s
615
+ # end
616
+ # end
617
+ #
618
+ # simple = SimpleObject.new
619
+ # isbn = Isbn.new(123)
620
+ #
621
+ # Hanami::Utils::Kernel.String(simple) # => "simple object"
622
+ # Hanami::Utils::Kernel.String(isbn) # => "123"
623
+ #
624
+ # @example Comparison with Ruby
625
+ # require 'hanami/utils/kernel'
626
+ #
627
+ # # nil
628
+ # Kernel.String(nil) # => ""
629
+ # Hanami::Utils::Kernel.String(nil) # => ""
630
+ #
631
+ # @example Unchecked Exceptions
632
+ # require 'hanami/utils/kernel'
633
+ #
634
+ # # Missing #to_s or #to_str
635
+ # input = BaseObject.new
636
+ # Hanami::Utils::Kernel.String(input) # => TypeError
637
+ def self.String(arg)
638
+ arg = arg.to_str if arg.respond_to?(:to_str)
639
+ super(arg)
640
+ rescue NoMethodError
641
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into String"
642
+ end
643
+
644
+ # Coerces the argument to be a Date.
645
+ #
646
+ # @param arg [Object] the argument
647
+ #
648
+ # @return [Date] the result of the coercion
649
+ #
650
+ # @raise [TypeError] if the argument can't be coerced
651
+ #
652
+ # @since 0.1.1
653
+ #
654
+ # @example Basic Usage
655
+ # require 'hanami/utils/kernel'
656
+ #
657
+ # Hanami::Utils::Kernel.Date(Date.today)
658
+ # # => #<Date: 2014-04-17 ((2456765j,0s,0n),+0s,2299161j)>
659
+ #
660
+ # Hanami::Utils::Kernel.Date(DateTime.now)
661
+ # # => #<Date: 2014-04-17 ((2456765j,0s,0n),+0s,2299161j)>
662
+ #
663
+ # Hanami::Utils::Kernel.Date(Time.now)
664
+ # # => #<Date: 2014-04-17 ((2456765j,0s,0n),+0s,2299161j)>
665
+ #
666
+ # Hanami::Utils::Kernel.Date('2014-04-17')
667
+ # # => #<Date: 2014-04-17 ((2456765j,0s,0n),+0s,2299161j)>
668
+ #
669
+ # Hanami::Utils::Kernel.Date('2014-04-17 22:37:15')
670
+ # # => #<Date: 2014-04-17 ((2456765j,0s,0n),+0s,2299161j)>
671
+ #
672
+ # @example Date Interface
673
+ # require 'hanami/utils/kernel'
674
+ #
675
+ # class Christmas
676
+ # def to_date
677
+ # Date.parse('Dec, 25')
678
+ # end
679
+ # end
680
+ #
681
+ # Hanami::Utils::Kernel.Date(Christmas.new)
682
+ # # => #<Date: 2014-12-25 ((2457017j,0s,0n),+0s,2299161j)>
683
+ #
684
+ # @example Unchecked Exceptions
685
+ # require 'hanami/utils/kernel'
686
+ #
687
+ # # nil
688
+ # input = nil
689
+ # Hanami::Utils::Kernel.Date(input) # => TypeError
690
+ #
691
+ # # Missing #respond_to?
692
+ # input = BasicObject.new
693
+ # Hanami::Utils::Kernel.Date(input) # => TypeError
694
+ #
695
+ # # Missing #to_s?
696
+ # input = BasicObject.new
697
+ # Hanami::Utils::Kernel.Date(input) # => TypeError
698
+ def self.Date(arg)
699
+ if arg.respond_to?(:to_date)
700
+ arg.to_date
701
+ else
702
+ Date.parse(arg.to_s)
703
+ end
704
+ rescue ArgumentError, NoMethodError
705
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Date"
706
+ end
707
+
708
+ # Coerces the argument to be a DateTime.
709
+ #
710
+ # @param arg [Object] the argument
711
+ #
712
+ # @return [DateTime] the result of the coercion
713
+ #
714
+ # @raise [TypeError] if the argument can't be coerced
715
+ #
716
+ # @since 0.1.1
717
+ #
718
+ # @example Basic Usage
719
+ # require 'hanami/utils/kernel'
720
+ #
721
+ # Hanami::Utils::Kernel.DateTime(3483943)
722
+ # # => Time.at(3483943).to_datetime #<DateTime: 1970-02-10T08:45:43+01:00 ((2440628j,27943s,0n),+3600s,2299161j)>
723
+ #
724
+ # Hanami::Utils::Kernel.DateTime(DateTime.now)
725
+ # # => #<DateTime: 2014-04-18T09:33:49+02:00 ((2456766j,27229s,690849000n),+7200s,2299161j)>
726
+ #
727
+ # Hanami::Utils::Kernel.DateTime(Date.today)
728
+ # # => #<DateTime: 2014-04-18T00:00:00+00:00 ((2456766j,0s,0n),+0s,2299161j)>
729
+ #
730
+ # Hanami::Utils::Kernel.Date(Time.now)
731
+ # # => #<DateTime: 2014-04-18T09:34:49+02:00 ((2456766j,27289s,832907000n),+7200s,2299161j)>
732
+ #
733
+ # Hanami::Utils::Kernel.DateTime('2014-04-18')
734
+ # # => #<DateTime: 2014-04-18T00:00:00+00:00 ((2456766j,0s,0n),+0s,2299161j)>
735
+ #
736
+ # Hanami::Utils::Kernel.DateTime('2014-04-18 09:35:42')
737
+ # # => #<DateTime: 2014-04-18T09:35:42+00:00 ((2456766j,34542s,0n),+0s,2299161j)>
738
+ #
739
+ # @example DateTime Interface
740
+ # require 'hanami/utils/kernel'
741
+ #
742
+ # class NewYearEve
743
+ # def to_datetime
744
+ # DateTime.parse('Jan, 1')
745
+ # end
746
+ # end
747
+ #
748
+ # Hanami::Utils::Kernel.Date(NewYearEve.new)
749
+ # # => #<DateTime: 2014-01-01T00:00:00+00:00 ((2456659j,0s,0n),+0s,2299161j)>
750
+ #
751
+ # @example Unchecked Exceptions
752
+ # require 'hanami/utils/kernel'
753
+ #
754
+ # # When nil
755
+ # input = nil
756
+ # Hanami::Utils::Kernel.DateTime(input) # => TypeError
757
+ #
758
+ # # Missing #respond_to?
759
+ # input = BasicObject.new
760
+ # Hanami::Utils::Kernel.DateTime(input) # => TypeError
761
+ #
762
+ # # Missing #to_s?
763
+ # input = BasicObject.new
764
+ # Hanami::Utils::Kernel.DateTime(input) # => TypeError
765
+ def self.DateTime(arg)
766
+ case arg
767
+ when ->(a) { a.respond_to?(:to_datetime) } then arg.to_datetime
768
+ when Numeric then DateTime(Time.at(arg))
769
+ else
770
+ DateTime.parse(arg.to_s)
771
+ end
772
+ rescue ArgumentError, NoMethodError
773
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into DateTime"
774
+ end
775
+
776
+ # Coerces the argument to be a Time.
777
+ #
778
+ # @param arg [Object] the argument
779
+ #
780
+ # @return [Time] the result of the coercion
781
+ #
782
+ # @raise [TypeError] if the argument can't be coerced
783
+ #
784
+ # @since 0.1.1
785
+ #
786
+ # @example Basic Usage
787
+ # require 'hanami/utils/kernel'
788
+ #
789
+ # Hanami::Utils::Kernel.Time(Time.now)
790
+ # # => 2014-04-18 15:56:39 +0200
791
+ #
792
+ # Hanami::Utils::Kernel.Time(DateTime.now)
793
+ # # => 2014-04-18 15:56:39 +0200
794
+ #
795
+ # Hanami::Utils::Kernel.Time(Date.today)
796
+ # # => 2014-04-18 00:00:00 +0200
797
+ #
798
+ # Hanami::Utils::Kernel.Time('2014-04-18')
799
+ # # => 2014-04-18 00:00:00 +0200
800
+ #
801
+ # Hanami::Utils::Kernel.Time('2014-04-18 15:58:02')
802
+ # # => 2014-04-18 15:58:02 +0200
803
+ #
804
+ # @example Time Interface
805
+ # require 'hanami/utils/kernel'
806
+ #
807
+ # class Epoch
808
+ # def to_time
809
+ # Time.at(0)
810
+ # end
811
+ # end
812
+ #
813
+ # Hanami::Utils::Kernel.Time(Epoch.new)
814
+ # # => 1970-01-01 01:00:00 +0100
815
+ #
816
+ # @example Unchecked Exceptions
817
+ # require 'hanami/utils/kernel'
818
+ #
819
+ # # When nil
820
+ # input = nil
821
+ # Hanami::Utils::Kernel.Time(input) # => TypeError
822
+ #
823
+ # # Missing #respond_to?
824
+ # input = BasicObject.new
825
+ # Hanami::Utils::Kernel.Time(input) # => TypeError
826
+ #
827
+ # # Missing #to_s?
828
+ # input = BasicObject.new
829
+ # Hanami::Utils::Kernel.Time(input) # => TypeError
830
+ def self.Time(arg)
831
+ case arg
832
+ when ->(a) { a.respond_to?(:to_time) } then arg.to_time
833
+ when Numeric then Time.at(arg)
834
+ else
835
+ Time.parse(arg.to_s)
836
+ end
837
+ rescue ArgumentError, NoMethodError
838
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Time"
839
+ end
840
+
841
+ # Coerces the argument to be a Boolean.
842
+ #
843
+ # @param arg [Object] the argument
844
+ #
845
+ # @return [true,false] the result of the coercion
846
+ #
847
+ # @raise [TypeError] if the argument can't be coerced
848
+ #
849
+ # @since 0.1.1
850
+ #
851
+ # @example Basic Usage
852
+ # require 'hanami/utils/kernel'
853
+ #
854
+ # Hanami::Utils::Kernel.Boolean(nil) # => false
855
+ # Hanami::Utils::Kernel.Boolean(0) # => false
856
+ # Hanami::Utils::Kernel.Boolean(1) # => true
857
+ # Hanami::Utils::Kernel.Boolean('0') # => false
858
+ # Hanami::Utils::Kernel.Boolean('1') # => true
859
+ # Hanami::Utils::Kernel.Boolean(Object.new) # => true
860
+ #
861
+ # @example Boolean Interface
862
+ # require 'hanami/utils/kernel'
863
+ #
864
+ # Answer = Struct.new(:answer) do
865
+ # def to_bool
866
+ # case answer
867
+ # when 'yes' then true
868
+ # else false
869
+ # end
870
+ # end
871
+ # end
872
+ #
873
+ # answer = Answer.new('yes')
874
+ # Hanami::Utils::Kernel.Boolean(answer) # => true
875
+ #
876
+ # @example Unchecked Exceptions
877
+ # require 'hanami/utils/kernel'
878
+ #
879
+ # # Missing #respond_to?
880
+ # input = BasicObject.new
881
+ # Hanami::Utils::Kernel.Boolean(input) # => TypeError
882
+ def self.Boolean(arg)
883
+ case arg
884
+ when Numeric then arg > 0 && arg <= 1
885
+ when String, '0' then Boolean(arg.to_i)
886
+ when ->(a) { a.respond_to?(:to_bool) } then arg.to_bool
887
+ else
888
+ !!arg
889
+ end
890
+ rescue NoMethodError
891
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Boolean"
892
+ end
893
+
894
+ # Coerces the argument to be a Pathname.
895
+ #
896
+ # @param arg [#to_pathname,#to_str] the argument
897
+ #
898
+ # @return [Pathname] the result of the coercion
899
+ #
900
+ # @raise [TypeError] if the argument can't be coerced
901
+ #
902
+ # @since 0.1.2
903
+ #
904
+ # @example Basic Usage
905
+ # require 'hanami/utils/kernel'
906
+ #
907
+ # Hanami::Utils::Kernel.Pathname(Pathname.new('/path/to')) # => #<Pathname:/path/to>
908
+ # Hanami::Utils::Kernel.Pathname('/path/to') # => #<Pathname:/path/to>
909
+ #
910
+ # @example Pathname Interface
911
+ # require 'hanami/utils/kernel'
912
+ #
913
+ # class HomePath
914
+ # def to_pathname
915
+ # Pathname.new Dir.home
916
+ # end
917
+ # end
918
+ #
919
+ # Hanami::Utils::Kernel.Pathname(HomePath.new) # => #<Pathname:/Users/luca>
920
+ #
921
+ # @example String Interface
922
+ # require 'hanami/utils/kernel'
923
+ #
924
+ # class RootPath
925
+ # def to_str
926
+ # '/'
927
+ # end
928
+ # end
929
+ #
930
+ # Hanami::Utils::Kernel.Pathname(RootPath.new) # => #<Pathname:/>
931
+ #
932
+ # @example Unchecked Exceptions
933
+ # require 'hanami/utils/kernel'
934
+ #
935
+ # # When nil
936
+ # input = nil
937
+ # Hanami::Utils::Kernel.Pathname(input) # => TypeError
938
+ #
939
+ # # Missing #respond_to?
940
+ # input = BasicObject.new
941
+ # Hanami::Utils::Kernel.Pathname(input) # => TypeError
942
+ def self.Pathname(arg)
943
+ case arg
944
+ when ->(a) { a.respond_to?(:to_pathname) } then arg.to_pathname
945
+ else
946
+ super
947
+ end
948
+ rescue NoMethodError
949
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Pathname"
950
+ end
951
+
952
+ # Coerces the argument to be a Symbol.
953
+ #
954
+ # @param arg [#to_sym] the argument
955
+ #
956
+ # @return [Symbol] the result of the coercion
957
+ #
958
+ # @raise [TypeError] if the argument can't be coerced
959
+ #
960
+ # @since 0.2.0
961
+ #
962
+ # @example Basic Usage
963
+ # require 'hanami/utils/kernel'
964
+ #
965
+ # Hanami::Utils::Kernel.Symbol(:hello) # => :hello
966
+ # Hanami::Utils::Kernel.Symbol('hello') # => :hello
967
+ #
968
+ # @example Symbol Interface
969
+ # require 'hanami/utils/kernel'
970
+ #
971
+ # class StatusSymbol
972
+ # def to_sym
973
+ # :success
974
+ # end
975
+ # end
976
+ #
977
+ # Hanami::Utils::Kernel.Symbol(StatusSymbol.new) # => :success
978
+ #
979
+ # @example Unchecked Exceptions
980
+ # require 'hanami/utils/kernel'
981
+ #
982
+ # # When nil
983
+ # input = nil
984
+ # Hanami::Utils::Kernel.Symbol(input) # => TypeError
985
+ #
986
+ # # When empty string
987
+ # input = ''
988
+ # Hanami::Utils::Kernel.Symbol(input) # => TypeError
989
+ #
990
+ # # Missing #respond_to?
991
+ # input = BasicObject.new
992
+ # Hanami::Utils::Kernel.Symbol(input) # => TypeError
993
+ def self.Symbol(arg)
994
+ case arg
995
+ when '' then raise TypeError.new "can't convert #{inspect_type_error(arg)}into Symbol"
996
+ when ->(a) { a.respond_to?(:to_sym) } then arg.to_sym
997
+ else
998
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Symbol"
999
+ end
1000
+ rescue NoMethodError
1001
+ raise TypeError.new "can't convert #{inspect_type_error(arg)}into Symbol"
1002
+ end
1003
+
1004
+ # Returns the most useful type error possible
1005
+ #
1006
+ # If the object does not respond_to?(:inspect), we return the class, else we
1007
+ # return nil. In all cases, this method is tightly bound to callers, as this
1008
+ # method appends the required space to make the error message look good.
1009
+ #
1010
+ # @since 0.4.3
1011
+ # @api private
1012
+ def self.inspect_type_error(arg)
1013
+ (arg.respond_to?(:inspect) ? arg.inspect : arg.to_s) << " "
1014
+ rescue NoMethodError => _
1015
+ # missing the #respond_to? method, fall back to returning the class' name
1016
+ begin
1017
+ arg.class.name << " instance "
1018
+ rescue NoMethodError
1019
+ # missing the #class method, can't fall back to anything better than nothing
1020
+ # Callers will have to guess from their code
1021
+ nil
1022
+ end
1023
+ end
1024
+
1025
+ class << self
1026
+ private :inspect_type_error
1027
+ end
1028
+ end
1029
+ end
1030
+ end
1031
+