Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mj-include support and eliminate tempfiles #100

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.9.0

* Darren Rush refactored the tempfile IO to stdin/stdout and added support for `<mj-include>`.

## 4.8.0

* Alan Halatian updated the MJML binary discovery when installed with Yarn.
Expand Down
4 changes: 2 additions & 2 deletions lib/mjml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ def self.check_version(bin)
false
end

def self.run_mjml(args, mjml_bin: valid_mjml_binary)
Open3.capture3("#{mjml_bin} #{args}")
def self.run_mjml(args, mjml_bin: valid_mjml_binary, stdin_data: nil)
Open3.capture3("#{mjml_bin} #{args}", stdin_data: stdin_data)
end

def self.valid_mjml_binary
Expand Down
5 changes: 4 additions & 1 deletion lib/mjml/handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@ def template_handler
def call(template, source = nil)
compiled_source = compile_source(source, template)

folder = File.dirname(template.identifier)

# Per MJML v4 syntax documentation[0] valid/render'able document MUST start with <mjml> root tag
# If we get here and template source doesn't start with one it means
# that we are rendering partial named according to legacy naming convention (partials ending with '.mjml')
# Therefore we skip MJML processing and return raw compiled source. It will be processed
# by MJML library when top-level layout/template is rendered
#
# [0] - https://github.com/mjmlio/mjml/blob/master/doc/components_1.md#mjml

if /<mjml.*?>/i.match?(compiled_source)
"Mjml::Parser.new(begin;#{compiled_source};end).render.html_safe"
"Mjml::Parser.new(begin;#{compiled_source};end, \"#{folder}\").render.html_safe"
else
compiled_source
end
Expand Down
39 changes: 18 additions & 21 deletions lib/mjml/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,49 @@ class Parser
class ParseError < StandardError; end

attr_reader :input
attr_reader :path

# Create new parser
#
# @param input [String] The string to transform in html
def initialize(input)
def initialize(input, path)
raise Mjml.mjml_binary_error_string unless Mjml.valid_mjml_binary

@input = input
@path = path
end

# Render mjml template
#
# @return [String]
def render
in_tmp_file = Tempfile.open(['in', '.mjml']) do |file|
file.write(input)
file # return tempfile from block so #unlink works later
end
run(in_tmp_file.path, Mjml.beautify, Mjml.minify, Mjml.validation_level)
run(input, Mjml.beautify, Mjml.minify, Mjml.validation_level)
rescue StandardError
raise if Mjml.raise_render_exception

''
ensure
in_tmp_file&.unlink
# in_tmp_file&.unlink
end

# Exec mjml command
#
# @return [String] The result as string
# rubocop:disable Style/OptionalBooleanParameter: Fixing this offense would imply a change in the public API.
def run(in_tmp_file, beautify = true, minify = false, validation_level = 'strict')
Tempfile.create(['out', '.html']) do |out_tmp_file|
command = "-r #{in_tmp_file} -o #{out_tmp_file.path} " \
"--config.beautify #{beautify} --config.minify #{minify} --config.validationLevel #{validation_level}"
_, stderr, status = Mjml.run_mjml(command)

unless status.success?
# The process status ist quite helpful in case of dying processes without STDERR output.
# Node exit codes are documented here: https://node.readthedocs.io/en/latest/api/process/#exit-codes
raise ParseError, "#{stderr.chomp}\n(process status: #{status})"
end

Mjml.logger.warn(stderr.chomp) if stderr.present?
out_tmp_file.read
def run(input, beautify = true, minify = false, validation_level = 'strict')
command = "-i " \
"--config.beautify #{beautify} --config.minify #{minify} --config.validationLevel #{validation_level} " \
"--config.filePath #{path}"
stdout, stderr, status = Mjml.run_mjml(command, stdin_data: input)

unless status.success?
# The process status ist quite helpful in case of dying processes without STDERR output.
# Node exit codes are documented here: https://node.readthedocs.io/en/latest/api/process/#exit-codes
raise ParseError, "#{stderr.chomp}\n(process status: #{status})"
end

Mjml.logger.warn(stderr.chomp) if stderr.present?
stdout
end
# rubocop:enable Style/OptionalBooleanParameter
end
Expand Down
2 changes: 1 addition & 1 deletion lib/mjml/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

module Mjml
# Version number no longer matches MJML.io version
VERSION = '4.8.0'
VERSION = '4.9.0'
end