Archive – Page 2

Using Lambdas and Callables for Deferred Evaluation, Control Flow, and New Language Patterns

Ruby blocks are simply amazing, and I’ve written about some of the cool things you can do with them.

But something which confused me early on as I learned Ruby and started using gems and frameworks was when to write a block vs. when to write an explicit proc or lambda.

For example, here’s some example code provided by Faraday, a popular HTTP client for Ruby:

conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
  builder.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }
  # more code here...
end

As you can see, there are two different use of blocks/procs here. The first one is the one passed to Faraday.new — it yields builder so you can configure the request. But the second one is a “stabby lambda” (-> {}) which is passed to the authorization configuration. Why do it this way? I mean, in theory you could write an API which swaps the two:

weird_conn = Appleaday.new("keeps the doctor away!", ->(builder) {
  builder.make_me_a(:treehouse, 'Painted') { MyFavoriteColors.default }
})

Now I don’t have a particularly valid technical reason why the Faraday syntax is “correct” and my silly example is not. The simplistic answer would be “idiomatic Ruby”. We know it when we see it, and otherwise it looks off.

But I think that’s a cop-out. There must be some greater heuristic we can keep top of mind as we evaluate the best shapes for an API.

Here are some thoughts I have as I study these sorts of APIs and design new ones in my own apps and gems.

Deferring Evaluation #

Let’s start with blocks. In the original Faraday example, using a block to provide configuration via the builder local variable is a very common Ruby pattern and lies at the heart of many DSLs. Some APIs provide a block without even yielding a variable, in which case you’re calling methods on the DSL object directly—like in Rodauth:

plugin :rodauth do
  enable :login, :logout
end

In this example, enable is a method of the DSL object that is the execution context of the block. If this were written like Faraday’s API instead, it might instead look like this:

plugin :rodauth do |config|
  config.enable :login, :logout
end

You with me? Cool. So blocks are great for configuration DSLs and for setting up various sorts of “declarative” data structures. Heck, what is a .gemspec file if not a DSL using a block?

# faraday.gemspec

Gem::Specification.new do |spec|
  spec.name    = 'faraday'
  spec.version = Faraday::VERSION
  # etc.
end

OK, but what about lambdas? When is it appropriate to use a lambda instead of just a typical block? (FWIW I just love stabby lambdas…aka the -> (variables) { ... } syntax as opposed to lambda { |variables| ... }.)

I think it makes a lot of sense to use lambdas when you need to provide an expression which later gets resolved to a value—perhaps over and over again. We might call this deferred evaluation. Some APIs for example will accept either a data value or a lambda…the data expression would be evaluated right at the call site, but the lambda would be evaluated at runtime when it’s needed.

HypotheticalConfiguration.new do
  immediate_evaluation Time.now
  deferred_evaluation -> { Time.now }
end

In this example, the hypothetical configuration option has been provided two settings. The first setting receives the current time. That setting can never change—whatever the current time was when the statement was evaluated has been “baked” into the configuration. However, the second setting receives a lambda. Executing this lambda returns the current time at the time the setting is later accessed. Each occurrence of runtime logic accessing that setting would get the time at that precise moment, not a previous time when the configuration was created.

Here’s a real-world example. In the Bridgetown web framework I work on, this is part and parcel of our “Ruby front matter DSL”. Front matter is resolved immediately when a page template is read, which occurs at an earlier time than when a page template is transformed (aka rendered) to its final format (typically HTML). In an example provided by the Bridgetown documentation:

~~~ruby
# some_page.md
front_matter do
  layout :page

  url_segments = ["custom"]
  url_segments << "permalink"
  segments url_segments

  title "About Us"
  permalink -> { "#{data.segments.join("/")}/#{Bridgetown::Utils.slugify(data.title)}" }
end
~~~

This will now show up for the path: **/custom/permalink/about-us**.

Here we see code providing front matter variables such as layout, segments, and title. But it’s also providing a lambda for permalink. Because the value of permalink is a lambda, Bridgetown knows that at the time it’s about to transform the page template, it should then resolve that expression to the permalink variable.

We don’t use standard blocks for this because we also allow nested data structures via standard blocks. So you’d mix and match depending on the use case. For example:

front_matter do
  image do
    url "/path/to/image.jpg"
    alt -> { "My alternative text for #{data.title}" }
  end

  title "My Wonderful Page"
end

Here we can see there’s an image variable which will resolve to a hash: { image: "/path/to/image.jpg", alt: -> { ... } }. Because the value of alt is a lambda, it will resolve when the page renders to become "My alternative text for My Wonderful Page. We can then use those variables in the template:

<img url="<%= data.image[:url] %>" alt="<%= data.image[:alt] %>" />

Controlling the Flow #

Another interesting use of lambdas I’ve experimented with at various times is utilizing them in control flow scenarios. For instance, did you know you could write your own if and unless statements?

def do_if(condition, &block)
  condition.().then { _1 ? block.(_1) : nil }
end

def do_unless(condition, &block)
  condition.().then { _1 ? nil : block.(_1) }
end

a = 123

do_if -> { a == 123 } do |result|
  puts "it's #{result}" # => "it's true!"
end

unless_if -> { a == 123 } do |result|
  puts "it's #{result}" # => oops, this never gets run!
end

Now I don’t exactly know why you’d write anything like this, other than as a cool experiment. But maybe you need to write a method which takes two lambdas, one for the “success” case and one for the “failure” case:

def perform_work(value:, success:, failure:)
  go_do_other_stuff(value) => result

  result.completed ? success.(result) : failure.(result)
end

perform_work value: [:abc, 123],
             success: ->(result) do
               puts "Yay, it was a success! #{result.output}"
             end,
             failure: ->(result) do
               raise "Darn, we blew it. :( #{result.problem}"
             end

Sure you could write this another way, like if perform_work yielded a config object and you could pass blocks to its success and failure methods. But the nice thing about this pattern is you could use other object methods instead by converting them into lambda-like callables. Oh yes!

def time_for_work(value)
  perform_work value:, success: method(:success), failure: method(:failure)
end

def success(result)
  puts "Yay, it was a success! #{result.output}"
end

def failure(result)
  puts "Darn, we blew it. :( #{result.problem}"
end

Because you can call lambdas and Method objects the same way, the API doesn’t need to care which is which.

# calling a lambda
my_lambda.call(:abc)
# or my preferred syntax:
my_lambda.(:abc)

# calling a method object
my_method.(:abc)

# did you know any object can be callable
# if it responds to a `call` method?
class MyCallable
  def call(value)
    "Value is: #{value}"
  end
end

MyCallable.new.(123) # => 123

So actually with that last example, we see that you can pass any callable object to an API which accepts lambdas (more or less). How cool is that?!

Wait, could we try that with our earlier do_if example?

class ValueCallable
  def initialize(value)
    @value = value
  end

  def call
    @value
  end
end

do_if ValueCallable.new(123) do |result|
  puts "it's as easy as #{result}" # => "it's as easy as 123!"
end

do_if ValueCallable.new(nil) do |result|
  puts "nope" # this never executes
end

And if we wanted to make that ever slicker, we could make the class itself callable!

class ValueCallable
  def self.call(...)
    # forward all arguments
    new(...).call
  end

  # ...
end

MyCallable.(123) # => new instance of MyCallable with @value: 123

Discovering New Language Patterns #

I hope you’re starting to get a sense for how understanding the difference between blocks and lambdas—or perhaps we just think of them as “callables”—can help you build more expressive APIs utilizing modular building-blocks which offer deferred evaluation, control flow, and entirely new patterns.

This is what I love so much about Ruby. We think of Ruby as a programming language, and it certainly is—and a “high level” one at that with an impressive heaping of abstractions. When “all cylinders are firing”, “programmer happiness” ensues.

But Ruby goes even a step beyond. Because of the expressiveness of the syntax Ruby provides us, we can in a sense write our own languages. Our APIs can have an advanced shape, with many affordances people typically assume must be baked into the language itself.

I’ve been hard at work on Bridgetown 2.0, and one of the areas of improvements I hope to focus on is our use of blocks vs. callables and the distinction between the two. Blocks are so easily used (and abused!), but I think the callable pattern may be one of Ruby’s best unsung heroes. The fact you can not only pass a lambda but any object method or really any object of any kind as a “callable” is, well, pretty mind-blowing.

I’ll close with one more wild factoid I always forget until I remember…you can convert methods directly to procs! What’s the point of doing that? Because then you can pass your callables into any standard methods which expect blocks. Like, for real!

class MultiplyNumber
  def initialize(multiplicand)
    @multiplicand = multiplicand
  end

  def call(multiplier)
    @multiplicand * multiplier
  end

  def to_proc = method(:call).to_proc
end

multi = MultiplyNumber.new(10)

[1, 2, 3].map(&multi) # => [10, 20, 30]

Ruby is so weird. I love it. small red gem symbolizing the Ruby language



Ruby, Fully Stacked

This is a comeback story of sorts for Fullstack Ruby, but it’s more than that.

I’ll spare you the intimate details of my serious Ruby-flavored burnout in the back half of 2023—if you really care to you can read up on it here as part of the Bridgetown 2.0 kickoff announcement. (Did I mention I’m hard on work on the next version of Bridgetown? 😁)

TL;DR: I got thoroughly bummed about the state of the Rails & Turbo ecosystems due to a long series of epic fails (in this author’s opinion) on the part of DHH and the cult of personality surrounding him which should have resulted in his ouster but instead seemed to cause Rails/Turbo to slide into yet more sorta-mostly-but-not-really-open-source insularity.

I mean, if I’m going to choose to continue working in a language community that is already niche (let’s face it, no matter how much you love Ruby in 2024, it’s a niche language at this point in its history), do I really want to bolster behavior I find extremely offensive? It makes no sense. I’m no real fan of TypeScript and the state of fullstack JS frameworks, but gosh darnnit I might as well just go full dark side if I’m unable to identify with a community I truly enjoy and respect.

Obviously I’m Still Here, So What Happened? #

Time, essentially. By “emotionally walking away” from the fray of Ruby development and focusing on projects out in other corners of the industry (The Spicy Web, That HTML Blog, The Internet Review…uh, apparently I solve burnout in one area by burning out in some other area. Do I need therapy? DON’T ANSWER THAT! 😅), I was able to get some much needed perspective. And that period of rumination led me to this perhaps unsurprising conclusion:

I love Ruby! 😍

And more to the self-serving point, I love Bridgetown + Roda (+ Sequel apparently as the next logical link in this chain). Nothing makes me happier than using these tools to craft delightful online experiences, and while I can name tools in other language ecosystems I definitely appreciate—namely Eleventy by the inimitable Zach Leatherman, Fastify for rapid API development, and of course the One Frontend Bundler to Rule Them All: esbuild—my happy place still remains in the Ruby corner.

So it was time to step up to the plate once again. Time to go all in on Ruby, and specifically “alt Ruby” outside of the confines of a certain train-themed framework which dominates the space. It’s a niche within a niche, certainly, but at least it’s a niche I can be proud of. I am now a man “unconstrained”…able and willing to jump down any rabbit hole I might come across. Are you ready for some real “Keep Ruby Weird” vibes? I sure am!

Yes! Next question…

But seriously (not too seriously), I do intend to get everything up and running again. The full redesign of this website is the first step. I completely started over with a fresh Bridgetown install, using a variant of a theme I developed for That HTML Blog, which itself is a riff off of an older theme I developed for JaredWhite.com. (Hey, it’s a layout I really like!) I also rewrote the About page and my bio with a bit of a, hmm shall we say, salty take on the state of Rubyist frontends. 🤓

Now that this has all gone live, I can finally start mapping out future content. I have a ton of material in the form of work I’ve put into various projects over the past year utilizing both Bridgetown and Roda, so henceforth I can start “plumbing the depths” and bringing this material to the surface. It’ll not only benefit readers & listeners of Fullstack Ruby like yourself, but provide educational resources to users of Bridgetown & Roda. Best of all possible worlds? Precisely!

OK, but Jared. All I do every day as a Ruby programmer is write Rails controllers and models and views and models and controllers and service objects [don’t!] and background jobs. How does any of this help me??”

Well, there’s nothing like exploring other patterns and techniques and ways of looking at things in other frameworks and languages and whatnot to level up your skills in your primary stack of choice. My love of Ruby has definitely made me a better JavaScript developer, and guess what? The reverse is also true, believe it or not! Researching other stacks can help solidify what it is you love about the tools you already use, or better yet it can give you brand new ideas to serve as a launching pad for writing better software.

I guess all I am saying is give alternative Ruby a chance. ✌️

You might be very pleasantly surprised. small red gem symbolizing the Ruby language



Episode 9: Preact Signals and the Signalize Gem

What are signals? What is find-grained reactivity? Why is everyone talking about them on the frontend these days? And what, if anything, can we apply from our newfound knowledge of signals to backend programming? Is it possible to use signals in Ruby? (Yes!) Learn all about signals, the Preact Signals library, and the new Signalize gem right here in the latest episode of Fullstack Ruby.


Become a part of the Fullstack Ruby community and learn how to put your Ruby skills to work on the backend AND the frontend. Know somebody who’s a JavaScript developer but is interested in learning more about Ruby? Share the site, podcast, or newsletter with them!

Theme music courtesy of Epidemic Sound.


Subscribe to the RSS feed

in your podcast player of choice.



Episode 8: Hotwiring Multi-Platform Rails Apps with Ayush Newatia

Ayush is on the core team of Bridgetown, a specialist in Ruby on Rails and Hotwire app development, and a personal friend. I’m very excited to have him on the show today to talk about all things fullstack web dev, his new book The Rails & Hotwire Codex, and why “vanilla” is awesome!


Become a part of the Fullstack Ruby community and learn how to put your Ruby skills to work on the backend AND the frontend. Know somebody who’s a JavaScript developer but is interested in learning more about Ruby? Share the site, podcast, or newsletter with them!

Theme music courtesy of Epidemic Sound.


Subscribe to the RSS feed

in your podcast player of choice.



Episode 7: Ruby on Wasm: Is This The Future?

Ruby can now run on Wasm! WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications. So…we can now run “real” Ruby in the browser, right? Yes! …and no. Caveat emptor, but nevertheless this is a very welcome development and promising technology for the future. Let’s dive in.


Become a part of the Fullstack Ruby community and learn how to put your Ruby skills to work on the backend AND the frontend. Know somebody who’s a JavaScript developer but is interested in learning more about Ruby? Share the site, podcast, or newsletter with them!

Theme music courtesy of Epidemic Sound.


Subscribe to the RSS feed

in your podcast player of choice.



Episode 6: How Do You Manage Ruby Application Dependencies?

Every Ruby web framework has its own way of configuring itself as well as third-party dependencies. In some cases it’s largely up to you, in other cases it’s clearly spelled out. There may or may not also be some “magic” involved in requiring gems added to a Gemfile. As a maintainer of Bridgetown, I’m currently working through all these issues as I ready the next major release which will feature a brand-new initialization system. Listen to the show to hear a rundown of some of the configuration setups out there and what we’ve chosen to focus on for Bridgetown!


Become a part of the Fullstack Ruby community and learn how to put your Ruby skills to work on the backend AND the frontend. Know somebody who’s a JavaScript developer but is interested in learning more about Ruby? Share the site, podcast, or newsletter with them!

The Fullstack Ruby Podcast is a production of Whitefusion, a boutique web studio based in Portland, OR.

Theme music courtesy of Epidemic Sound.


Subscribe to the RSS feed

in your podcast player of choice.



How to Think Like a Framework Developer

Credit: Xavi Cabrera on Unsplash

I’ve always really enjoyed working on frameworks. Not even libraries per se, but frameworks.

I worked on a PHP/JS frontend framework called Xajax back in the early 2000s, and then briefly out of that spawned a Rails-like framework called Willowgarden for the then-new PHP 5.

Once I officially switched to Ruby on Rails, I never looked back. But one thing that was a bit of a bummer after a while was missing that feeling you get when you’re building a framework.

Having now worked a couple years on Bridgetown, I’m so thankful that I get to work on frameworks as part of my job. But more than that, I’ve discovered over the years that you can learn to think like a framework developer even when you’re not directly programming a framework.

The Why #

The first question a framework developer always asks when evaluating a new idea, a feature request, an improvement, a refactoring, is why. I don’t mean a reflexive why, like “why should we bother looking into this, gosh!” I mean a long-term, justifying why which is chiefly concerned with the long-term health of the project.

When you’re just banging out feature after feature for a specific application, it’s easy to get lost in the daily grind. Another ticket on the board. Another comment on an issue. Another meeting. Another sprint. There’s often little time to reflect on the rationale of the feature you’re working on, how it might relate to other features already in flight, who is responsible for ensuring the feature’s stability over time, the downsides of adding the feature (there are always downsides to every new feature), how to document gotchas or architectural dragons lurking due to how the feature gets implemented, etc., etc. And do you even have the time or authority to investigate features you can deprecate or remove? (lol 😂)

When you’re working on a framework, you must ask yourself these questions. Architectural integrity, documentation, maintainability, health of the project as a whole—those aren’t secondary concerns compared to feature implementation. Those ARE the concerns! Feature implementation is subservient to the big picture, and sometimes it’s perfectly fine—desirable even–to say no.

I believe most applications would be better served if they were run more like a framework. It might seem at first like the velocity slows way down, and the pace of feature rollout slows to a trickle. But over time, I’ve become convinced the quality and the stability of the software and the team building it is greatly increased.

(I also believe most applications would be better served if they were run like an open source project even if they aren’t open source, but that’s a conversation for another day…)

The How #

The other main question a framework developer must ask when evaluating an addition or change is how. Now that might seem silly on the face of it. How?!?! By opening your code editor and writing some code! That’s how! (Duh. 😝)

What I mean though is how are you going to implement that new feature or make that change in the “best” way possible given a wide variety of potential solutions all involving pros and cons which could have long-lasting ramifications for the project as a whole.

When I’m in full-blown “framework” mode, I’ll spend a great deal of time toying with the design of a single API. I’ll try out different method names, different class names. I’ll split apart this object from that object. I’ll combine some objects together. I’ll experiment with mixins. I’ll consider metaprogramming. I’ll try using the API as a “consumer” and see how it feels. I’ll pseudocode a new approach in the consuming app, then backport that into the framework. It might take me hours, days—even weeks—just to end up with what might be in the end simply a few lines of code.

Because it has to feel RIGHT.

It’s hard to change a framework once a bunch of people already depend on it. You hate to have a horde of angry developers coming after you with pitchforks and torches just because you renamed a method or removed a class. So it’s really, really, REALLY important you get things right before you commit them. It requires a lot of care and finesse. Sometimes you end up having to scrap an entire PR and start over from scratch.

How many app projects have you been on where the team regularly comes together, evaluates a speculative approach, wonders “hmm, not sure if this feels right”, then decides to completely scrap it and start over? Yeah, me neither. Maybe when you’re evaluating a brand-new library or framework or build tool. But day-to-day features? It’s rare indeed.

But again, my assertion is that most applications would be better served if they were run more like a framework—more tasks that are speculative, concerned primarily with architectural integrity, fully malleable, ready to be tossed out if it feels smelly or overly complex. Because you’re not just building “a pile of features” or “a bag of UI”. You’re building a holistic blueprint for comprehensible interactivity and data processing. You need to be able to zoom out to the 20,000 foot level at a moment’s notice and intuitively “grok” how all the component parts fit together and why they’re there and how they operate without too much friction or overlap. And if you can’t do that, I can almost guarantee you your users won’t be able to do that either.



The Who #

Depending on the project you’re working on, your experience level, and the dynamics of your team, it may actually be rather challenging to rise to the level of “framework” developer within the context of your job. You may experience some pushback. You may be thought of as pedantic, even grumpy. I had to apologize just the other day for repeatedly stressing the same points over and over about an architectural concern I had to other folks in the meeting. It was frustrating to feel like I could see something obvious that we’d all need to come to a clear consensus on before moving forward, yet seemingly nobody else was visualizing that concern.

All I can tell you is: it’s worth it to put in that extra effort. Eventually people will notice. And then instead of simply throwing features over the wall to you and expecting you to blandly implement them, they’ll start asking you questions. And you’ll be able to ask them questions back. And now you have a dialog going about how to improve the long-term quality of your software and your team.

It’s a beautiful thing. small red gem symbolizing the Ruby language



Episode 5: Optimized for Programmer Happiness

Ruby is optimized for programmer happiness. What does that even mean? Which programmer? Whose happiness? What if you use Ruby and aren’t happy? Does that mean Ruby failed?

All this and much more to be covered in today’s episode—not a deep dive into a technical topic, but a deep dive into the philosophy of programming, the “Ruby way”, OOP, the dangers of monocultures, and the need to recognize implicit biases when engaging in technology debates.


Become a part of the Fullstack Ruby community and learn how to put your Ruby skills to work on the backend AND the frontend. Know somebody who’s a JavaScript developer but is interested in learning more about Ruby? Share the site, podcast, or newsletter with them!

The Fullstack Ruby Podcast is a production of Whitefusion, a boutique web studio based in Portland, OR.

Theme music courtesy of Epidemic Sound.


Subscribe to the RSS feed

in your podcast player of choice.

Newer Posts Older Posts
Skip to content