From 0d7e9fd42d382e0895bfc6005033a31f7d64da36 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Wed, 9 Feb 2011 18:14:01 -0800 Subject: [PATCH 1/8] Quick hacks: compat with recent RSpec and Hoe. --- Rakefile | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Rakefile b/Rakefile index c8f6993..d2368d2 100644 --- a/Rakefile +++ b/Rakefile @@ -3,25 +3,26 @@ require 'rubygems' require 'hoe' require './lib/superators.rb' -require 'spec/rake/spectask' +require 'rspec/core/rake_task' -Spec::Rake::SpecTask.new do |opts| - opts.spec_opts = %w'-c' +RSpec::Core::RakeTask.new do |opts| + opts.rspec_opts = %w'-c' end desc "Generate a HTML report of the RSpec specs" -Spec::Rake::SpecTask.new "report" do |opts| - opts.spec_opts = %w'--format html:report.html' +RSpec::Core::RakeTask.new "report" do |opts| + opts.rspec_opts = %w'--format html:report.html' end -Hoe.new('superators', Superators::VERSION) do |p| - p.rubyforge_name = 'superators' - p.author = 'Jay Phillips' - p.email = 'jay -at- codemecca.com' - p.summary = 'Superators add new sexy operators to your Ruby objects.' - p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n") - p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1] - p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n") +Hoe.spec 'superators' do + self.version = Superators::VERSION + self.rubyforge_name = 'superators' + self.author = 'Jay Phillips' + self.email = 'jay -at- codemecca.com' + self.summary = 'Superators add new sexy operators to your Ruby objects.' + self.description = paragraphs_of('README.txt', 2..5).join("\n\n") + self.url = paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1] + self.changes = paragraphs_of('History.txt', 0..1).join("\n\n") end # vim: syntax=Ruby From e7f357c7d50f0fc47f52b9561c7180e257d96866 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Wed, 9 Feb 2011 18:38:00 -0800 Subject: [PATCH 2/8] More minor compat updates. --- Rakefile | 2 +- spec/superator_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index d2368d2..e7ec46b 100644 --- a/Rakefile +++ b/Rakefile @@ -21,7 +21,7 @@ Hoe.spec 'superators' do self.email = 'jay -at- codemecca.com' self.summary = 'Superators add new sexy operators to your Ruby objects.' self.description = paragraphs_of('README.txt', 2..5).join("\n\n") - self.url = paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1] + self.url = paragraphs_of('README.txt', 0).first.split(/\n/)[-1].strip self.changes = paragraphs_of('History.txt', 0..1).join("\n\n") end diff --git a/spec/superator_spec.rb b/spec/superator_spec.rb index 1ba7322..b8da2cf 100644 --- a/spec/superator_spec.rb +++ b/spec/superator_spec.rb @@ -1,4 +1,4 @@ -require 'lib/superators' +require './lib/superators' describe "The 'superator' macro" do From 20bfd572f64a28e4e8feaf84ed3b2bceeb10afd8 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 22 Feb 2011 11:10:12 -0800 Subject: [PATCH 3/8] Tweak Hoe spec. --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index e7ec46b..267840d 100644 --- a/Rakefile +++ b/Rakefile @@ -20,7 +20,7 @@ Hoe.spec 'superators' do self.author = 'Jay Phillips' self.email = 'jay -at- codemecca.com' self.summary = 'Superators add new sexy operators to your Ruby objects.' - self.description = paragraphs_of('README.txt', 2..5).join("\n\n") + self.description = paragraphs_of('README.txt', 2..4).join("\n\n") self.url = paragraphs_of('README.txt', 0).first.split(/\n/)[-1].strip self.changes = paragraphs_of('History.txt', 0..1).join("\n\n") end From 7bb9418bf9e9f23f873c5c05e9bb388e0f34e637 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 22 Feb 2011 13:29:48 -0800 Subject: [PATCH 4/8] Checkpoint work on MRI 1.9 port. Down from 14 test failures to 5. Notable fixes: * Class#instance_methods is now an Array of Symbol, not of String * Indexing into a String now produces a String, not an Integer * When a method is defined using define_method and a block, we now can't call the method with an argument if the block has arity zero --- lib/superators/macro.rb | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/superators/macro.rb b/lib/superators/macro.rb index f178433..79a6f6f 100644 --- a/lib/superators/macro.rb +++ b/lib/superators/macro.rb @@ -9,12 +9,19 @@ module SuperatorMixin VALID_SUPERATOR = /^(#{BINARY_OPERATOR_PATTERN})(#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN})+$/ - def superator_send(sup, operand) - if respond_to_superator? sup - __send__ superator_definition_name_for(sup), operand - else + def superator_send(sup, block_arity, operand) + unless respond_to_superator? sup raise NoMethodError, "Superator #{sup} has not been defined on #{self.class}" end + + # If the user supplied a block that doesn't take any arguments, Ruby 1.9 + # objects if we try to pass it an argument + meth = superator_definition_name_for(sup) + if block_arity == 0 + __send__ meth + else + __send__ meth, operand + end end def respond_to_superator?(sup) @@ -36,8 +43,8 @@ def superator(operator, &block) class_eval do # Step in front of the old operator's dispatching. alias_for_real_method = superator_alias_for real_operator - - if instance_methods.include?(real_operator) && !respond_to_superator?(operator) + + if instance_methods.any? {|m| m.to_s == real_operator} && !respond_to_superator?(operator) alias_method alias_for_real_method, real_operator end @@ -50,7 +57,7 @@ def superator(operator, &block) sup = operand.superator_queue.unshift(real_operator).join operand.superator_queue.clear - superator_send(sup, operand) + superator_send(sup, block.arity, operand) else # If the method_alias is defined if respond_to? alias_for_real_method @@ -86,7 +93,10 @@ def undef_superator(sup) def superator_encode(str) tokenizer = /#{BINARY_OPERATOR_PATTERN}|#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN}/ - str.scan(tokenizer).map { |op| op.split('').map { |s| s[0] }.join "_" }.join "__" + r = str.scan(tokenizer).map do |op| + op.enum_for(:each_byte).to_a.join "_" + end + r.join "__" end def superator_decode(str) @@ -115,4 +125,4 @@ def superator_valid?(operator) end -module SuperatorFlag;end \ No newline at end of file +module SuperatorFlag;end From e9360703f5e67858ba1e167574040fbc3a1bbec9 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 22 Feb 2011 15:08:17 -0800 Subject: [PATCH 5/8] Optimize superator dispatch. Calling `respond_to_superator?` before invoking the superator is costly. Instead, just invoke the superator and catch a NoMethodError if one is raised. This improves the performance of a microbenchmark (superator invocation x 10000) from ~2 seconds to ~1.3 seconds. --- lib/superators/macro.rb | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/superators/macro.rb b/lib/superators/macro.rb index 79a6f6f..ba7c826 100644 --- a/lib/superators/macro.rb +++ b/lib/superators/macro.rb @@ -10,17 +10,23 @@ module SuperatorMixin VALID_SUPERATOR = /^(#{BINARY_OPERATOR_PATTERN})(#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN})+$/ def superator_send(sup, block_arity, operand) - unless respond_to_superator? sup - raise NoMethodError, "Superator #{sup} has not been defined on #{self.class}" - end - - # If the user supplied a block that doesn't take any arguments, Ruby 1.9 - # objects if we try to pass it an argument meth = superator_definition_name_for(sup) - if block_arity == 0 - __send__ meth - else - __send__ meth, operand + begin + # If the user supplied a block that doesn't take any arguments, Ruby 1.9 + # objects if we try to pass it an argument + if block_arity.zero? + __send__ meth + else + __send__ meth, operand + end + rescue NoMethodError + # Checking for respond_to_superator? is relatively slow, we only do this + # if calling the superator didn't work out as expected. + if not respond_to_superator? sup + raise NoMethodError, "Superator #{sup} has not been defined on #{self.class}" + else + raise + end end end From 666d2580331aab7de067d56d4504abd96bb6d706 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 22 Feb 2011 15:14:19 -0800 Subject: [PATCH 6/8] Tweak comment. --- lib/superators/macro.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/superators/macro.rb b/lib/superators/macro.rb index ba7c826..93960d2 100644 --- a/lib/superators/macro.rb +++ b/lib/superators/macro.rb @@ -20,8 +20,8 @@ def superator_send(sup, block_arity, operand) __send__ meth, operand end rescue NoMethodError - # Checking for respond_to_superator? is relatively slow, we only do this - # if calling the superator didn't work out as expected. + # Checking for respond_to_superator? is relatively slow, so only do this + # if calling the superator didn't work out as expected if not respond_to_superator? sup raise NoMethodError, "Superator #{sup} has not been defined on #{self.class}" else From 1977b6ed16bc66db6277764feb7a32dc57d8ea50 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 22 Feb 2011 16:55:19 -0800 Subject: [PATCH 7/8] Avoid changing the signature of superator_send(). We previously detected the arity of the superator block by passing it into superator_send(); we now lookup the corresponding Method via Object#method() and use Method#arity. This is very slightly slower, but avoids changing the API. --- lib/superators/macro.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/superators/macro.rb b/lib/superators/macro.rb index 93960d2..82c5641 100644 --- a/lib/superators/macro.rb +++ b/lib/superators/macro.rb @@ -9,15 +9,15 @@ module SuperatorMixin VALID_SUPERATOR = /^(#{BINARY_OPERATOR_PATTERN})(#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN})+$/ - def superator_send(sup, block_arity, operand) - meth = superator_definition_name_for(sup) + def superator_send(sup, operand) + meth = method(superator_definition_name_for(sup)) begin # If the user supplied a block that doesn't take any arguments, Ruby 1.9 # objects if we try to pass it an argument - if block_arity.zero? - __send__ meth + if meth.arity.zero? + meth.call else - __send__ meth, operand + meth.call(operand) end rescue NoMethodError # Checking for respond_to_superator? is relatively slow, so only do this @@ -63,7 +63,7 @@ def superator(operator, &block) sup = operand.superator_queue.unshift(real_operator).join operand.superator_queue.clear - superator_send(sup, block.arity, operand) + superator_send(sup, operand) else # If the method_alias is defined if respond_to? alias_for_real_method From bb8c5be1268b87bf52ecf94e59b4e040d2444717 Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Tue, 22 Feb 2011 17:03:43 -0800 Subject: [PATCH 8/8] Fix another MRI 1.9 incompatibility. --- lib/superators/macro.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/superators/macro.rb b/lib/superators/macro.rb index 82c5641..d81f9b6 100644 --- a/lib/superators/macro.rb +++ b/lib/superators/macro.rb @@ -35,7 +35,7 @@ def respond_to_superator?(sup) end def defined_superators - methods.grep(/^superator_definition_/).map { |m| superator_decode(m) } + methods.grep(/^superator_definition_/).map { |m| superator_decode(m.to_s) } end protected @@ -109,7 +109,7 @@ def superator_decode(str) tokens = str.match /^(superator_(definition|alias_for))?((_?\d{2,3})+)((__\d{2,3})+)$/ #puts *tokens if tokens - (tokens[3].split("_" ) + tokens[5].split('__')).reject { |x| x.empty? }.map { |s| s.to_i.chr }.join + (tokens[3].split("_") + tokens[5].split("__")).reject { |x| x.empty? }.map { |s| s.to_i.chr }.join end end