Skip to content

Instantly share code, notes, and snippets.

@ethagnawl
Forked from tmichel/elm_sprockets.rb
Created July 29, 2017 03:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ethagnawl/751c4903979051cdf364e4c0c4ddd93e to your computer and use it in GitHub Desktop.
Save ethagnawl/751c4903979051cdf364e4c0c4ddd93e to your computer and use it in GitHub Desktop.
Elm Rails 4.2 integration (Sprockets 3.x)
# Elm integration for Sprockets 3.x (Rails 4.2)
#
# Revised version of a Sprockets 3.x processor for Elm. It supports
# dependencies. One little quirk is that only .js.elm files are
# requireable in application.js.
#
# How to use it?
#
# Put this file in your config/initializers/ and create
# app/assets/javascripts/MyModule.js.elm and require it in your
# application.js. For Elm modules that are imported by other Elm modules
# add a file with .elm extension (no .js !).
#
# Don't forget to add app/assets/javascripts to your elm-package.json
require "open3"
# Elm Sprockets integration
class ElmProcessor
VERSION = "1"
class CompileError < StandardError; end
class << self
def call(input)
new(input).call
end
def cmd
@_cmd ||= ENV.fetch("ELM_MAKE_PATH", "elm-make")
end
def elm_version
@_elm_version ||= begin
line = `#{cmd} --help`.lines.first.chomp
line.match(/\(Elm Platform (.+)\)/) { |m| m[1] }
end
end
def cache_key
@_cache_key ||= "#{name}:#{elm_version}:#{VERSION}"
end
end
def initialize(input)
@input = input
end
def call
input[:cache].fetch([cache_key, hexdigest]) { compile }
end
private
attr_reader :input
def compile
Open3.popen3(cmd, "--yes", "--output", output_file.to_s, filename, chdir: Rails.root) do |_in, out, err, t|
compiler_out = out.read
compiler_err = err.read
if t.value != 0
raise CompileError, compiler_err
end
add_dependencies
context.metadata.merge(data: output_file.read)
end
end
def output_file
root.join("tmp", "cache", "assets", "elm", "#{input[:name]}.js")
end
def cmd
self.class.cmd
end
def cache_key
self.class.cache_key
end
def filename
input[:filename]
end
def data
input[:data]
end
def root
Pathname.new(input[:environment].root)
end
def logger
input[:environment].logger
end
def context
@_context ||= input[:environment].context_class.new(input)
end
def add_dependencies
dependencies do |dep|
context.depend_on(dep.name)
end
end
def hexdigest
dependencies.map(&:hexdigest).push(Digest::SHA1.hexdigest(data))
end
def dependencies
return to_enum(__method__) unless block_given?
queue = [data]
while curr = queue.pop
curr.scan(/^import\s+([^\s]+)/).map do |import|
logical_name = import.first.gsub(".", "/")
path = File.join(input[:load_path], "#{logical_name}.elm")
if File.file?(path)
dep = Dependency.new(logical_name, path, File.read(path))
queue.push(dep.content)
yield dep
end
end
end
end
Dependency = Struct.new(:name, :path, :content) do
def hexdigest
Digest::SHA1.hexdigest(content)
end
end
end
Rails.application.assets.register_engine(".elm", ElmProcessor)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment