6 ms·
The best example I can think of is Rails' ActiveSupport. The changes it brought to Ruby aren't fundamental in any way but they are convenient. For example aski
by joelbluminator 5y ago
The best example I can think of is Rails' ActiveSupport. The changes it brought to Ruby aren't fundamental in any way but they are convenient.
For example asking whether a variable is present? (truthy, so empty str and empty [] should return false) is not possible with plain Ruby but is possible in Rails. It's stuff web developers need and makes things simpler.
This is done by monkeypatching.
Another example is if you're working on a library that has some bug but you can't quite yet jump to the next version.
- eloisius 5y agoMy favorite ActiveSupport monkey patching is on number classes. Things like 2.weeks.from_now are just too useful to scorn monkey patching on some programming ideological reasons.
- joelbluminator 5y agoOh I love these yes please!
- Groxx 5y agoI wish every language had open classes for this reason. It makes ruby such a joy to write in. Something like Kotlin's class extensions might be a happier middle ground for many languages though, since that way you only get what you pulled in, in any given file. Yes, of course you can do horrific things with it. But the ruby community has largely been absolutely stellar about not doing that - most keep insanities tightly bounded, where they can be used to make pleasant and error-resistant APIs. And even when they're not (which is usually self-inflicted by internal code), the language is flexible enough to let you unfuck just about any fuckery that you run across, if needed.
- quetzthecoatl 5y agoOpen classes are against SOLID principles (O stands for - classes should be extendable, but not modifiable). So, a big no in some circuits. Aside from open classes, ruby also have instance_eval and class_eval taking to violating that principle to whole new level.
- Fire-Dragon-DoL 5y agoI'd argue the opposite. Most ruby codebases are infected with monkey patches, extracting libraries now depends on activesupport being used, forcing the dependency
- Groxx 5y agoActivesupport is hugely widespread, yeah. It's a bit unfortunate since it provides so much, and it's rare that all (or even most) of it is used. But beyond that though, the vast majority of issues I've seen with monkey patching has been stuff that was created in the project/company that's experiencing the problems. Libraries are generally very good about how they monkeypatch (because doing it wrong with hundreds of using-projects VERY quickly runs into problems), but those ad-hoc internal monkeypatches are routinely done by people who don't fully understand what they're doing, or take shortcuts. Those can have latent bugs linger for a very long time, and yeah - they can be nasty to unravel.
- lovegoblin 5y ago> Activesupport is hugely widespread, yeah. It's a bit unfortunate since it provides so much Even this is becoming less and less necessary as more of the commonly-used syntactic sugar gets pulled into Ruby core.
- mypalmike 5y agoOh god no. If you want to parse strings, parse strings. I avoid Ruby like the plague (after about 2 years where it was my primary language) because of this kind of clever magic spaghetti that subtly breaks in all sorts of cases.
- joelbluminator 5y agoThat's Rails not Ruby actually. And it never bothered me once but to each his own...
- pharrington 5y agoWeeks(2).from_now I don't understand how monkey patching is beneficial beyond personal preference.
- eloisius 5y agoThe entire Ruby ethos and surrounding APIs are basically all a result of a particular set of personal preferences. One of the core values of Ruby is programmer happiness. That’s obviously qualitative and varies from person to person. That said, this kind of pattern is extremely _Ruby_ and if you like it, you like it. I love writing Ruby code because of this kind of attention to making code almost like prose. However, I’ve also worked on a lot of terrible Ruby projects that were a mess of metaprogramming and clever dynamic language abuse, so it’s hard to mount a rigorous defense.
- chousuke 5y agoMy first experience with Ruby was writing rspec tests and chef recipes, and I can't say I was very happy when I had to debug that code. It's given me a deep aversion towards any Ruby DSL that still persist. I will work with Ruby if I have to, but I am not happy about it. The way Ruby does metaprogramming makes debugging extremely frustrating because your stacktraces will contain references to methods that exist nowhere. Good luck then trying to find where they're defined so you can understand how they work. I have a very strong opinion that generating methods at runtime is a terrible feature that should be used only when nothing else can be done.
- goatlover 5y agoThat looks like you're having to make a Weeks class just so you can do the same thing, instead of adding it to Integer and having it available on any int.
- eloisius 5y agoIn Ruby at least this example would just be an instance method named Weeks on the kernel class and it wouldn’t really be any more clunky to implement than the ActiveSupport flavor. However because Ruby you’re still reopening a class. You’re just adding a method to Kernel instead of Integer.
- kryptiskt 5y agoFunny thing, that construction is actually pretty easy to achieve in buttoned-up and statically typed C# with extension methods: static TimeSpan Weeks(this System.Int32 n) { return new TimeSpan(n*7, 0, 0, 0); } static DateTime FromNow(this TimeSpan dt) { return DateTime.Now.Add(dt); } static void Main(string[] args) { Console.WriteLine(2.Weeks().FromNow()); }
- deleted 5y ago[deleted]
- joelbluminator 5y agoI'm impressed actually.
- eloisius 5y agoNot bad! I also feel like it’s worth mentioning that in rust you can extend primitive types with traits. I’ve done that to add some bit-munging helpers while working on a toy Game Boy emulator to make it easier to deal with 16- and 8-bit register values like (8, 8).join() and (l, h) = 16.split().
- Fire-Dragon-DoL 5y agoIt could have been done with various less invasive ways. E. G. Clock(2).weeks.from_now (I'm making this up on the fly, there are definitely better names). This wouldn't make your number 2 respond to "weeks" all of a sudden and the cost would be 7 additional characters. That's negligible.
- joelbluminator 5y agoWhat's wrong with 2.weeks.from_now ? I get that it hurts your sensibilities (SOLID etc), truth is it's fine. I've seen a good share of Rails projects and never had a problem with this yet. We tend to have a knee-jerk reaction to certain things, and sometimes for good reason, but ActiveSupport is used in millions of Rails projects and nothing horrible happens.
- Fire-Dragon-DoL 5y agoThere are things that go wrong when using ActiveSupport, but it's so ingrained in people's workflow that they won't notice. The one I consider the most annoying: it substantially increases load time. ActiveSupport is huge and just requiring it increases the load time by 1.x seconds. Given what it provides, that's substantial. The inability to extract a library (or a service) without depending on ActiveSupport is a cost that doesn't exist with the other approaches. Not to underestimate, your _number_ now responds to `weeks`, and duck typing is the only way you have "interfaces" in Ruby. I can easily picture having a "weeks" or days attribute on some entity and a service object expecting that, now there are subtle bugs when passing accidentally the entity's _value_ instead of the entity itself. Yeah, they are "minor" things, but the point is dying of thousands cuts. The last damage is more subtle: devs, even less experienced devs, will feel authorized to monkey patch. In the private codebases I saw (multiple) this is common. It's not a last resort anymore and it ends up becoming a hard problem to debug and resolve. The ability to monkeypatche pushes devs to NOT design their objects in an extensible fashion. This effectively is the highest damaging consequence, but it's incredibly hard to assess.
- joelbluminator 5y ago
- lovegoblin 5y ago> or example asking whether a variable is present? (truthy, so empty str and empty [] should return false) Actually this is kind of the whole point of #present? - it's not just a truthiness check. The only falsey values in Ruby are `false` and `nil`, but in the context of a Rails app you might want to treat things like "" and [] as "not present" (e.g., a user leaves a text field blank).