barista 0.6.1 → 0.7.0.pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,53 +3,151 @@ require 'digest/sha2'
3
3
  module Barista
4
4
  class Compiler
5
5
 
6
+ # TODO: Deprecate.
6
7
  class << self
7
- attr_accessor :bin_path, :js_path
8
- end
9
- self.bin_path ||= "coffee"
10
- self.js_path ||= File.expand_path('../coffee-script/coffee-script-0.9.4.js', File.dirname(__FILE__))
11
8
 
12
- def self.compilers
13
- [Compilers::Native, Compilers::Node]
9
+ def js_path
10
+ CoffeeScript::Source.path
11
+ end
12
+
13
+ def js_path=(value)
14
+ CoffeeScript::Source.path = value
15
+ end
16
+
17
+ def bin_path
18
+ CoffeeScript::Engines::Node.binary
19
+ end
20
+
21
+ def bin_path=(path)
22
+ CoffeeScript::Engines::Node.binary = path
23
+ end
24
+
25
+ def available?
26
+ CoffeeScript.engine.present? && CoffeeScript.engine.supported?
27
+ end
28
+
29
+ def check_availability!(silence = false)
30
+ available = available?
31
+ if !available && Barista.exception_on_error? && !silence
32
+ raise CompilerUnavailableError, "No method of compiling cofffescript is currently available. Please install therubyracer or node."
33
+ end
34
+ available
35
+ end
36
+
37
+ def compile(content, options = {})
38
+ self.new(content, options).to_js
39
+ end
40
+
41
+ def autocompile_file(file, force = false, silence_error = false)
42
+ # Ensure we have a coffeescript compiler available.
43
+ if !check_availability!(silence_error)
44
+ Barista.debug "The coffeescript compiler at '#{Compiler.bin_path}' is currently unavailable."
45
+ return nil
46
+ end
47
+ # Expand the path from the framework.
48
+ origin_path, framework = Framework.full_path_for(file)
49
+ return if origin_path.nil?
50
+ destination_path = framework.output_path_for(file)
51
+ return File.read(destination_path) unless dirty?(origin_path, destination_path) || force
52
+ Barista.debug "Compiling #{file} from framework '#{framework.name}'"
53
+ compiler = new(origin_path, :silence_error => silence_error, :output_path => destination_path)
54
+ content = compiler.to_js
55
+ compiler.save
56
+ content
57
+ end
58
+
59
+ def compile_as(file, type)
60
+ origin_path, framework = Framework.full_path_for(file)
61
+ return if origin_path.nil?
62
+ if type == :coffeescript
63
+ return File.read(origin_path), File.mtime(origin_path)
64
+ else
65
+ return autocompile_file(file), Time.now
66
+ end
67
+ end
68
+
69
+ def dirty?(from, to)
70
+ File.exist?(from) && (!File.exist?(to) || File.mtime(to) < File.mtime(from))
71
+ end
72
+
73
+ def setup_default_error_logger
74
+ Barista.on_compilation_error do |where, message|
75
+ if Barista.verbose?
76
+ Barista.debug "There was an error compiling coffeescript from #{where}:"
77
+ message.each_line { |line| Barista.debug line.rstrip }
78
+ end
79
+ end
80
+ end
81
+
14
82
  end
15
83
 
16
- def self.compiler=(value)
17
- name = "Barista::Compilers::#{value.to_s.classify}".constantize
18
- self.compiler_klass = name
19
- rescue
20
- self.compiler_klass = nil
84
+ def initialize(context, options = {})
85
+ @compiled = false
86
+ @options = options
87
+ setup_compiler_context context
21
88
  end
22
-
23
- def self.compiler
24
- compiler_klass.name.underscore.gsub("barista/compilers/", '').to_sym
89
+
90
+ def compile!
91
+ location = @options.fetch(:origin, 'inline')
92
+ @compiled_content = compile(@context, location)
93
+ @compiled_content = preamble(location) + @compiled_content if Barista.add_preamble?
94
+ @compiled = true
25
95
  end
26
-
27
- def self.compiler_klass
28
- @compiler_klass ||= compilers.detect(&:available?)
96
+
97
+ def to_js
98
+ compile! unless defined?(@compiled) && @compiled
99
+ @compiled_content
29
100
  end
30
101
 
31
- def self.compiler_klass=(value)
32
- @compiler_klass = value.present? ? value : nil
102
+ def compile(script, where = 'inline')
103
+ Barista.invoke_hook :before_compilation, where
104
+ out = CoffeeScript.compile script, :bare => Barista.bare?
105
+ Barista.invoke_hook :compiled, where
106
+ out
107
+ rescue CoffeeScript::Error => e
108
+ Barista.invoke_hook :compilation_failed, where, e.message
109
+ if Barista.exception_on_error? && !@options[:silence]
110
+ raise CompilationError, "CoffeeScript encountered an error compiling #{where}: #{e.message}"
111
+ end
112
+ compilation_error_for where, e.message
33
113
  end
34
114
 
35
- def self.available?
36
- compiler_klass.present?
115
+ def save(path = @options[:output_path])
116
+ return false unless path.is_a?(String) && !to_js.nil?
117
+ FileUtils.mkdir_p File.dirname(path)
118
+ File.open(path, "w+") { |f| f.write @compiled_content }
119
+ true
120
+ rescue Errno::EACCES
121
+ false
37
122
  end
38
123
 
39
- def self.check_availability!(silence = false)
40
- available?.tap do |available|
41
- if !available && Barista.exception_on_error? && !silence
42
- raise CompilerUnavailableError, "A coffee script compiler is currently unavailable. Please install therubyracer or coffee-script via node"
43
- end
44
- end
45
- end
124
+ protected
46
125
 
47
- def self.compile(path, options = {})
48
- compiler_klass.new(path, options).to_js
126
+ def preamble(location)
127
+ "/* DO NOT MODIFY. This file was compiled #{Time.now.httpdate} from\n * #{location.strip}\n */\n\n"
49
128
  end
50
-
51
- def self.dirty?(from, to)
52
- compiler_klass.dirty?(from, to)
129
+
130
+ def compilation_error_for(location, message)
131
+ details = "Compilation of '#{location}' failed:\n#{message}"
132
+ Barista.verbose? ? "alert(#{details.to_json});" : nil
53
133
  end
134
+
135
+ def setup_compiler_context(context)
136
+ if context.respond_to?(:read)
137
+ @context = context.read
138
+ @type = :string
139
+ default_path = context.respond_to?(:path) ? context.path : 'inline'
140
+ @options[:origin] ||= default_path
141
+ elsif !context.include?("\n") && File.exist?(context)
142
+ @context = File.read(context)
143
+ @type = :file
144
+ @options[:origin] ||= context
145
+ else
146
+ @context = context.to_s
147
+ @type = :string
148
+ @options[:origin] ||= 'inline'
149
+ end
150
+ end
151
+
54
152
  end
55
153
  end
@@ -0,0 +1,92 @@
1
+ module Barista
2
+ module Extensions
3
+
4
+ def self.included(parent)
5
+ parent.class_eval do
6
+ extend ClassMethods
7
+ include InstanceMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ def has_boolean_options(*names)
14
+ source = []
15
+ names.each do |name|
16
+ source << <<-EOM
17
+
18
+ def #{name}!
19
+ @#{name} = true
20
+ end
21
+
22
+ def #{name}?
23
+ defined?(@#{name}) ? @#{name} : default_for_#{name}
24
+ end
25
+
26
+ def #{name}=(value)
27
+ @#{name} = !!value
28
+ end
29
+
30
+ def default_for_#{name}
31
+ false
32
+ end
33
+
34
+ EOM
35
+ end
36
+ class_eval source.join("\n"), __FILE__, __LINE__
37
+ end
38
+
39
+ def has_hook_method(options)
40
+ source = []
41
+ options.each_pair do |name, event|
42
+ source << <<-EOM
43
+ def #{name}(&blk)
44
+ on_hook #{event.to_sym.inspect}, &blk
45
+ end
46
+ EOM
47
+ end
48
+ class_eval source.join("\n"), __FILE__, __LINE__
49
+ end
50
+
51
+ def has_delegate_methods(delegate, *args)
52
+ source = []
53
+ args.each do |method|
54
+ source << <<-EOM
55
+
56
+ def #{method}(*args, &blk)
57
+ #{delegate}.send(:#{method}, *args, &blk)
58
+ end
59
+
60
+ EOM
61
+ end
62
+ class_eval source.join("\n"), __FILE__, __LINE__
63
+ end
64
+
65
+ def has_deprecated_methods(*args)
66
+ source = []
67
+ args.each do |method|
68
+ source << <<-EOM
69
+
70
+ def #{method}(*args, &blk)
71
+ Barista.deprecate!(self, :#{method})
72
+ nil
73
+ end
74
+
75
+ EOM
76
+ end
77
+ class_eval source.join("\n"), __FILE__, __LINE__
78
+ end
79
+
80
+ end
81
+
82
+ module InstanceMethods
83
+
84
+ def deprecate!(object, method, other = nil)
85
+ klass_prefix = (object.is_a?(Class) || object.is_a?(Module)) ? "#{object.name}." : "#{object.class.name}#"
86
+ warn "#{klass_prefix}#{method} is deprecated and will be removed in 1.0. #{other}".strip
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+ end
@@ -1,10 +1,20 @@
1
1
  module Barista
2
2
  class Filter
3
3
 
4
- def self.filter(controller)
5
- Rails.logger.debug "[Barista] Compiling all scripts"
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ dup._call(env)
10
+ end
11
+
12
+ def _call(env)
13
+ Barista.debug 'Compiling all scripts for barista'
6
14
  Barista.compile_all!
15
+ # Now, actually call the app.
16
+ @app.call env
7
17
  end
8
18
 
9
19
  end
10
- end
20
+ end
@@ -10,9 +10,9 @@ module Barista
10
10
  end
11
11
 
12
12
  def self.all(include_default = false)
13
- all = (@all ||= [])
14
- all = [default_framework] + all if include_default
15
- all
13
+ (@all ||= []).dup.tap do |all|
14
+ all.unshift default_framework if include_default
15
+ end
16
16
  end
17
17
 
18
18
  def self.exposed_coffeescripts
@@ -20,6 +20,10 @@ module Barista
20
20
  collection + fw.exposed_coffeescripts
21
21
  end.uniq.sort_by { |f| f.length }
22
22
  end
23
+
24
+ def self.coffeescript_glob_paths
25
+ all(true).map { |fw| fw.coffeescript_glob_path }
26
+ end
23
27
 
24
28
  def self.full_path_for(script)
25
29
  script = script.to_s.gsub(/\.js$/, '.coffee').gsub(/^\/+/, '')
@@ -31,7 +35,7 @@ module Barista
31
35
  end
32
36
 
33
37
  def self.register(name, root)
34
- (@all ||= []) << self.new(name, root)
38
+ (@all ||= []) << self.new(:name => name, :root => root)
35
39
  end
36
40
 
37
41
  def self.[](name)
@@ -41,19 +45,34 @@ module Barista
41
45
 
42
46
  attr_reader :name, :framework_root, :output_prefix
43
47
 
44
- def initialize(name, root, output_prefix = nil)
45
- @name = name.to_s
46
- @output_prefix = nil
47
- @framework_root = File.expand_path(root)
48
+ def initialize(options, root = nil, output_prefix = nil)
49
+ unless options.is_a?(Hash)
50
+ Barista.deprecate! self, "initialize(name, root = nil, output_prefix = nil)", "Please use the option syntax instead."
51
+ options = {
52
+ :name => options,
53
+ :root => root,
54
+ :output_prefix => output_prefix
55
+ }
56
+ end
57
+ # actually setup the framework.
58
+ check_options! options, :name, :root
59
+ @name = options[:name].to_s
60
+ @output_prefix = options[:output_prefix]
61
+ @framework_root = File.expand_path(options[:root].to_s)
62
+ @output_root = options[:output_root] && Pathname(options[:output_root])
48
63
  end
49
64
 
50
65
  def coffeescripts
51
- Dir[File.join(@framework_root, "**", "*.coffee")]
66
+ Dir[coffeescript_glob_path]
67
+ end
68
+
69
+ def coffeescript_glob_path
70
+ @coffeescript_glob_path ||= File.join(@framework_root, "**", "*.coffee")
52
71
  end
53
72
 
54
73
  def short_name(script)
55
74
  short_name = remove_prefix script, @framework_root
56
- File.join *[@output_prefix, short_name].compact
75
+ File.join(*[@output_prefix, short_name].compact)
57
76
  end
58
77
 
59
78
  def exposed_coffeescripts
@@ -61,8 +80,8 @@ module Barista
61
80
  end
62
81
 
63
82
  def output_prefix=(value)
64
- value = value.to_s.gsub /(^\/|\/$)/, ''
65
- @output_prefix = value.blank? ? nil : value
83
+ value = value.to_s.gsub(/(^\/|\/$)/, '').strip
84
+ @output_prefix = value.empty? ? nil : value
66
85
  end
67
86
 
68
87
  def full_path_for(name)
@@ -70,10 +89,26 @@ module Barista
70
89
  File.exist?(full_path) ? full_path : nil
71
90
  end
72
91
 
92
+ def output_root
93
+ @output_root || Barista.output_root
94
+ end
95
+
96
+ def output_path_for(file, format = 'js')
97
+ # Strip the leading slashes
98
+ file = file.to_s.gsub(/^\/+/, '')
99
+ output_root.join(file).to_s.gsub(/\.[^\.]+$/, ".#{format}")
100
+ end
101
+
73
102
  protected
74
103
 
75
104
  def remove_prefix(path, prefix)
76
- path.to_s.gsub /^#{Regexp.escape(prefix.to_s)}\/?/, ''
105
+ path.to_s.gsub(/^#{Regexp.escape(prefix.to_s)}\/?/, '')
106
+ end
107
+
108
+ def check_options!(options, *keys)
109
+ keys.each do |option|
110
+ raise ArgumentError, "#{option.inspect} is a required options." if options[option].nil?
111
+ end
77
112
  end
78
113
 
79
114
  end
@@ -0,0 +1,42 @@
1
+ module Barista
2
+ module HamlFilter
3
+ module CoffeeScript
4
+
5
+ def render_with_options(text, options)
6
+ type = render_type
7
+ case type
8
+ when :coffeescript
9
+ content_type = 'text/coffeescript'
10
+ cdata_wrapper = '#%s'
11
+ inner = text
12
+ when :javascript
13
+ content_type = 'text/javascript'
14
+ cdata_wrapper = '//%s'
15
+ inner = Barista::Compiler.compile(text)
16
+ end
17
+ output = []
18
+ output << "<script type=#{options[:attr_wrapper]}#{content_type(type)}#{options[:attr_wrapper]}>"
19
+ output << " #{cdata_wrapper % '<![CDATA['}"
20
+ output << " #{inner}".rstrip.gsub("\n", "\n ")
21
+ output << " #{cdata_wrapper % ']]>'}"
22
+ output << "</script>"
23
+ output.join("\n")
24
+ end
25
+
26
+ def render_type
27
+ Barista.embedded_interpreter? ? :coffeescript : :javascript
28
+ end
29
+
30
+ def content_type(render_type)
31
+ Barista::Server::CONTENT_TYPE_MAPPING[render_type]
32
+ end
33
+
34
+ end
35
+
36
+ def self.setup
37
+ if defined?(Haml)
38
+ CoffeeScript.module_eval { include Haml::Filters::Base }
39
+ end
40
+ end
41
+ end
42
+ end