widow 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/widow +11 -5
- data/lib/widow.rb +209 -158
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20d7246686fc97b87af58bfc06e60321bd923037
|
4
|
+
data.tar.gz: 62ff7b3c5921f3696faeb9e35a34a6c5fa2ef171
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a99ca1e7844279c441093f964b4c7f4b11a86359fe5d58828b7fcd3d5e62f68a449dd2f0d255d0ea9c3a6477cd68fcc8fb35283689436e6dc6127fb9627be136
|
7
|
+
data.tar.gz: 19fd974e4a248a8746d6354ae0f50043afb9710dd73e07d3013b1e9c325dd0e5b642c45c508dbe1d9c4830d8170e62d5b286b9352e2712208f33c49d6369651a
|
data/bin/widow
CHANGED
@@ -3,21 +3,27 @@ require 'thor'
|
|
3
3
|
require 'widow'
|
4
4
|
|
5
5
|
class Spiderling < Thor
|
6
|
+
class_option :debug, type: :boolean, desc: "enables debug logging"
|
7
|
+
|
6
8
|
desc "cut", "Compile files from current directory into current directory. Copies files that cannot be processed by tilt"
|
7
9
|
method_option :from, type: :string, desc: "Defaults to current directory"
|
8
10
|
method_option :to, type: :string, desc: "Defaults to current directory"
|
9
|
-
def cut
|
10
|
-
Widow.
|
11
|
+
def cut
|
12
|
+
Widow.perform :cut, symbolise(options)
|
11
13
|
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
15
|
desc "spin", "Start a webserver on localhost and serve files from the destination forlder, compiling them as necessary"
|
16
16
|
method_option :from, type: :string, desc: "Defaults to current directory"
|
17
17
|
method_option :to, type: :string, desc: "Defaults to current directory"
|
18
18
|
method_option :port, type: :numeric, desc: "Defaults to 8888"
|
19
|
+
method_option :serve_source, type: :boolean, desc: "Serves source files if they are asked for directly, even if they are ignored, off by default"
|
19
20
|
def spin
|
20
|
-
Widow.
|
21
|
+
Widow.perform :spin, symbolise(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def symbolise options
|
26
|
+
return options.map { |k, v| [k.to_sym, v] }.to_h
|
21
27
|
end
|
22
28
|
end
|
23
29
|
|
data/lib/widow.rb
CHANGED
@@ -1,160 +1,211 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'tilt'
|
3
|
-
require 'find'
|
4
|
-
require 'pathname'
|
5
|
-
require 'fileutils'
|
6
|
-
require 'rack'
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
require 'optparse'
|
2
|
+
require 'tilt'
|
3
|
+
require 'find'
|
4
|
+
require 'pathname'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'rack'
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
class Widow
|
10
|
+
@@mime_map = [
|
11
|
+
['js', 'application/javascript'],
|
12
|
+
['css', 'text/css'],
|
13
|
+
['html', 'text/html']
|
14
|
+
]
|
15
|
+
|
16
|
+
class CompileError < Exception
|
17
|
+
def initialize tilt, exception
|
18
|
+
super "#{tilt.file} with template #{tilt} failed to compile:\n#{exception.message}\n\t#{exception.backtrace.join "\n\t"}"
|
19
|
+
end
|
13
20
|
end
|
14
|
-
|
21
|
+
|
22
|
+
def self.perform method, options
|
23
|
+
widow = Widow.new(options)
|
24
|
+
widow.public_send(method, options)
|
25
|
+
widow.kill
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize options = {}
|
29
|
+
log_level = options.delete(:debug) ? :debug : :info
|
30
|
+
@logger = Logger.new(STDOUT, level: log_level)
|
31
|
+
@logger.info "log level #{log_level}"
|
32
|
+
end
|
33
|
+
|
15
34
|
def cut options = {}
|
16
|
-
options = {from: Pathname.pwd, to: Pathname.pwd}.merge options
|
17
|
-
from = Pathname.new(options[:from])
|
18
|
-
to = Pathname.new(options[:to])
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
if
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
35
|
+
options = {from: Pathname.pwd, to: Pathname.pwd}.merge options
|
36
|
+
from = Pathname.new(options[:from])
|
37
|
+
to = Pathname.new(options[:to])
|
38
|
+
@logger.info "cut from #{from} to #{to}"
|
39
|
+
exclude = Exclude.new from, @logger
|
40
|
+
|
41
|
+
Find.find from do |path|
|
42
|
+
path = Pathname.new path
|
43
|
+
next if path == from
|
44
|
+
if exclude.ignored? path
|
45
|
+
@logger.info "#{path} ignored"
|
46
|
+
next
|
47
|
+
end
|
48
|
+
begin
|
49
|
+
make from, path.relative_path_from(from), to
|
50
|
+
rescue CompileError
|
51
|
+
end
|
52
|
+
end
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
def spin options = {}
|
57
|
+
options = {from: Pathname.pwd, to: Pathname.pwd, port: 8888, serve_source: false}.merge options
|
58
|
+
from = Pathname.new(options[:from])
|
59
|
+
to = Pathname.new(options[:to])
|
60
|
+
@logger.info "spin from #{from} to #{to}"
|
61
|
+
@logger.info "port #{options[:port]}"
|
62
|
+
@logger.info "serve_source = #{options[:serve_source]}"
|
63
|
+
exclude = Exclude.new from, @logger
|
64
|
+
|
65
|
+
mime_to_ext = Hash.new { |hash, key| hash[key] = [] }
|
66
|
+
Tilt.lazy_map.map do |ext, _|
|
67
|
+
begin
|
68
|
+
tilt = Tilt[ext]
|
69
|
+
@logger.info "loaded template #{tilt} for extention #{ext}"
|
70
|
+
next [tilt.metadata[:mime_type] || tilt.default_mime_type, ext]
|
71
|
+
rescue LoadError
|
72
|
+
next
|
73
|
+
end
|
74
|
+
end.compact.each do | mime_type, ext |
|
75
|
+
mime_to_ext[mime_type] << ext
|
76
|
+
end
|
77
|
+
|
78
|
+
Rack::Handler.default.run(
|
79
|
+
Proc.new do |env|
|
80
|
+
@logger.debug "#{env["PATH_INFO"]} requested"
|
81
|
+
|
82
|
+
file = Pathname.new env["PATH_INFO"][1..-1]
|
83
|
+
file += 'index.html' if (from + file).directory?
|
84
|
+
search = from + file.dirname + "#{file.basename file.extname}.{#{(mime_to_ext[mime_type file] + [file.extname[1..-1]]).join ?,}}"
|
85
|
+
@logger.debug "searching for \'#{search}\'"
|
86
|
+
original = Pathname.glob(search).first
|
87
|
+
file = to + file
|
88
|
+
|
89
|
+
if (exclude.ignored?(original) && !(options[:serve_source] && original.basename == file.basename))
|
90
|
+
@logger.info "#{file} ignored"
|
91
|
+
next file_not_found
|
92
|
+
elsif (original.nil? || !original.exist?)
|
93
|
+
@logger.debug "source not found"
|
94
|
+
next file_not_found
|
95
|
+
end
|
96
|
+
@logger.debug "#{original} found"
|
97
|
+
|
98
|
+
begin
|
99
|
+
if !file.exist? || file.mtime < original.mtime
|
100
|
+
if original.basename == file.basename
|
101
|
+
proxy from, original.relative_path_from(from), to
|
102
|
+
else
|
103
|
+
make from, original.relative_path_from(from), to
|
104
|
+
end
|
105
|
+
end
|
106
|
+
next [200, {'Content-Type' => mime_type(file)}, [file.file? ? file.read : '']]
|
107
|
+
rescue CompileError => e
|
108
|
+
next [502, {'Content-Type' => 'text/html'}, [e.message]]
|
109
|
+
end
|
110
|
+
end,
|
111
|
+
Port: options[:port]
|
112
|
+
)
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
# Called to clean up
|
117
|
+
def kill
|
118
|
+
@logger.close
|
119
|
+
return
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
class Exclude
|
124
|
+
def initialize root_dir, logger
|
125
|
+
@patterns = Pathname.glob("#{root_dir}/**/.widowignore").map do |file|
|
126
|
+
next [file, File.readlines(file)]
|
127
|
+
end.to_h.map do |file, lines|
|
128
|
+
lines.map do |line|
|
129
|
+
/^#{file.dirname}\/#{line.sub /\s*$/, ''}/ unless line =~ /^\s*$/
|
130
|
+
end.compact
|
131
|
+
end.flatten(1) << /.*\/\.widowignore$/
|
132
|
+
logger.info "ignoring:\n#{to_s}"
|
133
|
+
end
|
134
|
+
|
135
|
+
def ignored? file
|
136
|
+
return @patterns.any? do |pattern|
|
137
|
+
file.to_s =~ pattern
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def ignored_patterns
|
142
|
+
return @patterns.map &:to_s
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_s
|
146
|
+
return ignored_patterns.join "\n"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# proxies or renders a file based on it's properties and tilt capacity
|
151
|
+
# throws a CompileError when the file fails to compile
|
152
|
+
def make root, file, to
|
153
|
+
unless Tilt.registered? file.extname[1..-1].to_s
|
154
|
+
proxy root, file, to
|
155
|
+
return
|
156
|
+
end
|
157
|
+
|
158
|
+
begin
|
159
|
+
render root, file, to
|
160
|
+
rescue LoadError
|
161
|
+
@logger.warn "#{file} has a Tilt template that cannot be loaded"
|
162
|
+
proxy root, file, to
|
163
|
+
rescue CompileError => e
|
164
|
+
@logger.error e.to_s
|
165
|
+
raise e
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# creates a carbon copy of a file elsewhere
|
170
|
+
def proxy root, file, to
|
171
|
+
src = root + file
|
172
|
+
return if src.directory?
|
173
|
+
|
174
|
+
obj = to + file
|
175
|
+
obj.dirname.mkpath unless obj.dirname.exist?
|
176
|
+
|
177
|
+
FileUtils.cp src, obj
|
178
|
+
@logger.info "\t#{src} => #{obj}"
|
179
|
+
end
|
180
|
+
|
181
|
+
# Takes a file and tries to render it through tilt to the destination directory
|
182
|
+
# Throws load error when tilt template is not resolvable
|
183
|
+
# Throws a BuildError when on an internal error
|
184
|
+
def render root, file, to
|
185
|
+
tilt = Tilt.new root + file
|
186
|
+
|
187
|
+
begin
|
188
|
+
output = tilt.render
|
189
|
+
rescue Exception => e
|
190
|
+
raise CompileError.new tilt, e
|
191
|
+
end
|
192
|
+
|
193
|
+
dest = to + "#{file.basename file.extname}.#{default_ext tilt.metadata[:mime_type]}"
|
194
|
+
dest.dirname.mkpath unless dest.dirname.exist?
|
195
|
+
File.write(dest, output)
|
196
|
+
@logger.info "\t#{tilt.file} => #{dest}"
|
197
|
+
end
|
198
|
+
|
199
|
+
def default_ext mime_type
|
200
|
+
return (@@mime_map.find{ |e, t| t == mime_type } || ['html', nil])[0]
|
201
|
+
end
|
202
|
+
|
203
|
+
def mime_type filename
|
204
|
+
name = Pathname.new(filename).extname[1..-1]
|
205
|
+
return (@@mime_map.find{ |e, t| e == name } || [nil, 'text/plain'])[1]
|
206
|
+
end
|
207
|
+
|
208
|
+
def file_not_found
|
209
|
+
return [404, {'Content-Type' => 'text/html'}, ["File not found!"]]
|
210
|
+
end
|
211
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: widow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Victorovich Matveev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -96,7 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
96
|
version: '0'
|
97
97
|
requirements: []
|
98
98
|
rubyforge_project:
|
99
|
-
rubygems_version: 2.6.
|
99
|
+
rubygems_version: 2.6.8
|
100
100
|
signing_key:
|
101
101
|
specification_version: 4
|
102
102
|
summary: Spins web superlanguages as you write!
|