RubyMine IDE: code insights or distractions? Part 1
By: Andrew Roberts
Tags: Ruby, Jetbrains, Rubymine, Ruby on Rails, Rails

My day job is developing a Ruby-on-Rails application. I’m won’t pretend to be a Rails fanboy by any stretch, but it does allow for rapid development which is useful in start-up life, and it’s good enough for now. Ruby is a dynamic language with a rich standard library. The Rails platform is extremely rich too, known for its convention over configuration, which allows one to side-step a certain amount of boilerplate, at the expense of the additional cognitive load of having to remember the conventions. To make my life a little easier, I’ve opted for an IDE: Jetbrains RubyMine to be specific.

I’m not getting in the IDE vs editor debate, other than to say I use both, and I perhaps tend slightly towards IDEs whenever the project is a little more complex. That habit probably formed when I was primarily a Java developer, and the features on offer for a strongly typed language really shine.

Jetbrains has been dominant in the Java IDE space for years with Intellij IDEA for understandable reasons: it’s a jolly good tool. Jetbrains has expanded and created new IDEs tailored for other languages including C#, Go, Php, Python and Ruby. These things cost a significant amount of cash via annual subscription (although for some languages they do offer free, stripped down “community” editions).

Jetbrains conducts a survey every year and got into a bit of trouble in their analysis of the Java ecosystem when they noted that Microsoft’s free, yet excellent, VS Code editor was growing in popularity:

“VS Code is growing which is concerning, not from a competitive point of view but actually from the point of view that there is clearly a lack of understanding of what an IDE gives you. VS Code is a code editor with some features that you’d find in an IDE, and extensions that can provide additional functionality — so if people are turning to VS Code for developing it may imply that developers don’t know what a fully-featured IDE can give them. In the web space it is understandable to use an editor as web developers are typically working with dynamic languages, and often use other tools like browser plugins to give them what they need. But in Java, especially professional Java, you really get a lot out of a good tool that has integration with the application server and you can really use the analysis and refactoring and everything.”

In the context of Java, I don’t think it’s a stretch to believe that an IDE like Intellij can bring an awful lot to the table to make development more productive, and that is the context of the above quote. However, I’m not sure they could be so confident in an analysis with the Ruby ecosystem, because quite frankly, their RubyMine IDE is nowhere near as good Intellij, and in fact in terms of raw code editing, it can frequently hinder the developer. Perhaps I’m in a grumpy mood, but I frequently find myself wondering whether anyone at Jetbrains actually dogfoods RubyMine for their day job.

I’ve recently started a list called “RubyMine woes” where I itemise instances where RubyMine doesn’t support my development efforts. I’ll only cover the first six in this post, and I’ll no doubt do more posts.

Unhelpful preselected completion

Let’s say we have a basic controller in our Rails app. Idiomatic Rails says that for a full set of CRUD operations I need to follow the convention of having new, create, edit, update and destroy methods. Of course, it’s possible to use additional and/or alternative methods, but for typical project, the top six method names in a controller will be index , new , create , edit , update and destroy .

Watch what happens as I try to write stubs for the new and create methods.

RubyMine will, unhelpfully, replace my create method with its own suggestion.

I typed def create<ENTER> without pause or hesitation. But because it already had a candidate suggestion for a method I could override that also began with ‘create’ — a completely irrelevant suggestion — it presumed my ENTER wished to accept that suggestion.

It’s quite typical for editors to assume you are selecting the suggestion upon ENTER, but it tends not to be a bother because they are auto-completing existing variables, functions or classes. In this case, RubyMine is getting in the way of the developer when he/she is declaring a new method. And as we’ll see later, given how many potential overridable method names it knows of, it’s a complete distraction.

Jetbrains do not consider this preselection a bug. Quite the opposite: this is a fearure and has its own dedicated preference within the Code Completion settings and is enabled by default. Alternatively, you can use the oh-so-convenient SHIFT+ENTER to override the preselect or I could press ESCAPE before ENTER to deactivate the suggestion.

The other slight peeve in this example (of method names for controllers) is that despite this being a common Rails convention (and RubyMine does know about some of them) it doesn’t have any helpful generate/autocomplete options here.

Method Override Noise

So why exactly did RubyMine offer create_shell_runner as a possible suggestion in the first place? This method comes via the rake gem that is a Rails dependency. The rake gem?! That’s the Ruby equivalent of Make for running tasks, and Rails uses this a lot for project initialisation and set-up, it’s not intended to be part of a Rails application runtime.

I certainly haven’t read through the Rails source but I’m pretty sure they are not extending any of Rake’s classes. However, a glance through the Rake source I noticed:

# Extend the main object with the DSL commands. This allows top-level

# calls to task, etc. to work from a Rakefile without polluting the

# object inheritance tree.

self.extend Rake::DSL

That would explain why I’m seeing loads of Rake DSL methods and FileUtils methods as overridable. Ruby has a pretty verbose object hierarchy already, and it quickly swells up because of the way developers love to monkey patch existing classes. A “clean” Rails project may pull in 70+ gems before you’ve even started, and before you know it base classes like Object have been extended with methods galore.

And what do you know, RubyMine’s parser is churning away and indexing all this stuff. It works wonderfully for Java; but becomes extremely noisy for Rails developers.

Start typing def and look at the list of suggestions it has. It’s massive. (Ruby allows you to override private methods so those don’t get filtered out.)

A long, long list of method names.

Strangely, and this is an odd complaint for me, but it’s not even complete. Rails adds the method html_safe? to the Object class. It’s actually something a Rails developer may wish to override unlike all those FileUtils methods. But it’s not showing as an overridable method. One needs to go to the dedicated Override Method helper and search for it.

The list of overridable methods goes on and on… why is it grouped so oddly?

Note just how huge and disorganised this list is. (You can just start typing and it’ll filter the list).

This method override feature (whether the autocomplete or dedicated helper modal) works very nicely in Java-land, but it doesn’t translate into an easy-to-use feature with Ruby. In the real-world any decent sized Rails project will be swamped with hundreds if not thousands of methods in that list.

As a result Jetbrains actually needs to actually apply some common-sense to this issue. I understand that RubyMine’s just following the code, but that’s not enough: for RubyMine to actually be useful to Rails developers, it needs to find a way to allow developers to avoid all this noise.

Routes

Rails expects all the application routes to be declared in a single file. Because routes are an essential part of a Rails application, RubyMine has some understanding of the routes system. As a result it should be able to detect more than just simple syntax errors, e.g. if there’s a route for which there’s currently no controller. Very helpful. If it worked.

Let’s illustrate via an example in which I need routes for managing a User. I will use the convenient Rails shorthand of a “resource” to create all the necessary routes for the standard CRUD paths for creating, updating and showing users. In addition, each user can have a single, sub-resource of “profile”.

resources :user do

resource :profile

end

Despite the route for profile being singular e.g. (/users/1/profile) by default it maps to the ProfilesController (note ‘Profiles’ is plural) as we can see from Rails’ own routes output.

The list of user profile routes as defined in the Rails routes config

Oh dear, RubyMine isn’t happy though as it expects ProfileController.

Despite the correct controllers existing for the defined routes, RubyMine shows an error for the profile resource because it thinks it should be singular, when Rails itself wants plural.

Next, let’s stick a new “posts” resource in. RubyMine correctly identifies that I don’t yet have a corresponding controller. No problem, like any good IDE, I’m sure that if I open the Quick Fixes bar it’ll offer to create this missing file.

No option to create the missing controller.

Ah, ok. Perhaps not. It’s not a terribly useful set of options at the best of times, and doesn’t resolve an easy, low-hanging productivity gain.

The other big reason why RubyMine parses the routes is because each one has a corresponding helper “prefix” that developers tend to use. E.g., if I wish to link to the profile of user with id=1, I can use user_profile_path(1) , and that translates to /users/1/profile . RubyMine can offer these url helpers whilst I type. And for a large project this really can be helpful, assuming RubyMine is accurate.

Unfortunately, it seems it doesn’t like plural namespaced routes. For example, let’s wrap a ‘posts’ resource in a ‘reviews’ namespace.

namespace :reviews do

resources :posts

end

Here’s how Rails interprets this route:

The list of routes under the “reviews” namespace.

So the url helper for a new review post would be new_reviews_post_path . However, RubyMine’s autocomplete has ‘review’ as a singular. There’s no convention at play here whereby namespaces have to be singular, it’s just an incorrect assumption that RubyMine has.

RubyMine’s suggestion for the new post route incorrectly has the singular “review”.

From the command-line you can call rails routes which loads in the routes configuration and outputs the true set of URIs, prefixes and controller mappings. It’s darn slow, but I have to frequently call that rather than trust RubyMine’s interpretation.

Note: apparently there’s a major revamp of routes coming soon that should fix these issues. But c’mon, it’s almost the end of 2020. I guess I’d rather hoped that RubyMine might have nailed this in, say 2014.

Resolved references

Let’s pretend we need to run some code against the profile, and we intend to put it in a little helper method called do_something() .

The do_something method doesn’t exist and rightly RubyMine complains.

Well, at this stage, RubyMine is spot on: it cannot find the method do_something because I’ve not written it yet. So let’s make it.

Things.do_something() now exists. Green ticks everywhere! Hmm.

It now exists as an instance method of the Things class. But RubyMine now thinks this is sufficient to resolve the original error in ProfilesController . Wtf?!

By pure fluke, I noticed that the variable profile wasn’t being flagged as unresolved too. It should be because it hasn’t been declared. So why not? Ah, because the ‘selenium-webdriver’ gem happens to contain a class with an instance variable also called profile .

RubyMine has found another class elsewhere containing the variable name ‘profile’. It’ll therefore never complain about any other variable called ‘profile’ being unresolved.

It’s out of scope, but nonetheless reinforces the notion that RubyMine’s code insights are not terribly insightful. I find this stuff anti-productive, because in the previous screenshot, the IDE, with all the power it extols, gives the ProfilesController a nice green tick: no errors here!

Resolved references — part 2: through Rails associations

Shifting now to a new level of flimsiness. Let’s imagine I have some posts and images, and I’d like users to be able to comment on either. I could create a new PostComment and ImageComment , or I could create a single Comment and use Rails’ built-in “polymorphic” associations. It means I have one table modelling the comments, and because I’ve used the key name “commentable”, there will be two columns called commentable_type and commentable_id , and with this Rails will allow me to associate the Comment with any other model should I declare it. (They’re not everyone’s cup of tea, but they are a convenient shortcut.) This is how it is setup in code:

class Comment < ActiveRecord::Base

belongs_to :commentable, polymorphic: true

end



class Post < ActiveRecord::Base

has_many :comments, as :commentable

nd



class Image < ActiveRecord::Base

has_many :comments, as :commentable

end

Now let’s imagine in the same project I’ve got a little service object that may operate on an instance of Post or Image . Or you could ignore that because the backstory doesn’t actually matter. So here’s a bit of code.

The ‘ commentable’ variable on line 7 is undefined. RubyMine doesn’t flag this. (NB ‘do_something’ is also undefined! 0/2)

The error here is on line 7: the variable should be @commentable (with the @ symbol) not commentable , but RubyMine doesn’t flag it. In fact, if I hover over the undefined variable, it shows me what it knows about it. It thinks that it’s something to do with the Comment class because it declares the belongs_to :commentable association. Even if you think “Hm. Nice guess”, why does the existence of this ActiveRecord association declaration satisfy RubyMine into thinking that the invalid commentable variable is in fact resolved?

Again, this is not a bug for Jetbrains. 😳 Apparently, one can improve this by altering a setting, that on the one hand will finally cause it to detect the unresolved variable… at the expense of lighting up every other line of code in the project with false warnings. Either way, it’s noise all the way down.

Types in doc strings

The reason Intellij can be so much smarter is because it’s operating on a strongly typed language. RubyMine will always be constrained until Ruby adopts type annotations. But as luck would have it, RubyMine supports Yard doc strings and if you annotate your methods with appropriate doc strings, it will gladly leverage that information.

RubyMine param types are missing String and Integer.

Alas, what a shame that despite having a quick-fix option to add param tags, it isn’t able to suggest the two common data-types: String or Integer . In fact it doesn’t provide Array or Hash either. Petty? Sure, but again, it illustrates the problem that this IDE is not fully charged up for the job it purports to do. This is IDE bread-and-butter. It’s trying to suggest stuff; given that I’d expect to see Integer before WIN32OLEQueryInterfaceError . What else is it missing?

Ok, well let’s see how well this info is in fact leveraged. Let’s create a class that accepts an Array when initialised and then the incoming parameter as an instance variable.

It’s possible to view RubyMine’s inferred types by hovering over each variable.

At line 4 RubyMine knows ‘items’ and ‘@items’ are arrays. By line 8 it’s unknown.

At line 4 RubyMine knows @items is an array. By line 8, it doesn’t have any type information on @items .

Contrast this to Visual Studio Code. VSC correctly holds the type information and provides useful, relevant suggestions, i.e., actual Array methods. 😱

Visual Studio Code correctly identifies the type and offers sensible Array methods as suggestions.

In fairness, it is possible to get this working in RubyMine by adding another yard doc instance variable annotation. So that’s ok — just twice the effort required compared to VSC.

Proof that RubyMine can be convinced that the items instance variable is an Array.

In conclusion

I could write an article about what RubyMine does well. It has a good debugger. It’s a breeze to search and move around projects. I18n support is useful. Setting up a Rails project isn’t terribly difficult, but if you use RVM then it certainly makes it even easier to set up your ruby version and gemsets. Its Ruby syntax highlighting is probably one of the better ones. In many respects, the figurative cheque I write every year is my favourable review.

I don’t envy anyone who tries to make an IDE around Ruby, or any dynamic language for that matter. However, if you are going to do it, then you’d better ensure it’s geared around developer productivity. It’s very hard not to be distracted by the code editor when it’s throwing up weird suggestions. It’s also very hard not to despair when it’s missing easy to catch errors, whilst also flagging perfectly valid code with warnings. It does not instil confidence. The ace up the sleeve of most IDEs are its suite of refactorings. (RubyMine does not have an impressive suite.) It’s a tense moment when you run a method rename.

I find editors like Visual Studio Code perfectly reasonable: decent editing and in addition good auto-complete suggestions and easy navigation/lookup of class/method/variable declarations. They won’t flag syntax errors, but then it doesn’t report false positives either. It’s not pretending to offer insights therefore I can’t be disappointed if I don’t get any. (Although I’m sure there’s handful of plugins available to make it more insightful.) That said, if you haven’t tried Typescript development in Visual Studio Code then I think it’s a model of what can be achieved with a dynamic language with type annotations.

In the meantime, if RubyMine isn’t going to bring anything else to the table to make the Ruby experience more palatable, Frankly I’d rather it dialled down its insights if they are going to remain this flimsy. I suppose there’s an option already to disable them.

I wish it offered better intelligence for Rails projects too. I’ll probably cover this in a dedicated post. As I’ve said, it has some Rails-specific tools and features, e.g., I really like the ease at which one can quickly switch between a controller method and its associated view (and vice versa) — to do that it needs some built-in logic about Rails. It’s got a bit of logic w.r.t. models/schemas and routes but lacklustre at best.