Fluent chaining drying

Hello all. Thanks to subjects, custom matchers and fluent chaining I was
able to greatly simplify my spec. But now I want to DRY my custom
matchers.

I have two custom matchers: ‘have_text’ and ‘have_number’ and both
contain exactly the same chains ‘on_column’ and ‘on_columns’.

Is there a way to DRY this up?

module CustomColumnsMatchers

RSpec::Matchers.define(:have_text) do |expected_string|
chain(:on_column) do |colnumber|
@index = colnumber - 1
@width = 1
end
chain(:on_columns) do |range|
a = range.to_a
@index = Range.new( a[0]-1, a[-1]-1 )
@width = a[-1] - a[0] + 1
end
match do |line|
line[@index] == expected_string.ljust(@width)
end
end

RSpec::Matchers.define(:have_number) do |expected_number_string|
chain(:on_column) do |colnumber|
@index = colnumber - 1
@width = 1
end
chain(:on_columns) do |range|
a = range.to_a
@index = Range.new( a[0]-1, a[-1]-1 )
@width = a[-1] - a[0] + 1
end
match do |line|
line[@index] == expected_number_string.rjust(@width,‘0’)
end
end

end

Regards,

Gustavo D.

On May 25, 2011, at 10:07 AM, Gustavo D. wrote:

chain(:on_column) do |colnumber|
end
@width = a[-1] - a[0] + 1

Gustavo D.

I’d probably do something like:

RSpec::Matchers.define(:have_text) do |expected_string|
chain(:on_column, &HaveXXXHelpers.on_column)
chain(:on_columns, &HaveXXXHelpers.on_columns)
match do |line|
line[@index] == expected_string.ljust(@width)
end
end

HTH,
David

On Wed, May 25, 2011 at 11:20 AM, David C.
[email protected]wrote:

 @width = a[-1] - a[0] + 1

end
end
chain(:on_columns, &HaveXXXHelpers.on_columns)
http://rubyforge.org/mailman/listinfo/rspec-users

Ahh David beat me to the lambda approach! Here is another way:

module CustomColumnsMatchers

RSpec::Matchers.define(:have_text) do |expected_string|
extend ChainMethods

match do |line|
line[@index] == expected_string.ljust(@width)
end
end

RSpec::Matchers.define(:have_number) do |expected_number_string|
extend ChainMethods

match do |line|
line[@index] == expected_number_string.rjust(@width,‘0’)
end
end

module ChainMethods
def self.extended(matcher)
matcher.instance_eval do
chain(:on_column) do |colnumber|
@index = colnumber - 1
@width = 1
end
chain(:on_columns) do |range|
a = range.to_a
@index = Range.new( a[0]-1, a[-1]-1 )
@width = a[-1] - a[0] + 1
end
end
end
end

end

Thank you Justin & David.

I was able to to use Justin solution. I also tried hard to understand
and use David’s solution but I could not.

Now I am just curious. What is &HaveXXXHelpers.on_column? A reference to
a class method?

Regards,

Gustavo D.

On Wed, May 25, 2011 at 9:30 PM, Gustavo D. [email protected]
wrote:

Gustavo D.
was able to greatly simplify my spec. But now I want to DRY my custom

RSpec::Matchers.define(:have_text) do |expected_string|
line[@index] == expected_string.ljust(@width)
@index = Range.new( a[0]-1, a[-1]-1 )
Regards,
end

 matcher.instance_eval do

end
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Actually, the lambda approach won’t work, because you’re carrying state
(@index & @width) that is outside of scope in the matcher. This is what
it
would look like though:

module CustomColumnsMatchers

RSpec::Matchers.define(:have_text) do |expected_string|
chain(:on_column, &ChainHelpers.on_column)
chain(:on_columns, &ChainHelpers.on_columns)

match do |line|
line[@index] == expected_string.ljust(@width)
end
end

RSpec::Matchers.define(:have_number) do |expected_number_string|
chain(:on_column, &ChainHelpers.on_column)
chain(:on_columns, &ChainHelpers.on_columns)

match do |line|
line[@index] == expected_number_string.rjust(@width,‘0’)
end
end

module ChainHelpers
def self.on_column
lambda do |colnumber|
@index = colnumber - 1
@width = 1
end
end

def self.on_columns
  lambda do |range|
     a = range.to_a
     @index = Range.new( a[0]-1, a[-1]-1 )
     @width = a[-1] - a[0] + 1
  end
end

end

end

You could keep all of the state in the ChainHelpers module:

module CustomColumnsMatchers

RSpec::Matchers.define(:have_text) do |expected_string|
chain(:on_column, &ChainHelpers.on_column)
chain(:on_columns, &ChainHelpers.on_columns)

match do |line|
line[ChainHelpers.index] ==
expected_string.ljust(ChainHelpers.width)
end
end

RSpec::Matchers.define(:have_number) do |expected_number_string|
chain(:on_column, &ChainHelpers.on_column)
chain(:on_columns, &ChainHelpers.on_columns)

match do |line|
line[ChainHelpers.index] ==
expected_number_string.rjust(ChainHelpers.width,‘0’)
end
end

module ChainHelpers
class << self
attr_accessor :index, :width
end

def self.on_column
lambda do |colnumber|
self.index = colnumber - 1
self.width = 1
end
end

def self.on_columns
  lambda do |range|
     a = range.to_a
     self.index = Range.new( a[0]-1, a[-1]-1 )
     self.width = a[-1] - a[0] + 1
  end
end

end

end

That is kinda dirty and ugly - I’d stick with the “extend” solution.

Thank you so much Justin. Crystal clear now.

Regards,

Gustavo D.

On May 26, 2011, at 12:05 AM, Justin Ko wrote:

end

     a = range.to_a

class << self
def self.on_columns

That is kinda dirty and ugly - I’d stick with the “extend” solution.


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

On May 25, 2011, at 12:50 PM, David C. wrote:

HTH,
David