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.