wake-assets 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +126 -0
- data/lib/wake.rb +6 -0
- data/lib/wake/assets.rb +148 -0
- data/lib/wake/assets/renderer.rb +100 -0
- metadata +85 -0
data/README.md
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
# Wake::Assets
|
2
|
+
|
3
|
+
This module helps you render links to assets managed using
|
4
|
+
[wake](http://github.com/jcoglan/wake). It's easy to set up and works with any
|
5
|
+
Ruby web framework.
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
```
|
11
|
+
$ gem install wake-assets
|
12
|
+
```
|
13
|
+
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
These examples are based on the [wake example build
|
18
|
+
config](https://github.com/jcoglan/wake#usage).
|
19
|
+
|
20
|
+
#### At boot time
|
21
|
+
|
22
|
+
When your app boots, create an instance of `Wake::Assets` and keep this object
|
23
|
+
around through the lifetime of the app process. For example, in Rails:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'wake/assets'
|
27
|
+
|
28
|
+
dev = Rails.env.development?
|
29
|
+
|
30
|
+
$wake = Wake::Assets.new(
|
31
|
+
:wake => File.expand_path('node_modules/.bin/wake', Rails.root),
|
32
|
+
:root => File.expand_path('public', Rails.root),
|
33
|
+
:mode => dev ? :sources : :targets,
|
34
|
+
:monitor => dev
|
35
|
+
)
|
36
|
+
```
|
37
|
+
|
38
|
+
The options are:
|
39
|
+
|
40
|
+
* `:wake` - the path to your `wake` executable
|
41
|
+
* `:root` - the document root of your application
|
42
|
+
* `:mode` - `:sources` if you want to render links to source files, `:targets`
|
43
|
+
if you want optimised files
|
44
|
+
* `:monitor` - whether to monitor the filesystem for changes, recommended in
|
45
|
+
development but not in production
|
46
|
+
|
47
|
+
#### At request time
|
48
|
+
|
49
|
+
On each request, create a renderer from your `Assets` instance. In Rails, you
|
50
|
+
might do this with a helper:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
module AssetsHelper
|
54
|
+
CONFIG_PATH = File.expand_path('package.json', Rails.root)
|
55
|
+
ASSET_HOSTS = JSON.parse(File.read(CONFIG_PATH))['wake']['css']['hosts']
|
56
|
+
|
57
|
+
def assets
|
58
|
+
@assets ||= $wake.renderer(
|
59
|
+
:builds => {
|
60
|
+
'css' => request.ssl? ? 'ssl' : 'min',
|
61
|
+
'javascript' => 'min',
|
62
|
+
'binary' => 'min'
|
63
|
+
},
|
64
|
+
:hosts => ASSET_HOSTS[Rails.env][request.ssl? ? 'https' : 'http'],
|
65
|
+
:inline => false
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
The options are:
|
72
|
+
|
73
|
+
* `:builds` - which build to use for each asset type, the default for each is
|
74
|
+
`min`
|
75
|
+
* `:hosts` - the set of asset hosts to use for rendering links, the default is
|
76
|
+
an empty list
|
77
|
+
* `:inline` - whether to render assets inline so the browser does not make
|
78
|
+
additional requests for them, default is `false`
|
79
|
+
|
80
|
+
#### In your templates
|
81
|
+
|
82
|
+
With this helper in place, you can render links to JavaScript, CSS and images:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
assets.include_js 'scripts.js'
|
86
|
+
# => '<script type="text/javascript" src="/assets/scripts-bb210c6.js"></script>'
|
87
|
+
|
88
|
+
assets.include_css 'style.css'
|
89
|
+
# => '<link rel="stylesheet" type="text/css" href="/assets/styles-5a2ceb1.css">'
|
90
|
+
|
91
|
+
assets.include_image 'logo.png', :html => {:alt => 'Logo'}
|
92
|
+
# => '<img src="/assets/logo-2fa8d38.png" alt="Logo">'
|
93
|
+
```
|
94
|
+
|
95
|
+
You can pass the `:inline` option to any of these to override the per-request
|
96
|
+
`:inline` setting:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
assets.include_js 'scripts.js', :inline => true
|
100
|
+
# => '<script type="text/javascript">alert("Hello, world!")</script>'
|
101
|
+
```
|
102
|
+
|
103
|
+
|
104
|
+
## License
|
105
|
+
|
106
|
+
(The MIT License)
|
107
|
+
|
108
|
+
Copyright (c) 2013 James Coglan, Songkick
|
109
|
+
|
110
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
111
|
+
this software and associated documentation files (the 'Software'), to deal in
|
112
|
+
the Software without restriction, including without limitation the rights to
|
113
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
114
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
115
|
+
subject to the following conditions:
|
116
|
+
|
117
|
+
The above copyright notice and this permission notice shall be included in all
|
118
|
+
copies or substantial portions of the Software.
|
119
|
+
|
120
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
121
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
122
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
123
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
124
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
125
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
126
|
+
|
data/lib/wake.rb
ADDED
data/lib/wake/assets.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'erb'
|
3
|
+
require 'json'
|
4
|
+
require 'listen'
|
5
|
+
require 'mime/types'
|
6
|
+
require 'pathname'
|
7
|
+
require 'set'
|
8
|
+
|
9
|
+
module Wake
|
10
|
+
class Assets
|
11
|
+
|
12
|
+
DEFAULT_BUILD = 'min'
|
13
|
+
DEFAULT_MODE = :targets
|
14
|
+
DEFAULT_WAKE = './node_modules/wake/bin/wake'
|
15
|
+
CACHE_FILE = '.wake.json'
|
16
|
+
MANIFEST = '.manifest.json'
|
17
|
+
PACKAGE_FILE = 'package.json'
|
18
|
+
WAKE_FILE = 'wake.json'
|
19
|
+
CONFIG_FILES = Set.new([CACHE_FILE, MANIFEST, PACKAGE_FILE, WAKE_FILE])
|
20
|
+
|
21
|
+
CSS = 'css'
|
22
|
+
JS = 'javascript'
|
23
|
+
IMG = 'binary'
|
24
|
+
|
25
|
+
class InvalidReference < StandardError
|
26
|
+
end
|
27
|
+
|
28
|
+
autoload :Renderer, File.expand_path('../assets/renderer', __FILE__)
|
29
|
+
|
30
|
+
def initialize(options)
|
31
|
+
@pwd = File.expand_path(options.fetch(:pwd, Dir.pwd))
|
32
|
+
@wake = options.fetch(:wake, File.expand_path(DEFAULT_WAKE, @pwd))
|
33
|
+
@root = Pathname.new(File.expand_path(options.fetch(:root, @pwd)))
|
34
|
+
@mode = options.fetch(:mode, DEFAULT_MODE)
|
35
|
+
@manifest = new_manifest_cache
|
36
|
+
@paths = new_path_cache
|
37
|
+
|
38
|
+
system(@wake, '--cache')
|
39
|
+
read_config
|
40
|
+
|
41
|
+
return unless options[:monitor]
|
42
|
+
|
43
|
+
listener = Listen.to(@pwd).change do |modified, added, removed|
|
44
|
+
all = (modified + added + removed).map &File.method(:basename)
|
45
|
+
system(@wake, '--cache') if (added.any? or removed.any?) and not all.include?(CACHE_FILE)
|
46
|
+
update! if (CONFIG_FILES & all).any?
|
47
|
+
end
|
48
|
+
listener.force_polling(true)
|
49
|
+
listener.start
|
50
|
+
end
|
51
|
+
|
52
|
+
def deployment?
|
53
|
+
@mode == :targets
|
54
|
+
end
|
55
|
+
|
56
|
+
def paths_for(group, names, options = {})
|
57
|
+
build = options.fetch(:build, DEFAULT_BUILD)
|
58
|
+
unless @config[group].fetch('builds', {}).has_key?(build)
|
59
|
+
build = DEFAULT_BUILD
|
60
|
+
end
|
61
|
+
names.map { |name| @paths[group][name][build] }.flatten
|
62
|
+
end
|
63
|
+
|
64
|
+
def relative(path)
|
65
|
+
'/' + Pathname.new(path).relative_path_from(@root).to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def renderer(options = {})
|
69
|
+
Renderer.new(self, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def find_paths_for(group, name, build)
|
75
|
+
absolute_paths = begin
|
76
|
+
cache = @cache[group][name]
|
77
|
+
if @mode.to_s == 'sources'
|
78
|
+
cache.fetch('sources')
|
79
|
+
else
|
80
|
+
[cache.fetch('targets').fetch(build)]
|
81
|
+
end
|
82
|
+
rescue
|
83
|
+
nil
|
84
|
+
end
|
85
|
+
|
86
|
+
if absolute_paths.nil?
|
87
|
+
raise InvalidReference, "Could not find assets, group: #{group}, name: #{name}, build: #{build}"
|
88
|
+
end
|
89
|
+
|
90
|
+
absolute_paths.map do |path|
|
91
|
+
basename = File.basename(path)
|
92
|
+
dirname = File.dirname(path)
|
93
|
+
manifest = File.join(dirname, MANIFEST)
|
94
|
+
|
95
|
+
File.join(dirname, @manifest[manifest].fetch(basename, basename))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def new_manifest_cache
|
100
|
+
Hash.new do |hash, path|
|
101
|
+
hash[path] = File.file?(path) ? JSON.parse(File.read(path)) : {}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def new_path_cache
|
106
|
+
Hash.new do |h, group|
|
107
|
+
h[group] = Hash.new do |i, name|
|
108
|
+
i[name] = Hash.new do |j, build|
|
109
|
+
j[build] = find_paths_for(group, name, build)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def read_config
|
116
|
+
cache = File.join(@pwd, CACHE_FILE)
|
117
|
+
wake = File.join(@pwd, WAKE_FILE)
|
118
|
+
package = File.join(@pwd, PACKAGE_FILE)
|
119
|
+
|
120
|
+
@config = if File.file?(wake)
|
121
|
+
JSON.parse(File.read(wake))
|
122
|
+
elsif File.file?(package)
|
123
|
+
JSON.parse(File.read(package))['wake']
|
124
|
+
else
|
125
|
+
{}
|
126
|
+
end
|
127
|
+
|
128
|
+
@cache = JSON.parse(File.read(cache))
|
129
|
+
end
|
130
|
+
|
131
|
+
def update!
|
132
|
+
paths = new_path_cache
|
133
|
+
read_config
|
134
|
+
|
135
|
+
@paths.each do |group, a|
|
136
|
+
a.each do |name, b|
|
137
|
+
b.each do |build, files|
|
138
|
+
paths[group][name][build]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
@manifest = new_manifest_cache
|
143
|
+
@paths = paths
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Wake
|
2
|
+
class Assets
|
3
|
+
class Renderer
|
4
|
+
|
5
|
+
def initialize(assets, options)
|
6
|
+
@assets = assets
|
7
|
+
@builds = options.fetch(:builds, {})
|
8
|
+
@hosts = options.fetch(:hosts, [])
|
9
|
+
@inline = options[:inline]
|
10
|
+
end
|
11
|
+
|
12
|
+
def include_css(*names)
|
13
|
+
names, options = extract_options(names)
|
14
|
+
|
15
|
+
tags = if options.fetch(:inline, @inline)
|
16
|
+
paths_for(CSS, names, options).map do |path|
|
17
|
+
%Q{#{tag :style, options, :type => 'text/css'}#{File.read path}</style>}
|
18
|
+
end
|
19
|
+
else
|
20
|
+
urls_for(CSS, names, options).map do |url|
|
21
|
+
tag :link, options, :rel => 'stylesheet', :type => 'text/css', :href => url
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
html_safe(tags * '')
|
26
|
+
end
|
27
|
+
|
28
|
+
def include_image(*names)
|
29
|
+
names, options = extract_options(names)
|
30
|
+
|
31
|
+
tags = if options.fetch(:inline, @inline)
|
32
|
+
paths_for(IMG, names, options).map do |path|
|
33
|
+
base64 = Base64.strict_encode64(File.read(path))
|
34
|
+
mime = MIME::Types.type_for(path).first
|
35
|
+
tag :img, options, :src => "data:#{mime};base64,#{base64}"
|
36
|
+
end
|
37
|
+
else
|
38
|
+
urls_for(IMG, names, options).map { |url| tag :img, options, :src => url }
|
39
|
+
end
|
40
|
+
|
41
|
+
html_safe(tags * '')
|
42
|
+
end
|
43
|
+
|
44
|
+
def include_js(*names)
|
45
|
+
names, options = extract_options(names)
|
46
|
+
|
47
|
+
tags = if options.fetch(:inline, @inline)
|
48
|
+
paths_for(JS, names, options).map do |path|
|
49
|
+
%Q{#{tag :script, options, :type => 'text/javascript'}#{File.read path}</script>}
|
50
|
+
end
|
51
|
+
else
|
52
|
+
urls_for(JS, names, options).map do |url|
|
53
|
+
%Q{#{tag :script, options, :type => 'text/javascript', :src => url}</script>}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
html_safe(tags * '')
|
58
|
+
end
|
59
|
+
|
60
|
+
def paths_for(type, names, options = {})
|
61
|
+
@assets.paths_for(type, names, {:build => @builds[type]}.merge(options))
|
62
|
+
end
|
63
|
+
|
64
|
+
def urls_for(type, names, options = {})
|
65
|
+
paths = paths_for(type, names, options).map { |p| @assets.relative(p) }
|
66
|
+
|
67
|
+
return paths unless @hosts.any?
|
68
|
+
|
69
|
+
paths.map do |path|
|
70
|
+
@hosts[path.hash % @hosts.size].gsub(/\/*$/, '') + path
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def extract_options(names)
|
77
|
+
[names.grep(String), names.grep(Hash).first || {}]
|
78
|
+
end
|
79
|
+
|
80
|
+
def h(string)
|
81
|
+
ERB::Util.html_escape(string)
|
82
|
+
end
|
83
|
+
|
84
|
+
def html_safe(string)
|
85
|
+
if defined? ActiveSupport
|
86
|
+
ActiveSupport::SafeBuffer.new(string)
|
87
|
+
else
|
88
|
+
string
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def tag(name, options, attrs)
|
93
|
+
attrs = attrs.merge(options.fetch(:html, {}))
|
94
|
+
"<#{name} #{ attrs.map { |k,v| %Q{#{k}="#{h v.to_s}"} }.join(' ') }>"
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wake-assets
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- James Coglan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: listen
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mime-types
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description:
|
47
|
+
email: jcoglan@gmail.com
|
48
|
+
executables: []
|
49
|
+
extensions: []
|
50
|
+
extra_rdoc_files:
|
51
|
+
- README.md
|
52
|
+
files:
|
53
|
+
- README.md
|
54
|
+
- lib/wake.rb
|
55
|
+
- lib/wake/assets.rb
|
56
|
+
- lib/wake/assets/renderer.rb
|
57
|
+
homepage: http://github.com/jcoglan/wake-assets
|
58
|
+
licenses: []
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --main
|
62
|
+
- README.md
|
63
|
+
- --markup
|
64
|
+
- markdown
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ! '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 1.8.23
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Renders links to assets managed by wake
|
85
|
+
test_files: []
|