Ruby Keyword Argument Implementation Differences Between 2.1 and 2.2

I'm currently reading through Ruby Under a Microscope: An Illustrated Guide to Ruby Internals, and when I tried to test the "hidden hash" in ruby 2.2, I got different results from the book.

In Ruby 2.2, it appears that the default arguments are used as one expects, without regard to the Hash monkey patch:

### CODE ###
class Hash
  def key?(val)
    false
  end
end
 
def blah(a: 2, b: 5)
  puts a + b
end
blah
blah(a:3)
blah(b:3)
blah(a:2000, b:2000)
 
### DISASSEMBLY ###
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putspecialobject 3
0004 putnil
0005 defineclass      :Hash, <class:Hash>, 0
0009 pop
0010 trace            1                                               (   7)
0012 putspecialobject 1
0014 putspecialobject 2
0016 putobject        :blah
0018 putiseq          blah
0020 opt_send_without_block <callinfo!mid:core#define_method, argc:3, ARGS_SIMPLE>
0022 pop
0023 trace            1                                               (  10)
0025 putself
0026 opt_send_without_block <callinfo!mid:blah, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0028 pop
0029 trace            1                                               (  11)
0031 putself
0032 putobject        3
0034 opt_send_without_block <callinfo!mid:blah, argc:1, kw:1, FCALL>
0036 pop
0037 trace            1                                               (  12)
0039 putself
0040 putobject        3
0042 opt_send_without_block <callinfo!mid:blah, argc:1, kw:1, FCALL>
0044 pop
0045 trace            1                                               (  13)
0047 putself
0048 putobject        2000
0050 putobject        2000
0052 opt_send_without_block <callinfo!mid:blah, argc:2, kw:2, FCALL>
0054 leave
== disasm: <RubyVM::InstructionSequence:<class:Hash>@<compiled>>========
0000 trace            2                                               (   1)
0002 trace            1                                               (   2)
0004 putspecialobject 1
0006 putspecialobject 2
0008 putobject        :key?
0010 putiseq          key?
0012 opt_send_without_block <callinfo!mid:core#define_method, argc:3, ARGS_SIMPLE>
0014 trace            4                                               (   5)
0016 leave                                                            (   2)
== disasm: <RubyVM::InstructionSequence:key?@<compiled>>================
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] val<Arg>
0000 trace            8                                               (   2)
0002 trace            1                                               (   3)
0004 putobject        false
0006 trace            16                                              (   4)
0008 leave                                                            (   3)
== disasm: <RubyVM::InstructionSequence:blah@<compiled>>================
local table (size: 4, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: 2@0, kwrest: -1])
[ 4] a          [ 3] b          [ 2] ?
0000 trace            8                                               (   7)
0002 trace            1                                               (   8)
0004 putself
0005 getlocal_OP__WC__0 4
0007 getlocal_OP__WC__0 3
0009 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>
0011 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0013 trace            16                                              (   9)
0015 leave                                                            (   8)
 
### RESULTS ###
7
8
5
4000

If I reset back to ruby 2.1, I get the unconditional usage of the default arguments:

### CODE ###
class Hash
  def key?(val)
    false
  end
end
 
def blah(a: 2, b: 5)
  puts a + b
end
blah
blah(a:3)
blah(b:3)
blah(a:2000, b:2000)
 
### DISASSEMBLY ###
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
0000 trace            1                                               (   1)
0002 putspecialobject 3
0004 putnil
0005 defineclass      :Hash, <class:Hash>, 0
0009 pop
0010 trace            1                                               (   7)
0012 putspecialobject 1
0014 putspecialobject 2
0016 putobject        :blah
0018 putiseq          blah
0020 opt_send_simple  <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0022 pop
0023 trace            1                                               (  10)
0025 putself
0026 opt_send_simple  <callinfo!mid:blah, argc:0, FCALL|VCALL|ARGS_SKIP>
0028 pop
0029 trace            1                                               (  11)
0031 putself
0032 putspecialobject 1
0034 putobject        [:a, 3]
0036 opt_send_simple  <callinfo!mid:core#hash_from_ary, argc:1, ARGS_SKIP>
0038 opt_send_simple  <callinfo!mid:blah, argc:1, FCALL|ARGS_SKIP>
0040 pop
0041 trace            1                                               (  12)
0043 putself
0044 putspecialobject 1
0046 putobject        [:b, 3]
0048 opt_send_simple  <callinfo!mid:core#hash_from_ary, argc:1, ARGS_SKIP>
0050 opt_send_simple  <callinfo!mid:blah, argc:1, FCALL|ARGS_SKIP>
0052 pop
0053 trace            1                                               (  13)
0055 putself
0056 putspecialobject 1
0058 putobject        [:a, 2000, :b, 2000]
0060 opt_send_simple  <callinfo!mid:core#hash_from_ary, argc:1, ARGS_SKIP>
0062 opt_send_simple  <callinfo!mid:blah, argc:1, FCALL|ARGS_SKIP>
0064 leave
== disasm: <RubyVM::InstructionSequence:<class:Hash>@<compiled>>========
0000 trace            2                                               (   1)
0002 trace            1                                               (   2)
0004 putspecialobject 1
0006 putspecialobject 2
0008 putobject        :key?
0010 putiseq          key?
0012 opt_send_simple  <callinfo!mid:core#define_method, argc:3, ARGS_SKIP>
0014 trace            4                                               (   5)
0016 leave                                                            (   2)
== disasm: <RubyVM::InstructionSequence:key?@<compiled>>================
local table (size: 2, argc: 1 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] val<Arg>
0000 trace            8                                               (   2)
0002 trace            1                                               (   3)
0004 putobject        false
0006 trace            16                                              (   4)
0008 leave                                                            (   3)
== disasm: <RubyVM::InstructionSequence:blah@<compiled>>================
local table (size: 4, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 2@2] s0)
[ 4] a          [ 3] b          [ 2] ?
0000 getlocal_OP__WC__0 2                                             (   7)
0002 dup
0003 putobject        :a
0005 opt_send_simple  <callinfo!mid:key?, argc:1, ARGS_SKIP>
0007 branchunless     18
0009 dup
0010 putobject        :a
0012 opt_send_simple  <callinfo!mid:delete, argc:1, ARGS_SKIP>
0014 setlocal_OP__WC__0 4
0016 jump             22
0018 putobject        2
0020 setlocal_OP__WC__0 4
0022 dup
0023 putobject        :b
0025 opt_send_simple  <callinfo!mid:key?, argc:1, ARGS_SKIP>
0027 branchunless     38
0029 dup
0030 putobject        :b
0032 opt_send_simple  <callinfo!mid:delete, argc:1, ARGS_SKIP>
0034 setlocal_OP__WC__0 3
0036 jump             42
0038 putobject        5
0040 setlocal_OP__WC__0 3
0042 pop
0043 trace            8
0045 trace            1                                               (   8)
0047 putself
0048 getlocal_OP__WC__0 4
0050 getlocal_OP__WC__0 3
0052 opt_plus         <callinfo!mid:+, argc:1, ARGS_SKIP>
0054 opt_send_simple  <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0056 trace            16                                              (   9)
0058 leave                                                            (   8)
 
### RESULTS ###
7
7
7
7

Rails AntiPatterns Review/Summary

I've had the Rails AntiPatterns: Best Practice Ruby on Rails Refactoring (Addison-Wesley Professional Ruby Series) book on my Safari Books Online bookshelf for almost a year now, and finally started reading through it. It was "updated for Rails 3", and there doesn't appear to be a newer version, so I was a bit skeptical about how much I'd get out of the book.

This book has some overlap with Ruby Science, but it is more Rails-centric in its approach. Going through the book I found quite a few topics that I've not really read in-depth on in any other book or come across in a couple years of Rails development experience.

  • Use of ActiveSupport::Concern included for specifying validations as part of a module.
  • Discussion of Rails plugins (which later were mentioned as falling out of favor)
  • serialize :value, Hash vs checkbox/boolean model
  • How Rails helps in the building of Semantic HTML
  • Gems Clearance and AuthLogic as simple authentication gems.
  • MVP pattern and ActivePresenter (looks like Rails 3 was the end?)
  • Emphasis on controller as a representation of a resource
  • Rails responders for changing respond_to to respond_with(object)
  • rescue *EXPECTED_EXCEPTIONS (EXPECTED_EXPECTIONS as a constant array) to whitelist exceptions that are anticipated.
  • Mechanize and RestClient as automated interface gems with other sites.
  • render :status => :unprocessable_entity (or :created and other symbols that represent HTTP status codes)
  • Strategies for testing rake tasks, including FileUtils::NoWrite
  • sqlite3 ":memory:" database for gem testing
  • Thoughts on when to add DB indexes
  • AR association methods count vs. length vs. size and their behavior and resource usage.
  • .flatten() as a code smell for ruby code that should be represented as SQL.
  • Run rake db:migrate && rake db:migrate:redo for all migrations created.
  • Don't go against AR bias against DB constraints except for NULL & default values.
  • Never fail quietly, whether ignoring return values or overzealously catching all exceptions.
  • rescue_from on controller to redirect specific exceptions.

I think the general concepts covered provided valuable background and food for thought even if not all the solutions are still the same. I don't regret the help examining current practices in my daily Rails development.

Introducing Elixir

I read through Introducing Elixir on Safari Books Online because I had read through a beta edition of Programming Elixir: Functional |> Concurrent |> Pragmatic |> Fun through The Pragmatic Bookshelf and wanted to see about getting a slightly different take on Elixir at the same time I was getting caught up with the current state of Elixir.

The Introducing Elixir book read to me like a quick tour of Elixir, despite being about 80% of the length of Programming Elixir. That's probably because I took some time to work through the examples in Programming Elixir, back when Elixir was on its 0.12 release. Programming Elixir is set up to be much more of a textbook in its presentation, with programming problems posed, etc. Introducing Elixir hits upon functional programming, the underlying Erlang and OTP libraries, and throws a few ideas of what you can do with it. It's a decent refresher to shake the cobwebs out, but it doesn't as much in depth probing into the language as the Programming Elixir book does. I'd recommend the Programming Elixir book from pragprog.com first.

(Amazon affiliate link)