Feature #21160 open
Added by JustJosh (Joshua Stowers) 8 days ago.
Updated 7 days ago.
Description
When writing DSL-style helper methods, I often store block arguments as procs to use as callbacks.
Using return
in a proc will return from the context it was created in, which is unsuitable in the following example.
Since procs cannot be converted to lambdas, I end up using next
to return a value from them early.
Example:
fulfills_promise :generate_large_image do | image_data |
next false if image_data . nil?
puts 'Saving image..'
# etc.
end
This works but confuses most readers.
I propose introducing an alias for it that is more appropriate for this use case.
Perhaps pass
or continue
?
It's worth noting that return
would work with fulfills_promise :foo, -> (bar) do
, though it detracts a bit from a DSL's expressiveness.
next
isn't necessarily the correct thing to use here, break
is:
foo = fulfills_promise :generate_large_image do | image_data |
break false if image_data . nil?
puts 'Saving image..'
# etc.
end
foo #=> false
And the name exactly conveys the concept of breaking out of the block, in my opinion.
For example:
result = ( 1 .. 100 ). each do | num |
break num if num > 3
puts num
end
puts "Got result #{ result } "
will print
1
2
3
Got result 4
ufuk (Ufuk Kayserilioglu) wrote in #note-1 :
next
isn't necessarily the correct thing to use here, break
is:
I've tried break
without success - I get LocalJumpError: break from proc-closure
Here is a more complete example of what I am doing:
class Example
def self . fulfills_promise ( promise_name , & block )
@@promise_callbacks ||= {}
@@promise_callbacks [ promise_name ] = block
end
def self . fulfill_promise ( promise_name , data )
puts "Fulfilling promise: #{ promise_name } "
callback = @@promise_callbacks [ promise_name ]
if callback . call ( data )
puts 'Complete!'
else
puts 'Failed!'
end
end
fulfills_promise :generate_large_image do | image_data |
puts 'Will finalize large image...'
break false # Indicate something went wrong
puts 'Finalized!'
true
end
end
Example . fulfill_promise ( :generate_large_image , 'image data' )
Why not rescue LocalJumpError
?
def self . fulfill_promise ( promise_name , data )
puts "Fulfilling promise: #{ promise_name } "
callback = @@promise_callbacks [ promise_name ]
begin
complete = callback . call ( data )
rescue LocalJumpError => e
complete = e . exit_value
end
if complete
puts 'Complete!'
else
puts 'Failed!'
end
end
This works with both of return
and break
in already released versions of Ruby, and even your DSL does not need to change.
If you want to add new keyword, you'll have to wait until at least the end of the year.
nobu (Nobuyoshi Nakada) wrote in #note-3 :
Why not rescue LocalJumpError
?
That is definitely a better solution than requiring developers to use next
.
Thank you for the suggestion.
As I understand it, the reason break
doesn't work in my example is because the proc is "orphaned."
Ref: https://docs.ruby-lang.org/en/3.3/Proc.html#class-Proc-label-Orphaned+Proc
If an orphan-friendly implementation or alternative to break
is worth considering, I would be happy to take a shot.
Also available in: Atom
PDF
Like 0
Like 0 Like 0 Like 0 Like 0