require 'tempfile'
require 'securerandom'
FenceBlock = Struct.new(:attributes, :content)
BLOCK_QUOTE = '```'
def append_block(block, content, attribute, body)
left = content[0, block.attributes[:'point-max']]
right = content[block.attributes[:'point-max']..]
left + "\n#{BLOCK_QUOTE}" + attribute + "\n" + body + "\n#{BLOCK_QUOTE}" + right
end
def extract_fence_blocks(site)
site.scan(/#{BLOCK_QUOTE}(.*?)\n(.*?)\n#{BLOCK_QUOTE}/m).map do |attributes_str, content|
attributes_str.chomp!
language = attributes_str.scan(/^([^\s]+)/).first.first
attributes = {language: language}
attributes_str.scan(/@([^\s]+)/) do |matches|
attributes[matches[0].to_sym] = true
end
attributes_str.scan(/([^\s:]+):([^\s]+)/) do |matches|
attributes[matches[0].to_sym] = matches[1].strip
end
point_min = site.index("#{BLOCK_QUOTE}#{attributes_str}\n#{content}\n#{BLOCK_QUOTE}")
# +1 = \n
# +6 = ```..```
point_max = point_min + attributes_str.size + 1 + content.size + 1 + (BLOCK_QUOTE.size * 2)
attributes[:'point-min'] = point_min
attributes[:'point-max'] = point_max
FenceBlock.new(attributes, content)
end
end
def execute_block(block, app_dir)
executable = block.attributes.fetch(:executable, "sh")
filename = block.attributes.fetch(:file, SecureRandom.hex(10))
chdir = block.attributes.fetch(:chdir, "")
app_file = File.join(app_dir, filename)
Dir.chdir(app_dir) do
File.write(filename, block.content)
Tempfile.open('command-out') do |file|
Dir.chdir(File.join(app_dir, chdir)) do
system("#{executable} '#{app_file}'", out: file)
end
file.rewind
if !block.attributes.fetch(:silence, false)
puts file.read
end
end
end
end
def render_block(block, app_dir)
filename = block.attributes[:file]
app_file = File.join(app_dir, filename)
FileUtils.mkdir_p(File.dirname(app_file))
open_mode = 'a'
open_mode = 'w' if block.attributes.fetch(:create, false)
File.open(app_file, open_mode) do |file|
file.write(block.content)
file.write("\n")
end
end
# publish
def mdtoapp(content, app_dir:, mode: nil)
blocks = extract_fence_blocks(content)
.reject { |b| b.attributes.fetch(:mdtoapp, false) == false }
as_artifact = mode == :artifact
as_tdd = mode == :tdd
last_executable_block = nil
# ejecutar
blocks
.each do |block|
for_artifact = block.attributes.fetch(:artifact, false)
for_tdd = block.attributes.fetch(:tdd, false)
# DEPRECATED hook:execute
is_executable = block.attributes.fetch(:hook, nil) == 'execute' || block.attributes.fetch(:execute, false)
if is_executable
next if as_artifact && for_artifact == false
last_executable_block = block
if (as_tdd && for_tdd) || for_artifact == true || mode.nil?
execute_block(block, app_dir)
end
elsif !block.attributes.fetch(:file, nil).nil?
render_block(block, app_dir)
end
end
if as_tdd
execute_block(last_executable_block, app_dir)
end
end
# script
if __FILE__==$0
if ENV['TO']
FileUtils.mkdir_p(ENV['TO'])
dir = ENV['TO']
else
dir = Dir.mktmpdir()
end
begin
source = ARGF.read
rescue Errno::ENOENT
require 'open-uri'
if ARGF.filename.start_with?('https://chiselapp.com/user/bit4bit')
source = URI.open(ARGF.filename).read
else
raise 'invalid file markdown'
end
end
mode = case true
# al habilitar TDD se ejecuta el ultimo bloque ejecutable
# y todos los marcados como @tdd
when ENV['TDD'].nil? == false
:tdd
# al habilitar ARTIFACT solo se ejecutan los bloques marcados como @artifact
# si generan los archivos
when ENV['ARTIFACT'].nil? == false
:artifact
else
nil
end
mdtoapp(source, app_dir: dir, mode: mode)
end