Sunday, March 20, 2022

Comments off due to spam

Due to a spate of spam comments, commenting has been turned off for this blog. Eventually, all posts will be moved to a new home at https://www.codosaur.us/blog anyway, and this Blogger account will be turned off.

Tuesday, December 6, 2016

And this is crazy . . . .

   Just a bit of Ruby silliness to brighten your day.  You can save this to a file (like, oh, say, carly.rb), and run it a bunch of times, or you can just paste it into irb and run just the last line a bunch of times.
me = ->{42}
class Proc
  def maybe
    rand(2)==0 ? self : ->{}
  end
end
def call(it)
  it.call
end
puts call me.maybe

Thursday, November 10, 2016

x == x = WTF?!

   A short time ago in a project close by, someone (not me, but ironically enough someone who has been hacking Ruby about twice as long as me) had committed code similar to:
if status == status = 1 || status = 2 || status = 3
   That probably makes most of you go WTF, and you probably figure the author meant "if status == 1 || status == 2 || status == 3" but somehow screwed up to the point of insanity, and rightly so.   But bear with me, it's a fun little exploration of some bizarre Ruby usage.

   First, since "status = 1" would always return a truthy value, the rest of it would get short-circuited.  So, we can omit the rest from further investigation, and just use "status == status = 1".

  But more importantly, this got me thinking, what would that even DO?  The rest of this post is a quote from the analysis I wrote up just because I thought he would find it amusing.  If anybody is familiar with how MRI works under the hood, please let me know if I'm guessing right!

===8<---cut here---

This LOOKS like it might mean "assign status, and check if it still has the value we just assigned to it", but after some experimentation in irb, it's really "assign status, and check if that matches its previous value".  Enlightening experimental irb session:

$ irb
:001 > x == x = 1
NameError: undefined local variable or method `x' for main:Object
Did you mean?  x
        from (irb):1 [...]
:002 > x
 => nil
:003 > x == x = 1
 => false
:004 > x
 => 1
:005 > x == x = 1
 => true
:006 >
At line 001, we get a NameError because we haven't told it about x.   At 002, x now exists (but is nil), because referencing it brought it to Ruby's attention.  At 003, there is no NameError, so it can get to the assignment, but the old value was nil, so the comparison is effectively "nil == 1".  At 004, we can see that it did indeed get assigned 1.  At 005, the values finally match.

Apparently the == receives the original value of status, as though the order of ops under the hood is "push status, assign status, push status, call ==".  With most Ruby objects, this would always result in true... but Fixnums are odd ducks, in that they are all unique values.  1 is 1 (and all alone) and ever more shall be so.  ;-)  Any Ruby object that is a Fixnum with the value of 1 is the same object and will never have another value.  If you reassign the variable, you're setting the variable itself to refer to a different object.  In C terms, you're changing the pointer, not some field in what it points to.   So, the odd-numbered lines above were equivalent to "push something that doesn't exist so barf with a NameError", "push nil, assign x 1, push 1, call ==", and "push 1, assign x 1, push 1, call ==".   If we were to now do "x == x = 2", it would be "push 1, assign x 2, push 2, call ==", so that the comparison would fail but afterwards we would find that x is indeed 2 -- and doing that same Ruby command again would succeed the second time, as it would then start with "push 2".  Mind you though, that's in MRI; I have no idea if it's the same in mruby.

Saturday, March 14, 2015

TMI from RSpec

   When using RSpec, there is a small annoyance waiting to getcha, when you compare numbers.

   TL;DR: using "be" (or "equal", which does exactly the same thing) produces distracting and usually meaningless extra verbiage, while "eq" (or "eql", which is a little different but close enough for this purpose) does not.

   Frex, if you are sending an HTTP request and expecting to get back the OK status code, you might say:

    expect(response.status).to be 200
   This will work fine, and so long as the status actually is 200, then all is right with the world.

   The minor, trivial, nitpicky, but still a bit annoying, problem, comes when the status is not 200.  Sure, you'll still get a test failure... but you'll get some added little distractions, the bold parts here:

    Failure/Error: expect(response.status).to be 200

       expected #<Fixnum:401> => 200
            got #<Fixnum:1001> => 500

       Compared using equal?, which compares object identity,
       but expected and actual are not the same object. Use
       `expect(actual).to eq(expected)` if you don't care about
       object identity in this example.
   All you really needed is this:

       expected: 200
            got: 500

       (compared using ==)
   (Okay, granted, you didn't really need to know it was compared using "==", but still, that's not too bad... at least it's a lot less distracting verbiage to wade through than the prior message.)

   (And okay, granted, four short lines of additional feedback aren't that much of a pain... but still, they add up.  If you're new on a project with lots of failing tests, as I am now, it can make it harder to wade through all the failures and see which ones actually mean anything.)

   So how do you get rid of it?  You might try using "==" instead of "be", especially if you're used to RSpec's older "should" syntax rather than the new "expect" syntax (which I'll ass-u-me you are now using)... but then you'd see:

     Failure/Error: expect(response.status).to == 200
     ArgumentError:
       The expect syntax does not support operator matchers, so you must pass a matcher to `#to`.
   But despair not, dear reader, all is not lost!  It's still very simple.  Instead of "be", use "eq".  Then you get:

     Failure/Error: expect(response.status).to eq 200

       expected: 200
            got: 500

       (compared using ==)
   As you may recall from my Ruby Gotchas presentation, there are several ways to compare things in Ruby:

  • "==" compares the value, without regard for the actual class(es) of the objects.  So, frex, "1.0 == 1" is true.  This is what RSpec uses when you say "eq" (or "==" in the older syntax); hence the message "(compared using ==)".

  • ".eql?" compares the value and class!  So, "1.0.eql? 1" is false!

  • ".equal?" is object identity.  That is, even if two things have the same value, and the same class, if they're not the exact same object, they are not ".equal?".  One of the oddities of the Ruby object system is that an object is created for each new unique Float value, so the result of:

        x = 1.0
        y = 1.0
        x.equal? y
    is indeed true!  However, if we use a more complex class, like String, we can see that "'foo'.equal? 'foo'" is false.
   You may now be wondering, what if you try "expect(response.status).to eql 200"?  Pretty much the same thing, just now it says "(compared using eql?)".

   But... if you take it one step further, and use "expect(response.status).to equal 200"... then you're back to Square One.  That shouldn't surprise any of you who paid close attention to the original verbose error message: it told you that when we used "be", it "Compared using equal?, which compares object identity".

Saturday, February 21, 2015

Case Study: TDD Is Worth It!

   One of my most popular blog posts, counting its reading elsewhere, is Is TDD Worth It?  Recently I had a great example of how, to quote myself, "It's almost like giving you guard rails."

   Suppose the application domain is as follows:
  • Things have Parts.
  • Parts have Types, which have Categories.
  • Within a given Thing, Parts are used with various specific Connectors.
   I had been asked to develop functions to find the Things that use a given Connector, and:
  • Parts of a given Type or Category.
  • Parts of any Type or Category out of a set of Types or Categories.
  • Parts of all the Types or Categories out of a set of them.
  • The negatives of all of the above, i.e., those using:
    • no Parts of that Type or Category,
    • no Parts of any Type or Category of a set,
    • and those not using Parts of all (though they may use some) Types or Categories of a given set.
   The problem is, I had been given these requirements one at a time, so I thought the client wanted a bunch of methods on Thing with names like:
  • find_with_connector_and_part_type(conn_type, part_type)
  • find_with_connector_and_any_part_types(conn_type, part_types)
  • find_with_connector_and_all_part_types(conn_type, part_types)
  • find_with_connector_and_part_category(conn_type, part_category)
  • find_with_connector_and_any_part_categories(conn_type, part_categories)
  • find_with_connector_and_all_part_categories(conn_type, part_categories)
  • find_without_connector_and_part_type(conn_type, part_type)
  • find_without_connector_and_any_part_types(conn_type, part_types)
  • find_without_connector_and_all_part_types(conn_type, part_types)
  • find_without_connector_and_part_category(conn_type, part_category)
  • find_without_connector_and_any_part_categories(conn_type, part_categories)
  • find_without_connector_and_all_part_categories(conn_type, part_categories)
   I kept the code as DRY as I could, making the negatives simply call the positives and invert the finding, and making the singles pass a single-element set to the "any" case. That still left a lot of structural duplication.  Since they were class-methods used as scopes, they all looked like this:
def self.find_with_connector_and_part_SOMETHING(conn_type,
                                                SOMETHINGS)
  id_query = assorted_magic_happens_here(several_lines)
  where(id: id_query)
  # negative version: where("id NOT IN (?)", id_query)
end
   That got me thinking about how to combine this set of literally a dozen different functions into one, something like Thing.find_with_parts(part_options), where part_options would include what Connector, what Types (and whether we want all or just any, which would be the default), what Categories (ditto), and whether to invert the finding.  When the client later said they didn't in fact want a bunch of separate methods, I was ready, and had a lot of the idea already thought out.

   But... how do I get from Point A to Point B?  This is where TDD came in handy!

   Of course, I had been TDD'ing the original functions, using the classic "red, green, refactor" cycle.  (Actually, I use a variant that adds a step: "refactor the tests".)  So, I substituted a simple call to my proposed new function, for the guts of one of the old ones:
def self.find_with_connector_and_part_type(conn_type,
                                           part_type)
  self.find_with_parts(connector_type: conn_type,
                       part_types: [part_type])
end
and reran its tests.  Of course it failed, as I hadn't written find_with_parts yet... but that came pretty easily, based on the logic that had previously found Things having Parts of any of several Types, used with the given Connector.  That test quickly passed.

   Long story short, I followed this pattern, over and over:
  1. Substitute a call to find_with_parts for the guts of a specific method.
  2. Run its test.
  3. If it works, break it by changing find_with_parts!  You always want to start with a failing test!
  4. Fix find_with_parts to make the test pass.
  5. Run the rest of the tests!
  6. If any of them fail, go back to fixing find_with_parts.
When all the old methods had had their guts substituted, I knew the new method could do it all, so I ripped the old methods out.

   Had it not been for having an existing test suite, which I had because I had TDD'ed the original code, I would have had to be a lot more slow, careful, and methodical in that process.  Instead, I could just quickly try what came to mind, and see if it worked without breaking anything else.

   The resulting function, including some extracted functions to keep that code dry, runs to a mere 43 lines of code, and is structured in such a way as to make it very easy to add additional items to filter on, such as the Material a Part Type is made of, the color a given Part is painted when used in that Thing, etc.

   (Yes, yes, I could have created the test suite just before embarking on this... but seriously, what are the chances?  Most developers would not bother, for something smallish like this.  Perhaps for a larger code archaeology expedition, where writing a test suite to ensure continuing the current behavior, whether correct or not, is a common first step.)

Tuesday, December 2, 2014

HookLyingSyncer: gem to keep method_missing and respond_to_missing? in sync

   A while back, I wrote about the need to keep method_missing and respond_to_missing? in sync.

   (A brief refresher: in 2011, Avdi Grimm wrote a blog post about that.  In the comments, I wrote up a quick and dirty hack to do so, in very restricted cases, and then a still-dirty improvement, that unfortunately has since been mangled somehow.)

   At RubyConf 2014, Betsy Haibel spoke on Ruby metaprogramming, including that same need.  That inspired me to work on the concept again, taking a different approach (essentially a decorator) that I had briefly considered in those comments.

   The result is my new gem HookLyingSyncer.  (I was going to call it FirstResponder, but that name was already taken.)  The code is at https://github.com/davearonson/hook_lying_syncer.  For now, the code looks like:
class HookLyingSyncer

  def initialize(object, matcher, &block)
    @object = object
    @matcher = matcher
    @block = block
  end

  private

  def respond_to_missing?(sym, include_all=false)
    matches = find_matches(sym)
    matches.any? ? true : @object.send(:respond_to?, sym, include_all)
  end

  def method_missing(sym, *args, &blk)
    matches = find_matches(sym)
    if matches.any?
      @block.call(@object, matches, *args)
    else
      @object.send(sym, *args, &blk)
    end
  end

  def find_matches(sym)
    result = @matcher.call(sym)
    result ? result : []
  end

end
   The tests contain some examples, with further usage explanation in the README.  Long story short, it can be used on instances and classes, to add or override method definitions, including overriding new so as to add methods to all new instances of a class.

   This is my first time actually making a gem, and I haven't done much with metaprogramming before, especially something that other people are going to use to do their metaprogramming.  So, any feedback would be greatly appreciated!

Thursday, November 6, 2014

Windows and Linux and Mac, Oh My!

   Someone recently asked in the Google Plus Ruby on Rails community: Which platform would be the best to use for Rails?  My answer got so long, and is so applicable to working in most other languages, that I decided to turn it into a blog post, so here it is, with a few minor edits.

===8<--- cut here ---

   Basically, any reasonably popular platform EXCEPT Windows.

   Windows (ignoring the Server variants) is made for the desktops of non-technical people (and people developing software specifically for Windows).  It comes with essentially zero development tools, and you have to pay for most of the serious ones, especially for the MS stack.  You can kinda-sorta fake a lot of Unix by installing Cygwin, but that's just a kluge and falls way short.

   Linux and other Unix variants such as BSD, Solaris, etc. are made for servers, and the desktops of VERY technical people.  They come with (optional on installation) lots of serious development tools, with many more easily available, most of them free.  Of these OSes, Solaris is pretty much dead, and BSD is very rare outside the server room (so there's nowhere near as many resources for help and education), and the others except Linux have very tiny market share, so let's focus on Linux.  However, Linux  has a bad reputation for requiring a lot of tweaking to get it to work reasonably well, especially if you're using hardware that is in any way not absolutely standard, such as a graphics card from within the past year or a maker that's not one of the top three.  This was reality, and why I switched to a Mac, in 2004, but I've heard Linux has gotten a LOT better about this since then, especially the Ubuntu "distro" ("what's a distro" is a whole 'nother question!), which (I've heard) places great emphasis on working right out of the box.  Linux is also free (though you can buy boxed sets with docs, support, and so on), and generally efficient enough to make good use of older PCs that won't run well under recent versions of Windows.

   A Mac is a reasonable compromise, at least as of when OSX first came out.  (Before then, it was aimed mainly at graphics people, like artists and people who put together print newsletters and magazines.)  The tooling situation is similar to Unix, except that it doesn't come with quite so many, and usually older versions.  It also doesn't require anywhere near as much tweaking as Linux does, because you're running it on exactly the hardware it was designed for.  It's even more consistent in its UI behavior and look-and-feel than Windows, and about as easy to understand -- but it's different, so you'll have a lot to "unlearn" if the Windows way of doing things is deeply ingrained in your habits.  It also used to be much more stable and secure than Windows, but MS has made great strides in their security and stability, catching up and, depending how you measure things, possibly surpassing OSX's security (not sure about stability).  On the other claw, it's a good bit more expensive than a bog-standard Windows box, never mind Linux, but frankly, they're so good that putting together a Windows PC with the same performance will usually cost about just as much, even before factoring in the cost of serious dev tools, OS upgrades (usually dirt-cheap or even free on a Mac), etc.  You can get some good bargains on a used Mac, one or two generations old; ask a Mac-using friend to sell (or even give, if you're lucky!) you one of his old castoffs, or take your chances on eBay.