Can I incrementally configure the test subject in nexted contexts? - ruby

I'm new to Ruby and RSpec, and I'm looking for a way to incrementally configure the test subject in nested contexts. The sort of thing I want to do is illustrated by the following, obviously contrived, example:
describe "Array" do
context "An array" do
let (:array_length) do
a = Array.new
config_array a
return a.length
end
context "with 1 element" do
def config_array(arr)
arr << '1'
end
it "should have length 1" do
expect(array_length).to eq(1)
end
context "and an additional element" do
def config_array(arr)
arr << '1'
arr << '2'
end
it "should have length 2" do
expect(array_length).to eq(2)
end
context "and yet another additional element" do
def config_array(arr)
arr << '1'
arr << '2'
arr << '3'
end
it "should have length 3" do
expect(array_length).to eq(3)
end
end
end
end
end
end
The tests in this example pass, but I'd like to be able to do something like:
# snip
context "and an additional element" do
def config_array(arr)
parent_context_config_array arr
arr << '2'
end
it "should have length 2" do
expect(array_length).to eq(2)
end
context "and yet another additional element" do
def config_array(arr)
parent_context_config_array arr
arr << '3'
end
# snip
Is there a simple way to do this? Obviously the real test subject I'm working with is more complex than this. I'm basically looking for a way to keep the code DRY while adding additional configuration to the test subject in nested contexts.

I would do something like that:
describe 'Array' do
subject { Array.new }
context '#length' do
context 'with 1 element' do
before { subject << '1' }
it 'should eq 1' do
expect(subject.length).to eq 1
end
context 'and an additional element' do
before { subject << '2' }
it 'should eq 2' do
expect(subject.length).to eq 2
end
# ...
end
end
end
end
All the before blocks (shortcut for before(:each)) in the parent contexts of a given it block are going to be evaluated in descending order before the actual it code. Together, that forms what we call an example.
The subject method is simply syntactic sugar around the let method. The first time we call it inside an example the block that we provided in the subject declaration is evaluated and its result memoized. Therefore any ulterior call to subject in that particular example would return the same object.
We take advantage of that in the second example, the one for the two elements. The first before calls to subject which evaluates Array.new returns the array and memoizes it. Then, the << method modifies the now memoized object, that will be modified again in the second before block so by the time it reaches the expectation will indeed contain two elements.

Related

validation pattern to not to allow a separator at the end of a string

I am new to Test Driven Developmemt(TDD) and I have been practicing RSpec as my TDD tool. Now, I am trying to validate a pattern in my code. With the current code, all my test passed except the last example in RSPEC test. How can I go about it without breaking other example. Below is what I have done so far.
def add(*nums)
joined_arguments = nums.join(",")
list_of_strings = joined_arguments.split(/[, \n]/)
sum = list_of_strings.inject(0) { |sum, num| sum + num.to_i}
end
RSpec.describe "string calculator" do
it "returns zero when an empty string is entered" do
expect(add("")).to eq(0)
end
it "returns one when one is entered" do
expect(add("1")).to eq(1)
end
it "returns sum of two arguments" do expect(add("1,2")).to
eq(3)
end
it "returns sum of two arguments that makes use of split method" do
expect(add("1", "1,2")).to eq(4)
end
it "returns sum when split with newlines separators and comma" do
expect(add("1,2\n3")).to eq(6)
end
it "returns an error when separator is added to end of an element" do
expect(add("1,2,")).to eq("error")
end
end
All the examples run except the last example which is more of a validation. How do I go about it without breaking the code.
Thanks
You can do the following
def add(*nums)
return 'error' if nums.map {|num| num[-1] == ',' }.any?
joined_arguments = nums.join(",")
list_of_strings = joined_arguments.split(/[, \n]/)
sum = list_of_strings.inject(0) { |sum, num| sum + num.to_i}
end
The following line checks if the last character of each of the arguments is , and if it is the case it returns true
nums.map {|num| num[-1] == ',' }.any?

Is there any way to handling an Exception for Ruby's SyntaxError "both arg and actual block given"?

I want to write a method passing a block, but if a proc and an actual block are given at the same time, it will take only the first one.
I have tried to raise an Exception for SyntaxError, but it keeps prompting an error. This is one of the things that I was trying.
def my_map(&proc)
raise SyntaxError, "using first block given"
rescue
arr = []
proc = proc.call(i) || yield(i)
self.my_each do |i|
arr << proc
end
arr
end
I also tried to add a condition for the raise keyword.
Of course, code works if only one block is given.
I want to write a method passing a block, but if a proc and an actual block are given at the same time, it will take only the first one.
def f(*args)
if args.length == 1
args.first.call
else
yield
end
end
puts 'test 1'
f(->() { puts 'a' }) { puts 'b' }
puts 'test 2'
f { puts 'b' }
Output
test 1
a
test 2
b

How do I call a method from an rspec test

I'm learning Ruby and TDD (rspec) at the same time.
I've written the following test:
describe is_eligible do
it "returns true if the passed in string is not part of a list" do
result = is_eligible("abc")
result.should eq(false)
end
end
It is testing the following code:
def is_eligible(team_name)
array = Array.new
array << "abc" << "def" << "ghi"
if array.include?(team_name)
return false
else
return true
end
end
I'm receiving the following error and can't find out why.
*/Users/joel.dehlin/top32/lib/ineligible_teams.rb:6:in `is_eligible': wrong number of arguments (0 for 1) (ArgumentError)*
Any help is appreciated!
The problem is that the describe method expects a string or something that can evaluate to string. If you say "is_eligible" without the quotes, it will actually try to call the method and you get the error.
describe "is_eligible" do
it "returns true if the passed in string is not part of a list" do
result = is_eligible("abc")
result.should eq(false)
end
end

Function calls in hash come up empty in Ruby

I've been sifting through the prior questions and answers on stackoverflow, and I have gotten most of my question figured out. I figured out that I can't place a function call within a hash, without placing it within a proc, or a similar container.
What I'm ultimately trying to do is have a menu displayed, grab user input, and then iterate through the hash, and run the specified function:
def Main()
menu_titles = {"Answer1" => Proc.new{Choice1()}}
Menu(menu_titles)
end
def Choice1()
puts "Response answer"
end
def Menu(menu_titles)
menu_titles.each_with_index do |(key, value),index|
puts "#{index+1}. #{key}"
end
user_input = 0
menu_titles.each_with_index do |(key, value), index|
if index.eql?(user_input)
menu_titles[value]
break
end
end
end
Main()
The issue I'm having right now is that I'm not entering the functions that my hash calls for. Whether I use a return or a "puts", I either get a blank line or nothing at all. If anyone has other recommendations about my code, I'm all ears also. To be honest, I don't like using procs, but that's mostly because I don't entirely know how they work and where to use them.
Right now for my menus I have:
user_input = 1
if user_input == 1
Choice1()
...
end
Here's how I would refactor this:
class Menu
attr_reader :titles
# initialize sets up a hard-coded titles instance variable,
# but it could easily take an argument.
def initialize
#titles = {
"Answer1" => Proc.new{ puts "choice 1" },
"Answer2" => Proc.new{ puts "choice 2" }
}
end
# This is the only public instance method in your class,
# which should give some idea about what the class is for
# to whoever reads your code
def choose
proc_for_index(display_for_choice)
end
private
# returns the index of the proc.
def display_for_choice
titles.each_with_index { |(key,value), index| puts "#{index + 1}. #{key}" }
gets.chomp.to_i - 1 # gets will return the string value of user input (try it in IRB)
end
# first finds the key for the selected index, then
# performs the hash lookup.
def proc_for_index(index)
titles[titles.keys[index]]
end
end
If you're serious about Ruby (or object-oriented programming in general), I would highly recommend learning about the advantages of packaging your code into behavior-specific classes. This example allows you to do this:
menu = Menu.new
proc = menu.choose
#=> 1. Answer1
#=> 2. Answer2
2 #(user input)
proc.call
#=> choice 2
And you could actually run it on one line:
Menu.new.choose.call

Implicit return values in Ruby

I am somewhat new to Ruby and although I find it to be a very intuitive language I am having some difficulty understanding how implicit return values behave.
I am working on a small program to grep Tomcat logs and generate pipe-delimited CSV files from the pertinent data. Here is a simplified example that I'm using to generate the lines from a log entry.
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
return output
end
end
end
puts LineMatcher.match("00:00:13,207 06/18 INFO stateLogger - TerminationRequest[accountId=AccountId#66679198[accountNumber=0951714636005,srNumber=20]",
/^(\d{2}:\d{2}:\d{2},\d{3}).*?(\d{2}\/\d{2}).*?\[accountNumber=(\d*?),srNumber=(\d*?)\]/)
When I run this code I get back the following, which is what is expected when explicitly returning the value of output.
00:00:13,207|06/18|0951714636005|20
However, if I change LineMatcher to the following and don't explicitly return output:
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
end
end
end
Then I get the following result:
00:00:13,207
06/18
0951714636005
20
Obviously, this is not the desired outcome. It feels like I should be able to get rid of the output variable, but it's unclear where the return value is coming from. Also, any other suggestions/improvements for readability are welcome.
Any statement in ruby returns the value of the last evaluated expression.
You need to know the implementation and the behavior of the most used method in order to exactly know how your program will act.
#each returns the collection you iterated on. That said, the following code will return the value of line.scan(regexp).
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
If you want to return the result of the execution, you can use map, which works as each but returns the modified collection.
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).map do |matched|
matched.join("|")
end.join("\n") # remember the final join
end
end
end
There are several useful methods you can use depending on your very specific case. In this one you might want to use inject unless the number of results returned by scan is high (working on arrays then merging them is more efficient than working on a single string).
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).inject("") do |output, matched|
output << matched.join("|") << "\n"
end
end
end
end
In ruby the return value of a method is the value returned by the last statement. You can opt to have an explicit return too.
In your example, the first snippet returns the string output. The second snippet however returns the value returned by the each method (which is now the last stmt), which turns out to be an array of matches.
irb(main):014:0> "StackOverflow Meta".scan(/[aeiou]\w/).each do |match|
irb(main):015:1* s << match
irb(main):016:1> end
=> ["ac", "er", "ow", "et"]
Update: However that still doesn't explain your output on a single line. I think it's a formatting error, it should print each of the matches on a different line because that's how puts prints an array. A little code can explain it better than me..
irb(main):003:0> one_to_three = (1..3).to_a
=> [1, 2, 3]
irb(main):004:0> puts one_to_three
1
2
3
=> nil
Personally I find your method with the explicit return more readable (in this case)

Resources