"Warning: colon will be obsoleted; use semicolon"

23 Aug 2006

If you’re getting the title of this post as a strange error showing up in your rails app you’re not alone. If you happen to see this you’re probably running a new version of Ruby (1.8.5).

The source behind this error is in the syntax for Ruby’s control structures (particularly using case and if. Here’s an example of some code from the Rails trunk that follows the currently popular style:

    # active_record/base.rb:

    def convert_number_column_value(value)
      case value
        when FalseClass: 0
        when TrueClass:  1
        when '':         nil
        else value
      end
    end

    # action_view/base.rb:

    def find_template_extension_for(template_path)
      if match = delegate_template_exists?(template_path)
        match.first.to_sym
      elsif erb_template_exists?(template_path):        :rhtml
      elsif builder_template_exists?(template_path):    :rxml
      elsif javascript_template_exists?(template_path): :rjs
      else
        raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}"
      end
    end

If you’re getting the title of this post as a strange error showing up in your rails app you’re not alone. If you happen to see this you’re probably running a new version of Ruby (1.8.5).

The source behind this error is in the syntax for Ruby’s control structures (particularly using case and if. Here’s an example of some code from the Rails trunk that follows the currently popular style:

    # active_record/base.rb:

    def convert_number_column_value(value)
      case value
        when FalseClass: 0
        when TrueClass:  1
        when '':         nil
        else value
      end
    end

    # action_view/base.rb:

    def find_template_extension_for(template_path)
      if match = delegate_template_exists?(template_path)
        match.first.to_sym
      elsif erb_template_exists?(template_path):        :rhtml
      elsif builder_template_exists?(template_path):    :rxml
      elsif javascript_template_exists?(template_path): :rjs
      else
        raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}"
      end
    end

The warning comes from the use of colons to separate a condition from a statement. Apparently Ruby is working toward eliminating the use of this colon in favor of a semicolon. Since a semicolon marks the end of a line of code it already does what this colon is doing.
Here’s the code rewritten to use semicolons (so Ruby won’t barf out errors)

    def convert_number_column_value(value)
      case value
        when FalseClass; 0
        when TrueClass;  1
        when '';         nil
        else value
      end
    end

which is really just the same as:

    def convert_number_column_value(value)
      case value
        when FalseClass then        0
        when TrueClass  then        1
        when ''         then        nil
        else value
      end
    end

and with newlines instead of “then”s:

    def convert_number_column_value(value)
      case value
        when FalseClass
         0
        when TrueClass
         1
        when ''
         nil
        else value
      end
    end

and

    def find_template_extension_for(template_path)
      if match = delegate_template_exists?(template_path)
        match.first.to_sym
      elsif erb_template_exists?(template_path);        :rhtml
      elsif builder_template_exists?(template_path);    :rxml
      elsif javascript_template_exists?(template_path); :rjs
      else
        raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}"
      end
    end

which is really:

    def find_template_extension_for(template_path)
      if match = delegate_template_exists?(template_path)
        match.first.to_sym
      elsif erb_template_exists?(template_path)        then     :rhtml
      elsif builder_template_exists?(template_path)    then     :rxml
      elsif javascript_template_exists?(template_path) then     :rjs
      else
        raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}"
      end
    end

and with newlines:

    def find_template_extension_for(template_path)
      if match = delegate_template_exists?(template_path)
        match.first.to_sym
      elsif erb_template_exists?(template_path) 
       :rhtml
      elsif builder_template_exists?(template_path)
       :rxml
      elsif javascript_template_exists?(template_path)
       :rjs
      else
        raise ActionViewError, "No rhtml, rxml, rjs or delegate template found for #{template_path} in #{@base_path}"
      end
    end

As is often the case with Ruby/Rails this is largely an aesthetic issue. You can see some discussion about it here.

I’m curious what you think, are the colons pretty enough to stay or should the Rails code be adopted?

Edit: Ryan’s right, I’ve been making some alterations to this code while the post was live - my apologies for the underhandedness. Also I’m totally sorry for my spazzy comments. Even if it gives you some weird error it probably saved just fine. I’m working on un-jacking it.

Edit: If you’re running some form of *x system (macs included) and you’re getting annoyed by the warning output theres a simple way to hide everything but the regular ruby output:

rake test:units 2> /dev/null
  • Ryan Bates said: How about the "then" keyword? I haven't heard any mention of that relating to this subject, even though it does what the colon would do. I'm fine with using semi-colons, but I wonder if it was really necessary to break a lot of existing code for this change. There must be a major motive behind this, perhaps it cleans up the Ruby core a bit. Still, you would think they would save a change this significant for 2.0 or atleast 1.9.
  • Ben Kittrell said: Uh, hello, earth to Ryan, 'then' is four characters and a semi-colon is only one! But seriously, I just tend to use a new-line anyway. I left trying to fit everything on one line back with Perl in the 90's.
  • Ryan Bates said: Either I'm going crazy or Danger edited the post and added "then". Sneaky Danger. ;) Looking at them all side by side, the colon is prettiest, but we'll get used to the semi-colon and it does make more sense logically. Speaking of the colon, I've never been fond of the syntax for a symbol. I wish they used a different character other than the colon. I think it's cause for a lot of beginner confusion. I know it was for me. :/ Okay, sorry for the off-topic rant.
  • Ben Kittrell said: I think you're crazy :) For some reason I really like the semi-colon for symbols. I think it's concept of symbols that's confusing for noobs.
  • Danger said: @Ryan - I was just playing with options, I actually did the edit before your comment. Uhhh, I mean, yeah I totally am sneaky :-)
  • Danger said: I totally agree with you Ryan that this seems like a major version change - not the kind of thing that should be introduced between 1.8.4 and 1.8.5. I don't know if you guys are running off 1.8.5 yet but when I run my tests my console fills up with crap. I don't really care what solution is found to this - I just want the warnings to go away.
  • Ben Kittrell said: Does your console fill up because of stuff in Rails, or your own code? I'm sure Rails 1.2 will probably have all of this refactored.
  • Danger said: I'm sure they'll fix this eventually (there's already a patch for it) but I don't know how long it'll take them to implement it. My code doesn't contain any of this syntax, it's all coming from my ./vendor/rails directory (edge has it as well as every gem version).
  • Vinnie said: I prefer the newline over the others really. "then" reminds me too much of VB6 :)

Please if you found this post helpful or have questions.