From: "estum (Anton (estum))" Date: 2022-09-22T23:18:13+00:00 Subject: [ruby-core:110015] [Ruby master Feature#19015] Language extension by a heredoc Issue #19015 has been updated by estum (Anton (estum)). Wow, I am not the only such geek %) My solution of the similar goal is 7 yo and it'll go to school soon. ``` $ git log lib/osascript.rb commit 1f39d1d42b499d1424af1fa5a109ecd6ab219563 (HEAD -> master) Author: Anton Date: Thu Jun 11 08:47:12 2015 +0300 ``` ``` ruby # @example Simple # Osascript.new(<<~SCPT.freeze).() # activate application "Finder" # SCPT # # @example JSC with args # # The script takes 2 arguments: directory path & image path # # to set a folder icon to the given directory. # script = Osascript.new(<<-JS.freeze, lang: 'JavaScript') # ObjC.import("Cocoa"); # function run(input) { # var target_path = input[0].toString(); # var source_image = $.NSImage.alloc.initWithContentsOfFile(input[1].toString()); # var result = $.NSWorkspace.sharedWorkspace.setIconForFileOptions(source_image, target_path, 0); # return target_path; # } # JS # script.(target_dir, folder_icon) class Osascript attr_accessor :script def initialize(script = nil, lang: "AppleScript") @script = block_given? ? yield : script @lang = lang end def call(*other) handle_errors do cmd = ["/usr/bin/env", "osascript", *params(*other)] IO.popen cmd, "r+", 2 => %i(child out) do |io| io.write script io.close_write io.readlines end end end def params(*args) ["-l", @lang].tap { |e| e.concat(args.unshift(?-)) unless args.empty? } end ERROR_PATTERN = /(?<=execution error: )(.+?)(?=$)/ USER_CANCELLED_PATTERN = /user canceled/i NL = "\n" private def handle_errors yield().each_with_object([]) do |line, buf| line.match(ERROR_PATTERN) { |m| raise error_for(m[0]), m[0], caller(4) } buf << line.strip end.join(NL) end def error_for(msg) USER_CANCELLED_PATTERN.test?(msg) ? UserCanceled : ExecutionError end class ExecutionError < RuntimeError CAPTURE_MSG_AND_CODE = /(.+?) \((-?\d+?)\)$/ attr_reader :code def initialize(msg) msg.match(CAPTURE_MSG_AND_CODE) { |m| msg, @code, * = m.captures } super(msg) end end UserCanceled = Class.new(ExecutionError) end ``` I've wrote it when I've known that cool syntax hook at the first time ��� an ability to pass only the opening heredoc word in closed parenthesis on single line and you can ducktype it infinitely. Oh, and I just called in mind one more thing about heredoc: there is some tricky heredoc syntax in core source file `forwardable.rb` which brakes my brain when I try to understand it: ``` ruby if _valid_method?(method) loc, = caller_locations(2,1) pre = "_ =" mesg = "#{Module === obj ? obj : obj.class}\##{ali} at #{loc.path}:#{loc.lineno} forwarding to private method " method_call = "#{<<-"begin;"}\n#{<<-"end;".chomp}" begin; unless defined? _.#{method} ::Kernel.warn #{mesg.dump}"\#{_.class}"'##{method}', uplevel: 1 _#{method_call} else _.#{method}(*args, &block) end end; end _compile_method("#{<<-"begin;"}\n#{<<-"end;"}", __FILE__, __LINE__+1) begin; proc do def #{ali}(*args, &block) #{pre} begin #{accessor} end#{method_call} end end end; ``` Pretty cryptic, isn't it? ---------------------------------------- Feature #19015: Language extension by a heredoc https://bugs.ruby-lang.org/issues/19015#change-99266 * Author: ko1 (Koichi Sasada) * Status: Open * Priority: Normal ---------------------------------------- This propose new heredoc extension with `<Hello <%= name %> erb puts html #=>
Hello ko1
``` ## Background / considerations * Sometimes we write Ruby syntax string with `<