Lighthouse has a new layout. Prefer the old one? Return to the old layout, and switch back any time from the link at the top of each page.

This project is archived and is in readonly mode.

[PATCH] Rails 3.0 console hard aborts on attempted autocompletion in Ruby 1.8.7, and prints inappropriate deprecation notice in Ruby 1.9.2

#5657

The rails console aborts on attempted irb autocompletion of a partial expression. Try the following from any rails 3.0 project

rails c
Loading development environment (Rails 3.0.0)
ree-1.8.7-2010.02 > Time.now.

After typing the period, hit escape. Boom! A bizarre error message

this module is deprecated, please use the PREDICATES constant

is produced, irb aborts, and the terminal is left in a peculiar state. The attached patch repairs this problem, which arises from some crufty "deprecation" notices in Arel.

Discussion:

IRB autocompletion makes a valiant effort to obtain the class of a partial expression when the value is not known, which involves a fair amount of introspection on the overall state of the present environment. Unsurprisingly, it makes considerable use of the #instance_methods method on various classes -- well all the classes it can find to be true. Here lies the problem. Part of its pruning entails calling #sort! on the array of results it obtains. This is where it blows up in the rails environment.

It turns out that there is a class in Arel, called Predications, that overrides #instance_methods, but instead of returning an array of Strings, as Class and its progeny do, Arel::Attribute::Predications.method_missing returns an array of Symbols. And an array of symbols does not like to be sorted, since symbols do not respond to "<=>". Boom!

Predications is defined as follows in lib/arel/algebra/attributes/attribute.rb

    PREDICATES = [
      :eq, :eq_any, :eq_all, :not_eq, :not_eq_any, :not_eq_all, :lt, :lt_any,
      :lt_all, :lteq, :lteq_any, :lteq_all, :gt, :gt_any, :gt_all, :gteq,
      :gteq_any, :gteq_all, :matches, :matches_any, :matches_all, :not_matches,
      :not_matches_any, :not_matches_all, :in, :in_any, :in_all, :not_in,
      :not_in_any, :not_in_all
    ]

    Predications = Class.new do
      def self.instance_methods *args
        warn "this module is deprecated, please use the PREDICATES constant"
        PREDICATES
      end
    end

The first commit in the patch repairs the blow-up by returning PREDICATES.map(:&to_s). I also presumed to change the logger warning into one using Rails ActiveSupport::Deprecation, so that the message is routed approporiately.

This worked so far as it went, and the console no longer blew up. However, it continues to print the deprecation message each and every time you try to autocomplete a partial expression in the Rails console (or any other app that requires Arel for that matter).

Inspecting the file's git history, it appeared that this section of code historically appeared like the following:

module Predications

   def eq
   ...
   end
   ... other definitions ...
end
include Predications

During the Arel speedup en route to Rails 3.0, much of the metaprogramming and including and such was removed, including the references to module Predications. At one point it was deleted completely, and then later added back as a class, rather than as a module as shown above. I didn't see any point to the vestigial structure being permitted to continue to exist while destroying my beloved rails console, and rewrote it as a module with the corrected #instance_methods, without the deprecation warning there, and adding a deprecation warning upon inclusion, using "#included." That was the second commit referred to in the patch.

The resulting code passes the test suite (using Sqlite only, i didn't have the heart to build the mysql and postgress environments just to include this patch) and some new ones added to highlight the problem.

Now my autocompleting life has returned and I am a little bit happier. Thought I would share the ponies and rainbows with my favorite community. Hope I have done more good than harm.

Reported by Andrew C. Greenberg · September 18th, 2010 @ 07:40 PM

State: resolved
Milestone: none
Assigned to: nobody
Importance: Low

Activity

  1. Andrew C. Greenberg
    Andrew C. Greenberg
    • Title changed from Rails 3.0 console hard aborts on attempted autocompletion [PATCH supplied] to [PATCH] Rails 3.0 console hard aborts on attempted autocompletion

    September 18th, 2010 @ 11:28 PM

  2. Brian Candler
    Brian Candler

    but instead of returning an array of Strings, as Class and its progeny do, Arel::Attribute::Predications.method_missing returns an array of Symbols

    Unfortunately, this depends on which Ruby version you are using.

    $ ruby -e 'p String.instance_methods[0,5]'
    ["%", "select", "[]=", "inspect", "<<"]
    $ ruby19 -e 'p String.instance_methods[0,5]'
    [:<=>, :==, :eql?, :hash, :casecmp]
    

    And an array of symbols does not like to be sorted, since symbols do not respond to "<=>". Boom!

    Similarly:

    $ ruby -e 'p [:foo, :bar].sort'
    -e:1:in `sort': undefined method `<=>' for :foo:Symbol (NoMethodError)
            from -e:1
    $ ruby19 -e 'p [:foo, :bar].sort'
    [:bar, :foo]
    

    However, even in 1.9 you can't mix Symbol and String in a sort:

    $ ruby19 -e 'p [:foo, "bar"].sort'
    -e:1:in `sort': comparison of Symbol with String failed (ArgumentError)
            from -e:1:in `<main>'
    

    So I think you should map PREDICATES to strings only if RUBY_VERSION < "1.9"

    September 20th, 2010 @ 11:32 AM

  3. Andrew C. Greenberg
    Andrew C. Greenberg

    A useful observation, and it may explain how this wasn't caught earlier. Even so, the deprecation notice is still routinely printed in the rails console for expression autoompletion, and that should be fixed. Accordingly, I added a test to catch the ruby version dependence, added your suggested change, and included the previous changes to remove the version dependence.

    In retrospect, it seems to me that using the array and override of #instance_methods is a brittle hack in the original. A better method might have been simply to metaprogam all the methods into the module (adding the dep warnings for the use of the procedure, not the introspection), and let the respective rubies determine how they would report the list of methods. Rather than do that, I leave it to the arel community to explain what functionality or purpose Predications still has, if any. If it is totally vestigial, perhaps the existing stuff is good enough.

    Anyway, I attach a patch that repairs the repair so it works now with 1.8.7 and 1.9.2, AND eliminates the random deprecation messages in the ruby console hsving no relation to the autocompletion.

    September 21st, 2010 @ 10:36 PM

  4. Andrew C. Greenberg
    Andrew C. Greenberg
    • Title changed from [PATCH] Rails 3.0 console hard aborts on attempted autocompletion to [PATCH] Rails 3.0 console hard aborts on attempted autocompletion in Ruby 1.8.7, and prints inappropriate deprecation notice in Ruby 1.9.2

    September 21st, 2010 @ 10:36 PM

  5. Andrew C. Greenberg
  6. Andrew C. Greenberg
    Andrew C. Greenberg
    • Tag changed from arel console to arel, console

    September 21st, 2010 @ 10:38 PM

  7. Ryan Bigg
    Ryan Bigg
    • Importance changed from to Low

    Your patch does not apply to the rails/arel repository because the files it modifies are no longer there. Please update it to reflect this.

    September 30th, 2010 @ 06:51 AM

  8. Ryan Bigg
    Ryan Bigg

    I cannot reproduce this on Rails master.

    September 30th, 2010 @ 06:53 AM

  9. Ben Mills
    Ben Mills
    • Assigned user set to Ryan Bigg

    Not reproducible on Rails master

    September 30th, 2010 @ 07:12 AM

  10. Ryan Bigg
    Ryan Bigg
    • State changed from new to resolved
    • Assigned user cleared.

    Confirmed fixed in master.

    September 30th, 2010 @ 07:12 AM

  11. Stephen Davis
    Stephen Davis

    This issue is still present in Rails 3.0.1

    
    $ rails -v
    Rails 3.0.1
    $ ruby -v
    ruby 1.8.7 (2010-01-10 patchlevel 249) [i486-linux]
    $ rails new testapp ; cd testapp
    $ rails c
    Loading development environment (Rails 3.0.1)
    irb(main):001:0> Time.now.
    

    press tab to autocomplete here, and you get

    
    this module is deprecated, please use the PREDICATES constant
    /usr/lib/ruby/1.8/irb/completion.rb:162:in `sort!': undefined method `<=>' for :not_in_all:Symbol (NoMethodError)
        from /usr/lib/ruby/1.8/irb/completion.rb:162
        from /usr/lib/ruby/1.8/irb/input-method.rb:97:in `call'
        from /usr/lib/ruby/1.8/irb/input-method.rb:97:in `readline'
        from /usr/lib/ruby/1.8/irb/input-method.rb:97:in `gets'
        from /usr/lib/ruby/1.8/irb.rb:140
        from /usr/lib/ruby/1.8/irb.rb:271:in `signal_status'
        from /usr/lib/ruby/1.8/irb.rb:139
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:189:in `call'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:189:in `buf_input'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:104:in `getc'
        from /usr/lib/ruby/1.8/irb/slex.rb:206:in `match_io'
        from /usr/lib/ruby/1.8/irb/slex.rb:76:in `match'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:287:in `token'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:263:in `lex'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:234:in `each_top_level_statement'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:230:in `loop'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:230:in `each_top_level_statement'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:229:in `catch'
        from /usr/lib/ruby/1.8/irb/ruby-lex.rb:229:in `each_top_level_statement'
        from /usr/lib/ruby/1.8/irb.rb:154:in `eval_input'
        from /usr/lib/ruby/1.8/irb.rb:71:in `start'
        from /usr/lib/ruby/1.8/irb.rb:70:in `catch'
        from /usr/lib/ruby/1.8/irb.rb:70:in `start'
        from /usr/lib/ruby/gems/1.8/gems/railties-3.0.1/lib/rails/commands/console.rb:44:in `start'
        from /usr/lib/ruby/gems/1.8/gems/railties-3.0.1/lib/rails/commands/console.rb:8:in `start'
        from /usr/lib/ruby/gems/1.8/gems/railties-3.0.1/lib/rails/commands.rb:23
        from script/rails:6:in `require'
        from script/rails:6
    

    November 14th, 2010 @ 09:38 PM