barista 0.6.1 → 0.7.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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