@elia i think there is a bug in js inlining, sometimes not generating a necessary return
example opal-active support String.camelize
non-working, generated by 0.11:
return (Opal.defn(self, '$camelize', TMP_String_camelize_2 = function $$camelize(first_letter) {
var self = this;
if (first_letter == null) {
first_letter = "upper";
}
self.$underscore().replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
var capitalize = first_letter === "upper" || index > 0;
return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
})
manual workaround:
return (Opal.defn(self, '$camelize', TMP_String_camelize_2 = function $$camelize(first_letter) {
var self = this;
if (first_letter == null) {
first_letter = "upper";
}
return self.$underscore().replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
var capitalize = first_letter === "upper" || index > 0;
return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
})
def camelize(first_letter = :upper)
`#{underscore}.replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
var capitalize = #{first_letter} === #{:upper} || index > 0;
return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
})`
end
def camelize(first_letter = :upper)
`return #{underscore}.replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
var capitalize = #{first_letter} === #{:upper} || index > 0;
return capitalize ? word.substr(0,1).toUpperCase()+word.substr(1) : word;
})`
end
.to_s
on the backtick string. I don't know why that would change what is happening. Does it force a native value to be cast into an Opal value or something?
@iliabylich changing https://github.com/opal/opal/blob/ee50768d368959b33a77b58028a90ec9bded64a1/lib/opal/compiler.rb#L436 to
if multiline
# xstr starts with interpolation
# then it must contain js_return inside
if first_child.type == :begin
s(:js_return, sexp)
else
sexp
end
else
if first_child.type == :str
helps the above troubled case, also tests pass, though i am not sure, if that is the right thing to do there, looks too specific, or would you be ok with that? then i would provide a PR
@elia @iliabylich found another issue, for:
def native_state_changed?(next_state_hash)
next_state = next_state_hash.to_n
%x{
var current_state = #{@native}.state
compiler generates:
next_state = next_state_hash.$to_n();
return
var current_state = self["native"].state
which wont work. Now fixed it here https://github.com/opal/opal/blob/ee50768d368959b33a77b58028a90ec9bded64a1/lib/opal/compiler.rb#L446 with this:
if first_child.type == :str
old_value = first_child.children[0]
if old_value.include?('return')
# 'return' is already there
sexp
else
second_child, *second_rest_children = *rest_children
if second_child && second_child.type == :str && !(old_value =~ /^\s*\n$/).nil? && !(second_child.children[0] =~ /^\s*var/).nil?
sexp
else
first_child = s(:js_return, first_child)
sexp.updated(nil, [first_child, *rest_children])
end
end
;
Opal decides that that's a single line x-string and injects a return
if x-string doesn't have it
strs.length > 1
I don't like the idea of mutating JS code, but we have to do it in cases like
list.map { |item| `item.toString()` }
In this case block becomes a JS function, so we have to inject explicit return