[rspec-users] fluent chaining drying

Gustavo Delfino
Thu, 26 May 2011 16:23:41 -0000

Thank you so much Justin. Crystal clear now.

Regards,

Gustavo Delfino

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

> 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.
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users


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

> 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 May 25, 2011, at 10:37 AM, Gustavo Delfino wrote:

> 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?