├── .rspec
├── lib
├── barista
│ ├── tasks.rb
│ ├── tasks
│ │ └── barista.rake
│ ├── version.rb
│ ├── filter.rb
│ ├── capistrano.rb
│ ├── integration
│ │ ├── rails2.rb
│ │ ├── sinatra.rb
│ │ └── rails3.rb
│ ├── hooks.rb
│ ├── integration.rb
│ ├── haml_filter.rb
│ ├── rake_task.rb
│ ├── helpers.rb
│ ├── extensions.rb
│ ├── server.rb
│ ├── framework.rb
│ └── compiler.rb
├── generators
│ ├── barista
│ │ └── install
│ │ │ ├── USAGE
│ │ │ ├── install_generator.rb
│ │ │ └── templates
│ │ │ └── initializer.rb
│ └── barista_install_generator.rb
└── barista.rb
├── .rvmrc.example
├── spec
├── assets
│ └── alert.coffee
├── spec_helper.rb
└── barista_spec.rb
├── .document
├── .gitignore
├── DESCRIPTION
├── Gemfile
├── LICENSE
├── Rakefile
├── Gemfile.lock
├── barista.gemspec
└── README.md
/.rspec:
--------------------------------------------------------------------------------
1 | --colour
--------------------------------------------------------------------------------
/lib/barista/tasks.rb:
--------------------------------------------------------------------------------
1 | load 'barista/tasks/barista.rake'
--------------------------------------------------------------------------------
/.rvmrc.example:
--------------------------------------------------------------------------------
1 | rvm --create use "ree-1.8.7-2010.02@barista"
2 |
--------------------------------------------------------------------------------
/spec/assets/alert.coffee:
--------------------------------------------------------------------------------
1 | hello ->
2 | alert 'hello world'
3 |
4 |
--------------------------------------------------------------------------------
/.document:
--------------------------------------------------------------------------------
1 | README.rdoc
2 | lib/**/*.rb
3 | bin/*
4 | features/**/*.feature
5 | LICENSE
6 |
--------------------------------------------------------------------------------
/lib/generators/barista/install/USAGE:
--------------------------------------------------------------------------------
1 | To copy a Barista initializer to your Rails App, with some configuration values, just do:
2 |
3 | rails generate barista:install
4 |
--------------------------------------------------------------------------------
/lib/barista/tasks/barista.rake:
--------------------------------------------------------------------------------
1 | require 'barista/rake_task'
2 |
3 | Barista::RakeTask.new do |t|
4 | t.namespace = :barista
5 | t.task_name = :brew
6 | t.rails = true
7 | end
8 |
--------------------------------------------------------------------------------
/lib/barista/version.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Version
3 | MAJOR = 1
4 | MINOR = 3
5 | PATCH = 0
6 | STRING = [MAJOR, MINOR, PATCH].join(".")
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/lib/generators/barista_install_generator.rb:
--------------------------------------------------------------------------------
1 | # Remove this file after deprecation
2 | if caller.none? { |l| l =~ %r{lib/rails/generators\.rb:(\d+):in `lookup!'$} }
3 | warn "[WARNING] `rails g barista_install` is deprecated, please use `rails g barista:install` instead."
4 | end
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## MAC OS
2 | .DS_Store
3 |
4 | ## TEXTMATE
5 | *.tmproj
6 | tmtags
7 |
8 | ## EMACS
9 | *~
10 | \#*
11 | .\#*
12 |
13 | ## VIM
14 | *.swp
15 |
16 | ## PROJECT::GENERAL
17 | coverage
18 | rdoc
19 | pkg
20 |
21 | ## PROJECT::SPECIFIC
22 |
23 | .rvmrc
24 | public
25 |
26 |
--------------------------------------------------------------------------------
/lib/generators/barista/install/install_generator.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Generators
3 | class InstallGenerator < Rails::Generators::Base
4 |
5 | source_root File.expand_path("templates", File.dirname(__FILE__))
6 |
7 | def create_initializer
8 | copy_file 'initializer.rb', 'config/initializers/barista_config.rb'
9 | end
10 |
11 | end
12 | end
13 | end
--------------------------------------------------------------------------------
/lib/barista/filter.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | class Filter
3 |
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' if Barista.auto_compile?
14 | Barista.compile_all!
15 | # Now, actually call the app.
16 | @app.call env
17 | end
18 |
19 | end
20 | end
21 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Barista provides simple, integrated support for CoffeeScript in Rack and Rails applications.
2 |
3 | Much like Compass does for Sass, It also provides Frameworks (bundleable code which can be shared via Gems).
4 |
5 | Lastly, it also provides a Rack Application (which can be used to server compiled code), a around_filter-style precompiler (as Rack middleware) and simple helpers for rails and Haml.
6 |
7 | For more details, please see the the README file bundled with it.
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 |
3 | gem 'coffee-script', '~> 2.2'
4 |
5 | group :development, :test do
6 | gem 'rails', '~> 3.0'
7 | gem 'jeweler', '~> 1.0'
8 | gem 'rspec', '~> 2.6'
9 | gem 'rspec-rails', '~> 2.6'
10 | gem 'rspec-core', '~> 2.6'
11 | gem 'rdoc', '~> 2.4'
12 | gem 'rr', '~> 1.0'
13 | gem 'ruby-debug', :platform => :ruby_18
14 | gem 'ruby-debug19', '~> 0.11', :platform => :ruby_19
15 | end
16 |
--------------------------------------------------------------------------------
/lib/barista/capistrano.rb:
--------------------------------------------------------------------------------
1 | Capistrano::Configuration.instance.load do
2 |
3 | before 'deploy:restart', 'barista:brew'
4 |
5 | _cset(:barista_role) { :app }
6 |
7 | namespace :barista do
8 | desc 'Compile CoffeeScripts.'
9 | task :brew, :roles => lambda { fetch(:barista_role) } do
10 | rails_env = fetch(:rails_env, "production")
11 | run("cd #{current_path} ; RAILS_ENV=#{rails_env} bundle exec rake barista:brew")
12 | end
13 | end
14 | end
15 |
16 |
--------------------------------------------------------------------------------
/lib/barista/integration/rails2.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Integration
3 | module Rails2
4 |
5 | def self.setup
6 | ActionController::Dispatcher.middleware.tap do |middleware|
7 | middleware.use Barista::Filter if Barista.add_filter?
8 | middleware.use Barista::Server::Proxy
9 | end
10 | Barista.setup_defaults
11 | ActionController::Base.helper Barista::Helpers
12 | end
13 |
14 | end
15 | end
16 | end
--------------------------------------------------------------------------------
/lib/barista/hooks.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | class Hooks
3 |
4 | def initialize
5 | @callbacks = Hash.new { |h,k| h[k] = [] }
6 | end
7 |
8 | def on(name, &blk)
9 | @callbacks[name.to_sym] << blk
10 | end
11 |
12 | def invoke(name, *args)
13 | @callbacks[name.to_sym].each do |callback|
14 | break if callback.call(*args) == false
15 | end
16 | nil
17 | end
18 |
19 | def has_hook?(name)
20 | @callbacks.has_key?(name)
21 | end
22 |
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 |
3 | ENV['RAILS_ENV'] ||= 'test'
4 | ENV['RAILS_ROOT'] ||= File.expand_path("..", __FILE__)
5 |
6 | require "action_controller/railtie"
7 | require "action_mailer/railtie"
8 | require "active_resource/railtie"
9 |
10 | require 'rspec/rails'
11 |
12 | Bundler.require(:default, Rails.env) if defined?(Bundler)
13 |
14 | class Application < Rails::Application; end
15 |
16 | require 'barista'
17 |
18 | RSpec.configure do |config|
19 | config.mock_with :rspec
20 | config.expect_with :rspec
21 | end
22 |
23 |
--------------------------------------------------------------------------------
/lib/barista/integration.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Integration
3 |
4 | autoload :Rails2, 'barista/integration/rails2'
5 | autoload :Rails3, 'barista/integration/rails3'
6 | autoload :Sinatra, 'barista/integration/sinatra'
7 |
8 | def self.setup
9 | setup_rails if defined?(Rails)
10 | end
11 |
12 | def self.setup_rails
13 | case Rails::VERSION::MAJOR
14 | when 3
15 | Rails3
16 | when 2
17 | # We need to manually call the initialiser stuff in Rails 2.
18 | Rails2.setup
19 | end
20 | end
21 |
22 | end
23 | end
--------------------------------------------------------------------------------
/lib/barista/integration/sinatra.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Integration
3 | module Sinatra
4 |
5 | def self.registered(app)
6 | app.configure do |inner_app|
7 | setup_defaults inner_app
8 | inner_app.use Barista::Filter if Barista.add_filter?
9 | inner_app.use Barista::Server::Proxy
10 | Barista.setup_defaults
11 | end
12 |
13 | end
14 |
15 | def self.setup_defaults(app)
16 | Barista.configure do |c|
17 | c.env = app.environment.to_s
18 | end
19 | end
20 |
21 | end
22 | end
23 | end
--------------------------------------------------------------------------------
/lib/barista/integration/rails3.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Integration
3 | module Rails3
4 | class Railtie < Rails::Railtie
5 |
6 | rake_tasks do
7 | load Barista.library_root.join('barista/tasks/barista.rake').to_s
8 | end
9 |
10 | initializer 'barista.wrap_filter' do
11 | config.app_middleware.use Barista::Filter if Barista.add_filter?
12 | config.app_middleware.use Barista::Server::Proxy
13 | end
14 |
15 | initializer 'barista.defaults' do
16 | Barista.setup_defaults
17 | end
18 |
19 | initializer 'barista.helpers' do
20 | ActionController::Base.helper Barista::Helpers
21 | end
22 |
23 | end
24 | end
25 | end
26 | end
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Darcy Laycock
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'bundler'
3 |
4 | begin
5 | Bundler.setup(:default, :development)
6 | rescue Bundler::BundlerError => e
7 | $stderr.puts e.message
8 | $stderr.puts "Run `bundle install` to install missing gems."
9 | exit e.status_code
10 | end
11 |
12 | require 'rake'
13 | require 'rspec/core/rake_task'
14 |
15 | require 'barista/version'
16 |
17 | require 'jeweler'
18 | Jeweler::Tasks.new do |gem|
19 | gem.name = "barista"
20 | gem.summary = %Q{Simple, transparent coffeescript integration for Rails and Rack applications.}
21 | gem.description = File.read(File.expand_path('DESCRIPTION', File.dirname(__FILE__)))
22 | gem.email = "sutto@sutto.net"
23 | gem.homepage = "http://github.com/Sutto/barista"
24 | gem.version = Barista::Version::STRING
25 | gem.authors = ["Darcy Laycock"]
26 | end
27 | Jeweler::GemcutterTasks.new
28 |
29 | RSpec::Core::RakeTask.new(:spec) do |spec|
30 | spec.pattern = "spec/**/*_spec.rb"
31 | end
32 |
33 | require 'rdoc/task'
34 | RDoc::Task.new do |rdoc|
35 | version = Barista::Version::STRING
36 | rdoc.rdoc_dir = 'rdoc'
37 | rdoc.title = "barista #{version}"
38 | rdoc.rdoc_files.include('README*')
39 | rdoc.rdoc_files.include('lib/**/*.rb')
40 | end
41 |
42 | task :default => :spec
43 |
44 | require 'barista'
45 |
46 |
47 |
--------------------------------------------------------------------------------
/lib/barista/haml_filter.rb:
--------------------------------------------------------------------------------
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 << ""
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 | require 'haml/filters'
39 | CoffeeScript.module_eval { include Haml::Filters::Base }
40 | end
41 | end
42 | end
43 | end
--------------------------------------------------------------------------------
/lib/barista/rake_task.rb:
--------------------------------------------------------------------------------
1 | require 'rake'
2 | require 'rake/tasklib'
3 |
4 | module Barista
5 | class RakeTask < ::Rake::TaskLib
6 | include Rake::DSL
7 |
8 | attr_writer :namespace, :task_name
9 | attr_writer :environment, :input_directory, :output_directory, :rails
10 |
11 | def initialize
12 | yield self if block_given?
13 | @namespace ||= :barista
14 | @task_name ||= :brew
15 | @task_name = @task_name.to_sym
16 | @rails = defined?(Rails) if @rails.nil?
17 |
18 | task_declaration = (@rails ? {@task_name => :environment} : @task_name)
19 |
20 | namespace @namespace do
21 | desc "Compiles all CoffeeScript sources to JavaScript"
22 | task task_declaration do
23 | setup_barista
24 | check_availability
25 | puts "Compiling all CoffeeScripts to their JavaScript equivalent."
26 | Barista.compile_all! true, false
27 | end
28 | end
29 | end
30 |
31 | # Proxy methods for rake tasks
32 |
33 | def respond_to?(method, include_private = false)
34 | super || Barista.respond_to?(method, include_private)
35 | end
36 |
37 | def method_missing(method, *args, &blk)
38 | if Barista.respond_to?(method)
39 | Barista.send method, *args, &blk
40 | else
41 | super
42 | end
43 | end
44 |
45 | private
46 |
47 | def setup_barista
48 | require 'barista'
49 |
50 | Barista.env = @environment if @environment
51 | if @input_directory
52 | Barista.root = File.expand_path(@input_directory, Dir.pwd)
53 | end
54 | if @output_directory
55 | Barista.output_root = File.expand_path(@output_directory, Dir.pwd)
56 | end
57 | end
58 |
59 | def check_availability
60 | unless Barista::Compiler.available?
61 | warn Barista::Compiler::UNAVAILABLE_MESSAGE
62 | exit 1
63 | end
64 | end
65 |
66 | end
67 | end
--------------------------------------------------------------------------------
/lib/generators/barista/install/templates/initializer.rb:
--------------------------------------------------------------------------------
1 | # Configure barista.
2 | Barista.configure do |c|
3 |
4 | # Change the root to use app/scripts
5 | # c.root = Rails.root.join("app", "scripts")
6 |
7 | # Change the output root, causing Barista to compile into public/coffeescripts
8 | # c.output_root = Rails.root.join("public", "coffeescripts")
9 | #
10 | # Disable auto compile, use generated file directly:
11 | # c.auto_compile = false
12 |
13 | # Add a new framework:
14 |
15 | # c.register :tests, :root => Rails.root.join('test', 'coffeescript'), :output_prefix => 'test'
16 |
17 | # Disable wrapping in a closure:
18 | # c.bare = true
19 | # ... or ...
20 | # c.bare!
21 |
22 | # Change the output root for a framework:
23 |
24 | # c.change_output_prefix! 'framework-name', 'output-prefix'
25 |
26 | # or for all frameworks...
27 |
28 | # c.each_framework do |framework|
29 | # c.change_output_prefix! framework, "vendor/#{framework.name}"
30 | # end
31 |
32 | # or, prefix the path for the app files:
33 |
34 | # c.change_output_prefix! :default, 'my-app-name'
35 |
36 | # or, change the directory the framework goes into full stop:
37 |
38 | # c.change_output_prefix! :tests, Rails.root.join('spec', 'javascripts')
39 |
40 | # or, hook into the compilation:
41 |
42 | # c.before_compilation { |path| puts "Barista: Compiling #{path}" }
43 | # c.on_compilation { |path| puts "Barista: Successfully compiled #{path}" }
44 | # c.on_compilation_error { |path, output| puts "Barista: Compilation of #{path} failed with:\n#{output}" }
45 | # c.on_compilation_with_warning { |path, output| puts "Barista: Compilation of #{path} had a warning:\n#{output}" }
46 |
47 | # Turn off preambles and exceptions on failure:
48 |
49 | # c.verbose = false
50 |
51 | # Or, make sure it is always on
52 | # c.verbose!
53 |
54 | # If you want to use a custom JS file, you can as well
55 | # e.g. vendoring CoffeeScript in your application:
56 | # c.js_path = Rails.root.join('public', 'javascripts', 'coffee-script.js')
57 |
58 | # Make helpers and the HAML filter output coffee-script instead of the compiled JS.
59 | # Used in combination with the coffeescript_interpreter_js helper in Rails.
60 | # c.embedded_interpreter = true
61 |
62 | end
63 |
--------------------------------------------------------------------------------
/lib/barista/helpers.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | module Helpers
3 |
4 | def coffeescript_interpreter_js
5 | return if defined?(@coffeescript_embedded) && @coffeescript_embedded
6 | check_for_helper_method! :javascript_include_tag
7 | @coffeescript_embedded = true
8 | if Barista.embedded_interpreter?
9 | javascript_include_tag 'coffeescript'
10 | end
11 | end
12 |
13 | def coffeescript_include_tag(*names)
14 | check_for_helper_method! :javascript_include_tag
15 | if Barista.embedded_interpreter?
16 | output = defined?(ActiveSupport::SafeBuffer) ? ActiveSupport::SafeBuffer.new : ""
17 | output << coffeescript_interpreter_js
18 | check_for_helper_method! :content_tag
19 | Array(names).each do |name|
20 | output << "\n"
21 | output << content_tag(:script, '', :type => 'text/coffeescript', :src => normalise_coffeescript_path(name.to_s))
22 | end
23 | output
24 | else
25 | javascript_include_tag(*names)
26 | end
27 | end
28 |
29 | def coffeescript_tag(code, html_options = {})
30 | check_for_helper_method! :javascript_tag
31 | if Barista.embedded_interpreter?
32 | check_for_helper_method! :content_tag
33 | output = defined?(ActiveSupport::SafeBuffer) ? ActiveSupport::SafeBuffer.new : ""
34 | output << coffeescript_interpreter_js
35 | embed = "\n#\n"
36 | embed = embed.html_safe if embed.respond_to?(:html_safe)
37 | output << content_tag(:script, embed, html_options.merge(:type => 'text/coffeescript'))
38 | output
39 | else
40 | javascript_tag Barista::Compiler.compile(code), html_options
41 | end
42 | end
43 |
44 | protected
45 |
46 | def normalise_coffeescript_path(path)
47 | if respond_to?(:compute_public_path)
48 | compute_public_path path, 'coffeescript', 'coffee'
49 | else
50 | path = path.gsub(/\.(js|coffee)$/, '') + '.coffee'
51 | path = "/coffeescripts/#{path}" unless path =~ /^\//
52 | path
53 | end
54 | end
55 |
56 | def check_for_helper_method!(name)
57 | raise "Please make sure #{name} is available." unless respond_to?(name)
58 | end
59 |
60 | end
61 | end
--------------------------------------------------------------------------------
/lib/barista/extensions.rb:
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
/spec/barista_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe Barista do
4 |
5 | context 'hooks'
6 |
7 | context 'configuration'
8 |
9 | context 'setting app_root' do
10 | it "defaults to Rails.root" do
11 | Barista::app_root.should == Rails.root
12 | end
13 | it "can be set to another directory" do
14 | new_path = File.expand_path("../../public/javascripts", __FILE__)
15 | Barista.configure do |c|
16 | c.app_root = new_path
17 | end
18 | Barista::app_root.to_s.should == new_path
19 | end
20 | end
21 |
22 | context 'preamble' do
23 | before(:each) do
24 | @assets_path = File.expand_path("../assets", __FILE__)
25 | @public_path = File.expand_path("../public", __FILE__)
26 | Barista.configure do |c|
27 | c.root = @assets_path
28 | c.output_root = @public_path
29 | end
30 | FileUtils.rm_rf @public_path if File.directory?(@public_path)
31 | end
32 | it "is written by default" do
33 | Barista.add_preamble = true
34 | Barista::compile_all!
35 | alert_js = IO.read(File.join(@public_path, "alert.js"))
36 | alert_js.should include "DO NOT MODIFY"
37 | end
38 | it "can be disabled" do
39 | Barista.add_preamble = false
40 | Barista::compile_all!
41 | alert_js = IO.read(File.join(@public_path, "alert.js"))
42 | alert_js.should_not include "DO NOT MODIFY"
43 | end
44 | end
45 |
46 | context 'compiling files'
47 |
48 | context 'compiling all' do
49 | before(:each) do
50 | @assets_path = File.expand_path("../assets", __FILE__)
51 | @public_path = File.expand_path("../public", __FILE__)
52 | Barista.configure do |c|
53 | c.root = @assets_path
54 | c.output_root = @public_path
55 | end
56 | FileUtils.rm_rf @public_path if File.directory?(@public_path)
57 | end
58 | it "compiles nothing" do
59 | lambda { Barista::compile_all! false, false }.should_not raise_error
60 | end
61 | it "produces alert.js" do
62 | Barista::compile_all!
63 | File.exist?(File.join(@public_path, "alert.js")).should be_true
64 | end
65 | it "logs when verbose is true" do
66 | log = StringIO.new
67 | Barista.logger = Logger.new(log)
68 | Barista.compile_all!
69 | log.string.should =~ /\[Barista\].+/
70 | end
71 | it "does not log when verbose is false" do
72 | log = StringIO.new
73 | Barista.logger = Logger.new(log)
74 | Barista.verbose = false
75 | Barista.compile_all!
76 | log.string.should be_empty
77 | end
78 | end
79 |
80 | end
81 |
--------------------------------------------------------------------------------
/lib/barista/server.rb:
--------------------------------------------------------------------------------
1 | require 'rack/utils'
2 |
3 | module Barista
4 | class Server
5 |
6 | CACHE_FOR_SECONDS = 300
7 | JS_CONTENT_TYPE = 'text/javascript'
8 | COFFEE_CONTENT_TYPE = 'text/coffeescript'
9 | PATH_REGEXP = /^\/(coffee|java)scripts\//
10 |
11 | # Extensions to the type.
12 | EXTENSION_MAPPING = {
13 | '.coffee' => :coffeescript,
14 | '.js' => :javascript
15 | }
16 |
17 | # Content types for responses.
18 | CONTENT_TYPE_MAPPING = {
19 | :coffeescript => COFFEE_CONTENT_TYPE,
20 | :javascript => JS_CONTENT_TYPE
21 | }
22 |
23 | class Proxy
24 |
25 | def initialize(app)
26 | @app = app
27 | @server = Server.new
28 | end
29 |
30 | def call(env)
31 | result = @server.call(env)
32 | if result[0] == 404
33 | @app.call(env)
34 | else
35 | result
36 | end
37 | end
38 |
39 | end
40 |
41 | def initialize
42 | # Cache the responses for common errors.
43 | forbidden
44 | not_found
45 | end
46 |
47 | def call(env)
48 | dup._call(env)
49 | end
50 |
51 | def _call(env)
52 | @path_info = Rack::Utils.unescape(env['PATH_INFO'].to_s)
53 | return not_found unless @path_info =~ PATH_REGEXP
54 | # Strip the prefix.
55 | @path_info.gsub! PATH_REGEXP, ''
56 | # Check it's a valid path.
57 | return forbidden if @path_info.include?('..')
58 |
59 | # If coffeescript.js is the request, render the coffeescript compiler code.
60 | if @path_info == 'coffeescript.js'
61 | return response_for_text(CoffeeScript::Source.contents)
62 | end
63 | # Look up the type of the file based off of the extension.
64 | @result_type = EXTENSION_MAPPING[File.extname(@path_info)]
65 | return not_found if @result_type.nil? || (@result_type == :coffeescript && !Barista.embedded_interpreter?)
66 | # Process the difference in content type.
67 | content, last_modified = Barista::Compiler.compile_as(@path_info, @result_type)
68 | if content.nil?
69 | not_found
70 | else
71 | response_for_text content, CONTENT_TYPE_MAPPING[@result_type], last_modified
72 | end
73 | end
74 |
75 | protected
76 |
77 | def forbidden
78 | @_forbidden_response ||= begin
79 | body = "Forbidden\n"
80 | [403, {
81 | 'Content-Type' => 'text/plain',
82 | 'Content-Length' => Rack::Utils.bytesize(body).to_s,
83 | 'X-Cascade' => 'pass'
84 | }, [body]]
85 | end
86 | end
87 |
88 | def not_found
89 | @_not_found_response ||= begin
90 | body = "Not Found\n"
91 | [404, {
92 | 'Content-Type' => 'text/plain',
93 | 'Content-Length' => Rack::Utils.bytesize(body).to_s,
94 | 'X-Cascade' => 'pass'
95 | }, [body]]
96 | end
97 | end
98 |
99 | def response_for_text(content, content_type = 'text/javascript', modified_at = nil)
100 | headers = {
101 | 'Content-Type' => content_type,
102 | 'Content-Length' => Rack::Utils.bytesize(content).to_s,
103 | 'Cache-Control' => "public, max-age=#{CACHE_FOR_SECONDS}"
104 | }
105 | headers.merge!('Last-Modified' => modified_at.httpdate) unless modified_at.nil?
106 | [200, headers, [content]]
107 | end
108 |
109 | end
110 | end
111 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | abstract (1.0.0)
5 | actionmailer (3.0.8)
6 | actionpack (= 3.0.8)
7 | mail (~> 2.2.19)
8 | actionpack (3.0.8)
9 | activemodel (= 3.0.8)
10 | activesupport (= 3.0.8)
11 | builder (~> 2.1.2)
12 | erubis (~> 2.6.6)
13 | i18n (~> 0.5.0)
14 | rack (~> 1.2.1)
15 | rack-mount (~> 0.6.14)
16 | rack-test (~> 0.5.7)
17 | tzinfo (~> 0.3.23)
18 | activemodel (3.0.8)
19 | activesupport (= 3.0.8)
20 | builder (~> 2.1.2)
21 | i18n (~> 0.5.0)
22 | activerecord (3.0.8)
23 | activemodel (= 3.0.8)
24 | activesupport (= 3.0.8)
25 | arel (~> 2.0.10)
26 | tzinfo (~> 0.3.23)
27 | activeresource (3.0.8)
28 | activemodel (= 3.0.8)
29 | activesupport (= 3.0.8)
30 | activesupport (3.0.8)
31 | archive-tar-minitar (0.5.2)
32 | arel (2.0.10)
33 | builder (2.1.2)
34 | coffee-script (2.2.0)
35 | coffee-script-source
36 | execjs
37 | coffee-script-source (1.1.1)
38 | columnize (0.3.4)
39 | diff-lcs (1.1.2)
40 | erubis (2.6.6)
41 | abstract (>= 1.0.0)
42 | execjs (1.1.0)
43 | multi_json (~> 1.0)
44 | git (1.2.5)
45 | i18n (0.5.0)
46 | jeweler (1.6.2)
47 | bundler (~> 1.0)
48 | git (>= 1.2.5)
49 | rake
50 | linecache (0.46)
51 | rbx-require-relative (> 0.0.4)
52 | linecache19 (0.5.12)
53 | ruby_core_source (>= 0.1.4)
54 | mail (2.2.19)
55 | activesupport (>= 2.3.6)
56 | i18n (>= 0.4.0)
57 | mime-types (~> 1.16)
58 | treetop (~> 1.4.8)
59 | mime-types (1.16)
60 | multi_json (1.0.3)
61 | polyglot (0.3.1)
62 | rack (1.2.3)
63 | rack-mount (0.6.14)
64 | rack (>= 1.0.0)
65 | rack-test (0.5.7)
66 | rack (>= 1.0)
67 | rails (3.0.8)
68 | actionmailer (= 3.0.8)
69 | actionpack (= 3.0.8)
70 | activerecord (= 3.0.8)
71 | activeresource (= 3.0.8)
72 | activesupport (= 3.0.8)
73 | bundler (~> 1.0)
74 | railties (= 3.0.8)
75 | railties (3.0.8)
76 | actionpack (= 3.0.8)
77 | activesupport (= 3.0.8)
78 | rake (>= 0.8.7)
79 | thor (~> 0.14.4)
80 | rake (0.9.2)
81 | rbx-require-relative (0.0.5)
82 | rdoc (2.5.11)
83 | rr (1.0.2)
84 | rspec (2.6.0)
85 | rspec-core (~> 2.6.0)
86 | rspec-expectations (~> 2.6.0)
87 | rspec-mocks (~> 2.6.0)
88 | rspec-core (2.6.3)
89 | rspec-expectations (2.6.0)
90 | diff-lcs (~> 1.1.2)
91 | rspec-mocks (2.6.0)
92 | rspec-rails (2.6.1)
93 | actionpack (~> 3.0)
94 | activesupport (~> 3.0)
95 | railties (~> 3.0)
96 | rspec (~> 2.6.0)
97 | ruby-debug (0.10.4)
98 | columnize (>= 0.1)
99 | ruby-debug-base (~> 0.10.4.0)
100 | ruby-debug-base (0.10.4)
101 | linecache (>= 0.3)
102 | ruby-debug-base19 (0.11.25)
103 | columnize (>= 0.3.1)
104 | linecache19 (>= 0.5.11)
105 | ruby_core_source (>= 0.1.4)
106 | ruby-debug19 (0.11.6)
107 | columnize (>= 0.3.1)
108 | linecache19 (>= 0.5.11)
109 | ruby-debug-base19 (>= 0.11.19)
110 | ruby_core_source (0.1.5)
111 | archive-tar-minitar (>= 0.5.2)
112 | thor (0.14.6)
113 | treetop (1.4.9)
114 | polyglot (>= 0.3.1)
115 | tzinfo (0.3.29)
116 |
117 | PLATFORMS
118 | ruby
119 |
120 | DEPENDENCIES
121 | coffee-script (~> 2.2)
122 | jeweler (~> 1.0)
123 | rails (~> 3.0)
124 | rdoc (~> 2.4)
125 | rr (~> 1.0)
126 | rspec (~> 2.6)
127 | rspec-core (~> 2.6)
128 | rspec-rails (~> 2.6)
129 | ruby-debug
130 | ruby-debug19 (~> 0.11)
131 |
--------------------------------------------------------------------------------
/barista.gemspec:
--------------------------------------------------------------------------------
1 | # Generated by jeweler
2 | # DO NOT EDIT THIS FILE DIRECTLY
3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4 | # -*- encoding: utf-8 -*-
5 |
6 | Gem::Specification.new do |s|
7 | s.name = "barista"
8 | s.version = "1.3.0"
9 |
10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11 | s.authors = ["Darcy Laycock"]
12 | s.date = "2012-07-23"
13 | s.description = "Barista provides simple, integrated support for CoffeeScript in Rack and Rails applications.\n\nMuch like Compass does for Sass, It also provides Frameworks (bundleable code which can be shared via Gems).\n\nLastly, it also provides a Rack Application (which can be used to server compiled code), a around_filter-style precompiler (as Rack middleware) and simple helpers for rails and Haml.\n\nFor more details, please see the the README file bundled with it."
14 | s.email = "sutto@sutto.net"
15 | s.extra_rdoc_files = [
16 | "LICENSE",
17 | "README.md"
18 | ]
19 | s.files = [
20 | ".document",
21 | ".rspec",
22 | ".rvmrc.example",
23 | "DESCRIPTION",
24 | "Gemfile",
25 | "Gemfile.lock",
26 | "LICENSE",
27 | "README.md",
28 | "Rakefile",
29 | "barista.gemspec",
30 | "lib/barista.rb",
31 | "lib/barista/capistrano.rb",
32 | "lib/barista/compiler.rb",
33 | "lib/barista/extensions.rb",
34 | "lib/barista/filter.rb",
35 | "lib/barista/framework.rb",
36 | "lib/barista/haml_filter.rb",
37 | "lib/barista/helpers.rb",
38 | "lib/barista/hooks.rb",
39 | "lib/barista/integration.rb",
40 | "lib/barista/integration/rails2.rb",
41 | "lib/barista/integration/rails3.rb",
42 | "lib/barista/integration/sinatra.rb",
43 | "lib/barista/rake_task.rb",
44 | "lib/barista/server.rb",
45 | "lib/barista/tasks.rb",
46 | "lib/barista/tasks/barista.rake",
47 | "lib/barista/version.rb",
48 | "lib/generators/barista/install/USAGE",
49 | "lib/generators/barista/install/install_generator.rb",
50 | "lib/generators/barista/install/templates/initializer.rb",
51 | "lib/generators/barista_install_generator.rb",
52 | "spec/assets/alert.coffee",
53 | "spec/barista_spec.rb",
54 | "spec/spec_helper.rb"
55 | ]
56 | s.homepage = "http://github.com/Sutto/barista"
57 | s.require_paths = ["lib"]
58 | s.rubygems_version = "1.8.24"
59 | s.summary = "Simple, transparent coffeescript integration for Rails and Rack applications."
60 |
61 | if s.respond_to? :specification_version then
62 | s.specification_version = 3
63 |
64 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
65 | s.add_runtime_dependency(%q, ["~> 2.2"])
66 | s.add_development_dependency(%q, ["~> 3.0"])
67 | s.add_development_dependency(%q, ["~> 1.0"])
68 | s.add_development_dependency(%q, ["~> 2.6"])
69 | s.add_development_dependency(%q, ["~> 2.6"])
70 | s.add_development_dependency(%q, ["~> 2.6"])
71 | s.add_development_dependency(%q, ["~> 2.4"])
72 | s.add_development_dependency(%q, ["~> 1.0"])
73 | s.add_development_dependency(%q, [">= 0"])
74 | s.add_development_dependency(%q, ["~> 0.11"])
75 | else
76 | s.add_dependency(%q, ["~> 2.2"])
77 | s.add_dependency(%q, ["~> 3.0"])
78 | s.add_dependency(%q, ["~> 1.0"])
79 | s.add_dependency(%q, ["~> 2.6"])
80 | s.add_dependency(%q, ["~> 2.6"])
81 | s.add_dependency(%q, ["~> 2.6"])
82 | s.add_dependency(%q, ["~> 2.4"])
83 | s.add_dependency(%q, ["~> 1.0"])
84 | s.add_dependency(%q, [">= 0"])
85 | s.add_dependency(%q, ["~> 0.11"])
86 | end
87 | else
88 | s.add_dependency(%q, ["~> 2.2"])
89 | s.add_dependency(%q, ["~> 3.0"])
90 | s.add_dependency(%q, ["~> 1.0"])
91 | s.add_dependency(%q, ["~> 2.6"])
92 | s.add_dependency(%q, ["~> 2.6"])
93 | s.add_dependency(%q, ["~> 2.6"])
94 | s.add_dependency(%q, ["~> 2.4"])
95 | s.add_dependency(%q, ["~> 1.0"])
96 | s.add_dependency(%q, [">= 0"])
97 | s.add_dependency(%q, ["~> 0.11"])
98 | end
99 | end
100 |
101 |
--------------------------------------------------------------------------------
/lib/barista/framework.rb:
--------------------------------------------------------------------------------
1 | module Barista
2 | class Framework
3 |
4 | def self.default_framework
5 | @default_framework ||= self.new(:name => "default", :root => Barista.root)
6 | end
7 |
8 | def self.default_framework=(value)
9 | @default_framework = value
10 | end
11 |
12 | def self.all(include_default = false)
13 | (@all ||= []).dup.tap do |all|
14 | all.unshift default_framework if include_default
15 | end
16 | end
17 |
18 | def self.exposed_coffeescripts
19 | all(true).inject([]) do |collection, fw|
20 | collection + fw.exposed_coffeescripts
21 | end.uniq.sort_by { |f| f.length }
22 | end
23 |
24 | def self.exposed_javascripts
25 | all(true).inject([]) do |collection, fw|
26 | collection + fw.exposed_javascripts
27 | end.uniq.sort_by { |f| f.length }
28 | end
29 |
30 | def self.coffeescript_glob_paths
31 | all(true).map { |fw| fw.coffeescript_glob_path }
32 | end
33 |
34 | def self.full_path_for(script)
35 | javascript = script.to_s.gsub(/\.coffee$/, '.js').gsub(/^\/+/, '')
36 | coffeescript = script.to_s.gsub(/\.js$/, '.coffee').gsub(/^\/+/, '')
37 | all(true).each do |fw|
38 | full_path = fw.full_path_for(coffeescript) || fw.full_path_for(javascript)
39 | return full_path, fw if full_path
40 | end
41 | nil
42 | end
43 |
44 | def self.register(name, options = nil)
45 | if options.is_a?(Hash)
46 | framework = self.new(options.merge(:name => name))
47 | else
48 | framework = self.new(:name => name, :root => options)
49 | end
50 | (@all ||= []) << framework
51 | end
52 |
53 | def self.[](name)
54 | name = name.to_s
55 | (@all ||= []).detect { |fw| fw.name == name }
56 | end
57 |
58 | attr_reader :name, :framework_root, :output_prefix
59 |
60 | def initialize(options, root = nil, output_prefix = nil)
61 | unless options.is_a?(Hash)
62 | Barista.deprecate! self, "initialize(name, root = nil, output_prefix = nil)", "Please use the option syntax instead."
63 | options = {
64 | :name => options,
65 | :root => root,
66 | :output_prefix => output_prefix
67 | }
68 | end
69 | # actually setup the framework.
70 | check_options! options, :name, :root
71 | @name = options[:name].to_s
72 | @output_prefix = options[:output_prefix]
73 | @framework_root = File.expand_path(options[:root].to_s)
74 | self.output_root = options[:output_root]
75 | end
76 |
77 | def coffeescripts
78 | Dir[coffeescript_glob_path]
79 | end
80 |
81 | def javascripts
82 | Dir[javascript_glob_path]
83 | end
84 |
85 | def coffeescript_glob_path
86 | @coffeescript_glob_path ||= File.join(@framework_root, "**", "*.coffee")
87 | end
88 |
89 | def javascript_glob_path
90 | @javascript_glob_path ||= File.join(@framework_root, "**", "*.js")
91 | end
92 |
93 | def short_name(script)
94 | short_name = remove_prefix script, @framework_root
95 | File.join(*[@output_prefix, short_name].compact)
96 | end
97 |
98 | def exposed_coffeescripts
99 | coffeescripts.map { |script| short_name(script) }
100 | end
101 |
102 | def exposed_javascripts
103 | javascripts.map { |script| short_name(script) }
104 | end
105 |
106 | def output_prefix=(value)
107 | value = value.to_s.gsub(/(^\/|\/$)/, '').strip
108 | @output_prefix = value.empty? ? nil : value
109 | end
110 |
111 | def full_path_for(name)
112 | full_path = File.join(@framework_root, remove_prefix(name, @output_prefix.to_s))
113 | File.exist?(full_path) ? full_path : nil
114 | end
115 |
116 | def output_root
117 | @output_root || Barista.output_root
118 | end
119 |
120 | def output_root=(value)
121 | if value.nil?
122 | @output_root = nil
123 | else
124 | @output_root = Pathname(value.to_s)
125 | end
126 | end
127 |
128 | def output_path_for(file, format = 'js')
129 | # Strip the leading slashes
130 | file = file.to_s.gsub(/^\/+/, '')
131 | output_root.join(file).to_s.gsub(/\.[^\.]+$/, ".#{format}")
132 | end
133 |
134 | protected
135 |
136 | def remove_prefix(path, prefix)
137 | path.to_s.gsub(/^#{Regexp.escape(prefix.to_s)}\/?/, '')
138 | end
139 |
140 | def check_options!(options, *keys)
141 | keys.each do |option|
142 | raise ArgumentError, "#{option.inspect} is a required options." if options[option].nil?
143 | end
144 | end
145 |
146 | end
147 | end
148 |
--------------------------------------------------------------------------------
/lib/barista/compiler.rb:
--------------------------------------------------------------------------------
1 | require 'digest/sha2'
2 |
3 | module Barista
4 | class Compiler
5 |
6 | UNAVAILABLE_MESSAGE = "No method of compiling coffee-script is currently available. Please see the ExecJS page (https://github.com/sstephenson/execjs) for details on how to set one up."
7 |
8 | # TODO: Deprecate.
9 | class << self
10 |
11 | def js_path
12 | CoffeeScript::Source.path
13 | end
14 |
15 | def js_path=(value)
16 | CoffeeScript::Source.path = value
17 | end
18 |
19 | def bin_path
20 | execjs_runtime_call :binary
21 | end
22 |
23 | def bin_path=(path)
24 | execjs_runtime_call :binary=, path
25 | end
26 |
27 | def execjs_runtime_call(method, *args)
28 | runtime = ExecJS.runtime
29 | if runtime.respond_to?(method, true)
30 | runtime.send method, *args
31 | else
32 | nil
33 | end
34 | end
35 |
36 | def available?
37 | ExecJS.runtime and ExecJS.runtime.available?
38 | end
39 |
40 | def check_availability!(silence = false)
41 | available = available?
42 | if !available && Barista.exception_on_error? && !silence
43 | raise CompilerUnavailableError, UNAVAILABLE_MESSAGE
44 | end
45 | available
46 | end
47 |
48 | def compile(content, options = {})
49 | self.new(content, options).to_js
50 | end
51 |
52 | def autocompile_file(file, force = false, silence_error = false)
53 | # Expand the path from the framework.
54 | origin_path, framework = Framework.full_path_for(file)
55 | return if origin_path.nil?
56 | destination_path = framework.output_path_for(file)
57 |
58 | # read file directly if auto_compile is disabled
59 | if !Barista.auto_compile?
60 | if File.exist?(destination_path)
61 | return File.read(destination_path)
62 | else
63 | return nil
64 | end
65 | end
66 |
67 | return File.read(destination_path) unless dirty?(origin_path, destination_path) || force
68 | # Ensure we have a coffeescript compiler available.
69 | if !check_availability!(silence_error)
70 | Barista.debug UNAVAILABLE_MESSAGE
71 | return nil
72 | end
73 | Barista.debug "Compiling #{file} from framework '#{framework.name}'"
74 | compiler = new(origin_path, :silence_error => silence_error, :output_path => destination_path)
75 | content = compiler.to_js
76 | compiler.save
77 | content
78 | end
79 |
80 | def compile_as(file, type)
81 | origin_path, framework = Framework.full_path_for(file)
82 | return if origin_path.nil?
83 | if type == :coffeescript
84 | return File.read(origin_path), File.mtime(origin_path)
85 | else
86 | return autocompile_file(file), Time.now
87 | end
88 | end
89 |
90 | def dirty?(from, to)
91 | File.exist?(from) && (!File.exist?(to) || File.mtime(to) < File.mtime(from))
92 | end
93 |
94 | def setup_default_error_logger
95 | Barista.on_compilation_error do |where, message|
96 | if Barista.verbose?
97 | Barista.debug "There was an error compiling coffeescript from #{where}:"
98 | message.each_line { |line| Barista.debug line.rstrip }
99 | end
100 | end
101 | end
102 |
103 | end
104 |
105 | def initialize(context, options = {})
106 | @compiled = false
107 | @options = options
108 | setup_compiler_context context
109 | end
110 |
111 | def compile!
112 | location = @options.fetch(:origin, 'inline')
113 | @compiled_content = compile(@context, location)
114 | @compiled_content = preamble(location) + @compiled_content if location != 'inline' && Barista.add_preamble?
115 | @compiled = true
116 | end
117 |
118 | def to_js
119 | compile! unless defined?(@compiled) && @compiled
120 | @compiled_content
121 | end
122 |
123 | def copyable?(location)
124 | location != 'inline' && File.extname(location) == '.js'
125 | end
126 |
127 | def compile(script, where = 'inline')
128 | if copyable?(where)
129 | out = script
130 | else
131 | Barista.invoke_hook :before_compilation, where
132 | out = CoffeeScript.compile script, :bare => Barista.bare?
133 | Barista.invoke_hook :compiled, where
134 | end
135 | out
136 | rescue ExecJS::Error => e
137 | Barista.invoke_hook :compilation_failed, where, e.message
138 | if Barista.exception_on_error? && !@options[:silence]
139 | if e.is_a?(ExecJS::ProgramError)
140 | where_within_app = where.sub(/#{Regexp.escape(Barista.app_root.to_s)}\/?/, '')
141 | raise CompilationError, "Error: In #{where_within_app}, #{e.message}"
142 | else
143 | raise CompilationError, "CoffeeScript encountered an error compiling #{where}: #{e.message}"
144 | end
145 | end
146 | compilation_error_for where, e.message
147 | end
148 |
149 | def save(path = @options[:output_path])
150 | return false unless path.is_a?(String) && !to_js.nil?
151 | FileUtils.mkdir_p File.dirname(path)
152 | File.open(path, "w+") { |f| f.write @compiled_content }
153 | true
154 | rescue Errno::EACCES
155 | false
156 | end
157 |
158 | protected
159 |
160 | def preamble(location)
161 | inner_message = copyable?(location) ? "copied" : "compiled"
162 | if Barista.preamble
163 | Barista.preamble.call(location)
164 | else
165 | "/* DO NOT MODIFY. This file was #{inner_message} #{Time.now.httpdate} from\n * #{location.strip}\n */\n\n"
166 | end
167 | end
168 |
169 | def compilation_error_for(location, message)
170 | details = "Compilation of '#{location}' failed:\n#{message}"
171 | Barista.verbose? ? "alert(#{details.to_json});" : nil
172 | end
173 |
174 | def setup_compiler_context(context)
175 | if context.respond_to?(:read)
176 | @context = context.read
177 | @type = :string
178 | default_path = context.respond_to?(:path) ? context.path : 'inline'
179 | @options[:origin] ||= default_path
180 | elsif !context.include?("\n") && File.exist?(context)
181 | @context = File.read(context)
182 | @type = :file
183 | @options[:origin] ||= context
184 | else
185 | @context = context.to_s
186 | @type = :string
187 | @options[:origin] ||= 'inline'
188 | end
189 | end
190 |
191 | end
192 | end
193 |
--------------------------------------------------------------------------------
/lib/barista.rb:
--------------------------------------------------------------------------------
1 | require 'pathname'
2 | require 'time' # Required for httpdate
3 | require 'coffee_script'
4 |
5 | # Setup ExecJS extras if present
6 | if defined?(ExecJS::ExternalRuntime)
7 | ExecJS::ExternalRuntime.send :attr_accessor, :binary
8 | end
9 |
10 | module Barista
11 |
12 | Error = Class.new(StandardError)
13 | CompilationError = Class.new(Error)
14 | CompilerUnavailableError = Class.new(Error)
15 |
16 | autoload :Compiler, 'barista/compiler'
17 | autoload :Extensions, 'barista/extensions'
18 | autoload :Filter, 'barista/filter'
19 | autoload :Framework, 'barista/framework'
20 | autoload :HamlFilter, 'barista/haml_filter'
21 | autoload :Helpers, 'barista/helpers'
22 | autoload :Hooks, 'barista/hooks'
23 | autoload :Integration, 'barista/integration'
24 | autoload :Server, 'barista/server'
25 |
26 | class << self
27 | include Extensions
28 |
29 | def library_root
30 | @library_root ||= Pathname(__FILE__).dirname
31 | end
32 |
33 | # Hook methods
34 | #
35 | # Hooks are a generic way to define blocks that are executed at run time.
36 | # For a full list of hooks, see the readme.
37 |
38 | def hooks
39 | @hooks ||= Hooks.new
40 | end
41 |
42 | def on_hook(name, *args, &blk)
43 | hooks.on(name, *args, &blk)
44 | end
45 |
46 | def invoke_hook(name, *args)
47 | hooks.invoke(name, *args)
48 | end
49 |
50 | def has_hook?(name)
51 | hooks.has_hook?(name)
52 | end
53 |
54 | has_hook_method :on_compilation_error => :compilation_failed,
55 | :on_compilation => :compiled,
56 | :on_compilation_complete => :all_compiled,
57 | :on_compilation_with_warning => :compiled_with_warning,
58 | :before_full_compilation => :before_full_compilation,
59 | :before_compilation => :before_compilation
60 |
61 | # Configuration - Tweak how you use Barista.
62 |
63 | has_boolean_options :verbose, :bare, :add_filter, :add_preamble, :exception_on_error, :embedded_interpreter, :auto_compile
64 | has_delegate_methods Compiler, :bin_path, :bin_path=, :js_path, :js_path=
65 | has_delegate_methods Framework, :register
66 | has_deprecated_methods :compiler, :compiler=, :compiler_klass, :compiler_klass=
67 |
68 | def add_preamble(&blk)
69 | self.add_preamble = true
70 | if block_given?
71 | @preamble = blk
72 | end
73 | end
74 |
75 | def preamble
76 | @preamble
77 | end
78 |
79 | def configure
80 | yield self if block_given?
81 | end
82 |
83 | def env
84 | @env ||= default_for_env
85 | end
86 |
87 | def env=(value)
88 | @env = value.to_s.strip
89 | @env = nil if @env == ''
90 | end
91 |
92 | def logger
93 | @logger ||= default_for_logger
94 | end
95 |
96 | def logger=(value)
97 | @logger = value
98 | end
99 |
100 | def app_root
101 | @app_root ||= default_for_app_root
102 | end
103 |
104 | def app_root=(value)
105 | @app_root = value.nil? ? nil : Pathname(value.to_s)
106 | end
107 |
108 | def root
109 | @root ||= app_root.join("app", "coffeescripts")
110 | end
111 |
112 | def root=(value)
113 | @root = value.nil? ? nil : Pathname(value.to_s)
114 | Framework.default_framework = nil
115 | end
116 |
117 | def output_root
118 | @output_root ||= app_root.join("public", "javascripts")
119 | end
120 |
121 | def output_root=(value)
122 | @output_root = value.nil? ? nil : Pathname(value.to_s)
123 | end
124 |
125 | def no_wrap?
126 | deprecate! self, :no_wrap?, 'Please use bare? instead.'
127 | bare?
128 | end
129 |
130 | def no_wrap!
131 | deprecate! self, :no_wrap!, 'Please use bare! instead.'
132 | bare!
133 | end
134 |
135 | def no_wrap=(value)
136 | deprecate! self, :no_wrap=, 'Please use bare= instead.'
137 | self.bare = value
138 | end
139 |
140 | # Default configuration options
141 |
142 | def local_env?
143 | %w(test development).include? Barista.env
144 | end
145 |
146 | def default_for_env
147 | return Rails.env.to_s if defined?(Rails.env)
148 | ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
149 | end
150 |
151 | def default_for_app_root
152 | if defined?(Rails.root)
153 | Rails.root
154 | else
155 | Pathname(Dir.pwd)
156 | end
157 | end
158 |
159 | def default_for_logger
160 | if defined?(Rails.logger)
161 | Rails.logger
162 | else
163 | require 'logger'
164 | Logger.new(STDOUT)
165 | end
166 | end
167 |
168 | def default_for_verbose
169 | local_env?
170 | end
171 |
172 | def default_for_add_filter
173 | local_env?
174 | end
175 |
176 | def default_for_exception_on_error
177 | true
178 | end
179 |
180 | def default_for_embedded_interpreter
181 | false
182 | end
183 |
184 | def default_for_auto_compile
185 | true
186 | end
187 |
188 |
189 | # Actual tasks on the barista module.
190 |
191 | def compile_file!(file, force = false, silence_error = false)
192 | Compiler.autocompile_file file, force, silence_error
193 | end
194 |
195 | def compile_all!(force = false, silence_error = true)
196 | debug "Compiling all coffeescripts" if Barista.auto_compile?
197 | Barista.invoke_hook :before_full_compilation
198 | Framework.exposed_coffeescripts.each do |coffeescript|
199 | Compiler.autocompile_file coffeescript, force, silence_error
200 | end
201 | debug "Copying all javascripts"
202 | Framework.exposed_javascripts.each do |javascript|
203 | Compiler.autocompile_file javascript, force, silence_error
204 | end
205 | Barista.invoke_hook :all_compiled
206 | true
207 | end
208 |
209 | def change_output_prefix!(framework, prefix = nil)
210 | framework = Barista::Framework[framework] unless framework.is_a?(Barista::Framework)
211 | framework.output_prefix = prefix if framework
212 | end
213 |
214 | def change_output_root!(framework, root)
215 | framework = Barista::Framework[framework] unless framework.is_a?(Barista::Framework)
216 | framework.output_root = root if framework
217 | end
218 |
219 | def each_framework(include_default = false, &blk)
220 | Framework.all(include_default).each(&blk)
221 | end
222 |
223 | def output_path_for(file)
224 | output_root.join(file.to_s.gsub(/^\/+/, '')).to_s.gsub(/\.coffee$/, '.js')
225 | end
226 |
227 | def debug(message)
228 | logger.debug "[Barista] #{message}" if logger && verbose?
229 | end
230 |
231 | def setup_defaults
232 | Barista::HamlFilter.setup
233 | Barista::Compiler.setup_default_error_logger
234 | end
235 |
236 | end
237 |
238 | # Setup integration by default.
239 | Integration.setup
240 |
241 | end
242 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Barista
2 |
3 | Barista is a set of tools to make using [CoffeeScript](http://jashkenas.github.com/coffee-script/) in Rails 3, Rails 2 and Rack applications
4 | easier. You can think of it as similar to [Compass](http://compass-style.org/), but for CoffeeScript instead of [Sass](http://sass-lang.com/).
5 |
6 | As an added bonus, Barista also gives:
7 |
8 | * Automatic support for a `:coffeescript` filter in [Haml](http://haml-lang.com/) (when Haml is loaded before Barista) — automatically converting inline CoffeeScript to JavaScript for you.
9 | * Where possible, support for `coffeescript_include_tag` and `coffeescript_tag`.
10 | * When possible, instead of pre-compiling in development and test modes, Barista will embed CoffeeScript in the page for you.
11 | * Support for Heroku via [therubyracer-heroku](https://github.com/aler/therubyracer-heroku) and either pre-compiled JS or, optionally, a lightweight Rack app that generates on request.
12 |
13 | ## Getting Started
14 |
15 | Out of the box, Barista has semi-automatic support for Rails 3.0, Rails 2 (currently untested) and Sinatra. With a minimal amount of effort, you can also make it work in any Rack-based framework.
16 |
17 | ### Rails 3
18 |
19 | Adding Barista to your Rails 3 application should as simple as adding two gems to your `Gemfile`, and running two commands. To get started, open up your `Gemfile` and add the following:
20 |
21 | gem "json" # Only needed if on Ruby 1.8 / a platform that ships without JSON
22 | gem "barista"
23 |
24 | Next, you'll need to run the the following:
25 |
26 | bundle install
27 | rails g barista:install
28 |
29 | This will install the gem into your application and will generate a file in `config/initializers/barista_config.rb` that contains a set of options to configure Barista options.
30 |
31 | Place your CoffeeScripts in `app/coffeescripts` and Barista will automatically compile them on change into `public/javascripts`.
32 |
33 | ### Rails 2
34 |
35 | Much like on Rails 3, Barista supports deep integration into Rails 2. The only thing missing (that is currently supported in the Rails 3 version) is built in support for generating a config file. If you're using bundler in your application, all you need to do is add:
36 |
37 | gem "json" # Only needed if on Ruby 1.8 / a platform that ships without JSON
38 | gem "barista"
39 |
40 | To your `Gemfile`. If you're not using bundler, doing `gem install json barista` and requiring barista both in your application should be enough to get you started.
41 |
42 | If you wish to change the barista configuration, take a look at the [Rails 3 initializer](https://github.com/Sutto/barista/blob/master/lib/generators/barista/install/templates/initializer.rb) and modify it to suite your application as needed.
43 |
44 | If you wish to use barista tasks with rails 2 project, add
45 |
46 | load "barista/tasks/barista.rake"
47 |
48 | To your `Rakefile`.
49 |
50 | ### Sinatra
51 |
52 | Adding Barista to a Sinatra application is a relatively straight forward affair. Like in Rails 2 and Rails 3, you first need to add and require the barista gem and (optionally, the json gem). Unlike Rails 2 and 3 (which set it up automatically), you must also register the extension in your application. So, in the scope of your app (either the top level scope or the `Sinatra::Application` subclass you're using), you then need to simple add:
53 |
54 | register Barista::Integration::Sinatra
55 |
56 | Which will automatically set up the Barista environment and other similar details (e.g. the automatic compilation filter). Since you don't have initializers like you do in Rails, you
57 | can then simply run your `Barista.configure` call and block anywhere before your application starts serving requests.
58 |
59 | ### Other Rack-based Frameworks
60 |
61 | Lastly, even though it is built out of the box to support Rails and Sinatra, Barista can also be used with any Rack-based framework. For proper integration, several things must be done. Namely, wherever you declare your middleware (e.g. in a `config.ru` file), you should register the two pieces of middleware barista uses. `Barista::Filter` should only be registered when
62 | Barista performs compilation (e.g. in development mode) and `Barista::Server::Proxy` should be registered if you want it to support automatic serving of a `coffeescript.js` file and / or
63 | on the fly (versus pre-request compilation) of CoffeeScripts.
64 |
65 | For example, your `config.ru` may look like:
66 |
67 | # Setup goes here...
68 | use Barista::Filter if Barista.add_filter?
69 | use Barista::Server::Proxy
70 | run MyRackApplication
71 |
72 | Next, you need to configure barista anywhere before your the above code is run. e.g by adding the following immediatly preceeding it:
73 |
74 | # Barista (for CoffeeScript Support)
75 | Barista.app_root = root
76 | Barista.root = File.join(root, 'coffeescripts')
77 | Barista.setup_defaults
78 | barista_config = root + '/barista_config.rb'
79 | require barista_config if File.exist?(barista_config)
80 |
81 | Hence, if you'e using, for example, [serve](https://github.com/jlong/serve) users should have a `config.ru` that looks similar to [this example](https://github.com/YouthTree/site-design/blob/master/config.ru).
82 |
83 | ### A Quick Note on the JSON Gem
84 |
85 | Barista indirectly requires the json gem via the coffee-script gem, but it isn't listed as a dependency for very
86 | good reasons. If you encounter errors relating to `require 'json'`, Then you'll need to add either `gem 'json'`
87 | or `gem 'json_pure'` to your Gemfile.
88 |
89 | If you're already running Ruby 1.9, this will be unnecessary as JSON is shipped as part of the standard library.
90 |
91 | ## General Information
92 |
93 | Barista transparently compiles CoffeeScript to JavaScript. When a `.coffee` file is changed and the page is refreshed, Barista first regenerates all `.js` files whose `.coffee` sources have been recently changed. This way, you can refresh immediately after saving the `.coffee` file and not worry about an old `.js` file being sent to the browser (as often happens when using `coffee --watch`).
94 |
95 | Barista supports using `therubyracer` when installed or, by default, using either the `node` executable or `jsc` (on OS X) to compile your scripts. There is
96 | no need for you to install the coffee-script executable in Node as having Node itself, or any of the alternatives available, is enough.
97 |
98 | When you want to deploy, you can simple run `rake barista:brew` to force the compilation of all JavaScripts for the current application.
99 |
100 | ## In Practice
101 |
102 | Barista not only supports compiling all JavaScripts on demand (via `rake barista:brew` as above, or `Barista.compile_all!`) but it
103 | also ships with a simple Rack server app that will compile on demand for platforms such as Heroku, meaning you don't need write access
104 | (although it is helpful).
105 |
106 | If you're using [Jammit](http://documentcloud.github.com/jammit/), the precompilation phase (e.g. `rake barista:brew` before running Jammit) will make it possible for your application
107 | to automatically bundle not only normal JavaScripts but also your CoffeeScripts.
108 |
109 | To add Barista to your project, simply add `gem 'barista', '~> 1.0'` to your Gemfile and run `bundle install`.
110 |
111 | Please note that for Jammit compatibility, in test and development mode (by default) it will
112 | automatically compile all CoffeeScripts that have changed before rendering the page.
113 |
114 | Barista works out of the box with Rails 3 (and theoretically, Rails 2) — with support for Rack if
115 | you're willing to set it up manually. More docs on how to set it up for other platforms
116 | will be posted in the near future.
117 |
118 | ## Sinatra
119 |
120 | To use Barista with [Sinatra](http://www.sinatrarb.com/), you'll need to first require the Barista gem in your application
121 | and then add the following to your application scope (e.g. if you're using a custom class, there):
122 |
123 | register Barista::Integration::Sinatra
124 |
125 | This will automatically setup the filter as needed, setup a server proxy for the `coffee-script.js`
126 | file and setup the defaults based on your applications environment
127 |
128 | ## Configuration
129 |
130 | Please note that Barista lets you configure several options. To do this,
131 | it's as simple as setting up an initializer with:
132 |
133 | rails generate barista:install
134 |
135 | Then editing `config/initializers/barista_config.rb`. The options available are:
136 |
137 | ### Boolean Options
138 |
139 | All of these come in the form of `#option?` (to check its status), `#option=(value)` (to set it)
140 | and `#option!` (to set the value to true):
141 |
142 | * `verbose` – Output debugging error messages. (Defaults to true in test / dev)
143 | * `bare` – Don't wrap the compiled JS in a Closure.
144 | * `add_filter` – Automatically add an around filter for processing changes. (Defaults to true in test / dev)
145 | * `add_preamble` – Add a time + path preamble to compiled JS. (Defaults to true in test / dev)
146 | * `exception_on_error` – Raise an exception on compilation errors (defaults to true)
147 | * `embedded_interpreter` – Embeds coffeescript + link to coffee file instead of compiling for include tags and haml filters. (Defaults to true in test / dev)
148 | * `auto_compile` – Automatically compile CoffeeScript to JS when CoffeeScript is newer than the generated JS file. After you turn it off, your server will use the generated JS file directly and won't depend on any CoffeeScript compilers. (Defaults is true)
149 |
150 | ### Path options
151 |
152 | * `root` – The folder path to read CoffeeScripts from. (Defaults to `app/coffeescripts`.)
153 | * `output_root` – The folder to write compiled JS files to. (Defaults to `public/javascripts`.)
154 | * `change_output_prefix!` – Method to change the output prefix for a framework.
155 | * `change_output_root!` - Method to change the output root for a given framework.
156 | * `verbose` – Whether or not Barista will add a preamble to files.
157 | * `js_path` – Path to the pure-JavaScript compiler.
158 | * `env` – The application environment. (Defaults to `Rails.env`.)
159 | * `app_root` – The application's root path.
160 | * `bin_path` – The path to the `node` executable if non-standard and not using `therubyracer`.
161 | * All of the hook methods mentioned below.
162 |
163 | ### Custom Preamble
164 |
165 | You can generate a custom preamble using a code block. For example, you can replace the location of the original `.coffee` file by a relative one to `Rails.root`.
166 |
167 | Barista.add_preamble do |location|
168 | "/* : DO NOT MODIFY - compiled from #{Pathname.new(location).relative_path_from(Rails.root).to_s}\n\n"
169 | end
170 |
171 | ## Frameworks
172 |
173 | One of the other main features Barista adds (over other tools) is frameworks similar
174 | to Compass. The idea being, you add CoffeeScripts at runtime from gems etc. To do this,
175 | in your gem just have a `coffeescript` directory and then in your gem add the following code:
176 |
177 | Barista::Framework.register 'name', 'full-path-to-directory' if defined?(Barista::Framework)
178 |
179 | For an example of this in practice, check out [bhm-google-maps](http://github.com/YouthTree/bhm-google-maps)
180 | or, the currently-in-development, [shuriken](http://github.com/Sutto/shuriken). The biggest advantage of this
181 | is you can then manage JS dependencies using existing tools like Bundler.
182 |
183 | In your `Barista.configure` block, you can also configure on a per-application basis the output directory
184 | for individual frameworks (e.g. put shuriken into `vendor/shuriken`, bhm-google-maps into `vendor/bhm-google-maps`):
185 |
186 | Barista.configure do |c|
187 | c.change_output_prefix! 'shuriken', 'vendor/shuriken'
188 | c.change_output_prefix! 'bhm-google-maps', 'vendor/bhm-google-maps'
189 | end
190 |
191 | Alternatively, to prefix all, you can use `Barista.each_framework` (if you pass true, it includes the 'default' framework
192 | which is your application root).
193 |
194 | Barista.configure do |c|
195 | c.each_framework do |framework|
196 | c.change_output_prefix! framework.name, "vendor/#{framework.name}"
197 | end
198 | end
199 |
200 | ## Hooks
201 |
202 | Barista lets you hook into the compilation at several stages, namely:
203 |
204 | * before compilation
205 | * after compilation
206 | * after compilation fails
207 | * after compilation complete
208 |
209 | To hook into these hooks, you can do the following:
210 |
211 | * `Barista.before_compilation { |path| puts "Barista: Compiling #{path}" }`
212 | * `Barista.on_compilation { |path| puts "Barista: Successfully compiled #{path}" }`
213 | * `Barista.on_compilation_with_warning { |path, output| puts "Barista: Compilation of #{path} had a warning:\n#{output}" }`
214 | * `Barista.on_compilation_error { |path, output| puts "Barista: Compilation of #{path} failed with:\n#{output}" }`
215 | * `Barista.on_compilation_complete { puts "Barista: Successfully compiled all files" }`
216 |
217 | These allow you to do things such as notify on compilation, automatically
218 | perform compression post compilation and a variety of other cool things.
219 |
220 | An excellent example of these hooks in use is [barista\_growl](http://github.com/TrevorBurnham/barista_growl),
221 | by Trevor Burnham — a gem perfect for development purposes that automatically shows Growl messages
222 | on compilation.
223 |
224 | ## Deployment
225 |
226 | Add `require 'barista/capistrano'` to your `deploy.rb`.
227 |
228 | # Contributors / Credits
229 |
230 | The following people have all contributed to Barista:
231 |
232 | * [Xavier Shay](https://github.com/xaviershay) – Added preamble text to generated text in verbose mode.
233 | * [einarmagnus](https://github.com/einarmagnus) – Fixed jruby support.
234 | * [Matt Dean](https://github.com/trabian) – Added `before_full_compilation` and `on_compilation_complete` hooks.
235 | * [Trevor Burnham](https://github.com/TrevorBurnham) – Misc. documentation tweaks and hooks idea.
236 | * [Sean McCullough](https://github.com/mcculloughsean) – Initial switch to support bare (vs. no\_wrap)
237 | * [Ben Atkin](https://github.com/benatkin) – Docs work.
238 | * [Ben Hoskings](https://github.com/benhoskings) – Misc. fixes, added preamble support.
239 | * [Kim Joar Bekkelund](https://github.com/kjbekkelund) – Docs work.
240 | * [Jeffrey ODell](https://github.com/jodell) - Fixed an issue with messages during autocompile.
241 | * [Paul McMahon](https://github.com/pwim) - Fixed a typo.
242 | * [Ravil Bayramgalin](https://github.com/brainopia) - Fixes for Rakefiles on Rails 2.
243 | * [Daniel Doubrovkine](https://github.com/dblock) - Dynamic Preambles, Making it easier to spec the application.
244 |
245 | Barista was originally heavily inspired by [Bistro Car](https://github.com/jnicklas/bistro_car), but has taken a few fundamentally
246 | different approach in a few areas.
247 |
248 | Barista builds upon the awesome [coffee-script](https://github.com/josh/ruby-coffee-script) gem.
249 |
250 | It's all possible thanks to [CoffeeScript](https://github.com/jashkenas/coffee-script) by Jeremy Ashkenas.
251 |
252 | If I've missed you're name and you've contributed to Barista, please let me know and I'll add you to the list (or
253 | fork this document and send a pull request).
254 |
255 | ## Note on Patches/Pull Requests ##
256 |
257 | 1. Fork the project.
258 | 2. Make your feature addition or bug fix.
259 | 3. Add tests for it. This is important so I don't break it in a future version unintentionally.
260 | 4. Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
261 | 5. Send me a pull request. Bonus points for topic branches.
262 |
263 | ## Copyright ##
264 |
265 | Copyright (c) 2010 Darcy Laycock. See LICENSE for details.
266 |
--------------------------------------------------------------------------------