Please provide the Ruby Blocks content you want me to translate. I need the text to be able to translate it to Chinese. Just paste the Ruby code here, and I will give you the Chinese translation.
Ruby Blocks

原始链接: https://tech.stonecharioteer.com/posts/2025/ruby-blocks/

## Ruby 的力量:理解块 作者通过认识到**块**的基本作用,在理解 Ruby 上取得了突破——本质上是*带着*代码块作为输入调用的方法。这不仅限于测试框架(如 RSpec,虽然在那里很常见,例如 `it` 方法),而是贯穿整个语言。 Ruby 的设计允许方法链式调用,并且几乎*所有*方法都可以接受一个块。这使得代码简洁易读,例如使用 `times` 进行迭代,或定义自定义控制流(如自定义 `unless` 或重试机制)。作者强调了这如何能够构建领域特定语言 (DSL)——创建代码,使其*读起来*像对任务的自然语言描述,如 Rails 路由和自定义 TodoList 示例所示。 这并非魔法,而是一个核心特性。Ruby 的语法旨在使这些块感觉不可见,让开发者专注于*做什么*,而不是*语言如何*解释它。这种可读性是 Ruby 力量和优雅的关键,理解这个概念对于真正掌握这门语言至关重要。尽管仍在探索 RSpec,但作者确信它的力量源于其本质上就是 Ruby 本身。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 Ruby 块 (stonecharioteer.com) 12 分,来自 stonecharioteer 2 小时前 | 隐藏 | 过去 | 收藏 | 1 条评论 stonecharioteer 23 分钟前 [–] 我非常喜欢 Ruby 以及它在各处使用块的方式! 这篇文章是我写来强调这一点的。回复 考虑申请 YC 的 2026 年冬季批次! 申请截止至 11 月 10 日 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

I think I’m really starting to get Ruby. At least I can read Ruby and tell you what’s happening.

I haven’t read a single tutorial on RSpec, the testing framework we use at Chatwoot for the Rails backend. I didn’t want to spend too much time in tutorial hell, and I’m sure that I should read more about it soon.

I’ve written about Ruby blocks before, but I think it bears repeating. This is a method call with a block as an input.

1
2
3
perform "input_value" do
  puts "I'll get called with that function"
end

Don’t worry about how the function is implemented for now. I’m hand-waving this so that you can notice something.

This is also a method call with a block as an input.

1
it("can do something") { puts "The cake is a lie" }

But here’s something else that’s a method call with a block as an input.

In lib/calculator.rb:

1
2
3
4
5
class Calculator
  def add(a, b)
    a + b
  end
end

In spec/calculator_spec.rb:

1
2
3
4
5
6
7
8
require_relative "../lib/calculator"

RSpec.describe Calculator do
  it "adds two numbers" do
    calc = Calculator.new
    expect(calc.add(2, 3)).to eq(5)
  end
end

The highlighted lines include a method call to it with a block.

Look at that again. It’s a method call.

That is crazy. It is so sublime that I can’t explain how excited this makes me. If I have to teach Ruby to a Pythonista I’d ask them to ensure they see what this is. Until you grok this, it won’t matter how much you try to understand Ruby. Ruby’s readability comes from this feature. I’m still not sold on RSpec though, but I am open to learning it because it is, ultimately, Ruby.

Let’s really see what you can do with blocks.

Building Your Own Language

1
2
3
4
5
6
7
3.times do
  puts "Hello"
end

5.times { puts "World" }

10.downto(1) { |i| puts i }

These are all method calls on integers. times is a method. downto is a method. And every method takes blocks.

But we can also take this to the monke.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Integer
  def seconds 
    self
  end
  def minutes
    self * 60
  end
  def from_now
    Time.now + self
  end
end

meeting_time = 30.minutes.from_now
puts "Meeting is in 30 minutes: #{meeting_time}"

That line is readable, not because Ruby is magically more readable than other languages, but because we’re daisy-chaining methods we implemented on the built-in type. We gave Integer wings.

We created a mini language that looks like it somehow isn’t Ruby but, sweet God in Vaikuntha, it really is.

Resource Management

In Python I’m used to doing this.

1
2
3
with open("data.txt", "w") as file:
  file.write("This is some data\n")
  file.write("This is some more data\n")

But you’d be surprised how much production code is in the wild that doesn’t use this. You should be writing it like this in Python, but with is a language keyword in Python.

What would I do in Ruby?

1
2
3
4
File.open("data.txt", "w") do |file| 
  file.puts "This is some data"
  file.puts "This is some more data"
end

File.open is a regular old Ruby method. It takes block. THEY ALL TAKE BLOCKS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def with_timer(name)
  start_time = Time.now
  yield
  end_time = Time.now
  puts "#{name} took #{end_time - start_time} seconds" 
end

with_timer("Database query") do
  # Some expensive operation
  sleep(2)
end

That just added a neat side-effect to the database operation.

Building a DSL

Now let’s really turn things up.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class TodoList
  def initialize
    @tasks = []
  end

  def task(description, &block)
    task_obj = Task.new(description)
    task_obj.instance_eval(&block) if block
    @tasks << task_obj
  end

  def show
    @tasks.each { |t| puts t}
  end
end

class Task
  attr_accessor :priority, :due_date, :description

  def initialize(description)
    @description = description
  end

  def priority(level)
    @priority = level
  end

  def due(date)
    @due_date = date
  end

  def to_s
    "#{@description} (Priority: #{@priority}, Due: #{@due_date})"
  end
end


# let's use this
def todo(&block)
  list = TodoList.new
  list.instance_eval(&block)
  list
end

my_todos = todo do
  task "Write a blog post" do
    priority "high"
    due "2025-10-15"
  end
  
  task "Review PR" do
    priority "medium"
    due "2025-10-14"
  end
end

my_todos.show

Just stop reading and look at that syntax.

I had a problem when I first tried learning Ruby in 2021. I had tried, because I was trying to just learn a new programming language (as one should definitely do annually), and I was a little gob-smacked because I didn’t understand the config.rb file from Rails. It had a bunch of things like this.

It looked like a damned configuration file.

Django tries to do this with the settings.py file, but it doesn’t succeed.

This is exactly why Rails routing looks like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Rails.application.routes.draw do
  resources :users do
    member do
      get :profile
      post :activate
    end
  end

  namespace :admin do
    resources :posts
  end
end

That’s not something special from Rails. That’s just a bunch of Ruby methods taking blocks! The draw, resources, member, namespace, they’re all just regular methods.

All of this looked like some magical DSL to me. But it was not. It was just regular old Ruby.

Custom Control Flow

Okay let’s make our own unless or if syntax. Just because I’m feeling like it.

1
2
3
4
5
6
7
8
def only_on_weekdays
  yield unless [0, 6].include?(Time.now.wday)
end

only_on_weekdays do
  puts "Let's get to work!"

end

Or maybe a retry function?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def with_retry(max_attempts: 3)
  attempts = 0
  begin
    attempts += 1
    yield
  rescue StandardError => e
    if attempts < max_attempts
      puts "Attempt #{attempts} failed, retrying..."
      retry
    else
      puts "Max attempts reached, giving up"
      raise e
    end
  end
end

with_retry(max_attempts: 3) do
  # some unreliable API

  puts "Attempting connection..."
  raise "Connection failed" if rand > 0.7
  puts "Success!"
end

Putting this all together

1
2
3
(1..5).select { |n| n.even? }
  .map { |n| n * 2 }
  .reduce(0) { |sum, n| sum + n }

Each and every one of those was a method call that took a block!

It’s all a block. Once I saw this pattern, I could not unsee it!

And why would I want to unsee it? It’s beautiful. It sparkles, damn it.

💡 The Real Magic

The magic isn’t just that you can do this. It’s that Ruby’s syntax makes it so natural that blocks become invisible. When you write 5.times { puts “Hello” }, you don’t think “I’m calling the times method and passing it a block.” You think “I’m doing something 5 times.” You read RSpec code and think “Yeah, that reads like English.”

That’s the genius of Ruby. The language gets out of your way and lets you think about the problem, not the syntax.

联系我们 contact @ memedata.com