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 Delfino
On May 25, 2011, at 10:07 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? > > > 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 DelfinoI''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 Chelimsky <dchelimsky at gmail.com>wrote:> On May 25, 2011, at 10:07 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? > > > > > > 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 Delfino > > 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 > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > 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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110525/6888f89a/attachment.html>
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 Delfino On May 25, 2011, at 2:04 PM, Justin Ko wrote:> > > On Wed, May 25, 2011 at 11:20 AM, David Chelimsky <dchelimsky at gmail.com> wrote: > On May 25, 2011, at 10:07 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? > > > > > > 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 Delfino > > 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 > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > 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 > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
On Wed, May 25, 2011 at 9:30 PM, Gustavo Delfino <gdelfino at umich.edu> wrote:> 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 Delfino > > On May 25, 2011, at 2:04 PM, Justin Ko wrote: > > > > > > > On Wed, May 25, 2011 at 11:20 AM, David Chelimsky <dchelimsky at gmail.com> > wrote: > > On May 25, 2011, at 10:07 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? > > > > > > > > > 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 Delfino > > > > 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 > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > 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 > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > 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. -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20110525/61806a4f/attachment-0001.html>
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-usersOn 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, > DavidOn 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? > > > 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 Delfino