Module: Vitrine

Defined in:
lib/version.rb,
lib/sourcemaps.rb,
lib/atomic_write.rb

Defined Under Namespace

Modules: Server Classes: App, AssetCompiler, SassImporter

Constant Summary collapse

VERSION =
'0.0.31'

Class Method Summary collapse

Class Method Details

.atomic_write(file_name, temp_dir = Dir.tmpdir) {|temp_file| ... } ⇒ Object

Stolen from Rails

Yields:

  • (temp_file)


3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/atomic_write.rb', line 3

def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
  require 'tempfile' unless defined?(Tempfile)
  require 'fileutils' unless defined?(FileUtils)

  temp_file = Tempfile.new(File.basename(file_name), temp_dir)
  temp_file.binmode
  yield temp_file
  temp_file.close

  begin
    # Get original file permissions
    old_stat = File.stat(file_name)
  rescue Errno::ENOENT
    # No old permissions, write a temp file to determine the defaults
    check_name = File.join(File.dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
    open(check_name, "w") { }
    old_stat = File.stat(check_name)
    File.unlink(check_name)
  end

  # Overwrite original file with temp file
  FileUtils.mv(temp_file.path, file_name)

  # Set correct permissions on new file
  begin
    File.chown(old_stat.uid, old_stat.gid, file_name)
    # This operation will affect filesystem ACL's
    File.chmod(old_stat.mode, file_name)
  rescue Errno::EPERM
    # Changing file ownership failed, moving on.
  end
end

.build_coffeescript_source_map_body(full_coffeescript_file_path, public_folder_path) ⇒ Object

Compile a CS source map. TODO: this method should be married to the method that compiles the source code itself



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/sourcemaps.rb', line 92

def self.build_coffeescript_source_map_body(full_coffeescript_file_path, public_folder_path)
  
  script = File.read(full_coffeescript_file_path)
  
  # We need to feed paths ON THE SERVER so that the browser can connect the coffee file, the map and the JS file
  # - specify coffee source file explicitly (see http://coffeescript.org/documentation/docs/sourcemap.html#section-8)
  # The paths need to be slash-prepended (server-absolute)
  relative_path = '/' + Pathname.new(full_coffeescript_file_path).relative_path_from(Pathname.new(public_folder_path)).to_s
  relative_js_path = '/' + relative_path.gsub(/\.coffee$/, '.js')
  
  options = {sourceMap: true}
  
  # coffee requires filename option to work with source maps (see http://coffeescript.org/documentation/docs/coffee-script.html#section-4)
  options[:filename] = relative_js_path
  options[:sourceFiles] = [relative_path]
  
  CoffeeScript.compile(script, options)["v3SourceMap"]
end

.compile_coffeescript(script, options = {}) ⇒ Object

Compile a script (String or IO) to JavaScript. This is a version lifted from here github.com/josh/ruby-coffee-script/blob/114b65b638f66ba04b60bf9c24b54360260f9898/lib/coffee_script.rb which propagates error line



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/sourcemaps.rb', line 64

def self.compile_coffeescript(script, options = {})
  script = script.read if script.respond_to?(:read)

  if options.key?(:bare)
  elsif options.key?(:no_wrap)
    options[:bare] = options[:no_wrap]
  else
    options[:bare] = false
  end

  wrapper = <<-WRAPPER
    (function(script, options) {
      try {
        return CoffeeScript.compile(script, options);
      } catch (err) {
        if (err instanceof SyntaxError && err.location) {
          throw new SyntaxError([options.filename, err.location.first_line + 1, err.location.first_column + 1].join(":") + ": " + err.message)
        } else {
          throw err;
        }
      }
    })
  WRAPPER
  CoffeeScript::Source.context.call(wrapper, script, options)
end

.compile_sass(scss_path, public_folder_path) ⇒ Object

Compiles SASS and it’s sourcemap and returns the CSS only



55
56
57
58
# File 'lib/sourcemaps.rb', line 55

def self.compile_sass(scss_path, public_folder_path)
  css, map = compile_sass_and_sourcemap(scss_path, public_folder_path)
  css
end

.compile_sass_and_sourcemap(scss_path, public_folder_path) ⇒ Object

Compile a SASS/SCSS file to CSS



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/sourcemaps.rb', line 17

def self.compile_sass_and_sourcemap(scss_path, public_folder_path)
  # Compute the paths relative to the webserver public root
  scss_uri = '/' + Pathname.new(scss_path).relative_path_from(Pathname.new(public_folder_path)).to_s
  css_uri = scss_uri.gsub(/\.scss$/, '.css')
  sourcemap_uri = css_uri + '.map'
  
  engine_opts = {importer: SassImporter.new(public_folder_path), sourcemap: true, cache: false}
  map_options = {css_path: css_uri, sourcemap_path: sourcemap_uri }
  
  engine = Sass::Engine.for_file(scss_path, engine_opts)
  
  # Determine the sourcemap URL for the SASS file
  raw_css, mapping = engine.render_with_sourcemap(sourcemap_uri)
  
  # Serialize the sourcemap
  # We need to pass the map options so that the generated sourcemap refers to the
  # file that can be pulled off the server as opposed to a file on the filesystem
  sourcemap_body = mapping.to_json(map_options)
  
  # We are using a pre-release version of SASS which still had old sourcemap reference
  # syntax, so we have to fix it by hand
  # TODO: remove once there is a gem for https://github.com/nex3/sass/pull/982
  chunk = Regexp.escape('/*@ sourceMappingURL')
  replacement = '/*# sourceMappingURL'
  re = /^#{chunk}/
  raw_css = raw_css.gsub(re,replacement)
  
  # And return both
  [raw_css, sourcemap_body]
end

.compile_sass_source_map(scss_path, public_folder_path) ⇒ Object

Compile SASS and return the source map only



49
50
51
52
# File 'lib/sourcemaps.rb', line 49

def self.compile_sass_source_map(scss_path, public_folder_path)
  css, map = compile_sass_and_sourcemap(scss_path, public_folder_path)
  map
end