gluey 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +1 -0
- data/gluey.gemspec +22 -0
- data/lib/gluey/dependencies/directory.rb +29 -0
- data/lib/gluey/dependencies/handlebars_bundle.rb +62 -0
- data/lib/gluey/dependencies/single_file.rb +29 -0
- data/lib/gluey/dependencies/texts_bundle.rb +62 -0
- data/lib/gluey/exceptions/file_not_found.rb +4 -0
- data/lib/gluey/exceptions/item_not_listed.rb +4 -0
- data/lib/gluey/glues/base.rb +25 -0
- data/lib/gluey/glues/js_script/handlebars_addons.rb +24 -0
- data/lib/gluey/glues/js_script.rb +53 -0
- data/lib/gluey/glues/sass.rb +26 -0
- data/lib/gluey/glues/script.rb +84 -0
- data/lib/gluey/material.rb +74 -0
- data/lib/gluey/tools/local_build.rb +86 -0
- data/lib/gluey/tools/tools.rb +19 -0
- data/lib/gluey/version.rb +3 -0
- data/lib/gluey/warehouse.rb +50 -0
- data/lib/gluey/workshop.rb +64 -0
- data/lib/gluey.rb +2 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2fde00ea1efaad06ddc4780ae8cd89ba0608d2c3
|
4
|
+
data.tar.gz: d51d6124fbecf62a1bab250a7e8f62e0f9fda44d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8be79aa84654f1e3b1ce3f6e220cb8bd767eef34593503809c06d92ed01c7f024ce1ef2f9878cffc35bff50983557b562e8dcebd1f93822b808297a4a27c083b
|
7
|
+
data.tar.gz: 177013b823b9a1bd66aaa8773e5fc501df265ceef0550afb15547b3962288598a53a5feb1f0cd336e3419f38c5bd1c11045660c3e20e31655131b428c1f8bbba
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 doooby
|
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,37 @@
|
|
1
|
+
# Gluey
|
2
|
+
|
3
|
+
This is my custom alternative for processing assets in ruby web apps. Firt, there was just dislike in sprockets
|
4
|
+
(and their integration into rails), but then i realized it's not that big a deal to write my own caching and processing system.
|
5
|
+
This has unlimited abilities, because it's very easy to write custom directives (that headers in assets that you know from
|
6
|
+
sprockets). For example, you can precompile handlebars a nd insert them into page's js file as an object (it cannot be easier).
|
7
|
+
You can append, prepend, insert (...) other files, merge them into one asset, that you wanth to hit on
|
8
|
+
particular http path. There is no docs nor test for now, and probably never will be, but the code should not be hard
|
9
|
+
to read, so if you're interested, just digg into the code itself.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'gluey'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install gluey
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
look at my nesselsburg2 integration (a repository just next door)
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it ( https://github.com/[my-github-username]/gluey/fork )
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/gluey.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'gluey/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'gluey'
|
7
|
+
spec.version = Gluey::VERSION
|
8
|
+
spec.authors = ['doooby']
|
9
|
+
spec.email = ['zelazk.o@email.cz']
|
10
|
+
spec.summary = 'Rails\' asset pipelane replacement'
|
11
|
+
spec.description = %q{Concatenating and processing asset files with possibly complicated dependencies.}
|
12
|
+
# spec.homepage = ""
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
# spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
# spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
21
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'single_file'
|
2
|
+
|
3
|
+
module Gluey::Dependencies
|
4
|
+
class Directory < SingleFile
|
5
|
+
|
6
|
+
def initialize(dir, dir_pattern=nil)
|
7
|
+
@dir_pattern = "#{dir}/#{ dir_pattern || '**/*' }"
|
8
|
+
super dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def actualize
|
12
|
+
@files_list = files_list
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def changed?
|
17
|
+
@files_list != files_list
|
18
|
+
end
|
19
|
+
|
20
|
+
def exists?
|
21
|
+
Dir.exists? @file
|
22
|
+
end
|
23
|
+
|
24
|
+
def files_list
|
25
|
+
Dir[@dir_pattern]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'handlebars'
|
2
|
+
require_relative 'directory'
|
3
|
+
|
4
|
+
module Gluey::Dependencies
|
5
|
+
class Handlebars_bundle < SingleFile
|
6
|
+
|
7
|
+
def initialize(dir, logical_path, context)
|
8
|
+
tmp_dir = "#{context.tmp_path}/.texts_bundle"
|
9
|
+
Dir.mkdir tmp_dir unless Dir.exists? tmp_dir
|
10
|
+
@cache_path = "#{tmp_dir}/#{logical_path.gsub '/', '-'}"
|
11
|
+
Dir.mkdir @cache_path unless Dir.exists? @cache_path
|
12
|
+
|
13
|
+
@dir_dep = ::Gluey::Dependencies::Directory.new(dir, '**/*.hb')
|
14
|
+
@dependencies = []
|
15
|
+
super "#{@cache_path}.hb_bundle"
|
16
|
+
end
|
17
|
+
|
18
|
+
def changed?
|
19
|
+
@dependencies.any?{|d| d.changed?} || @dir_dep.changed? || (File.mtime(@file).to_i != @mtime rescue true)
|
20
|
+
end
|
21
|
+
|
22
|
+
def actualize
|
23
|
+
# remove deleted files
|
24
|
+
@dependencies.delete_if{|dep| !dep.exists? }
|
25
|
+
# add new files
|
26
|
+
new_files = (@dir_dep.files_list - @dependencies.map(&:file)).map do |f|
|
27
|
+
template = f[/#{@dir_dep.file}\/(.+)\.hb$/, 1]
|
28
|
+
::Gluey::Dependencies::SingleFile.new f, template: template,
|
29
|
+
hb_precompiled: "#{@cache_path}/#{template.gsub '/', '-'}"
|
30
|
+
end
|
31
|
+
@dependencies.concat new_files
|
32
|
+
@dir_dep.actualize
|
33
|
+
|
34
|
+
# re-precompile changed
|
35
|
+
handlebars_context = ::Handlebars::Context.new
|
36
|
+
@dependencies.each do |dep|
|
37
|
+
next if !dep.changed? && File.exists?(dep.data[:hb_precompiled])
|
38
|
+
dep.actualize
|
39
|
+
precompile_output = handlebars_context.precompile File.read(dep.file)
|
40
|
+
File.write dep.data[:hb_precompiled], precompile_output
|
41
|
+
end
|
42
|
+
|
43
|
+
write_bundle
|
44
|
+
@mtime = File.mtime(@file).to_i
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def write_bundle
|
51
|
+
File.open @file, 'w' do |f|
|
52
|
+
f.write '{'
|
53
|
+
@dependencies.each_with_index do |dep, i|
|
54
|
+
f.write "#{', ' if i!=0}\"#{dep.data[:template]}\": "
|
55
|
+
f.write File.read(dep.data[:hb_precompiled])
|
56
|
+
end
|
57
|
+
f.write '}'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Gluey::Dependencies
|
2
|
+
class SingleFile
|
3
|
+
|
4
|
+
attr_reader :file, :data
|
5
|
+
|
6
|
+
def initialize(file, **data)
|
7
|
+
@file = file
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
def actualize
|
12
|
+
@mtime = File.mtime(@file).to_i
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def changed?
|
17
|
+
File.mtime(@file).to_i != @mtime rescue true
|
18
|
+
end
|
19
|
+
|
20
|
+
def exists?
|
21
|
+
File.exists? @file
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
@file == other.file
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'directory'
|
2
|
+
|
3
|
+
module Gluey::Dependencies
|
4
|
+
class TextsBundle < SingleFile
|
5
|
+
JS_ESCAPE_MAP = {
|
6
|
+
'\\' => '\\\\',
|
7
|
+
"\r\n" => '\n',
|
8
|
+
"\n" => '\n',
|
9
|
+
"\r" => '\n',
|
10
|
+
'"' => '\\"',
|
11
|
+
"'" => "\\'"
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(dir, logical_path, context)
|
15
|
+
tmp_dir = "#{context.tmp_path}/.texts_bundle"
|
16
|
+
Dir.mkdir tmp_dir unless Dir.exists? tmp_dir
|
17
|
+
@cache_path = "#{tmp_dir}/#{logical_path.gsub '/', '-'}"
|
18
|
+
Dir.mkdir @cache_path unless Dir.exists? @cache_path
|
19
|
+
|
20
|
+
@dir_dep = ::Gluey::Dependencies::Directory.new(dir, '**/*')
|
21
|
+
@dependencies = []
|
22
|
+
super "#{@cache_path}.texts_bundle"
|
23
|
+
end
|
24
|
+
|
25
|
+
def changed?
|
26
|
+
@dependencies.any?{|d| d.changed?} || @dir_dep.changed? || (File.mtime(@file).to_i != @mtime rescue true)
|
27
|
+
end
|
28
|
+
|
29
|
+
def actualize
|
30
|
+
# remove deleted files
|
31
|
+
@dependencies.delete_if{|dep| !dep.exists? }
|
32
|
+
# add new files
|
33
|
+
new_files = (@dir_dep.files_list - @dependencies.map(&:file)).map do |f|
|
34
|
+
text_name = f[/#{@dir_dep.file}\/(.+)$/, 1]
|
35
|
+
::Gluey::Dependencies::SingleFile.new f, text_name: text_name
|
36
|
+
end
|
37
|
+
@dependencies.concat new_files
|
38
|
+
@dir_dep.actualize
|
39
|
+
@dependencies.each{|dep| dep.actualize if dep.changed? }
|
40
|
+
|
41
|
+
write_bundle
|
42
|
+
@mtime = File.mtime(@file).to_i
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def write_bundle
|
49
|
+
File.open @file, 'w' do |f|
|
50
|
+
f.write '{'
|
51
|
+
@dependencies.each_with_index do |dep, i|
|
52
|
+
f.write "#{', ' if i!=0}\"#{dep.data[:text_name]}\": "
|
53
|
+
text = File.read dep.file
|
54
|
+
text.gsub!(/(\\|\r\n|[\n\r"'])/u){|match| JS_ESCAPE_MAP[match] }
|
55
|
+
f.write "\"#{text}\""
|
56
|
+
end
|
57
|
+
f.write '}'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Gluey::Glues
|
2
|
+
class Base
|
3
|
+
|
4
|
+
def initialize(context, material)
|
5
|
+
@context = context
|
6
|
+
@material = material
|
7
|
+
end
|
8
|
+
|
9
|
+
def process(base_file, dependecies)
|
10
|
+
read_base_file base_file
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_base_file(file)
|
14
|
+
raw_content = File.read(file)
|
15
|
+
file[-4..-1]=='.erb' ? ERB.new(raw_content).result : raw_content
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.load(name, *addons_names)
|
21
|
+
require_relative name
|
22
|
+
addons_names.flatten.each{|an| require_relative "#{name}/#{an}_addons" }
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../../dependencies/handlebars_bundle'
|
2
|
+
|
3
|
+
module Gluey::Glues
|
4
|
+
class JsScript < Script
|
5
|
+
|
6
|
+
def pre_replace_with_handlebars(args)
|
7
|
+
dir = File.expand_path("../#{args[1]}", @base_file)
|
8
|
+
raise "cannot find dir containing handlebars templates for script=#{@base_file}" unless dir && Dir.exists?(dir)
|
9
|
+
|
10
|
+
logical_path = dir[/(?:^#{@context.root_path}\/)?(.+)$/, 1]
|
11
|
+
key = "dep:hb_bundle:#{logical_path}:#{@material.name}"
|
12
|
+
hb_dep = @context.cache[key]
|
13
|
+
unless hb_dep
|
14
|
+
hb_dep = ::Gluey::Dependencies::Handlebars_bundle.new dir, logical_path, @context
|
15
|
+
@context.cache[key] = hb_dep
|
16
|
+
end
|
17
|
+
|
18
|
+
hb_dep.actualize if hb_dep.changed?
|
19
|
+
@dependencies << hb_dep
|
20
|
+
@script.gsub! /"%#{args[0]}%"/, File.read(hb_dep.file)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'script'
|
2
|
+
require_relative '../dependencies/texts_bundle'
|
3
|
+
|
4
|
+
module Gluey::Glues
|
5
|
+
class JsScript < Script
|
6
|
+
|
7
|
+
def post_strict(_)
|
8
|
+
@output = "'use strict';\n#{@output}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def post_enclose(_)
|
12
|
+
@output = "(function(){\n#{@output}\n}());"
|
13
|
+
end
|
14
|
+
|
15
|
+
def post_strict_mode(_)
|
16
|
+
@output = "'use strict';\n#{@output}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def pre_replace(args)
|
20
|
+
file = find_nested_file(args[1])
|
21
|
+
cached_file, deps = get_nested_piece file
|
22
|
+
@dependencies.concat deps
|
23
|
+
@script.gsub! /"%#{args[0]}%"/, File.read(cached_file)
|
24
|
+
end
|
25
|
+
|
26
|
+
def pre_return(args)
|
27
|
+
@script = "#{@script}\nreturn #{args.first};"
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_return(_)
|
31
|
+
@output = "(function(){\n#{@output}\n}())"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def pre_replace_with_texts_bundle(args)
|
37
|
+
dir = File.expand_path("../#{args[1]}", @base_file)
|
38
|
+
raise "cannot find relative path #{args[1]} for script=#{@base_file}" unless dir && Dir.exists?(dir)
|
39
|
+
|
40
|
+
logical_path = dir[/(?:^#{@context.root_path}\/)?(.+)$/, 1]
|
41
|
+
key = "dep:txt_bundle:#{logical_path}:#{@material.name}"
|
42
|
+
hb_dep = @context.cache[key]
|
43
|
+
unless hb_dep
|
44
|
+
hb_dep = ::Gluey::Dependencies::TextsBundle.new dir, logical_path, @context
|
45
|
+
@context.cache[key] = hb_dep
|
46
|
+
end
|
47
|
+
|
48
|
+
hb_dep.actualize if hb_dep.changed?
|
49
|
+
@dependencies << hb_dep
|
50
|
+
@script.gsub! /"%#{args[0]}%"/, File.read(hb_dep.file)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'sass'
|
2
|
+
require_relative '../dependencies/single_file'
|
3
|
+
|
4
|
+
module Gluey::Glues
|
5
|
+
class Sass < Base
|
6
|
+
|
7
|
+
def process(base_file, deps)
|
8
|
+
opts = {
|
9
|
+
syntax: @material.file_extension.to_sym,
|
10
|
+
load_paths: [File.expand_path('..', base_file)],
|
11
|
+
cache_store: ::Sass::CacheStores::Filesystem.new("#{@context.tmp_path}/.sass"),
|
12
|
+
filename: base_file,
|
13
|
+
line_comments: true
|
14
|
+
}
|
15
|
+
engine = ::Sass::Engine.new(read_base_file(base_file), opts)
|
16
|
+
output = engine.render
|
17
|
+
|
18
|
+
engine.dependencies.each do |dependency|
|
19
|
+
deps << ::Gluey::Dependencies::SingleFile.new(dependency.options[:filename]).actualize
|
20
|
+
end
|
21
|
+
|
22
|
+
output
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative '../dependencies/single_file'
|
2
|
+
|
3
|
+
module Gluey::Glues
|
4
|
+
class Script < Base
|
5
|
+
|
6
|
+
PREFIXES = ['//'].map{|p| Regexp.escape(p)}
|
7
|
+
DIRECTIVES_REGEXP = Regexp.compile "\\A(?:\\s*#{PREFIXES.map{|p| "(?:#{p}=.*\\n?)+"}.join '|'})+"
|
8
|
+
|
9
|
+
def process(base_file, deps)
|
10
|
+
@script, @directives = strip_directives read_base_file(base_file)
|
11
|
+
return @script unless @directives
|
12
|
+
@dependencies = deps
|
13
|
+
@output = ''
|
14
|
+
@base_file = base_file
|
15
|
+
@marks = {append_self: ->{ @output += @script }}
|
16
|
+
@directives.each{|args| directive args, :pre }
|
17
|
+
@marks[:append_self].call if @marks[:append_self]
|
18
|
+
@directives.each{|args| directive args, :post }
|
19
|
+
@output
|
20
|
+
end
|
21
|
+
|
22
|
+
def pre_prepend(args)
|
23
|
+
file = find_nested_file(args.first)
|
24
|
+
cached_file, deps = get_nested_piece file
|
25
|
+
@dependencies.concat deps
|
26
|
+
@script = "#{File.read cached_file}#{@script}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def pre_append(args)
|
30
|
+
file = find_nested_file(args.first)
|
31
|
+
cached_file, deps = get_nested_piece file
|
32
|
+
@dependencies.concat deps
|
33
|
+
@script = "#{@script}#{File.read cached_file}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def pre_depend_on(args)
|
37
|
+
file = find_nested_file(args[1])
|
38
|
+
@dependencies << ::Gluey::Dependencies::SingleFile.new(file).actualize
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def strip_directives(data)
|
44
|
+
script = data
|
45
|
+
directives = data[DIRECTIVES_REGEXP]
|
46
|
+
if directives
|
47
|
+
script = $'
|
48
|
+
directives = directives.split("\n").reject{|dir| dir.empty?}.map do |dir|
|
49
|
+
dir.strip[/(?:#{PREFIXES.join '|'}=)\s*(.+)/, 1].split (' ')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
return script, directives
|
53
|
+
end
|
54
|
+
|
55
|
+
def directive(dir_array, run)
|
56
|
+
method = "#{run}_#{dir_array.first}"
|
57
|
+
send method, dir_array[1..-1] if respond_to? method
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_nested_file(rel_path)
|
61
|
+
file = File.expand_path("../#{rel_path}", @base_file)
|
62
|
+
File.exists?(file) || raise("cannot find '#{rel_path}' from script=#{@base_file}")
|
63
|
+
file
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_nested_piece(file)
|
67
|
+
path = file[/(?:^#{@context.root_path}\/)?(.+)$/, 1]
|
68
|
+
key = "script_piece:#{@material.name}:#{path}"
|
69
|
+
cache_file, dependencies = @context.cache[key]
|
70
|
+
return cache_file, dependencies if cache_file && File.exists?(cache_file) && !dependencies.any?{|dep| dep.changed?}
|
71
|
+
|
72
|
+
glue = self.class.new @context, @material
|
73
|
+
cache_dir = "#{@context.tmp_path}/.script"
|
74
|
+
Dir.mkdir cache_dir unless Dir.exists? cache_dir
|
75
|
+
cache_file = "#{cache_dir}/#{path}.#{@material.name}"
|
76
|
+
dependencies = [::Gluey::Dependencies::SingleFile.new(file).actualize]
|
77
|
+
FileUtils.mkdir_p cache_file[0..(cache_file.rindex('/')-1)]
|
78
|
+
File.write cache_file, glue.process(file, dependencies)
|
79
|
+
@context.cache[key] = [cache_file, dependencies]
|
80
|
+
return cache_file, dependencies
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative 'exceptions/file_not_found'
|
2
|
+
|
3
|
+
class Gluey::Material
|
4
|
+
|
5
|
+
attr_reader :name, :glue
|
6
|
+
attr_accessor :asset, :file_extension, :public_dir
|
7
|
+
|
8
|
+
def initialize(name, glue, context)
|
9
|
+
@name = name.to_sym
|
10
|
+
@glue = glue
|
11
|
+
@context = context
|
12
|
+
@asset = name.to_s
|
13
|
+
@paths = []
|
14
|
+
@items = []
|
15
|
+
yield self if block_given?
|
16
|
+
@file_extension ||= @asset.dup
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_path(path)
|
20
|
+
@paths << path
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_item(declaration)
|
24
|
+
@items << declaration
|
25
|
+
end
|
26
|
+
|
27
|
+
def is_listed?(path, file=nil)
|
28
|
+
@items.any? do |items_declaration|
|
29
|
+
case items_declaration
|
30
|
+
when String
|
31
|
+
path == items_declaration
|
32
|
+
when Regexp
|
33
|
+
path =~ items_declaration
|
34
|
+
when Proc
|
35
|
+
items_declaration[path, file]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"Material #{@name}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_base_file(path)
|
45
|
+
paths.each do |base_path|
|
46
|
+
p = "#{base_path}/#{path}.#{@file_extension}"; return p if File.exists? p
|
47
|
+
p = "#{p}.erb"; return p if File.exists? p
|
48
|
+
p = "#{base_path}/#{path}/index.#{@file_extension}"; return p if File.exists? p
|
49
|
+
p = "#{p}.erb"; return p if File.exists? p
|
50
|
+
end
|
51
|
+
raise(::Gluey::FileNotFound.new "#{to_s} cannot find base file for #{path}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def list_all_items
|
55
|
+
list = []
|
56
|
+
paths.map do |base_path|
|
57
|
+
glob_path = "#{base_path}/**/*.#{@file_extension}"
|
58
|
+
files = Dir[glob_path] + Dir["#{glob_path}.erb"]
|
59
|
+
files.select do |file|
|
60
|
+
path = file[/^#{base_path}\/(.+)\.#{@file_extension}(?:\.erb)?$/, 1]
|
61
|
+
path.gsub! /\/index$/, ''
|
62
|
+
list << path if is_listed? path, file
|
63
|
+
end
|
64
|
+
end
|
65
|
+
list.uniq
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def paths
|
71
|
+
@paths.map{|p| "#{@context.root_path}/#{p}"}
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'tools'
|
2
|
+
|
3
|
+
module Gluey::Tools
|
4
|
+
|
5
|
+
def self.build_into_public_dir(workshop, warehouse, public_dir='public/assets', **builders)
|
6
|
+
public_dirs = workshop.materials.values.inject({}) do |h, m|
|
7
|
+
h[m.name] = "#{workshop.root_path}/#{m.public_dir || public_dir}"
|
8
|
+
h
|
9
|
+
end
|
10
|
+
|
11
|
+
built = []
|
12
|
+
move_it = -> (cache_file, file) { FileUtils.mv cache_file, file }
|
13
|
+
self.each_asset_file workshop, warehouse do |cache_file, type, path|
|
14
|
+
file = "#{public_dirs[type]}/#{path}"
|
15
|
+
next if File.exists? file
|
16
|
+
FileUtils.mkdir_p file[0..(file.rindex('/')-1)]
|
17
|
+
(builders[type] || move_it)[cache_file, file]
|
18
|
+
built << file
|
19
|
+
puts "created #{file}"
|
20
|
+
end
|
21
|
+
return built
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clear_public_dir(workshop, warehouse, versions=2, public_dir='public/assets')
|
25
|
+
public_dirs = workshop.materials.values.inject({}) do |h, m|
|
26
|
+
h[m.name] = "#{workshop.root_path}/#{m.public_dir || public_dir}"
|
27
|
+
h
|
28
|
+
end
|
29
|
+
|
30
|
+
# process existing files into assets
|
31
|
+
eas_regexp = /^#{workshop.root_path}\/(.+)$/
|
32
|
+
assets = public_dirs.values.uniq.map{|dir| Dir["#{dir}/**/*.*.*"]}.flatten.
|
33
|
+
map{|f| Asset.try_create f[eas_regexp, 1]}.compact
|
34
|
+
assets = assets.inject({}){|h, asset| (h[asset.path] ||= []) << asset; h }
|
35
|
+
|
36
|
+
# items not on list
|
37
|
+
on_list = []
|
38
|
+
warehouse.assets.each do |type, mater_assets|
|
39
|
+
mater_assets.each do |_, real_path|
|
40
|
+
file = "#{public_dirs[type]}/#{real_path}"
|
41
|
+
asset = Asset.try_create file[eas_regexp, 1]
|
42
|
+
on_list << asset
|
43
|
+
end
|
44
|
+
end
|
45
|
+
on_list.map! &:path
|
46
|
+
assets.delete_if do |path, asseets_arr|
|
47
|
+
unless on_list.include? path
|
48
|
+
asseets_arr.each do |some_asset|
|
49
|
+
file = "#{workshop.root_path}/#{some_asset.orig_path}"
|
50
|
+
File.delete file
|
51
|
+
puts "deleted unknown #{file}"
|
52
|
+
end
|
53
|
+
true
|
54
|
+
end
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
# older versions
|
59
|
+
assets.values.select{|arr| arr.length > versions}.
|
60
|
+
map{|arr| arr.sort.slice 0..(-versions-1) }.compact.flatten.each do |old_asset|
|
61
|
+
file = "#{workshop.root_path}/#{old_asset.orig_path}"
|
62
|
+
File.delete file
|
63
|
+
puts "deleted old #{file}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Asset < Struct.new(:path, :time_stamp)
|
68
|
+
|
69
|
+
def self.try_create(file)
|
70
|
+
file.match /^([^\.]+)\.(\d+)\.(\w+)$/ do |m|
|
71
|
+
new "#{m[1]}.#{m[3]}", m[2]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def <=>(other)
|
76
|
+
time_stamp <=> other.time_stamp
|
77
|
+
end
|
78
|
+
|
79
|
+
def orig_path
|
80
|
+
ret = path.dup
|
81
|
+
ret.insert ret.rindex('.'), ".#{time_stamp}"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'gluey/warehouse'
|
2
|
+
|
3
|
+
module Gluey::Tools
|
4
|
+
|
5
|
+
def self.each_asset_file(workshop, warehouse)
|
6
|
+
warehouse.assets.each do |type, assets|
|
7
|
+
assets.each do |path, real_path|
|
8
|
+
cache_file = workshop.fetch_file type, path
|
9
|
+
yield cache_file, type, real_path
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.create_uglifier_builder(**opts)
|
15
|
+
require 'uglifier'
|
16
|
+
->(a, b){File.write b, ::Uglifier.new(opts.merge! copyright: :none).compile(File.read a)}
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative '../gluey'
|
2
|
+
require_relative 'exceptions/item_not_listed'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class Gluey::Warehouse
|
6
|
+
|
7
|
+
attr_reader :assets
|
8
|
+
|
9
|
+
def initialize(root, listing_file='assets/gluey_listing.json')
|
10
|
+
@listing_file = "#{root}/#{listing_file}"
|
11
|
+
read_listing
|
12
|
+
end
|
13
|
+
|
14
|
+
def real_path(asset_type, path)
|
15
|
+
listing = @assets[asset_type]
|
16
|
+
unless listing
|
17
|
+
raise ::Gluey::ItemNotListed.new("Asset type #{asset_type} is not defined! (listing file problem?)")
|
18
|
+
end
|
19
|
+
|
20
|
+
real_path = listing[path]
|
21
|
+
unless real_path
|
22
|
+
raise ::Gluey::ItemNotListed.new("Unknown asset: #{path}, type=#{asset_type}! (listing file problem?)")
|
23
|
+
end
|
24
|
+
|
25
|
+
real_path
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_listing
|
29
|
+
assets = if File.exists? @listing_file
|
30
|
+
JSON.parse File.read(@listing_file) rescue {}
|
31
|
+
else
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
raise "corrupted listing file at #{@listing_file}" unless assets.is_a? Hash
|
35
|
+
@assets = assets.keys.inject({}){|h, asset_type| h[asset_type.to_sym] = assets[asset_type]; h }
|
36
|
+
end
|
37
|
+
|
38
|
+
def write_listing(workshop)
|
39
|
+
@assets = workshop.materials.values.inject({}) do |listing, material|
|
40
|
+
list = material.list_all_items.inject({}) do |h, path|
|
41
|
+
h[path] = workshop.real_path material.name, path
|
42
|
+
h
|
43
|
+
end
|
44
|
+
listing[material.name.to_sym] = list
|
45
|
+
listing
|
46
|
+
end
|
47
|
+
File.write @listing_file, JSON.pretty_generate(@assets)
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../gluey'
|
2
|
+
|
3
|
+
require_relative 'exceptions/item_not_listed'
|
4
|
+
|
5
|
+
require_relative 'material'
|
6
|
+
require_relative 'glues/base'
|
7
|
+
|
8
|
+
class Gluey::Workshop
|
9
|
+
|
10
|
+
attr_reader :root_path, :tmp_path, :materials, :cache
|
11
|
+
|
12
|
+
def initialize(root, tmp_dir='tmp/gluey')
|
13
|
+
@root_path = root
|
14
|
+
@tmp_path = "#{root}/#{tmp_dir}"
|
15
|
+
Dir.mkdir @tmp_path unless Dir.exists? @tmp_path
|
16
|
+
@materials = {}
|
17
|
+
@cache = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def register_material(name, glue=::Gluey::Glues::Base, &block)
|
21
|
+
name = name.to_sym
|
22
|
+
material = ::Gluey::Material.new name, glue, self, &block
|
23
|
+
@materials[name] = material
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_file(material, path)
|
27
|
+
# check cache
|
28
|
+
cache_key = "lump:#{material}:#{path}"
|
29
|
+
file, dependencies = @cache[cache_key]
|
30
|
+
if file && File.exists?(file) && !dependencies.any?{|d| d.changed?}
|
31
|
+
return file
|
32
|
+
end
|
33
|
+
|
34
|
+
# prepare for processing
|
35
|
+
material = @materials[material.to_sym]
|
36
|
+
base_file = material.find_base_file path
|
37
|
+
file = "#{@tmp_path}/#{path}.#{material.asset}"
|
38
|
+
dependencies = [::Gluey::Dependencies::SingleFile.new(base_file).actualize]
|
39
|
+
# process
|
40
|
+
glue = material.glue.new self, material
|
41
|
+
FileUtils.mkdir_p file[0..(file.rindex('/')-1)]
|
42
|
+
File.write file, glue.process(base_file, dependencies)
|
43
|
+
|
44
|
+
# save and return
|
45
|
+
@cache[cache_key] = [file, dependencies]
|
46
|
+
file
|
47
|
+
end
|
48
|
+
|
49
|
+
def real_path(material, path)
|
50
|
+
material = @materials[material.to_sym]
|
51
|
+
file = material.find_base_file path
|
52
|
+
unless material.is_listed? path, file
|
53
|
+
raise ::Gluey::ItemNotListed.new("#{material.to_s} doesn't have enlisted item #{path} (file=#{file}).")
|
54
|
+
end
|
55
|
+
"#{path}.#{File.mtime(file).to_i}.#{material.asset}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def try_real_path(path)
|
59
|
+
path.match /^(.+)\.\d+\.(\w+)$/ do |m|
|
60
|
+
yield m[1], m[2]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/lib/gluey.rb
ADDED
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gluey
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- doooby
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Concatenating and processing asset files with possibly complicated dependencies.
|
42
|
+
email:
|
43
|
+
- zelazk.o@email.cz
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- gluey.gemspec
|
54
|
+
- lib/gluey.rb
|
55
|
+
- lib/gluey/dependencies/directory.rb
|
56
|
+
- lib/gluey/dependencies/handlebars_bundle.rb
|
57
|
+
- lib/gluey/dependencies/single_file.rb
|
58
|
+
- lib/gluey/dependencies/texts_bundle.rb
|
59
|
+
- lib/gluey/exceptions/file_not_found.rb
|
60
|
+
- lib/gluey/exceptions/item_not_listed.rb
|
61
|
+
- lib/gluey/glues/base.rb
|
62
|
+
- lib/gluey/glues/js_script.rb
|
63
|
+
- lib/gluey/glues/js_script/handlebars_addons.rb
|
64
|
+
- lib/gluey/glues/sass.rb
|
65
|
+
- lib/gluey/glues/script.rb
|
66
|
+
- lib/gluey/material.rb
|
67
|
+
- lib/gluey/tools/local_build.rb
|
68
|
+
- lib/gluey/tools/tools.rb
|
69
|
+
- lib/gluey/version.rb
|
70
|
+
- lib/gluey/warehouse.rb
|
71
|
+
- lib/gluey/workshop.rb
|
72
|
+
homepage:
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.2.2
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: Rails' asset pipelane replacement
|
96
|
+
test_files: []
|