HI IM RAILS I CAN HAZ MAGIC?

Again, why I hate all you fucking Ruby developers

I have a serious issue with Ruby magic.. no, wait, Rails magic… hmm, well no, maybe magic within the funky “sub-framework” I use?

Oh FUCKIN’ A, I don’t know. There’s a project I work on and it uses Rails. And it’s using an unnamed and incredibly shitty “sub-framework” (referred to henceforth as “Cyclops”) that forces us to use very specific gems whether we like it or not. In some cases the gems are cool, and in some cases they are the fucking devil himself.

It forces us to use shit like CanCan, which wouldn’t be awful on its own, but Cyclops has a lot of magic that’s sort of almost kinda tied into CanCan’s magic (Cyclops likes to intertwine itself into fucking everything), so I’m not sure if I blame CanCan or Rails or Cyclops when auth shit goes bad.

Magic, magic, magic all the stuffs!

Rails is built on the belief that magic code, hidden from the average developer, is better than writing code explicitly. Ruby developers in general seem to have embraced this approach. This makes standing up a new blog pretty damn easy in Rails… but never forget, “magic always comes with a price”.

I have a class with a fairly common name. My bad, okay, I know the name should have changed a while ago, but come on, it MAKES SENSE. I shouldn’t have to choose class names based on Rails doing hidden magic shit that causes problems.

So I have this class, we’ll pretend it’s called Context (it isn’t, I just want to distance this article from the actual codebase – politics and whatnot). I specify that certain users can :create instances of this class in CanCan ability rules, and using a horrible combination of the Cyclops standards and the CanCan conventions, I hook up these rules so that users cannot administer contexts unless they have permissions. This all works great at the controller level, but for some reason my check in the view (to hide the link to context admin) just will not work. No matter what I try, all users can see the link. I check for typos, I hack up permissions a million times. I do some serious in-depth debugging and discover that for some reason, the view believes that current_ability.can?(:create, Context) is always true even though this is not the case in the controller for the exact same user.

And the winner is…

After about three hours, I finally just typed in “Context” in pry. And it went a little something like this:

[1] pry(#<#<Class:0x007f125959e620>>)> Context
=> ActionView::Context

Yup, my check for permissions on Context was actually checking the ActionView::Context class, not my base class. The proper check requires me to type in ::Context to let Ruby know that I mean the top-level class, not the ActionView class.

So whose fault is this? Certainly I’m partly to blame because I should probably realize how shitty Rails is and just smash :: every time I code anything. And yes, my name wasn’t awesome. But I blame the environment of magic.

  • Rails uses some magic to make all views exist within the namespace ActionView. This is not awesome, because there’s no indication that this is happening, unlike in semi-reasonable systems where you have to explicitly state that a given file/package is operating in a given namespace.
  • Cyclops uses some magic to inject its own CanCan rules, and it would seem that it automatically gives all users the ability to :create anything. Literally any permissions check on :create returns true for all users. This is not awesome.
  • CanCan allows checking for permissions to do things to ANY object, it would seem. Perhaps this isn’t a bad thing for a flexible system, but I’d argue that this approach is just another side-effect of the “magic everything” philosophy. Instead of making a strict permissions system, they implemented a bunch of magic to try and make permissions checking take one or two lines of code. And then they actually allow blanket permissions like the one Cyclops does for :create. THIS IS FUCKING NOT AWESOME

Ultimately, I want to blame Cyclops because its “catch-all” magic is just so awful, so dreadfully wrong, that no developer should admit to having set up such a thing as a forced standard all apps using it have to accept.

But CanCan’s approach leaves me very bewildered. Even without Cyclops’s horrible hack, CanCan will allow ambiguous and painful typos with no error and no warnings:

[1] pry(#<#<Class:0x007f125959e620>>)> current_ability.can?(:mcgruff, Object.new)
=> false

No, :mcgruff isn’t a registered action anywhere in CanCan. No, there are no rules for Object or anything like Object. CanCan should be telling me I’m an idiot for asking for such an ability. But it doesn’t.

CanCan, as amazing as it is when you don’t have to debug it, wins. Its misguided magic might save five or six lines of explicit permission checking per controller, but when it takes this much to debug it, that’s just not a good trade.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.