embarista 1.1.4 → 1.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +49 -0
- data/.travis.yml +1 -1
- data/Gemfile +2 -1
- data/embarista.gemspec +1 -0
- data/lib/embarista.rb +5 -5
- data/lib/embarista/dynamic_index.rb +207 -0
- data/lib/embarista/filters.rb +2 -1
- data/lib/embarista/filters/manifest_url_filter.rb +39 -0
- data/lib/embarista/filters/precompile_handlebars_filter.rb +5 -78
- data/lib/embarista/helpers.rb +4 -0
- data/lib/embarista/manifest_builder.rb +44 -0
- data/lib/embarista/s3sync.rb +63 -13
- data/lib/embarista/sass_functions.rb +9 -2
- data/lib/embarista/server.rb +14 -12
- data/lib/embarista/tasks.rb +2 -0
- data/lib/embarista/tasks/coffeelint.rb +50 -0
- data/lib/embarista/tasks/updater.rb +121 -0
- data/lib/embarista/version.rb +1 -1
- data/spec/manifest_builder_spec.rb +77 -0
- data/spec/manifest_url_filter_spec.rb +79 -0
- metadata +114 -44
- data/lib/embarista/digest_helper.rb +0 -59
- data/lib/embarista/manifest_helper.rb +0 -14
data/lib/embarista/helpers.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
module Embarista
|
2
2
|
module Helpers
|
3
|
+
def rewrite_manifest_urls(*args, &block)
|
4
|
+
filter(Embarista::Filters::ManifestUrlFilter, *args, &block)
|
5
|
+
end
|
6
|
+
|
3
7
|
def precompile_handlebars(*args, &block)
|
4
8
|
filter(Embarista::Filters::PrecompileHandlebarsFilter, *args, &block)
|
5
9
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Embarista
|
5
|
+
class ManifestBuilder
|
6
|
+
include FileUtils
|
7
|
+
|
8
|
+
attr_reader :root, :tmp, :document_root, :tmp_document_root, :version, :manifest
|
9
|
+
|
10
|
+
def initialize(opts={})
|
11
|
+
@root = Pathname.new(opts[:root] || Dir.getwd).expand_path
|
12
|
+
@document_root = Pathname.new(opts[:document_root] || 'public').expand_path(@root)
|
13
|
+
@tmp = Pathname.new(opts[:tmp] || 'tmp').expand_path(@root)
|
14
|
+
@tmp_document_root = @tmp + @document_root.relative_path_from(@root)
|
15
|
+
@manifest = opts[:manifest] || {}
|
16
|
+
@version = opts[:version]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Pass in the file to be versioned and staged in the tmp document root.
|
20
|
+
def add(path, path_from_document_root=nil)
|
21
|
+
full_path = Pathname.new(path).expand_path(root)
|
22
|
+
path_from_document_root = Pathname.new(path_from_document_root || full_path.relative_path_from(document_root))
|
23
|
+
relative_dir = path_from_document_root.dirname
|
24
|
+
|
25
|
+
md5 = Digest::MD5.file(full_path).hexdigest
|
26
|
+
ext = full_path.extname
|
27
|
+
name_without_ext = full_path.basename.to_s.chomp(ext)
|
28
|
+
suffix = "-#{md5}"
|
29
|
+
suffix = "-#{version}#{suffix}" if version
|
30
|
+
versioned_file = "#{name_without_ext}#{suffix}#{ext}"
|
31
|
+
|
32
|
+
versioned_path_from_document_root = relative_dir + versioned_file
|
33
|
+
versioned_full_path = tmp_document_root + versioned_path_from_document_root
|
34
|
+
|
35
|
+
mkdir_p versioned_full_path.dirname
|
36
|
+
cp full_path, versioned_full_path
|
37
|
+
|
38
|
+
http_path = '/' + path_from_document_root.to_s
|
39
|
+
versioned_http_path = '/' + versioned_path_from_document_root.to_s
|
40
|
+
|
41
|
+
manifest[http_path] = versioned_http_path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/embarista/s3sync.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
module Embarista
|
2
2
|
class S3sync
|
3
|
-
attr_reader :origin, :bucket_name, :pwd, :tmp_root, :
|
3
|
+
attr_reader :origin, :bucket_name, :pwd, :tmp_root, :local_manifest_path, :remote_manifest_path
|
4
4
|
|
5
|
-
def initialize(origin, options)
|
6
|
-
bucket_name = options.fetch(:bucket_name)
|
7
|
-
|
5
|
+
def initialize(origin, options)
|
6
|
+
@bucket_name = options.fetch(:bucket_name)
|
7
|
+
@local_manifest_path = options[:local_manifest_path]
|
8
|
+
@remote_manifest_path = options[:remote_manifest_path]
|
8
9
|
aws_key = options.fetch(:aws_key)
|
9
10
|
aws_secret = options.fetch(:aws_secret)
|
10
11
|
|
@@ -15,9 +16,8 @@ module Embarista
|
|
15
16
|
|
16
17
|
@pwd = Pathname.new('').expand_path
|
17
18
|
@origin = origin
|
18
|
-
@bucket_name = bucket_name
|
19
19
|
@tmp_root = @pwd + @origin
|
20
|
-
@
|
20
|
+
@age = options[:age] || 31556900
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.sync(origin, options)
|
@@ -26,7 +26,19 @@ module Embarista
|
|
26
26
|
|
27
27
|
def store(name, file)
|
28
28
|
puts " -> #{name}"
|
29
|
-
|
29
|
+
|
30
|
+
opts = {
|
31
|
+
access: :public_read
|
32
|
+
}
|
33
|
+
|
34
|
+
if should_gzip?(name)
|
35
|
+
opts[:content_encoding] = 'gzip'
|
36
|
+
end
|
37
|
+
|
38
|
+
opts[:cache_control] = "max-age=#{@age.to_i}"
|
39
|
+
opts[:expires] = (Time.now + @age).httpdate
|
40
|
+
|
41
|
+
AWS::S3::S3Object.store(name, file, bucket_name, opts)
|
30
42
|
end
|
31
43
|
|
32
44
|
def sync
|
@@ -38,31 +50,69 @@ module Embarista
|
|
38
50
|
end
|
39
51
|
|
40
52
|
delta_manifest.values.each do |file_name|
|
41
|
-
|
53
|
+
compressed_open(file_name) do |file|
|
42
54
|
store(file_name, file)
|
43
55
|
end
|
44
56
|
end
|
45
57
|
|
46
|
-
open(
|
47
|
-
store(
|
58
|
+
open(local_manifest_path) do |file|
|
59
|
+
store(remote_manifest_file_name, file)
|
60
|
+
store(local_manifest_file_name, file)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def compressed_open(file_name)
|
65
|
+
if should_gzip?(file_name)
|
66
|
+
str_io = StringIO.new
|
67
|
+
open(tmp_root.to_s + file_name) do |f|
|
68
|
+
streaming_deflate(f, str_io)
|
69
|
+
end
|
70
|
+
str_io.reopen(str_io.string, "r")
|
71
|
+
yield str_io
|
72
|
+
str_io.close
|
73
|
+
else
|
74
|
+
open(tmp_root.to_s + file_name) do |f|
|
75
|
+
yield f
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def streaming_deflate(source_io, target_io, buffer_size = 4 * 1024)
|
81
|
+
gz = Zlib::GzipWriter.new(target_io, Zlib::BEST_COMPRESSION)
|
82
|
+
while(string = source_io.read(buffer_size)) do
|
83
|
+
gz.write(string)
|
48
84
|
end
|
85
|
+
gz.close
|
86
|
+
end
|
87
|
+
|
88
|
+
def should_gzip?(name)
|
89
|
+
name =~ /\.css|\.js\Z/
|
90
|
+
end
|
91
|
+
|
92
|
+
def remote_manifest_file_name
|
93
|
+
File.basename(remote_manifest_path)
|
94
|
+
end
|
95
|
+
|
96
|
+
def local_manifest_file_name
|
97
|
+
File.basename(local_manifest_path)
|
49
98
|
end
|
50
99
|
|
51
100
|
def build_delta_manifest
|
52
101
|
return local_manifest unless remote_manifest
|
53
102
|
|
54
103
|
new_manifest_values = local_manifest.values - remote_manifest.values
|
55
|
-
|
104
|
+
|
105
|
+
local_manifest.select {|key, value| new_manifest_values.include? value }
|
56
106
|
end
|
57
107
|
|
58
108
|
def remote_manifest
|
59
|
-
@remote_manifest ||= YAML.load(AWS::S3::S3Object.find(
|
109
|
+
@remote_manifest ||= YAML.load(AWS::S3::S3Object.find(remote_manifest_file_name, bucket_name).value)
|
60
110
|
rescue AWS::S3::NoSuchKey
|
61
111
|
puts 'no remote existing manifest, uploading everything'
|
62
112
|
end
|
63
113
|
|
64
114
|
def local_manifest
|
65
|
-
@local_manifest ||= YAML.load_file(
|
115
|
+
@local_manifest ||= YAML.load_file(local_manifest_path)
|
66
116
|
end
|
67
117
|
end
|
68
118
|
end
|
@@ -1,7 +1,13 @@
|
|
1
1
|
module Embarista
|
2
2
|
module SassFunctions
|
3
3
|
def manifest_url(path)
|
4
|
-
|
4
|
+
digested_path = lookup_manifest_path(path.value)
|
5
|
+
Sass::Script::String.new("url(#{digested_path})")
|
6
|
+
end
|
7
|
+
|
8
|
+
def manifest_path(path)
|
9
|
+
digested_path = lookup_manifest_path(path.value)
|
10
|
+
Sass::Script::String.new(digested_path)
|
5
11
|
end
|
6
12
|
|
7
13
|
private
|
@@ -17,7 +23,8 @@ module Embarista
|
|
17
23
|
|
18
24
|
def lookup_manifest_path(path)
|
19
25
|
if digest?
|
20
|
-
manifest
|
26
|
+
raise ::Sass::SyntaxError.new "manifest-url(#{path.inspect}) missing manifest entry" unless manifest.key? path
|
27
|
+
manifest[path]
|
21
28
|
else
|
22
29
|
path
|
23
30
|
end
|
data/lib/embarista/server.rb
CHANGED
@@ -70,25 +70,27 @@ module Embarista
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def start_testing
|
73
|
-
|
73
|
+
Thread.new do
|
74
74
|
while true
|
75
75
|
test_mutex.synchronize do
|
76
76
|
test_resource.wait(test_mutex)
|
77
77
|
end
|
78
78
|
puts 'RUNNING TESTS'
|
79
|
-
if system('rake test')
|
80
|
-
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
}
|
79
|
+
notification = if system('rake test')
|
80
|
+
{
|
81
|
+
app_name: project_name,
|
82
|
+
title: "#{project_name} error",
|
83
|
+
text: 'JS test GREEN!'
|
84
|
+
}
|
85
85
|
else
|
86
|
-
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
}
|
86
|
+
{
|
87
|
+
app_name: project_name,
|
88
|
+
title: "#{project_name} error",
|
89
|
+
text: 'JS test RED!'
|
90
|
+
}
|
91
91
|
end
|
92
|
+
|
93
|
+
GNTP.notify(notification) rescue nil
|
92
94
|
end
|
93
95
|
end
|
94
96
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module CoffeeLintHelper
|
2
|
+
extend Rake::DSL
|
3
|
+
|
4
|
+
def self.coffeelint(cmd_args)
|
5
|
+
tool = 'coffeelint'
|
6
|
+
cmd_args = cmd_args || ''
|
7
|
+
|
8
|
+
node = which('node')
|
9
|
+
if node.nil?
|
10
|
+
puts "Could not find node in your path."
|
11
|
+
return
|
12
|
+
end
|
13
|
+
|
14
|
+
npm = which('npm')
|
15
|
+
if npm.nil?
|
16
|
+
puts "Could not find npm in your path."
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
coffeelint = which('coffeelint')
|
21
|
+
|
22
|
+
if coffeelint
|
23
|
+
sh "coffeelint #{cmd_args}"
|
24
|
+
|
25
|
+
else
|
26
|
+
if !File.directory?("node_modules/#{tool}")
|
27
|
+
sh "\"#{npm}\" install #{tool}"
|
28
|
+
end
|
29
|
+
|
30
|
+
sh "\"#{node}\" node_modules/#{tool}/bin/#{tool} #{cmd_args}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.which(cmd)
|
35
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
36
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
37
|
+
exts.each { |ext|
|
38
|
+
sep = File::ALT_SEPARATOR || File::SEPARATOR
|
39
|
+
exe = "#{path}#{sep}#{cmd}#{ext}"
|
40
|
+
return exe if File.executable? exe
|
41
|
+
}
|
42
|
+
end
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'run coffeelint'
|
48
|
+
task :coffeelint do
|
49
|
+
CoffeeLintHelper.coffeelint '-q -f config/coffeelint.json -r app'
|
50
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
module Embarista
|
5
|
+
module Updater
|
6
|
+
extend self
|
7
|
+
def update_asset_file(regexp, replacement)
|
8
|
+
assetfile_contents = File.read('Assetfile')
|
9
|
+
assetfile_contents.gsub!(regexp, replacement)
|
10
|
+
File.open('Assetfile', 'w') do |f|
|
11
|
+
f.write(assetfile_contents)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class UpdateEmberTask < ::Rake::TaskLib
|
16
|
+
attr_accessor :name
|
17
|
+
|
18
|
+
def initialize(name = :update_ember)
|
19
|
+
@name = name
|
20
|
+
yield self if block_given?
|
21
|
+
define
|
22
|
+
end
|
23
|
+
|
24
|
+
def define
|
25
|
+
task name do |t, args|
|
26
|
+
old_sha, new_sha = nil, nil
|
27
|
+
regexp = /ember-([0-9a-f]{40})/
|
28
|
+
app_vendor_path = File.expand_path("app/vendor")
|
29
|
+
cd(app_vendor_path) do
|
30
|
+
old_filename = Dir['*'].grep(regexp)[0]
|
31
|
+
old_filename =~ regexp
|
32
|
+
old_sha = $1
|
33
|
+
end
|
34
|
+
raise "Couldn't find current ember.js version" if old_sha.nil?
|
35
|
+
cd('../ember.js') do
|
36
|
+
new_sha = `git rev-parse HEAD`.chomp
|
37
|
+
`bundle && bundle exec rake dist`
|
38
|
+
cd('./dist') do
|
39
|
+
cp('ember.js', "#{app_vendor_path}/ember-#{new_sha}.js")
|
40
|
+
cp('ember.min.js', "#{app_vendor_path}/ember-#{new_sha}.min.js")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
if old_sha != new_sha
|
44
|
+
cd(app_vendor_path) do
|
45
|
+
rm("ember-#{old_sha}.js")
|
46
|
+
rm("ember-#{old_sha}.min.js")
|
47
|
+
end
|
48
|
+
Embarista::Updater.update_asset_file(old_sha, new_sha)
|
49
|
+
end
|
50
|
+
puts "Updated from #{old_sha} to #{new_sha}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class UpdateEmberDataTask < ::Rake::TaskLib
|
56
|
+
attr_accessor :name
|
57
|
+
|
58
|
+
def initialize(name = :update_ember_data)
|
59
|
+
@name = name
|
60
|
+
yield self if block_given?
|
61
|
+
define
|
62
|
+
end
|
63
|
+
|
64
|
+
def define
|
65
|
+
task name do |t, args|
|
66
|
+
old_sha, new_sha = nil, nil
|
67
|
+
regexp = /ember-data-([0-9a-f]{40})/
|
68
|
+
app_vendor_path = File.expand_path("app/vendor")
|
69
|
+
cd(app_vendor_path) do
|
70
|
+
old_filename = Dir['*'].grep(regexp)[0]
|
71
|
+
old_filename =~ regexp
|
72
|
+
old_sha = $1
|
73
|
+
end
|
74
|
+
raise "Couldn't find current ember-data js version" if old_sha.nil?
|
75
|
+
cd('../ember-data') do
|
76
|
+
new_sha = `git rev-parse HEAD`.chomp
|
77
|
+
`bundle && bundle exec rake dist`
|
78
|
+
cd('./dist') do
|
79
|
+
cp('ember-data.js', "#{app_vendor_path}/ember-data-#{new_sha}.js")
|
80
|
+
cp('ember-data.min.js', "#{app_vendor_path}/ember-data-#{new_sha}.min.js")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
if old_sha != new_sha
|
84
|
+
cd(app_vendor_path) do
|
85
|
+
rm("ember-data-#{old_sha}.js")
|
86
|
+
rm("ember-data-#{old_sha}.min.js")
|
87
|
+
end
|
88
|
+
Embarista::Updater.update_asset_file(old_sha, new_sha)
|
89
|
+
end
|
90
|
+
puts "Updated from #{old_sha} to #{new_sha}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class UpdateJqueryTask < ::Rake::TaskLib
|
96
|
+
attr_accessor :name
|
97
|
+
|
98
|
+
def initialize(name = :update_jquery)
|
99
|
+
@name = name
|
100
|
+
yield self if block_given?
|
101
|
+
define
|
102
|
+
end
|
103
|
+
|
104
|
+
def define
|
105
|
+
task name do |t, args|
|
106
|
+
version = ENV['VERSION']
|
107
|
+
raise "please supply VERSION env var to specify jQuery version" if version.nil?
|
108
|
+
cd('./app/vendor') do
|
109
|
+
# remove old jquerys
|
110
|
+
rm Dir['jquery-*.js']
|
111
|
+
sh "curl -O http://code.jquery.com/jquery-#{version}.js"
|
112
|
+
sh "curl -O http://code.jquery.com/jquery-#{version}.min.js"
|
113
|
+
end
|
114
|
+
Embarista::Updater.update_asset_file(%r{JQUERY_VERSION = '\d+\.\d+\.\d+'}, "JQUERY_VERSION = '#{version}'")
|
115
|
+
puts "Updated to jQuery #{version}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
data/lib/embarista/version.rb
CHANGED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pathname'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
describe Embarista::ManifestBuilder do
|
6
|
+
let(:document_root) {
|
7
|
+
Pathname.new('public').expand_path
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:tmp_document_root) {
|
11
|
+
Pathname.new('tmp/public').expand_path
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:options) {
|
15
|
+
{}
|
16
|
+
}
|
17
|
+
|
18
|
+
subject {
|
19
|
+
described_class.new(options)
|
20
|
+
}
|
21
|
+
|
22
|
+
its(:document_root) { should == document_root }
|
23
|
+
|
24
|
+
its(:tmp_document_root) { should == tmp_document_root }
|
25
|
+
|
26
|
+
its(:manifest) { should == {} }
|
27
|
+
|
28
|
+
its(:version) { should be_nil }
|
29
|
+
|
30
|
+
|
31
|
+
def make_file(path, contents)
|
32
|
+
FileUtils.mkdir_p File.dirname(path)
|
33
|
+
File.open(path, 'w') {|f| f << contents}
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#add" do
|
37
|
+
before do
|
38
|
+
make_file('public/foo/bar/hello.txt', <<-CONTENT)
|
39
|
+
Hello World!
|
40
|
+
CONTENT
|
41
|
+
make_file('public/foo/baz/goodbye.txt', <<-CONTENT)
|
42
|
+
Goodbye Cruel World...
|
43
|
+
CONTENT
|
44
|
+
make_file('images/foo.png', <<-CONTENT
|
45
|
+
image
|
46
|
+
CONTENT
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should digest and copy the file' do
|
51
|
+
# echo 'Hello World!' | md5
|
52
|
+
# > 8ddd8be4b179a529afa5f2ffae4b9858
|
53
|
+
|
54
|
+
|
55
|
+
subject.add('public/foo/bar/hello.txt')
|
56
|
+
File.exist?('tmp/public/foo/bar/hello-8ddd8be4b179a529afa5f2ffae4b9858.txt').should be_true
|
57
|
+
subject.manifest.should == {
|
58
|
+
'/foo/bar/hello.txt' => '/foo/bar/hello-8ddd8be4b179a529afa5f2ffae4b9858.txt'
|
59
|
+
}
|
60
|
+
|
61
|
+
subject.add('public/foo/baz/goodbye.txt')
|
62
|
+
File.exist?('tmp/public/foo/baz/goodbye-8dcca69d946ae9576734c2c91dfddec4.txt').should be_true
|
63
|
+
subject.manifest.should == {
|
64
|
+
'/foo/bar/hello.txt' => '/foo/bar/hello-8ddd8be4b179a529afa5f2ffae4b9858.txt',
|
65
|
+
'/foo/baz/goodbye.txt' => '/foo/baz/goodbye-8dcca69d946ae9576734c2c91dfddec4.txt'
|
66
|
+
}
|
67
|
+
|
68
|
+
subject.add('images/foo.png', 'editor/images/foo.png')
|
69
|
+
File.exist?('tmp/public/editor/images/foo-4802fcebd761ca4f04c9a6320330fd10.png').should be_true
|
70
|
+
subject.manifest.should == {
|
71
|
+
'/foo/bar/hello.txt' => '/foo/bar/hello-8ddd8be4b179a529afa5f2ffae4b9858.txt',
|
72
|
+
'/foo/baz/goodbye.txt' => '/foo/baz/goodbye-8dcca69d946ae9576734c2c91dfddec4.txt',
|
73
|
+
'/editor/images/foo.png' => '/editor/images/foo-4802fcebd761ca4f04c9a6320330fd10.png'
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|