class RogerSassc::Middleware

Rack Middleware for Roger Sassc

This middleware transforms a given url scss -> css. It does this by checking if the .css can be resolved to .scss file, if so it compiles (with the help of libsass) the scss.

Attributes

project[RW]
resolver[W]

Public Class Methods

new(app, options = {}) click to toggle source
# File lib/roger_sassc/middleware.rb, line 15
def initialize(app, options = {})
  @app = app

  defaults = {
    load_paths: RogerSassc.load_paths,
    source_map: true
  }

  @options = defaults.update(options)

  # If the sourcemap is embedded, the content of the source must
  # be embedded, because the paths to the sources will not be correct,
  # and the browser will not be able to load the source.
  @options[:source_map_contents] = true if @options[:source_map_embed]
end

Public Instance Methods

call(env) click to toggle source
# File lib/roger_sassc/middleware.rb, line 31
def call(env)
  @project ||= env["roger.project"]
  @options[:roger_html_path] = @project.html_path

  url = ::Rack::Utils.unescape(env["PATH_INFO"].to_s).sub(%r{^/}, "")
  unless url.end_with?(".css") || url.end_with?(".css.map")
    return @app.call(env)
  end

  # Convert the url to an absolute path,
  # which is used to retrieve compile the scss
  scss_path = resolve_url(url)

  return @app.call(env) unless scss_path

  env["rack.errors"].puts "Invoking ruby-sassc for #{scss_path}"
  engine = create_scss_engine(scss_path)

  begin
    respond(engine, url.end_with?(".css.map"))
  rescue ::SassC::SyntaxError,
         ::SassC::NotRenderedError,
         ::SassC::InvalidStyleError => sassc_error
    respond_with_error(sassc_error)
  end
end

Private Instance Methods

create_scss_engine(scss_path) click to toggle source
# File lib/roger_sassc/middleware.rb, line 72
def create_scss_engine(scss_path)
  # Supply the filename for load path resolving
  @options[:filename] = scss_path.to_s

  # Set a sourcemap file if the sourcemap is not embedded
  if @options[:source_map] && !@options[:source_map_embed]
    @options[:source_map_file] = scss_path.to_s.gsub(/\.scss$/, ".css.map")
  end

  ::SassC::Engine.new(File.read(scss_path), @options)
end
debug_css(sassc_error) click to toggle source
# File lib/roger_sassc/middleware.rb, line 84
def debug_css(sassc_error)
  # Replace
  # * regular line-ends with css compat strings
  #     via: http://stackoverflow.com/questions/9062988/newline-character-sequence-in-css-content-property
  # * single quotes with double quotes for escaping
  sassc_error_css_string = sassc_error.to_s.gsub("\n", "\\A").gsub("'", "\"")

  # Build debug string
  debug = "/*\n"
  debug << "#{sassc_error}\n\n"
  debug << "Load paths:\n"
  debug << "#{@options[:load_paths]}\n\n"
  debug << "*/\n"
  debug << "body:before {\n"
  debug << "  white-space: pre;\n"
  debug << "  font-family: monospace;\n"
  debug << "  content: '#{sassc_error_css_string}'; }\n"
end
resolve_url(url) click to toggle source
# File lib/roger_sassc/middleware.rb, line 60
def resolve_url(url)
  # Make sure we strip off .map
  url = url.gsub(/.map\Z/, "")

  # A .css file is requested in the browser
  url.gsub!(/\.css$/, ".scss")

  # Use the resolver to translate urls to file paths
  # returns nill when no file is found via url
  resolver.url_to_path url, exact_match: true
end
resolver() click to toggle source
# File lib/roger_sassc/middleware.rb, line 103
def resolver
  @resolver ||= Roger::Resolver.new(@project.html_path)
end
respond(engine, map = false) click to toggle source
# File lib/roger_sassc/middleware.rb, line 117
def respond(engine, map = false)
  css = engine.render

  resp = ::Rack::Response.new do |res|
    res.status = 200
    res.headers["Content-Type"] = "text/css"
    # last modified header
    res.write map ? engine.source_map : css
  end
  resp.finish
end
respond_with_error(err) click to toggle source
# File lib/roger_sassc/middleware.rb, line 107
def respond_with_error(err)
  resp = ::Rack::Response.new do |res|
    res.status = 200
    res.headers["Content-Type"] = "text/css"
    # last modified header
    res.write debug_css(err)
  end
  resp.finish
end