How does Cucumber DSL work? - ruby

Let's take:
When /^(?:|I )fill in the following:$/ do |fields|
fields.rows_hash.each do |name, value|
When %{I fill in "#{name}" with "#{value}"}
end
end
With my rudimentary Ruby knowledge, I was thinking that When is a method call that takes a regular expression and a block.
But then, I am also thinking that this is a definition, and not a method call, but then how is it achieved? How can When define something?

The code is the following (code taken from here):
def register_rb_step_definition(regexp, symbol = nil, options = {}, &proc)
proc_or_sym = symbol || proc
RbDsl.register_rb_step_definition(regexp, proc_or_sym, options)
end
When, Given, Then are an alias to register_rb_step_definition.
You pass a regular expression as an argument, and a block.
Each step definition is registered with the regular expression and the block. When the test is executed, cucumber looks in the previously registered steps and if any regular expression matches it executes the block associated to that regular expression.

Related

In Ruby, is an if/elsif/else statement's subordinate block the same as a 'block' that is passed as a parameter?

I was doing some reading on if/elsif/else in Ruby, and I ran into some differences in terminology when describing how control expressions work.
In the Ruby Programming Wikibooks (emphasis added):
A conditional Branch takes the result of a test expression and executes a block of code depending whether the test expression is true or false.
and
An if expression, for example, not only determines whether a subordinate block of code will execute, but also results in a value itself.
Ruby-doc.org, however, does not mention blocks at all in the definitions:
The simplest if expression has two parts, a “test” expression and a “then” expression. If the “test” expression evaluates to a true then the “then” expression is evaluated.
Typically, when I have read about 'blocks' in Ruby, it has almost always been within the context of procs and lambdas. For example, rubylearning.com defines a block:
A Ruby block is a way of grouping statements, and may appear only in the source adjacent to a method call; the block is written starting on the same line as the method call's last parameter (or the closing parenthesis of the parameter list).
The questions:
When talking about blocks of code in Ruby, are we talking about
the group of code that gets passed in to a method or are we simply
talking about a group of code in general?
Is there a way to easily differentiate between the two (and is there
a technical difference between the two)?
Context for these questions: I am wondering if referring to the code inside of conditionals as blocks will be confusing to to new Ruby programmers when they are later introduced to blocks, procs, and lambdas.
TL;DR if...end is an expression, not a block
The proper use of the term block in Ruby is the code passed to a method in between do...end or curly braces {...}. A block can be and often is implicitly converted into a Proc within a method by using the &block syntax in the method signature. This new Proc is an object with its own methods that can be passed to other methods, stored in variables and data structures, called repeatedly, etc...
def block_to_proc(&block)
prc = block
puts prc
prc.class
end
block_to_proc { 'inside the block' }
# "#<Proc:0x007fa626845a98#(irb):21>"
# => Proc
In the code above, a Proc is being implicitly created with the block as its body and assigned to the variable block. Likewise, a Proc (or a lambda, a type of Proc) can be "expanded" into blocks and passed to methods that are expecting them, by using the &block syntax at the end of an arguments list.
def proc_to_block
result = yield # only the return value of the block can be saved, not the block itself
puts result
result.class
end
block = Proc.new { 'inside the Proc' }
proc_to_block(&block)
# "inside the Proc"
# => String
Although there's somewhat of a two-way street between blocks and Procs, they're not the same. Notice that to define a Proc we had to pass a block to Proc.new. Strictly speaking a block is just a chunk of code passed to a method whose execution is deferred until explicitly called. A Proc is defined with a block, its execution is also deferred until called, but it is a bonafide object just like any other. A block cannot survive on its own, a Proc can.
On the other hand, block or block of code is sometimes casually used to refer to any discreet chunk of code enclosed by Ruby keywords terminating with end: if...else...end, begin...rescue...end, def...end, class...end, module...end, until...end. But these are not really blocks, per se, and only really resemble them on the surface. Often they also have deferred execution until some condition is met. But they can stand entirely on their own, and always have return values. Ruby-doc.org's use of "expression" is more accurate.
From wikipedia
An expression in a programming language is a combination of one or
more explicit values, constants, variables, operators, and functions
that the programming language interprets (according to its particular
rules of precedence and of association) and computes to produce ("to
return", in a stateful environment) another value.
This is why you can do things like this
return_value = if 'expression'
true
end
return_value # => true
Try doing that with a block
return_value = do
true
end
# SyntaxError: (irb):24: syntax error, unexpected keyword_do_block
# return_value = do
# ^
A block is not an expression on its own. It needs either yield or a conversion to a Proc to survive. What happens when we pass a block to a method that doesn't want one?
puts("indifferent") { "to blocks" }
# "indifferent"
# => nil
The block is totally lost, it disappears with no return value, no execution, as if it never existed. It needs yield to complete the expression and produce a return value.
class Object
def puts(*args)
super
yield if block_given?
end
end
puts("mindful") { "of blocks" }
# "mindful"
# => "of blocks"

How does [ ] work on a class in Ruby

I see that I can get a list of files in a directory using
Dir["*"]
How am I supposed to read that syntax exactly ? As I know that you can use [ ] to fetch a value from a array or a hash.
How does [ ] work on a call ?
[] is simply a method, like #to_s, #object_id. etc.
You can define it on any object:
class CoolClass
def [](v)
puts "hello #{v}"
end
end
CoolClass.new["John"] # => "hello John"
In your case it's defined as singleton method, in this way:
class Dir
def self.[](v)
...
end
end
From the Ruby Docs, Dir["*"] is equivalent to Dir.glob(["*"]). (As pointed out, it's syntactic sugar)
Dir isn't a call, it's a class, and objects of class Dir are directory streams, which you access like an array.
In your specific case, Dir["*"] will return an array of filenames that are found from the pattern passed as Dir[patternString]. "*" as a pattern will match zero or more characters, in other words, it will match everything, and thus will return an array of all of the filenames in that directory.
For your second question, you can just define it as any other method like so:
class YourClass
def self.[](v)
#your code here
end
end
The method Dir::glob takes an argument, and provides an array of all directories and files nested under the argument. (From there, you can grab the index of the array with [0].) The argument may include a pattern to match, along with flags. The argument (pattern, flags) may be options similar (but not exactly) regular expressions.
From the docs, including a couple of patterns/flags that may be of interest to you:
Note that this pattern is not a regexp, it's closer to a shell glob. See File.fnmatch for the meaning of the flags parameter. Note that case sensitivity depends on your system (so File::FNM_CASEFOLD is ignored), as does the order in which the results are returned.
* - Matches any file. Can be restricted by other values in the glob. Equivalent to / .* /x in regexp.
[set] - Matches any one character in set. Behaves exactly like character sets in Regexp, including set negation ([^a-z]).
The shorthand of Dir::glob() is Dir[], although I prefer the long form. As you saw above, using brackets denotes a special pattern/flag for the argument. Here are some examples (from the docs) that may better explain this:
Dir["config.?"] #=> ["config.h"]
Dir.glob("config.?") #=> ["config.h"]
Dir.glob("*.[a-z][a-z]") #=> ["main.rb"]
Dir.glob("*") #=> ["config.h", "main.rb"]
It is possible for you to redefine the [] method for Dir, but I will not show how -- many (and myself) do not recommend monkey-patching core Ruby classes and modules. However, you can create the method in a class of your own. See the following:
class User
# Class method => User.new[arg]
def self.[](arg)
end
# Instance method => #user[arg]
def [](arg)
end
end
Dir is an object just like any other object (it just happens to be an instance of class Class), and [] is a method just like any other method (it just happens to have a funny name, and special syntactic conveniences that allow it to called using a different syntax in addition to the normal one).
So, you define it just like any other method:
class MyClass
def self.[](*) end
end

Whats the ruby syntax for calling a method with multiple parameters and a block?

Ruby doesn't like this:
item (:name, :text) {
label('Name')
}
And I don't know why. I'm attempting to create a DSL. The 'item' method looks like this:
def item(name, type, &block)
i = QbeItemBuilder.new(#ds, name, QbeType.gettype(type))
i.instance_exec &block
end
Take a name for the item, a type for the item, and a block. Construct an item builder, and execute the block in its context.
Regardless of whether or not I need to use instance_exec (I'm thinking that I don't - it can be stuffed in the initialiser), I get this:
SyntaxError (ds_name.ds:5: syntax error, unexpected ',', expecting ')'
item (:name, :text) {
^
How do I invoke method with multiple arguments and a block? What does ruby think I'm trying to do?
The space before parentheses is causing ruby to evaluate (:name, :text) as single argument before calling the method which results in a syntax error. Look at these examples for illustration:
puts 1 # equivalent to puts(1) - valid
puts (1) # equivalent to puts((1)) - valid
puts (1..2) # equivalent to puts((1..2)) - valid
puts (1, 2) # equivalent to puts((1, 2)) - syntax error
puts(1, 2) # valid
Your way of providing the block is syntactically valid, however when the block is not in the same line as the method call it is usually better to use do ... end syntax.
So to answer your question you can use:
item(:name, :text) { label('Name') }
or:
item(:name, :text) do
label('Name')
end
Remove the space before the ( in item (:name, :text) {

Ruby instance_exec / instance_eval with arguments

I'm trying to dynamically call a method given in a string using parameters given in the same string, I'm getting stuck on supplying the parameters though...
I currently have:
query = Query.new
while true
input = gets.split(%r{[/[[:blank:]]/,]})
puts (query.instance_exec(*input.drop(1)) { |x|
instance_eval input.at(0)
})
end
So the method name is input(0) and the arguments to this method are in the rest of input.
Is there any way to call this method with those parameters?
The method you are looking for is send. Its first argument will be the method, and the rest will be passed to that method.
query = Query.new
puts query.send(*gets.split(/\s+/)) while true
You can use while modifier.
Your regex looks complicated. I made it look simple.
Don't forget to use the splat operator *, which decomposes an array.

Ruby: nested regular expressions and string replace

I'm using CodeRay for syntax highlighting, but I'm having trouble with this regular expression. The text will look like this:
<pre><code>:::ruby
def say_hello
puts 'hello!'
end
</code></pre>
This part: :::ruby will tell CodeRay which language the code block should be interpreted as (but it needs to be optional). So here's what I have so far:
def coderay(text)
text.gsub(/\<pre\>\<code\>(.+?)\<\/code\>\<\/pre\>/m) do
CodeRay.scan($2, $3).div()
end
end
$2 contains the code that I'm formatting (including the line that says which language to format it in), but I need to extract that first line so I can pass it as the second parameter to scan() or pass it a default parameter if that language line wasn't found. How can I do this?
In Ruby 1.9, using named groups:
default_lang=:ruby
def coderay(text)
text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
if $~[:lang].nil?
lang=default_lang
else
lang = $~[:lang].intern
end
CodeRay.scan($~[:code], lang).div()
end
end
default_lang could also be a class or object variable rather than a local, depending on the context of coderay.
Same, but using an inline expression to handle the optional language:
default_lang=:ruby
def coderay(text)
text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
CodeRay.scan($~[:code], $~[:lang].nil? ? default_lang : $~[:lang].intern).div()
end
end
The second option is a little messier, hence you might want to avoid it.
It turns out named groups in a non-matching optional group are still counted in Ruby, so handling unmatched numbered groups isn't any different from unmatched named groups, unlike what I first thought. You can thus replace the named group references with positional references in the above and it should work the same.
default_lang=:ruby
def coderay(text)
text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
CodeRay.scan($2, $1.nil? ? default_lang : $1.intern).div()
end
end
def coderay(text)
text.gsub(%r!<pre><code>(?::{3}(?<lang>\w+)\s+)?(?<code>.+?)</code></pre>!m) do
if $1.nil?
lang=default_lang
else
lang = $1.intern
end
CodeRay.scan($2, lang).div()
end
end

Resources