Skip to content

Instantly share code, notes, and snippets.

@markburns
Created January 24, 2021 12:17
Show Gist options
  • Save markburns/b2593bee522e36814ad600c2b0f41589 to your computer and use it in GitHub Desktop.
Save markburns/b2593bee522e36814ad600c2b0f41589 to your computer and use it in GitHub Desktop.

10:00 awesome print

> require 'ap'
> ap 1.methods

9:45 and get this

> ap 1.methods
[
    [  0]                          !()                 Integer (BasicObject)
    [  1]                         !=(arg1)             Integer (BasicObject)
    [  2]                         !~(arg1)             Integer (Kernel)
    [  3]                          %(arg1)             Integer
    [  4]                          &(arg1)             Integer
    [  5]                          *(arg1)             Integer
    [  6]                         **(arg1)             Integer
    [  7]                          +(arg1)             Integer
    [  8]                         +@()                 Integer (Numeric)
    [  9]                          -(arg1)             Integer
    [ 10]                         -@()                 Integer
    [ 11]                          /(arg1)             Integer
    [ 12]                          <(arg1)             Integer
    [ 13]                         <<(arg1)             Integer
    [ 14]                         <=(arg1)             Integer
    [ 15]                        <=>(arg1)             Integer
    [ 16]                         ==(arg1)             Integer
    [ 17]                        ===(arg1)             Integer
    [ 18]                         =~(arg1)             Integer (Kernel)
    [ 19]                          >(arg1)             Integer
    [ 20]                         >=(arg1)             Integer
    [ 21]                         >>(arg1)             Integer
    [ 22]                         [](arg1)             Integer
    [ 23]                          ^(arg1)             Integer
    [ 24]                     __id__()                 Integer (BasicObject)
    [ 25]                   __send__(*arg1)            Integer (BasicObject)
    [ 26]                        abs()                 Integer
    [ 27]                       abs2()                 Integer (Numeric)
    [ 28]                         ai(*options)         Integer (Kernel)
    [ 29]                   allbits?(arg1)             Integer
    [ 30]              amazing_print(object, *options) Integer (Kernel)
    [ 31]                      angle()                 Integer (Numeric)

9:45 and get this

> ap 1.methods
[
    [  0]                          !()                 Integer (BasicObject)
    [  1]                         !=(arg1)             Integer (BasicObject)
    [  2]                         !~(arg1)             Integer (Kernel)
    [  3]                          %(arg1)             Integer
    [  4]                          &(arg1)             Integer
    [  5]                          *(arg1)             Integer
    [  6]                         **(arg1)             Integer
    [  7]                          +(arg1)             Integer
    [  8]                         +@()                 Integer (Numeric)
    [  9]                          -(arg1)             Integer
    [ 10]                         -@()                 Integer
    [ 11]                          /(arg1)             Integer
    [ 12]                          <(arg1)             Integer
    [ 13]                         <<(arg1)             Integer
    [ 14]                         <=(arg1)             Integer
    [ 15]                        <=>(arg1)             Integer
    [ 16]                         ==(arg1)             Integer
    [ 17]                        ===(arg1)             Integer
    [ 18]                         =~(arg1)             Integer (Kernel)
    [ 19]                          >(arg1)             Integer
    [ 20]                         >=(arg1)             Integer
    [ 21]                         >>(arg1)             Integer
    [ 22]                         [](arg1)             Integer
    [ 23]                          ^(arg1)             Integer
    [ 24]                     __id__()                 Integer (BasicObject)
    [ 25]                   __send__(*arg1)            Integer (BasicObject)
    [ 26]                        abs()                 Integer
    [ 27]                       abs2()                 Integer (Numeric)
    [ 28]                         ai(*options)         Integer (Kernel)
    [ 29]                   allbits?(arg1)             Integer
    [ 30]              amazing_print(object, *options) Integer (Kernel)
    [ 31]                      angle()                 Integer (Numeric)
  • arguments
  • where they're defined.

9:30 Vanilla

1.methods
=> [:-@, :**, :<=>, :upto, :<<, :<=, :>=, :==, :chr, :===, :>>, :[], :%, :&, :inspect, :*, :+, :ord, :-, :/, :size, :succ, :<, :>, :to_int, :coerce, :to_s, :to_i, :to_f, :divmod, :to_r, :fdiv, :modulo, :remainder, :abs, :magnitude, :integer?, :floor, :ceil, :round, :truncate, :^, :odd?, :even?, :allbits?, :anybits?, :nobits?, :downto, :times, :pred, :pow, :bit_length, :digits, :numerator, :denominator, :rationalize, :gcd, :lcm, :gcdlcm, :next, :div, :|, :~, :imag, :abs2, :+@, :phase, :to_c, :polar, :angle, :conjugate, :conj, :eql?, :singleton_method_added, :i, :real?, :zero?, :nonzero?, :finite?, :infinite?, :step, :positive?, :negative?, :clone, :dup, :arg, :quo, :rectangular, :rect, :real, :imaginary, :between?, :clamp, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :protected_methods, :instance_variables, :instance_variable_get, :private_methods, :public_methods, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :=~, :!~, :respond_to?, :freeze, :object_id, :send, :display, :nil?, :hash, :class, :singleton_class, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :equal?, :!, :instance_exec, :!=, :instance_eval, :__id__, :__send__]

I'll refer to these as vanilla

9:20 The methods are an array

1.methods.class
=> Array

9:10 an array of what?

1.methods.map(&:class)

9:10 an array of what?

1.methods.map(&:class)
=> [Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol, Symbol]

9:10 an array of what?

1.methods.map(&:class).uniq
=> [Symbol]

9:00 Not Rails magic

1.methods
1.hour.ago

8:45 Everything is an Object

1.methods
"".methods
User.first.methods
User.methods

1.method(:+).methods

8:30 Pretty or Vanilla for duplicate?

 ap 1.methods.dup

8:30 Duplicate?

 ap 1.methods.dup
[
    [  0]                          !()                 Integer (BasicObject)
    [  1]                         !=(arg1)             Integer (BasicObject)
    [  2]                         !~(arg1)             Integer (Kernel)
    [  3]                          %(arg1)             Integer
    [  4]                          &(arg1)             Integer
    [  5]                          *(arg1)             Integer
    [  6]                         **(arg1)             Integer
    [  7]                          +(arg1)             Integer
    [  8]                         +@()                 Integer (Numeric)
    [  9]                          -(arg1)             Integer
    [ 10]                         -@()                 Integer
    [ 11]                          /(arg1)             Integer
    [ 12]                          <(arg1)             Integer
    [ 13]                         <<(arg1)             Integer
    [ 14]                         <=(arg1)             Integer
    [ 15]                        <=>(arg1)             Integer
    [ 16]                         ==(arg1)             Integer
    [ 17]                        ===(arg1)             Integer
    [ 18]                         =~(arg1)             Integer (Kernel)
    [ 19]                          >(arg1)             Integer
    [ 20]                         >=(arg1)             Integer
    [ 21]                         >>(arg1)             Integer
    [ 22]                         [](arg1)             Integer
    [ 23]                          ^(arg1)             Integer
    [ 24]                     __id__()                 Integer (BasicObject)
    [ 25]                   __send__(*arg1)            Integer (BasicObject)
    [ 26]                        abs()                 Integer
    [ 27]                       abs2()                 Integer (Numeric)
    [ 28]                         ai(*options)         Integer (Kernel)
    [ 29]                   allbits?(arg1)             Integer
    [ 30]              amazing_print(object, *options) Integer (Kernel)
    [ 31]                      angle()                 Integer (Numeric)

7:45 Why is this surprising?

7:45 Kernel#ap

7:30 How to get vanilla

Can we get our less surprising vanilla output back?

methods = 1.methods

7:30 How to get vanilla

methods = 1.methods
ap methods
=> [...pretty...]

7:15 duplicating another way

m = 1.methods
ap m.take(m.length)

7:15 Vanilla

m = 1.methods
ap m.take(m.length)
[
    [  0] :-@,
    [  1] :**,
    [  2] :<=>,
    [  3] :upto,
    [  4] :<<,
    [  5] :<=,
    [  6] :>=,
    [  7] :==,
    [  8] :chr,
    [  9] :===,
    ...

Something special about the return value of methods

7:00 Confirm with a copy and paste?

Object.new.methods
=> [:__binding__, :pry, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :to_yaml, :pretty_print_instance_variables, :pretty_print_cycle, :pretty_print_inspect, :pretty_print, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :pretty_inspect, :extend, :awesome_print, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :byebug, :eql?, :respond_to?, :remote_byebug, :debugger, :freeze, :inspect, :object_id, :send, :awesome_inspect, :amazing_print, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
ap [:__binding__, :pry, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :to_yaml, :pretty_print_instance_variables, :pretty_print_cycle, :pretty_print_inspect, :pretty_print, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :pretty_inspect, :extend, :awesome_print, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :byebug, :eql?, :respond_to?, :remote_byebug, :debugger, :freeze, :inspect, :object_id, :send, :awesome_inspect, :amazing_print, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
[
    [  0] :binding,
    [  1] :pry
    [  2] :methods,
    [  3] :singleton_methods,
    [  4] :protected_methods,
    [  5] :public_methods,
    ...

Great! Vanilla

6:45 What is special here

ap Object.new.methods
[pretty]

ap [:__binding__, :pry, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :to_yaml, :pretty_print_instance_variables, :pretty_print_cycle, :pretty_print_inspect, :pretty_print, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :pretty_inspect, :extend, :awesome_print, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :byebug, :eql?, :respond_to?, :remote_byebug, :debugger, :freeze, :inspect, :object_id, :send, :awesome_inspect, :amazing_print, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
[vanilla]

return value from methods gives us pretty output the equivalent plain list of symbols, prints out a list of symbols

6:30 object_id?

> Object.new.methods.map(&:object_id)
=> [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]

> [:__binding__, :pry, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :to_yaml, :pretty_print_instance_variables, :pretty_print_cycle, :pretty_print_inspect, :pretty_print, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :pretty_inspect, :extend, :awesome_print, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :byebug, :eql?, :respond_to?, :remote_byebug, :debugger, :freeze, :inspect, :object_id, :send, :awesome_inspect, :amazing_print, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__].
  map(&:object_id)
=> [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]

6:30 object_ids are identical

> Object.new.methods.map(&:object_id)
=> [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]

> [:__binding__, :pry, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :to_yaml, :pretty_print_instance_variables, :pretty_print_cycle, :pretty_print_inspect, :pretty_print, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :pretty_inspect, :extend, :awesome_print, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :byebug, :eql?, :respond_to?, :remote_byebug, :debugger, :freeze, :inspect, :object_id, :send, :awesome_inspect, :amazing_print, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__].
  map(&:object_id)
=> [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]

> Object.new.methods.map(&:object_id) == [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]
=> true

6:15 equality ?

> Object.new.methods.map(&:object_id) == [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]
> Object.new.methods.map(&:object_id) === [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]
> Object.new.methods.map(&:object_id).eql? [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]

6:15 equality ?

> Object.new.methods.map(&:object_id) == [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]
=> true
> Object.new.methods.map(&:object_id) === [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]
=> true
> Object.new.methods.map(&:object_id).eql? [2078428, 1594268, 79388, 79708, 80028, 80348, 80668, 833308, 1940508, 1186268, 1942108, 824028, 82228, 82268, 82868, 83188, 83508, 83548, 81628, 80988, 81308, 177948, 424348, 424668, 424988, 426908, 1146588, 184988, 1592988, 448348, 448988, 1352668, 2668, 2788, 2828, 2848, 2751388, 3028, 3048, 2751708, 2752028, 53468, 53788, 54428, 59548, 1535388, 1536348, 64348, 321628, 74548, 74588, 74908, 75228, 75548, 75868, 76188, 76508, 76828, 77148, 77748, 77788, 78108, 78708, 78748, 79348, 72308, 668, 2768, 177628, 2808, 177308, 445148, 59868]
=> true

6:00 what on earth is the difference?

the hash?

6:00 what on earth is the difference?

the hash?

> Object.new.methods.hash === [:methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :awesome_inspect, :amazing_print, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__
].hash
Object.new.methods.hash == Object.new.methods.take(Object.new.methods.length).hash

6:00 what on earth is the difference?

the hash?

> Object.new.methods.hash === [:methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :define_singleton_method, :extend, :to_enum, :enum_for, :ai, :<=>, :===, :=~, :!~, :awesome_inspect, :amazing_print, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :to_s, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :==, :instance_exec, :!=, :instance_eval, :__id__, :__send__
].hash
=> true
Object.new.methods.hash == Object.new.methods.take(Object.new.methods.length).hash
=> true

6:00 what on earth is the difference?

  • same object_ids
  • equality comparisons
  • same hash

Now even the returned methods, the pasted array of vanilla symbols seem the same from every angle we've tried to inspect them.

5:45 Thought Experiment

5:45 How would you make this work?

5:45 How would you make this work?

It needs to quack like a Symbol

5:45 How would you make this work?

It needs to quack like a Symbol

  • have the class Symbol

5:45 How would you make this work?

It needs to quack like a Symbol

  • have the class Symbol
  • have the same object_id as the Symbol it represents

5:45 How would you make this work?

It needs to quack like a Symbol

  • have the class Symbol
  • have the same object_id as the Symbol it represents
  • have access to the original method and object

5:30 symbol-like method

class SymbolLikeMethod < SimpleDelegator
  attr_reader :object, :symbol

  def initialize(object, symbol)
    super(symbol)
    @symbol = symbol
    @object = object
  end

  def class
    Symbol
  end

  def object_id
    symbol.object_id
  end

  def pretty_print
    meth = object.method(symbol)

    [meth.name, meth.parameters, meth.owner].join(" ")
  end
end

sym = SymbolLikeMethod.new(1, :to_s)

[sym].hash == [:to_s].hash
=> true
sym.hash == :to_s.hash
=> true
sym.object_id == :to_s.object_id
=> true
sym.class == :to_s.class
=> true
sym.pretty_print
=> ":to_s [[:rest]] Integer"

4:45 what really happens though?

let's look at ap

> require 'pry'
> binding.pry
> show-source ap

def ap(object, options = {})
  puts object.ai(options)
  object unless AwesomePrint.console?
end

4:30 Kernel#ai

let's look at ai then on Kernel

> show-source Kernel

Number of monkeypatches: 9. Use the `-a` option to display all available monkeypatches
Number of lines: 21

module Kernel

  def ai(options = {})
    ap = AwesomePrint::Inspector.new(options)
    awesome = ap.awesome self
    if options[:html]
      awesome = "<pre>#{awesome}</pre>"
      awesome = awesome.html_safe if defined? ActiveSupport
    end
    awesome
  end
  alias :awesome_inspect :ai

  def ap(object, options = {})
    puts object.ai(options)
    object unless AwesomePrint.console?
  end
  alias :awesome_print :ap

  module_function :ap
end

4:30 AwesomePrint::Inspector#awesome

def awesome(object)
  if Thread.current[AP].include?(object.object_id)
    nested(object)
  else
    begin
      Thread.current[AP] << object.object_id
      unnested(object)
    ensure
      Thread.current[AP].pop
    end
  end
end

#nested and #unnested

4:15 AwesomePrint::Inspector#nested and AwesomePrint::Inspector#unnested

def nested(object)
  case printable(object)
  when :array  then @formatter.colorize('[...]', :array)
  when :hash   then @formatter.colorize('{...}', :hash)
  when :struct then @formatter.colorize('{...}', :struct)
  else @formatter.colorize("...#{object.class}...", :class)
  end
end

def unnested(object)
  @formatter.format(object, printable(object))
end

4:00 @formatter

class Inspector
  def initialize(options = {})
    # [... SNIP ...]
    @formatter = AwesomePrint::Formatter.new(self)

we see this is an AwesomePrint::Formatter

3:45 AwesomePrint::Formatter

> show-source AwesomePrint::Formatter

class Formatter
  def format(object, type = nil)
    core_class = cast(object, type)
    awesome = if core_class != :self
      send(:"awesome_#{core_class}", object) # Core formatters.
    else
      awesome_self(object, type) # Catch all that falls back to object.inspect.
    end
    awesome
  end

  CORE = [:array, :bigdecimal, :class, :dir, :file, :hash, :method, :rational, :set, :struct, :unbound_method]

  def cast(object, type)
    CORE.grep(type)[0] || :self
  end

3:30 #format (simplified for our Array case)

  def format(object, type = nil)
    awesome_array(object)
  end

3:15 #awesome_array

  def awesome_array(a)
    Formatters::ArrayFormatter.new(a, @inspector).format
  end

3:00 AwesomePrint::Formatters::ArrayFormatter#format

  def format
    if array.empty?
      '[]'
    elsif methods_array?
      methods_array
    else
      simple_array
    end
  end

  private

  def methods_array?
    array.instance_variable_defined?('@__awesome_methods__')
  end

2:45 bingo

  def methods_array?
    array.instance_variable_defined?('@__awesome_methods__')
  end

now our hunt has to change tactics.

  • pry's show-source
  • bundle info
  • grep/ag

2:30 find @awesome_methods

➜ bundle info awesome_print
  * awesome_print (1.8.0)
        Summary: Pretty print Ruby objects with proper indentation and colors
        Homepage: https://github.com/awesome-print/awesome_print
        Path: /home/mark/.asdf/installs/ruby/2.6.4/lib/ruby/gems/2.6.0/gems/awesome_print-1.8.0
➜ cd /home/mark/.asdf/installs/ruby/2.6.4/lib/ruby/gems/2.6.0/gems/awesome_print-1.8.0
➜ ag @__awesome_methods__

[...SNIP...]

awesome_print/core_ext/object.rb
18:      methods.instance_variable_set(:@__awesome_methods__, self)

2:15 Object core extension

class Object
  %w(methods private_methods protected_methods public_methods singleton_methods).each do |name|
    original_method = instance_method(name)

    define_method name do |*args|
      methods = original_method.bind(self).call(*args)
      methods.instance_variable_set(:@__awesome_methods__, self)
      methods.extend(AwesomeMethodArray)
      methods
    end
  end
end

2:10 Object core extension

1.methods

define_method name do |*args|                                # def methods(*args)
  methods = original_method.bind(self).call(*args)           #   methods = [ :-@, :**, :<=>, :upto, :<<, :<=, :>=, :==, :chr, :===, :>>, :[], :%, :&, :inspect, :*, :+, :ord, :-, :/, :size, :succ, :<, :>, :to_int, :coerce, :to_s, :to_i, :to_f, :divmod, :to_r, :fdiv, :modulo, :remainder, :abs, :magnitude, :integer?, :floor, :ceil, :round, :truncate, :^, :odd?, :even?, :allbits?, :anybits?, :nobits?, :downto, :times, :pred, :pow, :bit_length, :digits, :numerator, :denominator, :rationalize, :gcd, :lcm, :gcdlcm, :next, :div, :|, :~, :imag, :abs2, :+@, :phase, :to_c, :polar, :angle, :conjugate, :conj, :pretty_print, :pretty_print_cycle, :eql?, :singleton_method_added, :i, :real?, :zero?, :nonzero?, :finite?, :infinite?, :step, :positive?, :negative?, :clone, :dup, :arg, :quo, :rectangular, :rect, :real, :imaginary, :between?, :clamp, :__binding__, :singleton_methods, :methods, :pry, :protected_methods, :private_methods, :to_yaml, :public_methods, :pretty_print_inspect, :pretty_print_instance_variables, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :debugger, :define_singleton_method, :byebug, :remote_byebug, :pretty_inspect, :extend, :to_enum, :enum_for, :ai, :=~, :!~, :respond_to?, :awesome_inspect, :awesome_print, :freeze, :object_id, :send, :display, :nil?, :hash, :class, :singleton_class, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
  methods.instance_variable_set(:@__awesome_methods__, self) #   methods.instance_eval { @__awesome_methods__ = 1 }
  methods.extend(AwesomeMethodArray)                         #   methods.extend(AwesomeMethodArray)
  methods                                                    #   methods
end                                                          # end

1:45 (Over) Simplified version

1.methods

def methods(*args)
  methods = [ :-@, :**, :<=>, :upto, :<<, :<=, :>=, :==, :chr, :===, :>>, :[], :%, :&, :inspect, :*, :+, :ord, :-, :/, :size, :succ, :<, :>, :to_int, :coerce, :to_s, :to_i, :to_f, :divmod, :to_r, :fdiv, :modulo, :remainder, :abs, :magnitude, :integer?, :floor, :ceil, :round, :truncate, :^, :odd?, :even?, :allbits?, :anybits?, :nobits?, :downto, :times, :pred, :pow, :bit_length, :digits, :numerator, :denominator, :rationalize, :gcd, :lcm, :gcdlcm, :next, :div, :|, :~, :imag, :abs2, :+@, :phase, :to_c, :polar, :angle, :conjugate, :conj, :pretty_print, :pretty_print_cycle, :eql?, :singleton_method_added, :i, :real?, :zero?, :nonzero?, :finite?, :infinite?, :step, :positive?, :negative?, :clone, :dup, :arg, :quo, :rectangular, :rect, :real, :imaginary, :between?, :clamp, :__binding__, :singleton_methods, :methods, :pry, :protected_methods, :private_methods, :to_yaml, :public_methods, :pretty_print_inspect, :pretty_print_instance_variables, :instance_variable_defined?, :remove_instance_variable, :instance_of?, :kind_of?, :is_a?, :tap, :instance_variable_set, :instance_variables, :instance_variable_get, :public_send, :method, :public_method, :singleton_method, :debugger, :define_singleton_method, :byebug, :remote_byebug, :pretty_inspect, :extend, :to_enum, :enum_for, :ai, :=~, :!~, :respond_to?, :awesome_inspect, :awesome_print, :freeze, :object_id, :send, :display, :nil?, :hash, :class, :singleton_class, :itself, :yield_self, :then, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :equal?, :!, :instance_exec, :!=, :instance_eval, :__id__, :__send__]
  methods.instance_variable_set(:@__awesome_methods__, 1)
  methods.extend(AwesomeMethodArray)
  methods
end

Later on when awesome_print uses this return value, it now has access to the specific instance that has been set. Via @awesome_methods. In our case 1. And it can use that instance variable to do all its clever formatting stuff.

1:30 The final piece of the puzzle

back to AwesomePrint::Formatters::ArrayFormatter#format

  def format
    if array.empty?
      '[]'
    elsif methods_array?
      methods_array
    else
      simple_array
    end
  end

1:00 ArrayFormatter#methods_array

> show-source AwesomePrint::Formatters::ArrayFormatter#methods_array

def methods_array
  array.map!(&:to_s).sort!

  data = generate_printable_tuples.join("\n")

  "[\n#{data}\n#{outdent}]"
end

def generate_printable_tuples
  tuples.map.with_index do |item, index|
    tuple_prefix(index, width(tuples)).tap do |temp|
      indented { temp << tuple_template(item) }
    end
  end
end

def tuples
  @tuples ||= array.map { |name| generate_tuple(name) }
end

def generate_tuple(name)
  meth = case name
         when Symbol, String
           find_method(name)
         end

  meth ? method_tuple(meth) : [name.to_s, '(?)', '?']
end

0:30

def find_method(name)
  object = array.instance_variable_get('@__awesome_methods__')

  meth = begin
    object.method(name)
  rescue NameError, ArgumentError
    nil
  end

  meth || begin
    object.instance_method(name)
  rescue NameError
    nil
  end
end

def method_tuple(method)
  if method.respond_to?(:parameters)
    args = method.parameters.inject([]) do |arr, (type, name)|
      name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
      arr << case type
        when :req        then name.to_s
        when :opt, :rest then "*#{name}"
        when :block      then "&#{name}"
        else '?'
      end
    end
  else
    args = (1..method.arity.abs).map { |i| "arg#{i}" }
    args[-1] = "*#{args[-1]}" if method.arity < 0
  end

  if method.to_s =~ /(Unbound)*Method: (.*)[#\.]/
    unbound = $1 && '(unbound)'
    klass = $2
    if klass && klass =~ /(\(\w+:\s.*?\))/  # Is this ActiveRecord-style class?
      klass.sub!($1, '')                    # Yes, strip the fields leaving class name only.
    end
    owner = "#{klass}#{unbound}".gsub('(', ' (')
  end

  [method.name.to_s, "(#{args.join(', ')})", owner.to_s]
end

0:15 no more ruby magic

You can find the answer to nearly any ruby mystery just using the following:

  • byebug
  • binding.pry
  • show-source
  • bundle info
  • ag or grep
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment