Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: opal/opal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6f68a488a9b0
Choose a base ref
...
head repository: opal/opal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8df5e5750b4d
Choose a head ref
  • 2 commits
  • 7 files changed
  • 2 contributors

Commits on Mar 30, 2015

  1. Copy the full SHA
    d7e8131 View commit details
  2. Merge pull request #782 from vais/gsub

    String#gsub fully compliant with rubyspec
    elia committed Mar 30, 2015

    Unverified

    This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
    Copy the full SHA
    8df5e57 View commit details
2 changes: 1 addition & 1 deletion lib/opal/nodes/literal.rb
Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ class RegexpNode < Base
def compile
case value
when ''
push('/^/')
push('/(?:)/')
when %r{\?\<\w+\>}
message = "named captures are not supported in javascript: #{value.inspect}"
push "self.$raise(new SyntaxError('#{message}'))"
79 changes: 65 additions & 14 deletions opal/corelib/string.rb
Original file line number Diff line number Diff line change
@@ -427,22 +427,73 @@ def end_with?(*suffixes)
alias eql? ==
alias equal? ===

def gsub(pattern, replace = undefined, &block)
if String === pattern || pattern.respond_to?(:to_str)
pattern = /#{Regexp.escape(pattern.to_str)}/
end
def gsub(pattern, replacement = undefined, &block)
%x{
var result = '', match_data = nil, index = 0, match, _replacement;
unless Regexp === pattern
raise TypeError, "wrong argument type #{pattern.class} (expected Regexp)"
end
if (pattern.$$is_regexp) {
pattern = new RegExp(pattern.source, 'g' + (pattern.multiline ? 'm' : '') + (pattern.ignoreCase ? 'i' : ''));
} else {
pattern = #{Opal.coerce_to(`pattern`, String, :to_str)};
pattern = new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
}
%x{
var pattern = pattern.toString(),
options = pattern.substr(pattern.lastIndexOf('/') + 1) + 'g',
regexp = pattern.substr(1, pattern.lastIndexOf('/') - 1);
while (true) {
match = pattern.exec(self);
if (match === null) {
#{$~ = nil}
result += self.slice(index);
break;
}
match_data = #{MatchData.new `pattern`, `match`};
self.$sub.$$p = block;
return self.$sub(new RegExp(regexp, options), replace);
if (replacement === undefined) {
if (block === nil) {
#{raise ArgumentError, 'wrong number of arguments (1 for 2)'}
}
_replacement = block(match[0]);
}
else if (replacement.$$is_hash) {
_replacement = #{`replacement`[`match[0]`].to_s};
}
else {
if (!replacement.$$is_string) {
replacement = #{Opal.coerce_to(`replacement`, String, :to_str)};
}
_replacement = replacement.replace(/([\\]+)([0-9+&`'])/g, function (original, slashes, command) {
if (slashes.length % 2 === 0) {
return original;
}
switch (command) {
case "+":
for (var i = match.length - 1; i > 0; i--) {
if (match[i] !== undefined) {
return slashes.slice(1) + match[i];
}
}
return '';
case "&": return slashes.slice(1) + match[0];
case "`": return slashes.slice(1) + self.slice(0, match.index);
case "'": return slashes.slice(1) + self.slice(match.index + match[0].length);
default: return slashes.slice(1) + (match[command] || '');
}
}).replace(/\\\\/g, '\\');
}
if (pattern.lastIndex === match.index) {
result += (_replacement + self.slice(index, match.index + 1))
pattern.lastIndex += 1;
}
else {
result += (self.slice(index, match.index) + _replacement)
}
index = pattern.lastIndex;
}
#{$~ = `match_data`}
return result;
}
end

@@ -1036,7 +1087,7 @@ def sub(pattern, replacement = undefined, &block)
}
if (replacement.$$is_hash) {
return self.slice(0, result.index) + #{`replacement`[`result[0]`]} + self.slice(result.index + result[0].length);
return self.slice(0, result.index) + #{`replacement`[`result[0]`].to_s} + self.slice(result.index + result[0].length);
}
replacement = #{Opal.coerce_to(`replacement`, String, :to_str)};
31 changes: 0 additions & 31 deletions spec/filters/bugs/string.rb
Original file line number Diff line number Diff line change
@@ -6,37 +6,6 @@
fails "String#each_line uses $/ as the separator when none is given"
fails "String#each_line yields subclass instances for subclasses"

fails "String#gsub with pattern and replacement inserts the replacement around every character when the pattern collapses"
fails "String#gsub with pattern and replacement respects $KCODE when the pattern collapses"
fails "String#gsub with pattern and replacement doesn't freak out when replacing ^"
fails "String#gsub with pattern and replacement returns a copy of self with all occurrences of pattern replaced with replacement"
fails "String#gsub with pattern and replacement supports \\G which matches at the beginning of the remaining (non-matched) string"
fails "String#gsub with pattern and replacement replaces \\1 sequences with the regexp's corresponding capture"
fails "String#gsub with pattern and replacement treats \\1 sequences without corresponding captures as empty strings"
fails "String#gsub with pattern and replacement replaces \\& and \\0 with the complete match"
fails "String#gsub with pattern and replacement replaces \\` with everything before the current match"
fails "String#gsub with pattern and replacement replaces \\' with everything after the current match"
fails "String#gsub with pattern and replacement replaces \\+ with the last paren that actually matched"
fails "String#gsub with pattern and replacement treats \\+ as an empty string if there was no captures"
fails "String#gsub with pattern and replacement maps \\\\ in replacement to \\"
fails "String#gsub with pattern and replacement handles pattern collapse without $KCODE"
fails "String#gsub with pattern and replacement raises a TypeError when replacement can't be converted to a string"
fails "String#gsub with pattern and replacement sets $~ to MatchData of last match and nil when there's none"
fails "String#gsub with pattern and Hash returns a copy of self with all occurrences of pattern replaced with the value of the corresponding hash key"
fails "String#gsub with pattern and Hash ignores keys that don't correspond to matches"
fails "String#gsub with pattern and Hash returns an empty string if the pattern matches but the hash specifies no replacements"
fails "String#gsub with pattern and Hash ignores non-String keys"
fails "String#gsub with pattern and Hash uses a key's value as many times as needed"
fails "String#gsub with pattern and Hash uses the hash's default value for missing keys"
fails "String#gsub with pattern and Hash coerces the hash values with #to_s"
fails "String#gsub with pattern and Hash coerces the hash values with #to_s"
fails "String#gsub with pattern and Hash uses the hash's value set from default_proc for missing keys"
fails "String#gsub with pattern and Hash sets $~ to MatchData of last match and nil when there's none for access from outside"
fails "String#gsub with pattern and Hash doesn't interpolate special sequences like \\1 for the block's return value"
fails "String#gsub with pattern and block sets $~ for access from the block"
fails "String#gsub with pattern and block restores $~ after leaving the block"
fails "String#gsub with pattern and block sets $~ to MatchData of last match and nil when there's none for access from outside"

fails "String#lines uses $/ as the separator when none is given"
fails "String#lines yields subclass instances for subclasses"

1 change: 1 addition & 0 deletions spec/filters/unsupported/encoding.rb
Original file line number Diff line number Diff line change
@@ -60,6 +60,7 @@
fails "String#gsub with pattern and block uses the compatible encoding if they are compatible"
fails "String#gsub with pattern and block raises an Encoding::CompatibilityError if the encodings are not compatible"
fails "String#gsub with pattern and block replaces the incompatible part properly even if the encodings are not compatible"
fails "String#gsub with pattern and block raises an ArgumentError if encoding is not valid"

fails "String#initialize with an argument carries over the encoding invalidity"

1 change: 1 addition & 0 deletions spec/filters/unsupported/mutable_strings.rb
Original file line number Diff line number Diff line change
@@ -145,6 +145,7 @@
fails "String#gsub! with pattern and block uses the compatible encoding if they are compatible"
fails "String#gsub! with pattern and block raises an Encoding::CompatibilityError if the encodings are not compatible"
fails "String#gsub! with pattern and block replaces the incompatible part properly even if the encodings are not compatible"
fails "String#gsub! with pattern and block raises an ArgumentError if encoding is not valid"

fails "String#lstrip! modifies self in place and returns self"
fails "String#lstrip! returns nil if no modifications were made"
5 changes: 5 additions & 0 deletions spec/filters/unsupported/regular_expressions.rb
Original file line number Diff line number Diff line change
@@ -2,4 +2,9 @@
fails "MatchData#offset returns the offset for multi byte strings with unicode regexp"

fails "String#sub with pattern, replacement supports \\G which matches at the beginning of the string"

fails "String#gsub with pattern and replacement replaces \\k named backreferences with the regexp's corresponding capture"
fails "String#gsub with pattern and replacement doesn't freak out when replacing ^" #Only fails "Text\nFoo".gsub(/^/, ' ').should == " Text\n Foo"
fails "String#gsub with pattern and replacement supports \\G which matches at the beginning of the remaining (non-matched) string"
fails "String#gsub with pattern and replacement returns a copy of self with all occurrences of pattern replaced with replacement" #Only fails str.gsub(/\Ah\S+\s*/, "huh? ").should == "huh? homely world. hah!"
end
1 change: 1 addition & 0 deletions spec/filters/unsupported/symbols.rb
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@
fails "A Symbol literal can be an empty string"

fails "String#each_line raises a TypeError when the separator is a symbol"
fails "String#gsub with pattern and Hash ignores non-String keys"
fails "String#index raises a TypeError if passed a Symbol"
fails "String#lines raises a TypeError when the separator is a symbol"
fails "String#sub with pattern and Hash ignores non-String keys"