In my Ruby (on Rails) project, I’d like to forbid or restrict the usage of some methods provided by the standard library. Examples: I’d like to forbid calling Float#to_d because I had a rounding error when someone was using that method on a Float literal. I’d like to restrict String#to_d to work only with fully valid Strings because I had some bug resulting from 'string'.to_d returning 0.0.
Monkey-patching / overriding these methods globally is of course a bad idea. It may break some dependency.
Adding a linter rule that scans the code to not have calls to any #to_d method has the problem that it falsely restricts calling legit methods like Integer#to_d. Of course, all the legit methods could be added to the corresponding classes under a different name. But this requires adding a lot of boilerplace (for the methods) and changing all calls of these methods.
I also considered using refinements. This would be similar to monkey-patching, but apply only to scopes where the refinement is used. However, having to add using statements to every file would be ugly and error-prone. Is it possible to activate a refinement automatically for every file in my project, but not for dependencies?
You can monkeypatch if you're careful about it. I tried to implement it, see if it works. Obviously, you'll take a large performance hit, so I wouldn't recommend it in production :P One could speed it up quite a bit by memoising the tested locations instead of going up the directory tree each time the method is called.
The idea is to move the original method out of the way if defined on the class, then substitute a method that will check whether or not you're calling from your code. I'm using the directory that contains
.gitdirectory as your project directory; if you have avendordirectory directly in your project directory, it is exempt, as is everything outside the project directory. If you are in a location that is exempt, just pass things along to either the saved old method or up the inheritance chain; if not, scream foul.With this, you can make
Float#to_dand (conditionally)String#to_dfail in project code:If you pass a block, the function is only forbidden if the block condition is truthy. (The block will get passed all the method's arguments, and will execute with the forbidden method's receiver as
self.)