Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Add RubyFeatures.ripper_supported? #245

Merged
merged 3 commits into from
Oct 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Enhancements:

* Improve formatting of `Delegator` based objects (e.g. `SimpleDelgator`) in
failure messages and diffs. (Andrew Horner, #215)
* Add `ComparableVersion`. (Yuji Nakayama, #245)
* Add `Ripper` support detection. (Yuji Nakayama, #245)

Bug Fixes:

Expand Down
44 changes: 44 additions & 0 deletions benchmarks/ripper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require 'benchmark/ips'
require 'ripper'

ruby_version = defined?(JRUBY_VERSION) ? JRUBY_VERSION : RUBY_VERSION
puts "#{RUBY_ENGINE} #{ruby_version}"

source = File.read(__FILE__)

Benchmark.ips do |x|
x.report("Ripper") do
Ripper.sexp(source)
Ripper.lex(source)
end
end

__END__

ruby 1.9.3
Calculating -------------------------------------
Ripper 284.000 i/100ms

ruby 2.2.3
Calculating -------------------------------------
Ripper 320.000 i/100ms

jruby 1.7.5
Calculating -------------------------------------
Ripper 24.000 i/100ms

jruby 1.7.13
Calculating -------------------------------------
Ripper 25.000 i/100ms

jruby 1.7.14
Calculating -------------------------------------
Ripper 239.000 i/100ms

jruby 1.7.22
Calculating -------------------------------------
Ripper 231.000 i/100ms

jruby 9.0.1.0
Calculating -------------------------------------
Ripper 218.000 i/100ms
46 changes: 46 additions & 0 deletions lib/rspec/support/comparable_version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module RSpec
module Support
# @private
class ComparableVersion
include Comparable

attr_reader :string

def initialize(string)
@string = string
end

def <=>(other)
other = self.class.new(other) unless other.is_a?(self.class)

return 0 if string == other.string

longer_segment_count = [self, other].map { |version| version.segments.count }.max

longer_segment_count.times do |index|
self_segment = segments[index] || 0
other_segment = other.segments[index] || 0

if self_segment.class == other_segment.class
result = self_segment <=> other_segment
return result unless result == 0
else
return self_segment.is_a?(String) ? -1 : 1
end
end

0
end

def segments
@segments ||= string.scan(/[a-z]+|\d+/i).map do |segment|
if segment =~ /\A\d+\z/
segment.to_i
else
segment
end
end
end
end
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class looks nice and simple, but will it handle version numbers with .pre, .rc1, .beta, etc in the name? The segments method uses to_i which will silently convert those to 0. Looking at the JRuby downloads page, I see version numbers like 9.0.0.0.pre1 and 9.0.0.0.rc1. Since we pass the JRUBY_VERSION to this I think we need to handle those versions properly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

19 changes: 19 additions & 0 deletions lib/rspec/support/ruby_features.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'rbconfig'
RSpec::Support.require_rspec_support "comparable_version"

module RSpec
module Support
Expand Down Expand Up @@ -65,6 +66,24 @@ def supports_exception_cause?
end
end

ripper_requirements = [ComparableVersion.new(RUBY_VERSION) >= '1.9.2']

if Ruby.jruby?
ripper_requirements.push(ComparableVersion.new(JRUBY_VERSION) >= '1.7.5')
# Ripper on JRuby 9.0.0.0.rc1 or later reports wrong line number.
ripper_requirements.push(ComparableVersion.new(JRUBY_VERSION) < '9.0.0.0.rc1')
end

if ripper_requirements.all?
def ripper_supported?
true
end
else
def ripper_supported?
false
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! The ripper_requirements.all? thing works really simply. I like it :).


if Ruby.mri?
def kw_args_supported?
RUBY_VERSION >= '2.0.0'
Expand Down
39 changes: 39 additions & 0 deletions spec/rspec/support/comparable_version_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'rspec/support/comparable_version'

module RSpec::Support
describe ComparableVersion do
describe '#<=>' do
[
['1.2.3', '1.2.3', 0],
['1.2.4', '1.2.3', 1],
['1.3.0', '1.2.3', 1],
['1.2.3', '1.2.4', -1],
['1.2.3', '1.3.0', -1],
['1.2.10', '1.2.3', 1],
['1.2.3', '1.2.10', -1],
['1.2.3.0', '1.2.3', 0],
['1.2.3', '1.2.3.0', 0],
['1.2.3.1', '1.2.3', 1],
['1.2.3.1', '1.2.3.0', 1],
['1.2.3', '1.2.3.1', -1],
['1.2.3.0', '1.2.3.1', -1],
['1.2.3.rc1', '1.2.3', -1],
['1.2.3.rc1', '1.2.3.rc2', -1],
['1.2.3.rc2', '1.2.3.rc10', -1],
['1.2.3.alpha2', '1.2.3.beta1', -1],
['1.2.3', '1.2.3.rc1', 1],
['1.2.3.rc2', '1.2.3.rc1', 1],
['1.2.3.rc10', '1.2.3.rc2', 1],
['1.2.3.beta1', '1.2.3.alpha2', 1]
].each do |subject_string, other_string, expected|
context "with #{subject_string.inspect} and #{other_string.inspect}" do
subject do
ComparableVersion.new(subject_string) <=> ComparableVersion.new(other_string)
end

it { should eq(expected) }
end
end
end
end
end
59 changes: 59 additions & 0 deletions spec/rspec/support/ruby_features_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,65 @@ module Support
expect(RubyFeatures.caller_locations_supported?).to eq(RUBY_VERSION >= '2.0.0')
end
end

describe "#ripper_supported?" do
it 'does not load Ripper' do
expect { RubyFeatures.ripper_supported? }.not_to change { defined?(::Ripper) }
end

describe 'Ripper' do
let(:line_number) do
token = tokens.first
location = token.first
location.first
end

let(:tokens) do
require 'ripper'
::Ripper.lex('foo')
end

if Ruby.mri?
context 'on MRI' do
context '1.8.x', :if => RUBY_VERSION.start_with?('1.8.') do
it 'is not supported' do
expect { tokens }.to raise_error(LoadError)
end
end

context '1.9.x or later', :if => RUBY_VERSION >= '1.9' do
it 'is supported' do
expect(line_number).to eq(1)
end
end
end
end

if Ruby.jruby?
context 'on JRuby' do
context '1.7.x', :if => JRUBY_VERSION.start_with?('1.7.') do
context 'in 1.8 mode', :if => RUBY_VERSION.start_with?('1.8.') do
it 'is not supported' do
expect { tokens }.to raise_error(NameError)
end
end

context 'in non 1.8 mode', :unless => RUBY_VERSION.start_with?('1.8.') do
it 'is supported' do
expect(line_number).to eq(1)
end
end
end

context '9.x.x.x', :if => JRUBY_VERSION.start_with?('9.') do
it 'reports wrong line number' do
expect(line_number).to eq(2)
end
end
end
end
end
end
end
end
end