diff --git a/content/elsewhere/why-i-still-like-ruby-and-a-few-things-i-dont-like/index.md b/content/elsewhere/why-i-still-like-ruby-and-a-few-things-i-dont-like/index.md index 8fc4568..bf361b1 100644 --- a/content/elsewhere/why-i-still-like-ruby-and-a-few-things-i-dont-like/index.md +++ b/content/elsewhere/why-i-still-like-ruby-and-a-few-things-i-dont-like/index.md @@ -9,6 +9,18 @@ references: url: https://www.sitepoint.com/history-ruby/ date: 2023-11-16T14:54:03Z file: www-sitepoint-com-6vwmef.txt +- title: "In Ruby, Everything is Evaluated - Mufid's Code blog" + url: https://mufid.github.io/blog/2016/ruby-class-evaluation/ + date: 2024-01-31T15:55:28Z + file: mufid-github-io-2czqvc.txt +- title: "Decorating Ruby - Part Two - Method Added Decoration - DEV Community" + url: https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj + date: 2024-01-31T15:55:49Z + file: dev-to-pywrcp.txt +- title: "JavaScript Needs Blocks" + url: https://yehudakatz.com/2012/01/10/javascript-needs-blocks/ + date: 2024-01-31T15:55:29Z + file: yehudakatz-com-sacizu.txt - title: "When Should You NOT Use Rails?" url: https://codefol.io/posts/when-should-you-not-use-rails/ date: 2023-11-04T17:36:55Z diff --git a/static/archive/dev-to-pywrcp.txt b/static/archive/dev-to-pywrcp.txt new file mode 100644 index 0000000..41599e1 --- /dev/null +++ b/static/archive/dev-to-pywrcp.txt @@ -0,0 +1,799 @@ +[1]Skip to content +[3] DEV Community +[4][ ] +[6] +[7] Log in [8] Create account + +DEV Community + +● Add reaction +[sp] Like [mu] Unicorn [ex] Exploding Head [ra] Raised Hands [fi] Fire +Jump to Comments Save +Copy link +Copied to Clipboard +[20] Share to Twitter [21] Share to LinkedIn [22] Share to Reddit [23] Share to +Hacker News [24] Share to Facebook [25] Share to Mastodon +[26]Share Post via... [27]Report Abuse +[28] Cover image for Decorating Ruby - Part Two - Method Added Decoration +[29]Brandon Weaver +[30]Brandon Weaver + +Posted on Aug 18, 2019 • Updated on Jan 21, 2021 + +● ● ● ● ● + +Decorating Ruby - Part Two - Method Added Decoration + +[31]#ruby + +[32]Decorating Ruby (3 Part Series) + +[33] 1 Decorating Ruby - Part One - Symbol Method Decoration [34] 2 Decorating +Ruby - Part Two - Method Added Decoration [35] 3 Decorating Ruby - Part Three - +Prepending Decoration + +One precursor of me writing an article is if I keep forgetting how something's +done, causing me to write a reference to look back on for later. This is one +such article. + +[36] What's in Store for Today? + +We'll be looking at the next type of decoration, which involves intercepting +method_added to make a more fluent interface. + +[37]The "Dark Lord" Crimson with Metaprogramming magic + +Table of Contents + + • [38]Part One - Symbol Method Decoration + • [39]Part Two - Method Added Decoration + • [40]Part Three - Prepending Decoration + +[41]<< Previous | [42]Next >> + +[43] What Does Method Added Decoration Look Like? + +You've seen the Symbol Method variant: + +private def something; end + +Readers that were paying very close attention in the last article may have +noticed when I said that I preferred that style of declaring private methods in +Ruby, but this was after the way that can be debatably considered more popular +and widely used in the community: + +private + +def something; end +def something_else; end + +Using private like this means that every method defined after will be +considered private. We know how the first one works, but what about the second? +There's no way it's using method names because it catches both of those methods +and doesn't change the definition syntax. + +That's what we'll be looking into and learning today, and let me tell you it's +a metaprogramming trip. + +[44] Making Our Own Method Added Decoration + +As with the last article we're going to need to learn about a few tools before +we'll be ready to implement this one. + +[45] Module Inclusion + +Ruby uses Module inclusion as a way to extend classes with additional behavior, +sometimes requiring an interface to be met before it can do so. Enumerable is +one of the most common, and requires an each implementation to work: + +class Collection + include Enumerable + + def initialize(*items) + @items = items + end + + def each(&fn) + return @items.to_enum unless block_given? + @items.each { |item| fn.call(item) } + end +end + +(yield could be used here instead, but is less explicit and can be confusing to +teach.) + +By defining that one method we've given our class the ability to do all types +of amazing things like map, select, and more. + +Through those few lines we've added a lot of functionality to a class. Here's +the interesting part about Ruby: it also provides hooks to let Enumerable know +it was included, including what included it. + +[46] Feeling Included + +Let's say we have our own module, [47]Affable, which gives us a method to say +"hi": + +module Affable + def greeting + "It's so very lovely to see you today!" + end +end + +My, it is quite an [48]Affable module, now isn't it? + +We could even go as far as to make a particularly Affable lemur: + +class Lemur + include Affable + def initialize(name) @name = name; end +end + +Lemur.new("Indigo").greeting +=> "It's so very lovely to see you today!" + +What a classy lemur, yes. + +[49] Hook, Line, and Sinker + +Let's say that we wanted to tell what particular animal was Affable. We can use +included to see just that: + +module Affable + def self.included(klass) + puts "#{klass.name} has become extra Affable!" + end +end + +If we were to re-include that module: + +class Lemur + include Affable + def initialize(name) @name = name; end +end + +# STDOUT: Lemur has become extra Affable! +# => :initialize + +Right classy. Oh, right, speaking of classy... + +[50] Extra Classy Indeed + +So we can hook inclusion of a module, great! Why do we care? + +What if we wanted to both include methods into a class as well as extend its +behavior? + +With just include it will apply all the behavior to instances of a class. With +just extend it will apply all the behavior to the class itself. We can't do +both. + +...ok ok, it's Ruby, you caught me, we can totally do both. + +As it turns out, include and extend are just methods on a class. We could just +Lemur.extend(ExtraBehavior) if we wanted to, or we could use our fun little +hooks from earlier. + +A common convention for using this technique is a sub-module called +ClassMethods, like so: + +module Affable + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + def affable? + true + end + end +end + +This allows us to inject behavior directly into the class as well as other +behavior we want to include in instances. + +Part of me thinks this is so I don't have to remember the difference between +include and extend, but I always remember that and don't have to spend 20 +minutes flipping between the two and prepend to see which one actually works, +absolutely not. + +Now remember the title about Method Added being the technique for today? Oh +yes, there's a hook for that as well, but first we need to indicate that +something needs to be hooked in the first place. + +[51] Raise Your Flag + +We can intercept a method being added, but how do we know which method should +be intercepted? We'd need to add a flag to let that hook know it's time to +start intercepting in full force. + +module Affable + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + def extra_affable + @extra_affable = true + end + end +end + +If you remember private, this could be the flag to indicate that every method +afterwards should be private: + +private + +def something; end +def something_else; end + +Same idea here, and once a flag is raised it can also be taken down to make +sure later methods aren't impacted as well. We keep hinting at hooking method +added, so let's go ahead and do just that. + +[52] Method Added + +Now that we have our flag, we have enough to hook into method_added: + +module Affable + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + def extra_affable + @extra_affable = true + end + + def method_added(method_name) + return unless @extra_affable + + @extra_affable = false + # ... + end + end +end + +We can use our flag to ignore method_added unless said flag is set. After we +check that, we can take down the flag to make sure additional methods defined +after aren't affected as well. For private this doesn't happen, but we want to +be polite. It is and Affable module after all. + +[53] Politely Aliasing + +Speaking of politeness, it's not precisely kind to just overwrite a method +without giving a way to call it as it was. We can use alias_method to get a new +name to the method before we overwrite it: + +def method_added(method_name) + return unless @extra_affable + + @extra_affable = false + + original_method_name = "#{method_name}_without_affability".to_sym + alias_method original_method_name, method_name +end + +This means that we can access the original method through this name. + +[54] Wrap Battle + +So we have the original method aliased, our hook in place, let's get to +overwriting that method then! As with the last tutorial we can use +define_method to do this: + +module Affable + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + def extra_affable + @extra_affable = true + end + + def method_added(method_name) + return unless @extra_affable + + @extra_affable = false + + original_method_name = "#{method_name}_without_affability".to_sym + alias_method original_method_name, method_name + + define_method(method_name) do |*args, &fn| + original_result = send(original_method_name, *args, &fn) + + "#{original_result} Very lovely indeed!" + end + end + end +end + +Overwriting our original class again: + +class Lemur + include Affable + + def initialize(name) @name = name; end + + extra_affable + + def farewell + "Farewell! It was lovely to chat." + end +end + +We can give it a try: + +Lemur.new("Indigo").farewell +=> "Farewell! It was lovely to chat. Very lovely indeed!" + +[55] send Help! + +Wait wait wait wait, send? Didn't we use method last time? + +We did, but remember that method_added is a class method that does not have the +context of an instance of the class, or in other words it has no idea where the +farewell method is located. + +send lets us treat this as an instance again by sending the method name +directly. Now we could use method inside of here as well, but that can be a bit +more expensive. + +Only the contents inside define_method's block are executed in the context of +the instance. + +[56] executive Functions + +If we wanted to, we could have our special method take blocks which execute in +the context of an instance as well, and this is an extra special bonus trick +for this post. + +Say that we made extra_affable also take a block that allows us to manipulate +the original value and still execute in the context of the instance: + +class Lemur + include Affable + + def initialize(name) @name = name; end + + extra_affable { |original| + "#{@name}: #{original} Very lovely indeed!" + } + + def farewell + "Farewell! It was lovely to chat." + end +end + +With normal blocks, this will evaluate in the context of the class, but we want +it to evaluate in the context of the instance instead. That's what we have +instance_exec for: + +module Affable + def self.included(klass) + klass.extend(ClassMethods) + end + + module ClassMethods + def extra_affable(&fn) + @extra_affable = true + @extra_affable_fn = fn + end + + def method_added(method_name) + return unless @extra_affable + + @extra_affable = false + extra_affable_fn = @extra_affable_fn + + original_method_name = "#{method_name}_without_affability".to_sym + alias_method original_method_name, method_name + + define_method(method_name) do |*args, &fn| + original_result = send(original_method_name, *args, &fn) + instance_exec(original_result, &extra_affable_fn) + end + end + end +end + +Running that gives us this: + +Lemur.new("Indigo").farewell +# => "Indigo: Farewell! It was lovely to chat. Very lovely indeed!" + +Now pay very close attention to this line: + +extra_affable_fn = @extra_affable_fn + +We need to use this because inside define_method's block is inside the +instance, which has no clue what @extra_affable_fn is. That said, it can still +see outside to the context where the block was called, meaning it can see that +local version of extra_affable_fn sitting right there, allowing us to call it: + +instance_exec(original_result, &extra_affable_fn) + +[57] instance_eval vs instance_exec? + +Why not use instance_eval? instance_exec allows us to pass along arguments as +well, otherwise instance_eval would make a lot of sense to evaluate something +in an instance. Instead, we need to execute something in the context of an +instance, so we use instance_exec here. + +[58] Wrapping Up + +So that was quite a lot of magic, and it took me a fair bit to really +understand what some of it was doing and why. That's perfectly ok, if I +understood everything the first time I'd be worried because that means I'm not +really learning anything! + +One issue I think this will have later is I wonder how poorly having multiple +hooks to method_added will work. If it turns out it makes things go boom in a +spectacularly pretty and confounding way there'll be a part three. If not, this +paragraph will disappear and I'll pretend to not know what you're talking about +if you ask me about it. + +There's a lot of potential here for some really interesting things, but there's +also a lot of potential for abuse. Be sure to not abuse such magic, because for +every layer of redefinition code can become increasingly harder to reason about +and test later. + +In most cases I would instead advocate for SimpleDelegate, Forwardable, or +simple inheritance with super to extend behavior of classes. Don't use a +chainsaw where hedge trimmers will do, but on occasion it's nice to know a +chainsaw is there for those particularly gnarly problems. + +Discretion is the name of the game. + +Table of Contents + + • [59]Part One - Symbol Method Decoration + • [60]Part Two - Method Added Decoration + • [61]Part Three - Prepending Decoration + +[62]<< Previous | [63]Next >> + +[64]Decorating Ruby (3 Part Series) + +[65] 1 Decorating Ruby - Part One - Symbol Method Decoration [66] 2 Decorating +Ruby - Part Two - Method Added Decoration [67] 3 Decorating Ruby - Part Three - +Prepending Decoration + +Top comments (2) + +Subscribe +pic +[ ] +Personal Trusted User +[75] Create template + +Templates let you quickly answer FAQs or store snippets for re-use. + +Submit Preview [78]Dismiss + +[79] edisonywh profile image +[80] Edison Yap +Edison Yap +[82] [https] Edison Yap +Follow +An aspiring software engineer from the tiny city of Kuala Lumpur. + + • Location + Stockholm, Sweden + • Education + RMIT University, Melbourne + • Work + Software Engineer at Klarna + • Joined + Jul 25, 2018 + +• [84] Aug 18 '19 • Edited on Aug 18 • Edited + + • [86]Copy link + • + • Hide + • + • + • + +Wow this is really cool, thanks for sharing Brandon! + +Is there a way to hook onto the last method_added? For example I'd like to +execute something after all methods are added + +EDIT: also quick search online seems to say that method_added only works for +instance methods, but there's singleton_method_added hook for class methods +too! + +1 like Like [89] Reply + +[90] baweaver profile image +[91] Brandon Weaver +Brandon Weaver +[93] [https] Brandon Weaver +Follow +Principal Ruby Engineer at Gusto on Payroll Services. Autistic / ADHD, He / +Him. I'm the Lemur guy. + + • Location + San Francisco, CA + • Work + Principal Engineer - Payroll Services at Gusto + • Joined + Jan 16, 2019 + +• [95] Aug 18 '19 • Edited on Aug 18 • Edited + + • [97]Copy link + • + • Hide + • + • + • + +Technically in Ruby there's never a point in which methods are no longer added, +so it's a bit hard to hook that. One potential is to use TracePoint to hook the +ending of a class definition and retaining a class of "infected" classes, but +that'd be slow. + +Look for "Class End Event" in this article: [99]medium.com/@baweaver/ +exploring-tra... + +EDIT - ...though now I'm curious if one could use such things to freeze a class +from modifications. + +1 like Like [101] Reply +[102]Code of Conduct • [103]Report abuse + +Are you sure you want to hide this comment? It will become hidden in your post, +but will still be visible via the comment's [107]permalink. + +[109][ ] + +Hide child comments as well + +Confirm + +For further actions, you may consider blocking this person and/or [111] +reporting abuse + +Read next + +[112] +cherryramatis profile image + +Bringing more sweetness to Ruby with Sorbet types 🍦 + +Cherry Ramatis - Sep 18 '23 + +[113] +hungle00 profile image + +Ruby's main object + +hungle00 - Oct 1 '23 + +[114] +braindeaf profile image + +Making a YouTube Short + +RobL - Sep 28 '23 + +[115] +iberianpig profile image + +Enhance Your Touchpad Experience on Linux with ThumbSense! + +Kohei Yamada - Sep 27 '23 + +[116] [https] Brandon Weaver +Follow +Principal Ruby Engineer at Gusto on Payroll Services. Autistic / ADHD, He / +Him. I'm the Lemur guy. + + • Location + San Francisco, CA + • Work + Principal Engineer - Payroll Services at Gusto + • Joined + Jan 16, 2019 + +More from [118]Brandon Weaver + +[119] Understanding Ruby - Memoization +#ruby #beginners +[120] In Favor of Ruby Central Memberships +#ruby #community +[121] Pattern Matching Interfaces in Ruby +#ruby #rails #functional + +Once suspended, baweaver will not be able to comment or publish posts until +their suspension is removed. + + [ ] + [ ] + [ ] +Note: [ ] + +Submit & Suspend + +Once unsuspended, baweaver will be able to comment and publish posts again. + + [ ] + [ ] + [ ] +Note: [ ] + +Submit & Unsuspend + +Once unpublished, all posts by baweaver will become hidden and only accessible +to themselves. + +If baweaver is not suspended, they can still re-publish their posts from their +dashboard. + +Note:[ ] + +Unpublish all posts + +Once unpublished, this post will become invisible to the public and only +accessible to Brandon Weaver. + +They can still re-publish the post if they are not suspended. + +Unpublish Post + +Thanks for keeping DEV Community safe. Here is what you can do to flag +baweaver: + +[129]( ) Make all posts by baweaver less visible + +baweaver consistently posts content that violates DEV Community's code of +conduct because it is harassing, offensive or spammy. + +[130] Report other inappropriate conduct + +Confirm Flag + +Unflagging baweaver will restore default visibility to their posts. + +Confirm Unflag + +[133]DEV Community — A constructive and inclusive social network for software +developers. With you every step of your journey. + + • [134] Home + • [135] Podcasts + • [136] Videos + • [137] Tags + • [138] FAQ + • [139] Forem Shop + • [140] Advertise on DEV + • [141] About + • [142] Contact + • [143] Guides + • [144] Software comparisons + + • [145] Code of Conduct + • [146] Privacy Policy + • [147] Terms of use + +Built on [148]Forem — the [149]open source software that powers [150]DEV and +other inclusive communities. + +Made with love and [151]Ruby on Rails. DEV Community © 2016 - 2024. + +DEV Community + +We're a place where coders share, stay up-to-date and grow their careers. + +[152] Log in [153] Create account +● ● ● ● ● + +References: + +[1] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#main-content +[3] https://dev.to/ +[6] https://dev.to/search +[7] https://dev.to/enter +[8] https://dev.to/enter?state=new-user +[20] https://twitter.com/intent/tweet?text=%22Decorating%20Ruby%20-%20Part%20Two%20-%20Method%20Added%20Decoration%22%20by%20%40keystonelemur%20%23DEVCommunity%20https%3A%2F%2Fdev.to%2Fbaweaver%2Fdecorating-ruby-part-two-method-added-decoration-48mj +[21] https://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fdev.to%2Fbaweaver%2Fdecorating-ruby-part-two-method-added-decoration-48mj&title=Decorating%20Ruby%20-%20Part%20Two%20-%20Method%20Added%20Decoration&summary=How%20various%20forms%20of%20method%20decoration%20work%20in%20Ruby&source=DEV%20Community +[22] https://www.reddit.com/submit?url=https%3A%2F%2Fdev.to%2Fbaweaver%2Fdecorating-ruby-part-two-method-added-decoration-48mj&title=Decorating%20Ruby%20-%20Part%20Two%20-%20Method%20Added%20Decoration +[23] https://news.ycombinator.com/submitlink?u=https%3A%2F%2Fdev.to%2Fbaweaver%2Fdecorating-ruby-part-two-method-added-decoration-48mj&t=Decorating%20Ruby%20-%20Part%20Two%20-%20Method%20Added%20Decoration +[24] https://www.facebook.com/sharer.php?u=https%3A%2F%2Fdev.to%2Fbaweaver%2Fdecorating-ruby-part-two-method-added-decoration-48mj +[25] https://toot.kytta.dev/?text=https%3A%2F%2Fdev.to%2Fbaweaver%2Fdecorating-ruby-part-two-method-added-decoration-48mj +[26] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj# +[27] https://dev.to/report-abuse +[28] https://res.cloudinary.com/practicaldev/image/fetch/s--UjPHgJnM--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/rdvh6fph3zoga5f9pw98.png +[29] https://dev.to/baweaver +[30] https://dev.to/baweaver +[31] https://dev.to/t/ruby +[32] https://dev.to/baweaver/series/10894 +[33] https://dev.to/baweaver/decorating-ruby-part-1-symbol-method-decoration-4po2 +[34] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj +[35] https://dev.to/baweaver/decorating-ruby-part-three-prepending-decoration-1ehc +[36] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#whats-in-store-for-today +[37] https://res.cloudinary.com/practicaldev/image/fetch/s--L5_TPTzS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://thepracticaldev.s3.amazonaws.com/i/5pzy2brl5apjtt4edgrm.png +[38] https://dev.to/baweaver/decorating-ruby-part-1-symbol-method-decoration-4po2 +[39] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj +[40] https://dev.to/baweaver/decorating-ruby-part-three-prepending-decoration-1ehc +[41] https://dev.to/baweaver/decorating-ruby-part-1-symbol-method-decoration-4po2 +[42] https://dev.to/baweaver/decorating-ruby-part-three-prepending-decoration-1ehc +[43] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#what-does-method-added-decoration-look-like +[44] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#making-our-own-method-added-decoration +[45] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#module-inclusion +[46] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#feeling-included +[47] https://www.merriam-webster.com/dictionary/affable +[48] https://www.merriam-webster.com/dictionary/affable +[49] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#hook-line-and-sinker +[50] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#extra-classy-indeed +[51] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#raise-your-flag +[52] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#method-added +[53] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#politely-aliasing +[54] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#wrap-battle +[55] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#-raw-send-endraw-help +[56] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#-raw-exec-endraw-utive-functions +[57] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#-raw-instanceeval-endraw-vs-raw-instanceexec-endraw- +[58] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#wrapping-up +[59] https://dev.to/baweaver/decorating-ruby-part-1-symbol-method-decoration-4po2 +[60] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj +[61] https://dev.to/baweaver/decorating-ruby-part-three-prepending-decoration-1ehc +[62] https://dev.to/baweaver/decorating-ruby-part-1-symbol-method-decoration-4po2 +[63] https://dev.to/baweaver/decorating-ruby-part-three-prepending-decoration-1ehc +[64] https://dev.to/baweaver/series/10894 +[65] https://dev.to/baweaver/decorating-ruby-part-1-symbol-method-decoration-4po2 +[66] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj +[67] https://dev.to/baweaver/decorating-ruby-part-three-prepending-decoration-1ehc +[75] https://dev.to/settings/response-templates +[78] https://dev.to/404.html +[79] https://dev.to/edisonywh +[80] https://dev.to/edisonywh +[82] https://dev.to/edisonywh +[84] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#comment-e96o +[86] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#comment-e96o +[89] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#/baweaver/decorating-ruby-part-two-method-added-decoration-48mj/comments/new/e96o +[90] https://dev.to/baweaver +[91] https://dev.to/baweaver +[93] https://dev.to/baweaver +[95] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#comment-e9g0 +[97] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#comment-e9g0 +[99] https://medium.com/@baweaver/exploring-tracepoint-in-ruby-part-two-events-f4fd291992f5 +[101] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj#/baweaver/decorating-ruby-part-two-method-added-decoration-48mj/comments/new/e9g0 +[102] https://dev.to/code-of-conduct +[103] https://dev.to/report-abuse +[107] https://dev.to/baweaver/decorating-ruby-part-two-method-added-decoration-48mj# +[111] https://dev.to/report-abuse +[112] https://dev.to/cherryramatis/bringing-more-sweetness-to-ruby-with-sorbet-types-13jp +[113] https://dev.to/hungle00/rubys-main-object-5hni +[114] https://dev.to/braindeaf/making-a-youtube-short-5gih +[115] https://dev.to/iberianpig/enhance-your-touchpad-experience-on-linux-with-thumbsense-391n +[116] https://dev.to/baweaver +[118] https://dev.to/baweaver +[119] https://dev.to/baweaver/understanding-ruby-memoization-2be5 +[120] https://dev.to/baweaver/in-favor-of-ruby-central-memberships-15gl +[121] https://dev.to/baweaver/pattern-matching-interfaces-in-ruby-1b15 +[130] javascript:void(0); +[133] https://dev.to/ +[134] https://dev.to/ +[135] https://dev.to/pod +[136] https://dev.to/videos +[137] https://dev.to/tags +[138] https://dev.to/faq +[139] https://shop.forem.com/ +[140] https://dev.to/advertise +[141] https://dev.to/about +[142] https://dev.to/contact +[143] https://dev.to/guides +[144] https://dev.to/software-comparisons +[145] https://dev.to/code-of-conduct +[146] https://dev.to/privacy +[147] https://dev.to/terms +[148] https://www.forem.com/ +[149] https://dev.to/t/opensource +[150] https://dev.to/ +[151] https://dev.to/t/rails +[152] https://dev.to/enter +[153] https://dev.to/enter?state=new-user diff --git a/static/archive/mufid-github-io-2czqvc.txt b/static/archive/mufid-github-io-2czqvc.txt new file mode 100644 index 0000000..55cbc96 --- /dev/null +++ b/static/archive/mufid-github-io-2czqvc.txt @@ -0,0 +1,132 @@ +{ +fidz +} + +[1]Mufid's Code blog + + • [2]RSS + +[4][ ] + • [5]Blog + • [6]Archives + +In Ruby, Everything is Evaluated + +Jul 10th, 2016 | [7]Comments + +So if i write + +1 def hello +2 puts 'world' +3 end + +It will evaluate def, to which Ruby will “create a method named hello in global +scope, with puts ‘world’ as a block”. We can change “global scope” to any +object we want. + +1 class Greeting +2 def hello +3 puts 'world' +4 end +5 end + +The class “Greeting” is actually EVALUATED, NOT DEFINED (e.g. In Java, after we +define a signature of a class/method, we can’t change it, except using +reflection). So actually, we can put anything in “Greeting” block, like + +1 class Greeting +2 puts "Will define hello in greeting" +3 def hello +4 puts 'world' +5 end +6 end + +Save above script as “test.rb” (or anything) and try to run it. It will show +“Will define hello in greeting” EVEN you don’t call “Greeting” class or “hello” +class or you don’t even need to instantiate “Greeting” class. This language +feature allows meta programming, like what we see in Rails. + +This time i will use Class Attribute within active support. If you ever run +Rails, you should have it, but you can gem install active_support if you don’t. + +1 require 'active_support/core_ext/class/attribute' +2 +3 module Greeting; end +4 +5 class Greeting::Base +6 +7 class_attribute :blocks +8 +9 def hello(name) +10 self.blocks[:greeting].call(name) +11 self.blocks[:hello].call(name) +12 end +13 +14 protected +15 def self.define_greeting(sym, &blk) +16 self.blocks ||= {} +17 self.blocks[sym] = blk +18 end +19 end +20 +21 class Greeting::English < Greeting::Base +22 define_greeting :greeting do |who| +23 puts "Hi #{who}, Ruby will greet you with hello world!" +24 end +25 define_greeting :hello do |who| +26 puts "Hello World, #{who}!" +27 end +28 end +29 +30 class Greeting::Indonesian < Greeting::Base +31 define_greeting :greeting do |who| +32 puts "Halo kakak #{who}, Ruby akan menyapamu dengan Halo Dunia!" +33 end +34 define_greeting :hello do |who| +35 puts "Halo dunia! Salam, #{who}!" +36 end +37 end +38 +39 x = Greeting::English.new +40 x.hello "Fido" +41 # Hi Fido, Ruby will greet you with hello world! +42 # Hello World, Fido! +43 x = Greeting::Indonesian.new +44 x.hello "Fido" +45 # Halo kakak Fido, Ruby akan menyapamu dengan Halo Dunia! +46 # Halo dunia! Salam, Fido! + +Previously i want to move the class attribute logic to above code, but after i +see the [8]Active Support code, it is pretty complex, so i just require it : / + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +Previously, i posted [9]this in Reddit + +Posted by Mufid Jul 10th, 2016 + +[10]Tweet + +[11]« Using Class with Generics Without the T in C# [12]Cara Googling » + +Comments + +Please enable JavaScript to view the [13]comments powered by Disqus. + +Copyright © 2021 - Mufid - Powered by [14]Octopress + + +References: + +[1] https://mufid.github.io/blog/ +[2] https://mufid.github.io/atom.xml +[5] https://mufid.github.io/blog/ +[6] https://mufid.github.io/blog/blog/archives +[7] https://mufid.github.io/blog/2016/ruby-class-evaluation/#disqus_thread +[8] https://github.com/rails/rails/blob/e35b98e6f5c54330245645f2ed40d56c74538902/activesupport/lib/active_support/core_ext/class/attribute.rb +[9] https://www.reddit.com/r/ProgrammerTIL/comments/4s2vmr/ruby_til_in_ruby_everything_is_evaluated/ +[10] http://twitter.com/share +[11] https://mufid.github.io/blog/2016/generic-type-csharp/ +[12] https://mufid.github.io/blog/2016/how-to-google/ +[13] http://disqus.com/?ref_noscript +[14] http://octopress.org/ diff --git a/static/archive/yehudakatz-com-sacizu.txt b/static/archive/yehudakatz-com-sacizu.txt new file mode 100644 index 0000000..ec4cf75 --- /dev/null +++ b/static/archive/yehudakatz-com-sacizu.txt @@ -0,0 +1,357 @@ +[1] Katz Got Your Tongue + + • [3]Home + • [4]About + • [5]Projects + • [6]Talks + • [7]Podcasts + • [8]Schedule + +Login Subscribe +Jan 9, 2012 6 min read + +JavaScript Needs Blocks + +While reading Hacker News posts about JavaScript, I often come across the +misconception that Ruby's blocks are essentially equivalent to JavaScript's +"first class functions". Because the ability to pass functions around, +especially when you can create them anonymously, is extremely powerful, the +fact that both JavaScript and Ruby have a mechanism to do so makes it natural +to assume equivalence. + +In fact, when people talk about why Ruby's blocks are different from Python's +functions, they usually talk about anonymity, something that Ruby and +JavaScript share, but Python does not have. At first glance, a Ruby block is an +"anonymous function" (or colloquially, a "closure") just as a JavaScript +function is one. + +This impression, which I admittedly shared in my early days as a Ruby/ +JavaScript developer, misses an important subtlety that turns out to have large +implications. This subtlety is often referred to as "Tennent's Correspondence +Principle". In short, Tennent's Correspondence Principle says: + + "For a given expression expr, lambda expr should be equivalent." + +This is also known as the principle of abstraction, because it means that it is +easy to refactor common code into methods that take a block. For instance, +consider the common case of file resource management. Imagine that the block +form of File.open didn't exist in Ruby, and you saw a lot of the following in +your code: + +begin + f = File.open(filename, "r") + # do something with f +ensure + f.close +end + +In general, when you see some code that has the same beginning and end, but a +different middle, it is natural to refactor it into a method that takes a +block. You would write a method like this: + +def read_file(filename) + f = File.open(filename, "r") + yield f +ensure + f.close +end + +And you'd refactor instances of the pattern in your code with: + +read_file(filename) do |f| + # do something with f +end + +In order for this strategy to work, it's important that the code inside the +block look the same after refactoring as before. We can restate the +correspondence principle in this case as: + + ```ruby # do something with f ``` + + should be equivalent to: + + do + # do something with + end + +At first glance, it looks like this is true in Ruby and JavaScript. For +instance, let's say that what you're doing with the file is printing its mtime. +You can easily refactor the equivalent in JavaScript: + +try { + // imaginary JS file API + var f = File.open(filename, "r"); + sys.print(f.mtime); +} finally { + f.close(); +} + +Into this: + +read_file(function(f) { + sys.print(f.mtime); +}); + +In fact, cases like this, which are in fact quite elegant, give people the +mistaken impression that Ruby and JavaScript have a roughly equivalent ability +to refactor common functionality into anonymous functions. + +However, consider a slightly more complicated example, first in Ruby. We'll +write a simple class that calculates a File's mtime and retrieves its body: + +class FileInfo + def initialize(filename) + @name = filename + end + + # calculate the File's +mtime+ + def mtime + f = File.open(@name, "r") + mtime = mtime_for(f) + return "too old" if mtime < (Time.now - 1000) + puts "recent!" + mtime + ensure + f.close + end + + # retrieve that file's +body+ + def body + f = File.open(@name, "r") + f.read + ensure + f.close + end + + # a helper method to retrieve the mtime of a file + def mtime_for(f) + File.mtime(f) + end +end + +We can easily refactor this code using blocks: + +class FileInfo + def initialize(filename) + @name = filename + end + + # refactor the common file management code into a method + # that takes a block + def mtime + with_file do |f| + mtime = mtime_for(f) + return "too old" if mtime < (Time.now - 1000) + puts "recent!" + mtime + end + end + + def body + with_file { |f| f.read } + end + + def mtime_for(f) + File.mtime(f) + end + +private + # this method opens a file, calls a block with it, and + # ensures that the file is closed once the block has + # finished executing. + def with_file + f = File.open(@name, "r") + yield f + ensure + f.close + end +end + +Again, the important thing to note here is that we could move the code into a +block without changing it. Unfortunately, this same case does not work in +JavaScript. Let's first write the equivalent FileInfo class in JavaScript. + +// constructor for the FileInfo class +FileInfo = function(filename) { + this.name = filename; +}; + +FileInfo.prototype = { + // retrieve the file's mtime + mtime: function() { + try { + var f = File.open(this.name, "r"); + var mtime = this.mtimeFor(f); + if (mtime < new Date() - 1000) { + return "too old"; + } + sys.print(mtime); + } finally { + f.close(); + } + }, + + // retrieve the file's body + body: function() { + try { + var f = File.open(this.name, "r"); + return f.read(); + } finally { + f.close(); + } + }, + + // a helper method to retrieve the mtime of a file + mtimeFor: function(f) { + return File.mtime(f); + } +}; + +If we try to convert the repeated code into a method that takes a function, the +mtime method will look something like: + +function() { + // refactor the common file management code into a method + // that takes a block + this.withFile(function(f) { + var mtime = this.mtimeFor(f); + if (mtime < new Date() - 1000) { + return "too old"; + } + sys.print(mtime); + }); +} + +There are two very common problems here. First, this has changed contexts. We +can fix this by allowing a binding as a second parameter, but it means that we +need to make sure that every time we refactor to a lambda we make sure to +accept a binding parameter and pass it in. The var self = this pattern emerged +in JavaScript primarily because of the lack of correspondence. + +This is annoying, but not deadly. More problematic is the fact that return has +changed meaning. Instead of returning from the outer function, it returns from +the inner one. + +This is the right time for JavaScript lovers (and I write this as a sometimes +JavaScript lover myself) to argue that return behaves exactly as intended, and +this behavior is simpler and more elegant than the Ruby behavior. That may be +true, but it doesn't alter the fact that this behavior breaks the +correspondence principle, with very real consequences. + +Instead of effortlessly refactoring code with the same start and end into a +function taking a function, JavaScript library authors need to consider the +fact that consumers of their APIs will often need to perform some gymnastics +when dealing with nested functions. In my experience as an author and consumer +of JavaScript libraries, this leads to many cases where it's just too much +bother to provide a nice block-based API. + +In order to have a language with return (and possibly super and other similar +keywords) that satisfies the correspondence principle, the language must, like +Ruby and Smalltalk before it, have a function lambda and a block lambda. +Keywords like return always return from the function lambda, even inside of +block lambdas nested inside. At first glance, this appears a bit inelegant, and +language partisans often accuse Ruby of unnecessarily having two types of +"callables", in my experience as an author of large libraries in both Ruby and +JavaScript, it results in more elegant abstractions in the end. + +Iterators and Callbacks + +It's worth noting that block lambdas only make sense for functions that take +functions and invoke them immediately. In this context, keywords like return, +super and Ruby's yield make sense. These cases include iterators, mutex +synchronization and resource management (like the block form of File.open). + +In contrast, when functions are used as callbacks, those keywords no longer +make sense. What does it mean to return from a function that has already +returned? In these cases, typically involving callbacks, function lambdas make +a lot of sense. In my view, this explains why JavaScript feels so elegant for +evented code that involves a lot of callbacks, but somewhat clunky for the +iterator case, and Ruby feels so elegant for the iterator case and somewhat +more clunky for the evented case. In Ruby's case, (again in my opinion), this +clunkiness is more from the massively pervasive use of blocks for synchronous +code than a real deficiency in its structures. + +Because of these concerns, the ECMA working group responsible for ECMAScript, +TC39, [12]is considering adding block lambdas to the language. This would mean +that the above example could be refactored to: + +FileInfo = function(name) { + this.name = name; +}; + +FileInfo.prototype = { + mtime: function() { + // use the proposed block syntax, `{ |args| }`. + this.withFile { |f| + // in block lambdas, +this+ is unchanged + var mtime = this.mtimeFor(f); + if (mtime < new Date() - 1000) { + // block lambdas return from their nearest function + return "too old"; + } + sys.print(mtime); + } + }, + + body: function() { + this.withFile { |f| f.read(); } + }, + + mtimeFor: function(f) { + return File.mtime(f); + }, + + withFile: function(block) { + try { + var f = File.open(this.name, "r"); + block(f); + } finally { + f.close(); + } + } +}; + +Note that a parallel proposal, which replaces function-scoped var with +block-scoped let, will almost certainly be accepted by TC39, which would +slightly, but not substantively, change this example. Also note block lambdas +automatically return their last statement. + +Our experience with Smalltalk and Ruby show that people do not need to +understand the SCARY correspondence principle for a language that satisfies it +to yield the desired results. I love the fact that the concept of "iterator" is +not built into the language, but is instead a consequence of natural block +semantics. This gives Ruby a rich, broadly useful set of built-in iterators, +and language users commonly build custom ones. As a JavaScript practitioner, I +often run into situations where using a for loop is significantly more +straight-forward than using forEach, always because of the lack of +correspondence between the code inside a built-in for loop and the code inside +the function passed to forEach. + +For the reasons described above, I strongly approve of [13]the block lambda +proposal and hope it is adopted. + +[14] + +Published by: + +[15] Yehuda Katz +[16] +Katz Got Your Tongue © 2024 +[17]Powered by Ghost +[pixel] + +References: + +[1] https://yehudakatz.com/ +[3] http://www.yehudakatz.com/ +[4] https://yehudakatz.com/about/ +[5] https://yehudakatz.com/projects/ +[6] https://yehudakatz.com/talks/ +[7] https://yehudakatz.com/podcasts/ +[8] https://yehudakatz.com/schedule/ +[12] http://wiki.ecmascript.org/doku.php?id=strawman%3Ablock_lambda_revival&ref=yehudakatz.com +[13] http://wiki.ecmascript.org/doku.php?id=strawman%3Ablock_lambda_revival&ref=yehudakatz.com +[14] https://yehudakatz.com/2011/12/12/amber-js-formerly-sproutcore-2-0-is-now-ember-js/ +[15] https://yehudakatz.com/author/wycats/ +[16] https://yehudakatz.com/2012/04/13/tokaido-my-hopes-and-dreams/ +[17] https://ghost.org/