Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does `do` ... `end` statement work without a block parameter?

For example, in a Rails gemfile:

group :development, :test do
  gem 'rspec-rails', '~> 2.0'
end

what is going on with the do ... end statement? And with rspec:

describe "Foo" do
  context "bar" do
     expect...
  end
end

are the do ... end-s creating a block whose information in between is being used elsewhere? How is this working without setting a block parameter?

like image 810
Frank Avatar asked Mar 17 '23 03:03

Frank


2 Answers

This is called Domain-Specific Language

A Domain-Specific Language, or DSL, is “a programming language of limited expressiveness focused on a particular domain”. It makes tasks in its domain easier by removing extraneous code for a particular task and allowing you to focus on the specific task at hand. It also helps other people read the code, because the purpose of the code is so clear.

Basically this

group :development, :test do
  gem 'rspec-rails', '~> 2.0'
end

is just a call of the method group with arguments :development, :test and with a block gem 'rspec-rails', '~> 2.0'. Which is defined in this way in Bundler DSL:

def group(*args, &blk)
  ...
end

Same for your second example.

DSL defines methods to group examples, most notably describe, and exposes them as class methods of RSpec.

.describe is being implemented in this way:

def describe(doc_string, *metadata_keys, metadata = {}, &example_implementation)
  ...
end

You can read more about writing Domain-Specific Language in Ruby in this thoughtbot.com article

like image 184
Rustam A. Gasanov Avatar answered Mar 19 '23 18:03

Rustam A. Gasanov


Receiving a block parameter is optional. If you are not using block variables, you don't have to write |...| even if the block is called with block variables.

def foo &block
  block.call(:foo)
end

foo{|v| puts "I received `:foo', but am not using it."}
foo{puts "I am called with `:foo', but am not receiving it."}

And also if the method is defined not to pass any block variable, then you don't need |...| in the block.

def bar &block
  block.call
end

bar{puts "I am not called with a block variable in the first place."}
like image 37
sawa Avatar answered Mar 19 '23 18:03

sawa