gluey 0.2.2
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.
- 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: []
|