embarista 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -cfs -r spec_helper.rb
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rake-pipeline', :github => 'livingsocial/rake-pipeline'
6
+ gem 'rake-pipeline-web-filters', :github => 'wycats/rake-pipeline-web-filters'
7
+
8
+ gem 'pry'
9
+ gem 'pry-debugger'
10
+ gem 'pry-remote'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Yapp, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ Collection of Rake Pipeline stuff we use at Yapp for building Ember apps.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "bundler/setup"
4
+
5
+ require 'rspec/core/rake_task'
6
+
7
+ RSpec::Core::RakeTask.new
8
+
9
+ task :default => :spec
data/embarista.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../lib/embarista/version', __FILE__)
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Luke Melia", "Kris Selden"]
5
+ gem.email = ["tech@yapp.us"]
6
+ gem.description = %q{A collection of web filters for rake-pipeline}
7
+ gem.summary = %q{A collection of web filters for rake-pipeline used to build Yapp Ember.js apps}
8
+ gem.homepage = "http://github.com/yappbox/embarista"
9
+
10
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- spec/*`.split("\n")
13
+ gem.name = "embarista"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = Embarista::VERSION
16
+
17
+ gem.add_dependency "rake-pipeline"
18
+ gem.add_dependency "rake-pipeline-web-filters"
19
+ gem.add_dependency "ruby_gntp"
20
+ gem.add_dependency "listen"
21
+ gem.add_dependency "colored"
22
+ gem.add_dependency "rb-fsevent"
23
+
24
+ gem.add_development_dependency "rspec"
25
+ gem.add_development_dependency "rb-readline"
26
+ gem.add_development_dependency "execjs"
27
+ end
@@ -0,0 +1,28 @@
1
+ module Embarista
2
+ module Filters
3
+ class ErbFilter < Rake::Pipeline::Filter
4
+ attr_reader :options
5
+
6
+ def initialize(options= {}, &block)
7
+ @options = options
8
+ super(&block)
9
+ unless block_given?
10
+ @output_name_generator = proc { |input| input.sub(/\.erb$/, '') }
11
+ end
12
+ end
13
+
14
+ def generate_output(inputs, output)
15
+ inputs.each do |input|
16
+ erb = ERB.new(input.read, nil, '-')
17
+ binding = @options[:binding]
18
+ output.write erb.result(binding)
19
+ end
20
+ end
21
+
22
+ private
23
+ def external_dependencies
24
+ [ 'erb' ]
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,104 @@
1
+ module Embarista
2
+ module Filters
3
+ class PrecompileHandlebarsFilter < Rake::Pipeline::Filter
4
+ class << self
5
+ def emberjs_path=(path)
6
+ if defined?(@@emberjs_path)
7
+ raise "Cannot set emberjs_path to #{path}. It was already set to #{@@emberjs_path}"
8
+ else
9
+ @@emberjs_path = path
10
+ end
11
+ end
12
+
13
+ def handlebarsjs_path=(path)
14
+ if defined?(@@handlebarsjs_path)
15
+ raise "Cannot set handlebarsjs_path to #{path}. It was already set to #{@@handlebarsjs_path}"
16
+ else
17
+ @@handlebarsjs_path = path
18
+ end
19
+ end
20
+
21
+ def jquery_version=(jquery_version)
22
+ @@jquery_version = jquery_version
23
+ end
24
+
25
+ def headless_ember_preamble
26
+ <<-JS
27
+ // DOM
28
+ var Element = {};
29
+ Element.firstChild = function () { return Element; };
30
+ Element.innerHTML = function () { return Element; };
31
+
32
+ var document = { createRange: false, createElement: function() { return Element; } };
33
+ var window = this;
34
+ this.document = document;
35
+
36
+ // Console
37
+ var console = window.console = {};
38
+ console.log = console.info = console.warn = console.error = function(){};
39
+
40
+ // jQuery
41
+ var jQuery = window.jQuery = function() { return jQuery; };
42
+ jQuery.ready = function() { return jQuery; };
43
+ jQuery.inArray = function() { return jQuery; };
44
+ jQuery.jquery = "#{@@jquery_version}";
45
+ jQuery.event = { fixHooks: {} };
46
+ var $ = jQuery;
47
+
48
+ // Ember
49
+ function precompileEmberHandlebars(string) {
50
+ return Ember.Handlebars.precompile(string).toString();
51
+ }
52
+ JS
53
+ end
54
+
55
+ def contents
56
+ raise "Must set emberjs_path" unless @@emberjs_path
57
+ raise "Must set handlebarsjs_path" unless @@handlebarsjs_path
58
+ raise "Must set jquery_version" unless @@jquery_version
59
+ @@contents ||= [File.read(@@handlebarsjs_path),
60
+ headless_ember_preamble,
61
+ File.read(@@emberjs_path)].join("\n")
62
+ end
63
+
64
+ def context
65
+ @@context ||= ExecJS.compile(contents)
66
+ end
67
+ end
68
+
69
+ def initialize(options= {}, &block)
70
+ @options = options
71
+ self.class.emberjs_path = options[:emberjs]
72
+ self.class.handlebarsjs_path = options[:handlebarsjs]
73
+ self.class.jquery_version = options[:jquery_version]
74
+
75
+ @template_dir = options[:template_dir] || 'templates'
76
+
77
+ super(&block)
78
+ unless block_given?
79
+ @output_name_generator = proc { |input| input.sub(/\.handlebars$/, '.js') }
80
+ end
81
+ end
82
+
83
+
84
+ def generate_output(inputs, output)
85
+ inputs.each do |input|
86
+ name = File.basename(input.path, '.handlebars')
87
+ dirname = File.dirname(input.path)
88
+
89
+ dirname.gsub!(/^\/?#{@template_dir}\/?/,'')
90
+
91
+ full_name = [dirname, name].compact.reject(&:empty?).join('/')
92
+
93
+ compiled = self.class.context.call("precompileEmberHandlebars", input.read)
94
+ output.write "\nEmber.TEMPLATES['#{full_name}'] = Ember.Handlebars.template(#{compiled});\n"
95
+ end
96
+ end
97
+
98
+ private
99
+ def external_dependencies
100
+ [ 'execjs' ]
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,32 @@
1
+ module Embarista
2
+ module Filters
3
+ class RewriteMinispadeRequiresFilter < Rake::Pipeline::Filter
4
+ attr_reader :options
5
+
6
+ def initialize(options=nil, &block)
7
+ @options = options || {}
8
+ @options[:prefix] = '' unless @options.has_key?(:prefix)
9
+ super(&block)
10
+ end
11
+
12
+ def generate_output(inputs, output)
13
+ inputs.each do |input|
14
+ code = input.read
15
+ #puts input.path
16
+ relative_root = Pathname.new(input.path).dirname
17
+ code.gsub!(%r{\brequire(All)?\s*\(\s*(["'/])([^"']+)\2\s*}) do |m|
18
+ optional_all = $1
19
+ quote_char = $2
20
+ before_path = $3
21
+ path = before_path.dup
22
+ path = relative_root.join(path).to_s if path.start_with?('.')
23
+ path.gsub!(%r{^#{options[:root]}/}, options[:prefix]) if options[:root]
24
+ #puts "require#{optional_all}: #{before_path} -> #{path}"
25
+ "minispade.require#{optional_all}(#{quote_char}#{path}#{quote_char}"
26
+ end
27
+ output.write(code)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ module Embarista
2
+ module Filters
3
+ class StripEmberAssertsFilter < Rake::Pipeline::Filter
4
+ def generate_output(inputs, output)
5
+ inputs.each do |input|
6
+ result = input.read
7
+ result.gsub!(/Em(?:ber)?\.(assert|warn|deprecate|deprecateFunc)\((.*)\);/, '')
8
+ output.write(result)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require "rake-pipeline-web-filters"
2
+
3
+ module Embarista
4
+ module Filters
5
+ autoload :ErbFilter, 'embarista/filters/erb_filter'
6
+ autoload :StripEmberAssertsFilter, 'embarista/filters/strip_ember_asserts_filter'
7
+ autoload :PrecompileHandlebarsFilter, 'embarista/filters/precompile_handlebars_filter'
8
+ autoload :RewriteMinispadeRequiresFilter, 'embarista/filters/rewrite_minispade_requires_filter'
9
+ end
10
+ end
@@ -0,0 +1,57 @@
1
+ module Embarista
2
+ module Helpers
3
+ def precompile_handlebars(*args, &block)
4
+ filter(Embarista::Filters::PrecompileHandlebarsFilter, *args, &block)
5
+ end
6
+
7
+ def rewrite_minispade_requires(*args, &block)
8
+ filter(Embarista::Filters::RewriteMinispadeRequiresFilter, *args, &block)
9
+ end
10
+
11
+ def erb(*args, &block)
12
+ filter(Embarista::Filters::ErbFilter, *args, &block)
13
+ end
14
+
15
+ def strip_ember_asserts(*args, &block)
16
+ filter(Embarista::Filters::StripEmberAssertsFilter, *args, &block)
17
+ end
18
+
19
+ def concat_file_list(file_list, output_filenames)
20
+ match "{#{file_list.join(',')}}" do
21
+ concat(file_list) { Array(output_filenames) }
22
+ end
23
+ end
24
+
25
+ def tee(extension_regexp, second_extension)
26
+ concat {|path| [path, path.gsub(extension_regexp, second_extension)] }
27
+ end
28
+
29
+ def process_javascript(pattern, opts)
30
+ match pattern, &JavascriptPipeline.new(opts)
31
+ end
32
+
33
+ def sass_uncompressed(&block)
34
+ sass(:additional_load_paths => 'css',
35
+ :style => :expanded,
36
+ :line_comments => true,
37
+ &block)
38
+ end
39
+
40
+ def sass_compressed(&block)
41
+ sass(:additional_load_paths => 'css',
42
+ :style => :compressed,
43
+ :line_comments => false,
44
+ &block)
45
+ end
46
+
47
+ # rename "qunit-*.css" => "qunit.css"
48
+ def rename(renames_map)
49
+ renames_map.each do |pattern, output_filename|
50
+ match pattern do
51
+ concat output_filename
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,33 @@
1
+ module Embarista
2
+ class JavascriptPipeline
3
+ def initialize(options)
4
+ @options = options
5
+ end
6
+
7
+ def to_proc
8
+ options = @options
9
+ @options[:prefix] = '' unless @options.has_key?(:prefix)
10
+ Proc.new do
11
+ module_id_generator = proc {|input|
12
+ module_id = input.path.sub(/\.js(\.dev|\.prod)?$/, '')
13
+ module_id.gsub!(%r{^#{options[:root]}/}, options[:prefix]) if options[:root]
14
+ # puts input.path
15
+ # puts "module_id: #{module_id}"
16
+ module_id
17
+ }
18
+ rewrite_minispade_requires(root: options[:root], prefix: options[:prefix])
19
+ concat { |path| ["#{path}.prod", "#{path}.dev"] }
20
+ match "**/*.js.prod" do
21
+ strip_ember_asserts
22
+ uglify(copyright: false) {|input| input}
23
+ minispade(string: true, module_id_generator: module_id_generator)
24
+ concat { Array(options[:concat]).map{|path| path.gsub(/\.js$/, '.min.js')} }
25
+ end
26
+ match "**/*.js.dev" do
27
+ minispade :module_id_generator => module_id_generator
28
+ concat { Array(options[:concat]) }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,160 @@
1
+ require 'thread'
2
+ require 'rack/lock'
3
+ require 'rake-pipeline'
4
+ require 'listen'
5
+ require 'securerandom'
6
+ require 'ruby_gntp'
7
+
8
+ module Embarista
9
+ class Server
10
+ def initialize(opts = {})
11
+ @root = opts[:root]
12
+ @project_name = opts[:project_name] || File.basename(@root)
13
+ @run_tests = opts[:run_tests] || !!ENV['TEST']
14
+ @clean_on_startup = true
15
+ @document_root = opts[:document_root] || 'build'
16
+ if opts.key?(:clean_on_startup)
17
+ @clean_on_startup = opts[:clean_on_startup]
18
+ else
19
+ @clean_on_startup = true
20
+ end
21
+ @watch_dirs = opts[:watch_dirs] || ['app', 'spec']
22
+ setup_rakep_project
23
+ start_watching
24
+ end
25
+
26
+ def document_root
27
+ File.join(@root, @document_root)
28
+ end
29
+
30
+ def builder
31
+ @builder ||= begin
32
+ server = self
33
+ Rack::Builder.new do
34
+ use Rack::Lock, server.build_lock
35
+ run Rack::File.new(server.document_root)
36
+ end
37
+ end
38
+ end
39
+
40
+ def call(env)
41
+ builder.call(env)
42
+ end
43
+
44
+ attr_reader :run_tests, :clean_on_startup, :project_name
45
+
46
+ def setup_rakep_project
47
+ project.pipelines.each do |pipeline|
48
+ pipeline.rake_application = Rake.application
49
+ pipeline.setup
50
+ end
51
+ project.invoke_clean if clean_on_startup
52
+ end
53
+
54
+ def start_watching
55
+ watcher_queue = Queue.new
56
+ watcher_thread = Thread.new do
57
+ listener = Listen.to(*@watch_dirs)
58
+ listener.change(&build_proc)
59
+ watcher_queue << listener
60
+ listener.start # enter the run loop
61
+ end
62
+ watcher_thread.abort_on_exception = true
63
+
64
+ puts "Watching #{watcher_queue.pop.directories}"
65
+ start_testing if run_tests
66
+ end
67
+
68
+ def start_testing
69
+ test_thread = Thread.new do
70
+ while true
71
+ test_mutex.synchronize do
72
+ test_resource.wait(test_mutex)
73
+ end
74
+ puts 'RUNNING TESTS'
75
+ if system('rake test')
76
+ GNTP.notify({
77
+ :app_name => "yapp-dashboard",
78
+ :title => "yapp-dashboard error",
79
+ :text => "JS test GREEN!"
80
+ }) rescue nil
81
+ else
82
+ GNTP.notify({
83
+ :app_name => "yapp-dashboard",
84
+ :title => "yapp-dashboard error",
85
+ :text => "JS test RED!"
86
+ }) rescue nil
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def project
93
+ @project ||= Rake::Pipeline::Project.new(File.join(@root, 'Assetfile'))
94
+ end
95
+
96
+ def build_lock
97
+ @build_lock ||= Mutex.new
98
+ end
99
+
100
+ def build_proc
101
+ @build_proc ||= create_build_proc
102
+ end
103
+
104
+ private
105
+ def test_mutex
106
+ @test_mutex ||= Mutex.new
107
+ end
108
+
109
+ def test_resource
110
+ @test_resource ||= ConditionVariable.new
111
+ end
112
+
113
+ def create_build_proc
114
+ lambda {|modified, added, removed|
115
+ success = true
116
+ build_lock.synchronize {
117
+ puts 'FILE CHANGE'
118
+ puts ' modified: ' + modified.join(', ') unless modified.empty?
119
+ puts ' added: ' + added.join(', ') unless added.empty?
120
+ puts ' removed: ' + removed.join(', ') unless removed.empty?
121
+ start = Time.now
122
+ if added.size > 0 || removed.size > 0
123
+ puts "BUILD (CLEAN) #{start}"
124
+ begin
125
+ project.invoke_clean
126
+ rescue
127
+ success = false
128
+ puts "ERROR: #{$!.inspect}"
129
+ GNTP.notify({
130
+ :app_name => project_name,
131
+ :title => "#{project_name} error",
132
+ :text => "ERROR: #{$!.inspect}"
133
+ }) rescue nil
134
+ end
135
+ else
136
+ puts "BUILD #{start}"
137
+ begin
138
+ project.invoke
139
+ rescue
140
+ success = false
141
+ puts "ERROR: #{$!.inspect}"
142
+ GNTP.notify({
143
+ :app_name => project_name,
144
+ :title => "#{project_name} error",
145
+ :text => "ERROR: #{$!.inspect}"
146
+ }) rescue nil
147
+ end
148
+ end
149
+ puts "FINISHED #{Time.now.to_i - start.to_i} seconds"
150
+ }
151
+ # after UNLOCK
152
+ if run_tests && success
153
+ test_mutex.synchronize do
154
+ test_resource.signal
155
+ end
156
+ end
157
+ }
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,3 @@
1
+ module Embarista
2
+ VERSION = '1.1.0'
3
+ end
data/lib/embarista.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'rake-pipeline'
2
+
3
+ module Embarista
4
+ autoload :Helpers, 'embarista/helpers'
5
+ autoload :Filters, 'embarista/filters'
6
+ autoload :Version, 'embarista/version'
7
+
8
+ autoload :JavascriptPipeline, 'embarista/javascript_pipeline'
9
+ autoload :Server, 'embarista/server'
10
+ end
11
+
12
+ Rake::Pipeline::DSL::PipelineDSL.send(:include, Embarista::Helpers)
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Embarista::Filters::ErbFilter do
4
+ MemoryFileWrapper = Rake::Pipeline::SpecHelpers::MemoryFileWrapper
5
+
6
+ let(:file_wrapper_class) { MemoryFileWrapper }
7
+
8
+ let(:input_files) {
9
+ [
10
+ MemoryFileWrapper.new("/path/to/input", "foo.erb", "UTF-8", "Hello <%= test_var %>"),
11
+ ]
12
+ }
13
+
14
+ let(:output_root) { '/path/to/output' }
15
+
16
+ let(:test_var) { 'Bear' }
17
+
18
+ let(:options) {
19
+ {:binding => binding}
20
+ }
21
+
22
+ let(:rake_application) {
23
+ Rake::Application.new
24
+ }
25
+
26
+ let(:subject) {
27
+ filter = described_class.new(options) do |input|
28
+ input.sub(/\.(erb)$/, '.txt')
29
+ end
30
+ filter.file_wrapper_class = file_wrapper_class
31
+ filter.input_files = input_files
32
+ filter.output_root = output_root
33
+ filter.rake_application = rake_application
34
+ filter
35
+ }
36
+
37
+ it "generates output" do
38
+ tasks = subject.generate_rake_tasks
39
+ tasks.each(&:invoke)
40
+
41
+ file = MemoryFileWrapper.files["/path/to/output/foo.txt"]
42
+ file.body.should eq("Hello Bear")
43
+ file.encoding.should eq("UTF-8")
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Embarista::Helpers do
4
+ let(:pipeline) { Rake::Pipeline.new }
5
+ let(:dsl) { Rake::Pipeline::DSL::PipelineDSL.new(pipeline) }
6
+
7
+ describe '#rewrite_minispade_requires' do
8
+ it 'creates a Embarista::Filters::RewriteMinispadeRequiresFilter' do
9
+ dsl.rewrite_minispade_requires
10
+ pipeline.filters.last.should be_kind_of(Embarista::Filters::RewriteMinispadeRequiresFilter)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'execjs'
3
+
4
+ describe Embarista::Filters::PrecompileHandlebarsFilter do
5
+ MemoryFileWrapper = Rake::Pipeline::SpecHelpers::MemoryFileWrapper
6
+
7
+ let(:file_wrapper_class) { MemoryFileWrapper }
8
+
9
+ let(:input_hbs) {
10
+ <<-HBS
11
+ Hello {{foo}}
12
+ HBS
13
+ }
14
+
15
+ let(:input_files) {
16
+ [
17
+ MemoryFileWrapper.new("/path/to/input", "templates/bar/foo.handlebars", "UTF-8", input_hbs)
18
+ ]
19
+ }
20
+
21
+ let(:output_root) { '/path/to/output' }
22
+
23
+ let(:rake_application) {
24
+ Rake::Application.new
25
+ }
26
+
27
+ let(:emberjs) { File.expand_path('../../vendor/ember.js', __FILE__) }
28
+ let(:handlebarsjs) { File.expand_path('../../vendor/handlebars.js', __FILE__) }
29
+ let(:jquery_version) { '1.7.2' }
30
+
31
+ let(:subject) {
32
+ filter = described_class.new(:emberjs => emberjs, :handlebarsjs => handlebarsjs, :jquery_version => jquery_version)
33
+ filter.file_wrapper_class = file_wrapper_class
34
+ filter.input_files = input_files
35
+ filter.output_root = output_root
36
+ filter.rake_application = rake_application
37
+ filter
38
+ }
39
+
40
+ it "should precompile ember template" do
41
+ tasks = subject.generate_rake_tasks
42
+ tasks.each(&:invoke)
43
+
44
+ file = MemoryFileWrapper.files["/path/to/output/templates/bar/foo.js"]
45
+
46
+ file.should_not be_nil
47
+ file.body.should_not be_nil
48
+ file.body.should match(/^Ember\.TEMPLATES\[\'bar\/foo\'\] = Ember\.Handlebars\.template\(.*\"Hello \".*\"foo\"/m)
49
+ file.encoding.should eq('UTF-8')
50
+ end
51
+ end