Last active
August 22, 2019 19:56
-
-
Save ixti/b035c9b1988be13de34dda8f677df167 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def print_result(desc) | |
result = yield | |
puts "#{desc} class=#{result.class} inspect=#{result.inspect}" | |
rescue => e | |
warn "#{desc} fail: #{e}" | |
end | |
def test(obj) | |
puts "\n== #{obj.inspect} == " | |
print_result("[*obj] =>") { [*obj] } | |
print_result("Array(obj) =>") { Array(obj) } | |
print_result("Array.wrap(obj) =>") { Array.wrap(obj) } | |
end | |
# trivial cases work the same: | |
[nil, true, false, 161, [1, 3, 1, 2], { :foo => :bar }].each(&method(:test)) | |
# things become different with objects that implement implicit (to_ary) and/or | |
# explicit (to_a) coercions: | |
test(1..3) | |
# now, let's go crazy: | |
class Dummy | |
def initialize(to_a:, to_ary:) | |
@to_a = to_a | |
@to_ary = to_ary | |
define_singleton_method(:to_a) { @to_a } if @to_a | |
define_singleton_method(:to_ary) { @to_ary } if @to_ary | |
end | |
class << self | |
alias [] new | |
end | |
end | |
test(Dummy[:to_a => [1, 6, 1], :to_ary => [1, 3, 1, 2]]) | |
test(Dummy[:to_a => "AFA", :to_ary => [1, 3, 1, 2]]) | |
test(Dummy[:to_a => [1, 6, 1], :to_ary => "ACAB"]) | |
test(Dummy[:to_a => [1, 6, 1], :to_ary => nil]) | |
test(Dummy[:to_a => nil, :to_ary => [1, 6, 1]]) | |
test(Dummy[:to_a => "AFA", :to_ary => nil]) | |
test(Dummy[:to_a => nil, :to_ary => "AFA"]) | |
test(Dummy[:to_a => nil, :to_ary => nil]) |
Update 1: How [*obj]
behaves:
- it always ignores
#to_ary
implementation - returns Array with given object as its only element if object does not responds to
#to_a
- calls
#to_a
if object responds to that- raises
TypeError
if returned value isn't an Array
- raises
Seems like Array.wrap
is the most obvious implementation.
To make it easier to think of those methods, here's brief outline of behaviours:
Array(object)
- Return
object
as is if it's anArray
- Otherwise coerce object implicitly (if
object
responds to#to_ary
)- Raise
TypeError
if coercion returned in non-Array
- Raise
- Otherwise coerce object explicitly (if
object
responds to#to_a
)- Raise
TypeError
if coercion returned non-Array
- Raise
- Otherwise wrap
object
into anArray
(as in:[object]
)
Array.wrap(object)
- Return
object
as is if it's anArray
- Otherwise coerce object implicitly (if
object
responds to#to_ary
)- Return result of coercion as is if it's truthy (anything but
nil
orfalse
)
- Return result of coercion as is if it's truthy (anything but
- Otherwise wrap
object
into anArray
(as in:[object]
)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Overview
If object does not implement neither implicit coercion (
#to_ary
) nor explicit coercion (#to_a
):Both
Array.wrap
andArray
will return a new Array with given object as its element:If object implements
#to_a
and not#to_ary
:Array.wrap
will wrap it into a new array:Array
will coerce it with#to_a
but if
#to_a
returns not an Array, an exception will be raised:If object implements
#to_ary
:Array
will use it:but if
#to_ary
returns not an Array, an exception will be raised:Array.wrap
will use it, even if result will be not an Array:If both
#to_ary
and#to_a
are implemented:Array
will use#to_ary
and will not fallback to#to_a
in any matter