embarista 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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