A Live Developer Journal

Thirteen Sessions Of Learning Ruby Blocks

I absolutely loved the Ruby Blocks Course by Pragmatic Studio. As with the Ruby course I did with them, it was incredibly empowering. I kept a learning log for each of the sessions with my thoughts on how it went.


12/May/2021 - Basic block exercises (1h) - HOME (happy)

Almost all of the stuff covered was familiar to me, like using the times, and upto methods to repeat a block of code a arbitrary number of times. The bit I didn't know about which was cool to learn was that variables defined inside block scope, are local to the block only when there isn't already a global variable defined with the same name. We can force it to be a local scope variable by adding it inside of the block goalposts after a semi-colon, like this |n; local_variable_name|. I mentioned it to A and he was surprised that that happens because it isn't great to need to do that. He also said that if you need to force a local variable inside a block then that's also bad, because the block should be in its own local scope like a function anyway. Though I can see a use for it in a main program file that wants to print things for example. I'm pretty tired this morning and don't want to tire myself out too much for work later on. So won't do the three hours planned for this atm. Learned something new though, woo!

16/May/2021 - Each Iterator (35m) - HOME (pure joy)

This was a fun session playing with the each iterator. Still feeling totally familiar with everything covered in this. Some of the exercises included shuffling cards before printing the card names in uppercase. The most complex exercise was printing out what mile points a flyer would have gotten if they'd flown with a different airline (each airline has their own promotion). Writing that out sounds way more complicated than it was to actually implement. It just involved looping through the flyers, and inside that looping through the promotions and inside that printing out the flyers name, and the points times the promotion points. Woo!

16/May/2021 - Enumerable Methods (45m) - HOME (pure joy)

I want to say "I really like this course" every time I write a reflection session on it haha. In this work session, I watched both videos on the Enumerable module in Ruby, which contains a bunch of commonly used behaviours that we might want to perform against collections (like Arrays and Hashes). Then I played around with the first set of exercises, including: Identifying all of the frequent flyers, rejecting the non-frequent flyers, finding out if there are any platinum flyers and printing out the first bronze flyer. One of the things I really like about the exercises, is that the instructors provide reasons for what you might want to do with the data you have collected. E.g. We want to identify the first bronze flyer in the list so that we can call them and encourage them to move up to silver status. The implementation (using detect) is secondary to that. Similarly, the PragStudio instructors emphasize that there are many ways to solve the same problem (like a full reduce method vs a shorthand reduce method, chaining methods vs not chaining methods etc).

16/May/2021 - Enumerable Methods 2 (20m) - HOME (pure joy)

A quick overview of tasks covered: Separate premium flyers from everyone else (partition method). Print out name badges for each flyer (using map). Convert miles to kilometers (also inside map). Calculate total miles flown, and also total kilometers flown (using reduce). Work out total kilometers flown by bronze flyers (chain select and reduce methods). Identify player who has flown the most miles (using the max_by method). It was really easy to just code up the examples needed to answer all of these questions. That was a lot of fun. Now I need to quickly go get ready so I can catch the train to work!

18/May/2021 - Custom Iterators with Yield (1h30m) - HOME (okay)

This is the first part of the course that I struggled with. We can create our own customer iterators (like map, reduce, times and upto methods) by defining a normal method, and adding the "yield" keyword somewhere in the body of the method. To use it, we call the method and pass in a block as a parameter (iterator_name { #do something} or iterator_name do #do something end). I managed to solve the last few exercises without needing to look anything up. I liked that one of the exercises was to create a custom iterator that returned a percentage from 0-100 in 10% increments, and when we called it, our block parameter that we passed in just prints the percent sign. Generally though, I'm struggling to see why I'd want to use custom iterators in the real world, instead of normal methods. Though the instructors said they'd explain that in the next session, luckily. So I'm looking forward to that.

18/May/2021 - Use Enumerator on Objects (1h) - HOME (pure joy)

Typically we can only use Enumerable methods like select, map, reduce etc etc on collection classes like Arrays and Hashes. However, the Enumerable module can be used on anything that has an 'each' method defined on it. In the examples, we created a playlist with various songs using a playlist object and a song object. The playlist object contained an array of songs. Then we added an each method to the playlist object, and inside that used iterated through each of the songs in the song array using it's own each method (Array is collection object that has an each method already defined on it for us to use). Then we yield the song inside of each iterator, like this: def each \n songs.each { |song| yield song }. Once we've done that, we can then include the Enumerator module into our playlist class ("include Enumerable"), and then use all of it's methods on our playlist class. Each enumerable method calls the 'each' method, which in the case of the playlist works on our collection of songs. Once we'd done that, it was then easy for me to use the enumerable methods. I used partition to separate the short from the long songs, used select methods to find all songs containing a custom search query, and the reduce method to calculate the duration of the entire playlist. I did a few extra things not mentioned, like creating methods in the playlist to return its total duration (playlist.length), search for a query (playlist.length(query)) etc. To make everything a little more friendly to use. I also created a mixin to handle duration formatting, so that I could format the duration from 4.02 to "4.02 mins" in both the playlist and song classes. It'd probably be a great idea to do this for the other methods mentioned earlier, but no need to right now. I'm super excited for this, it's so powerful! I've had so many ideas to, for incorporating this into a pet adoption program, this project reflection app that I'm writing this in, creating my own version of spotify without all the adds haha. SO MANY AWESOME THINGS TO MAKE AHHHH!

18/May/2021 - Copy Enumerable Module (35m) - HOME (happy)

One of the exercises was to replace the enumerable module and methods that Ruby provides, with my own implementation of them using what I learned about creating custom iterators. I easily implemented my own versions of map and select, but struggled with the reduce method. I looked up the Enumerable module source code for the reduce method but that didn't help. I went to move on but the PragStudio instructors provided the solution for the reduce method as a bonus exercise. So I copied that, then wrote my own partition method. I commented out the enumerable module and included "MyEnumerable". Then I replaced the original enumerable methods with my custom versions (so 'map' and 'reduce' became 'my_map' and 'my_reduce') and checked that my playlist code still ran okay. It did. This is so cool!

18/May/2021 - Execute Around (1h15m) - HOME (happy)

This was a really cool learning session. One of the use-cases for custom - hmm. I was going to say custom 'iterator', but we're not actually iterating over anything. I think the right word might be 'generator'. Just Googled and most of the results are about Rails Generators, for generating things like controllers and modules etc. I'll stick with "custom generator" for now. So, one of the use-cases for custom generators is for splitting code that varies from code that stays the same. This is useful when we have a method that has a small bit of code inside that varies, but before and/or after we run something that never changes. I made some html tag generators (def tag(name) do \n puts "<#{name}>#{yield}" \n end). We can then do this: tag(:h1) { "Main Title" }, or tag(:p) { "Paragraph" } or even tag(:ul) { tag(:li) { "List Item" } }. I also made a little test expectation generator which tells you if a test passed or failed. If it passes then it gives me a gold star ⭐️, if it fails it tells me it fails and what the actual and expected results were. Another use-case was to check how long it took to run a chunk of code, and also to add debugging where you output what the state of a small chunk of code was at the time of running (inside the debugging method). We can combine that with the expectation generator to show what we expected the result to be. This is so powerful! I love that the course is covering lots of behind the scenes things like how the Enumerable module works, and how test expectations work. It makes me see how it's possible that people wrote their own test-suite gems like RSpec. It's so exciting and empowering!

18/May/2021 - Toggle Around (unclear still) (20m) - HOME (okay)

I quickly went through the exercises, which walks us through another real-world use-case for using custom generators. Which is toggling something so that you start with some kind of default, run the generator that switches from the default to something else, and then reverts back to the default way of doing things when it's done. An example is if you want to toggle between developer environments (production, testing, and development). Our default is the development environment, and when we run the generator to perform some kind of tasks, like connecting to a database, handling requests and writing to logs. Once we're done (end of generator code), we switch back to default. There were a few other examples mentioned, but I'm still a little unclear on this, so want to read up on it a little more another time. It made me think of feature flags, which came up in an interview I did a few months ago, where you want to toggle features on and off. Oh, another example mentioned is how you might want to turn off verbose mode when running tests now and again, so you might use this technique to turn it on again by default after finishing the verbose off command, so we don't have to remember to turn it off again. I'm a little tired, have gotten through a LOT today, as well it being a work day. Have learned a lot today!

18/May/2021 - Manage Resources (halp) (50m) - HOME (happy)

I'm struggling quite a bit to understand the real-world use-cases for using the blocks to initialize blocks and manage resources. I've done all of the exercises for both, but had no idea how to come up with the solutions by myself. Because of that, none of the last couple lessons stuck in my brain. I have a vague idea that you can connect and disconnect to databases and rollback transactions and sign users in and out with it. So, I need to roll up my sleeves and go through the last few lessons again, maybe look up some other examples online, and keep trying the exercises until I feel like I can do them without needing to look (and not relying on memorization). I'll download the workbook and look at and see if I can see what's different across the four use-case patterns mentioned.

19/May/2021 - Block Initialization(Github) (45m) - HOME (pure joy)

I went onto GitLab and searched for ruby files that contained the phrase "yield if block_given?" inside of the main GitLab repository (it's open source). A couple files came up, I clicked on the "gitlab/lib/gitlab/ci/trace/chunked_io.rb/" file, and found it on line 25 inside of the initialize block. I opened up a new file in my terminal and copied the initialize block, and wrapped it inside of the ChunkedIO class it was defined inside in the repo (ignoring the parent classes and modules encasing it). I also removed the initialize parameters and made some of the instance variables available through the attr_accessor method. I created a new ChunkedIO object, and printed out the build and size variables inside the object which were set by default to 0. Then I created a new ChunkedIO object and added a block where I set the values to something different (chunky = ChunkedIO.new do |c| \n c.build = 3 \n c.size = 14 \n end). Then I printed out the build and size variables to see that I was able to use a block to initialize the new object with custom variables, instead of creating the block and then updating the variables after the fact. I feel like I understand this much better now. Woo! I also went onto Github and searched RSpec for the same line, and only found one example of the block initializer (self yield) method, and that was in the "rspec/rspec-mocks/lib/rspec/mocks/mutate_const.rb" file. I just went onto stack overflow to read some answers on why we'd use it, and one answer said it's useful for when you want to create an anonymous object (not assigning it to a variable) with custom initial values. Cool.

19/May/2021 - Yield in the wild (15m) - HOME (pure joy)

I searched for ruby files containing the word "yield" in the Gitlab Repository, and several hundred files showed up. I clicked on one of the first results "spec/support/helpers/feature_flag_helpers.rb" and found a bunch of examples methods that said something along the lines of "select a specific class with the index offset by a custom amount" and yield the results. I then searched for examples where the method was called outside of that file to see some examples of what the block did. I found that the methods were called in test files, and the yield was used to reduce some of the test boilerplate to make them easier to read. I'm glad to be searching for examples in the wild, it's helping me to understand these much more easily.

19/May/2021 - Self.open in the wild (15m) - HOME (pure joy)

In the Gitlab repository, I searched for Ruby files containing "self.open", which in the "Resource Management" example of the blocks course says is a common pattern, where you want to connect to a resource, perform some kind of task and close the connection once you're done. We use a block to handle the opening and closing of the resource for us, and the block we give the open method we define to handle the custom tasks we want to do. I understand the database connection example much better now. The search results brought up three files, all of which relating to authentication, specifically access, adapter and proxy files. The first example checks if a user is allowed access, and if so proceeds to update user information whether or not they are allowed access, unless it's a read only database. They don't ensure anything to handle exceptions as shown in the resource management exercises, and they don't open or close the connection. So maybe open wasn't the right thing to use for this? Instead of requiring a block to be called after the open method, they allow a block to be passed in as a parameter using the "&block" proc (haha, block proc). Then inside the method you can do block.call, which is the same as if you'd called the method with an explicit block written. The benefit of using the proc way is that you can then store a block in a variable and reuse it. Cool!