hanami-utils 0.0.0 → 0.7.0

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