adva-static 0.0.3 → 0.0.4
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.
- data/lib/adva-static.rb +1 -0
- data/lib/adva/static.rb +13 -0
- data/lib/adva/static/export.rb +104 -0
- data/lib/adva/static/export/page.rb +45 -0
- data/lib/adva/static/export/path.rb +49 -0
- data/lib/adva/static/export/queue.rb +27 -0
- data/lib/adva/static/export/store.rb +30 -0
- data/lib/adva/static/export/templates/config.ru +14 -0
- data/lib/adva/static/import.rb +42 -0
- data/lib/adva/static/import/format.rb +58 -0
- data/lib/adva/static/import/model.rb +21 -0
- data/lib/adva/static/import/model/base.rb +78 -0
- data/lib/adva/static/import/model/blog.rb +33 -0
- data/lib/adva/static/import/model/page.rb +28 -0
- data/lib/adva/static/import/model/post.rb +78 -0
- data/lib/adva/static/import/model/section.rb +51 -0
- data/lib/adva/static/import/model/site.rb +59 -0
- data/lib/adva/static/import/request.rb +92 -0
- data/lib/adva/static/import/source.rb +82 -0
- data/lib/adva/static/rack.rb +15 -0
- data/lib/adva/static/rack/export.rb +59 -0
- data/lib/adva/static/rack/request.rb +39 -0
- data/lib/adva/static/rack/static.rb +40 -0
- data/lib/adva/static/rack/watch.rb +88 -0
- data/lib/adva/static/setup.rb +68 -0
- data/lib/adva/static/watch.rb +7 -0
- data/lib/adva/static/watch/handler.rb +57 -0
- data/lib/adva/tasks/static.rb +73 -0
- data/lib/adva_static/version.rb +3 -0
- data/lib/testing/step_definitions.rb +85 -0
- data/lib/testing/test_helper.rb +133 -0
- metadata +35 -5
- data/lib/bundler/repository.rb +0 -118
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Adva
|
4
|
+
class Static
|
5
|
+
module Rack
|
6
|
+
class Export
|
7
|
+
include Request
|
8
|
+
|
9
|
+
attr_reader :app, :target, :store
|
10
|
+
|
11
|
+
def initialize(app, options = {})
|
12
|
+
@app = app
|
13
|
+
@target = Pathname.new(options[:target] || File.expand_path('./export'))
|
14
|
+
@store = Adva::Static::Export::Store.new(target)
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
path = env['PATH_INFO'].dup # gets modified by routing_filter
|
19
|
+
app.call(env).tap do |status, headers, response|
|
20
|
+
export(path, response) if export?(env, status)
|
21
|
+
if headers.key?(PURGE_HEADER)
|
22
|
+
paths = normalize_paths(headers[PURGE_HEADER])
|
23
|
+
paths.each do |path|
|
24
|
+
purge(path)
|
25
|
+
request(path)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def export?(env, status)
|
34
|
+
env[STORE_HEADER].present? and status == 200
|
35
|
+
end
|
36
|
+
|
37
|
+
def export(path, response)
|
38
|
+
page = Adva::Static::Export::Page.new(path, response)
|
39
|
+
Adva.out.puts " storing #{page.url.filename}"
|
40
|
+
store.write(page.url, page.body)
|
41
|
+
end
|
42
|
+
|
43
|
+
def purge(path)
|
44
|
+
Adva.out.puts " purging #{path}"
|
45
|
+
store.purge(Adva::Static::Export::Path.new(path))
|
46
|
+
end
|
47
|
+
|
48
|
+
def request(path)
|
49
|
+
super('GET', Adva::Static::Export::Path.new(path), STORE_HEADER => true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def normalize_paths(paths)
|
53
|
+
paths = paths.split("\n") if paths.is_a?(String)
|
54
|
+
Array(paths)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Adva
|
4
|
+
class Static
|
5
|
+
module Rack
|
6
|
+
module Request
|
7
|
+
protected
|
8
|
+
def request(method, path, params = {})
|
9
|
+
Adva.out.puts " #{params['_method'] ? params['_method'].upcase : method} #{path} "
|
10
|
+
call(env_for(method, path, params)).tap do |status, headers, response|
|
11
|
+
Adva.out.puts " => #{status} " + (status == 302 ? "(Location: #{headers['Location']})" : '')
|
12
|
+
Adva.out.puts response if status == 500
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def env_for(method, path, params)
|
17
|
+
::Rack::MockRequest.env_for("http://#{site.host}#{path}", :method => method,
|
18
|
+
:input => ::Rack::Utils.build_nested_query(params),
|
19
|
+
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
|
20
|
+
'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:#{password}"].pack('m*'),
|
21
|
+
STORE_HEADER => params[STORE_HEADER]
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def username
|
26
|
+
'admin@admin.org'
|
27
|
+
end
|
28
|
+
|
29
|
+
def password
|
30
|
+
'admin!'
|
31
|
+
end
|
32
|
+
|
33
|
+
def site
|
34
|
+
@site ||= Site.first || raise('could not find any site')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Adva
|
4
|
+
class Static
|
5
|
+
module Rack
|
6
|
+
class Static < ::Rack::File
|
7
|
+
attr_reader :app, :root
|
8
|
+
|
9
|
+
def initialize(app, root)
|
10
|
+
@app = app
|
11
|
+
@root = root
|
12
|
+
Adva.out.puts "serving from #{root}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
if get?(env) && path = static(env)
|
17
|
+
super(env.merge('PATH_INFO' => path))
|
18
|
+
else
|
19
|
+
app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def static(env)
|
26
|
+
path = env['PATH_INFO'].chomp('/')
|
27
|
+
[path, "#{path}.html", "#{path}/index.html"].detect { |path| file?(path) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def file?(path)
|
31
|
+
File.file?(File.join(root, ::Rack::Utils.unescape(path)))
|
32
|
+
end
|
33
|
+
|
34
|
+
def get?(env)
|
35
|
+
env['REQUEST_METHOD'] == 'GET'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'watchr'
|
2
|
+
|
3
|
+
module Adva
|
4
|
+
class Static
|
5
|
+
module Rack
|
6
|
+
class Watch
|
7
|
+
autoload :Handler, 'adva/static/watch/handler'
|
8
|
+
|
9
|
+
include Request
|
10
|
+
|
11
|
+
attr_reader :app, :dir, :watch
|
12
|
+
|
13
|
+
delegate :call, :to => :app
|
14
|
+
|
15
|
+
def initialize(app, options = {}, &block)
|
16
|
+
@app = app
|
17
|
+
@dir = Pathname.new(options[:dir] || File.expand_path('import'))
|
18
|
+
dir.mkpath
|
19
|
+
run!
|
20
|
+
end
|
21
|
+
|
22
|
+
def update(path, event_type = nil)
|
23
|
+
Adva.out.puts "\n#{event_type}: #{path}"
|
24
|
+
import = Adva::Static::Import.new(:source => dir)
|
25
|
+
request = import.request_for(path)
|
26
|
+
status, headers, response = self.request('POST', request.path, request.params)
|
27
|
+
response = get(path) if !request.destroy? && status == 302
|
28
|
+
rescue Exception => e
|
29
|
+
Adva.out.puts e.message
|
30
|
+
e.backtrace.each { |line| Adva.out.puts line }
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(path)
|
34
|
+
import = Adva::Static::Import.new(:source => dir)
|
35
|
+
request = import.request_for(path)
|
36
|
+
self.request('GET', request.public_path, STORE_HEADER => true)
|
37
|
+
rescue Exception => e
|
38
|
+
Adva.out.puts e.message
|
39
|
+
e.backtrace.each { |line| Adva.out.puts line }
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def run!
|
45
|
+
@watch = fork { watch!(Adva.out) }
|
46
|
+
at_exit { kill_watch }
|
47
|
+
end
|
48
|
+
|
49
|
+
def watch!(out)
|
50
|
+
Adva.out.puts "watching #{dir} for changes"
|
51
|
+
Dir.chdir(dir)
|
52
|
+
handler.listen
|
53
|
+
rescue SignalException, SystemExit
|
54
|
+
rescue Exception => e
|
55
|
+
p e
|
56
|
+
e.backtrace.each { |line| puts line }
|
57
|
+
end
|
58
|
+
|
59
|
+
def trap_interrupt
|
60
|
+
Signal.trap('INT') do
|
61
|
+
if Time.now - @interrupt < 1
|
62
|
+
exit
|
63
|
+
else
|
64
|
+
STDERR.puts "\nReloading watched paths ... interrupt again to exit."
|
65
|
+
handler.refresh(watched_paths)
|
66
|
+
end
|
67
|
+
@interrupt = Time.now
|
68
|
+
end
|
69
|
+
@interrupt = Time.now
|
70
|
+
end
|
71
|
+
|
72
|
+
def handler
|
73
|
+
@handler ||= Adva::Static::Watch::Handler.new(self, dir.join("**/*.{#{Import::Source::TYPES.join(',')}}"))
|
74
|
+
end
|
75
|
+
|
76
|
+
def kill_watch
|
77
|
+
Process.kill('TERM', watch)
|
78
|
+
end
|
79
|
+
|
80
|
+
def watched_paths
|
81
|
+
paths = Dir[dir.join('**/*')].map {|path| Pathname(path).expand_path }
|
82
|
+
paths << Pathname.new(__FILE__) if paths.empty?
|
83
|
+
paths
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'core_ext/ruby/kernel/silence_stream'
|
2
|
+
|
3
|
+
module Adva
|
4
|
+
class Static
|
5
|
+
class Setup
|
6
|
+
attr_reader :app, :root, :source, :target, :host, :title, :remote
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@app = options[:app] || Rails.application
|
10
|
+
@root = Pathname.new(options[:root] || Dir.pwd)
|
11
|
+
@source = root.join(options[:source] || 'import')
|
12
|
+
@target = root.join(options[:target] || 'export')
|
13
|
+
@remote = options[:remote]
|
14
|
+
@host = options[:host] || 'example.org'
|
15
|
+
@title = options[:title] || host
|
16
|
+
|
17
|
+
Adva.out = StringIO.new('')
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
setup_directories
|
22
|
+
initial_import_and_export
|
23
|
+
setup_source_repository
|
24
|
+
setup_export_repository
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup_directories
|
28
|
+
source.mkdir rescue Errno::EEXIST
|
29
|
+
target.mkdir rescue Errno::EEXIST
|
30
|
+
site = source.join('site.yml')
|
31
|
+
File.open(site, 'w+') { |f| f.write(YAML.dump(:host => host, :title => title)) } unless site.exist?
|
32
|
+
end
|
33
|
+
|
34
|
+
def initial_import_and_export
|
35
|
+
Import.new(:source => source).run
|
36
|
+
Export.new(app, :target => target).run
|
37
|
+
end
|
38
|
+
|
39
|
+
def setup_source_repository
|
40
|
+
root.join('.gitignore').rmtree rescue Errno::ENOENT
|
41
|
+
root.join('.git').rmtree rescue Errno::ENOENT
|
42
|
+
|
43
|
+
File.open(root.join('.gitignore'), 'w+') { |f| f.write('export') }
|
44
|
+
|
45
|
+
Dir.chdir(root) do
|
46
|
+
`git init`
|
47
|
+
`git add .`
|
48
|
+
`git commit -am '#{host} source'`
|
49
|
+
`git branch source`
|
50
|
+
`git checkout --quiet source`
|
51
|
+
`git branch -D master`
|
52
|
+
`git remote add origin #{remote} -t source` if remote
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def setup_export_repository
|
57
|
+
root.join('export/.git').rmtree rescue Errno::ENOENT
|
58
|
+
|
59
|
+
Dir.chdir(target) do
|
60
|
+
`git init`
|
61
|
+
`git add .`
|
62
|
+
`git commit -am '#{host} export'`
|
63
|
+
`git remote add origin #{remote} -t master` if remote
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'observer'
|
2
|
+
require 'watchr'
|
3
|
+
require 'watchr/event_handlers/portable'
|
4
|
+
|
5
|
+
module Adva
|
6
|
+
class Static
|
7
|
+
module Watch
|
8
|
+
class Handler
|
9
|
+
include Observable
|
10
|
+
|
11
|
+
def initialize(observable, pattern)
|
12
|
+
add_observer(observable)
|
13
|
+
@pattern = pattern
|
14
|
+
@current = Dir[pattern]
|
15
|
+
@mtime = Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def listen
|
19
|
+
loop { trigger; sleep(0.5) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def trigger
|
23
|
+
events.each do |path, event|
|
24
|
+
changed(true)
|
25
|
+
notify_observers(path, event)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def events
|
32
|
+
@last = @current.dup
|
33
|
+
@current = Dir[@pattern]
|
34
|
+
deleted + created + modified
|
35
|
+
end
|
36
|
+
|
37
|
+
def modified
|
38
|
+
(@current & @last).each do |path|
|
39
|
+
mtime = File.mtime(path)
|
40
|
+
if mtime > @mtime
|
41
|
+
@mtime = mtime
|
42
|
+
return [[path, :modified]]
|
43
|
+
end
|
44
|
+
end && []
|
45
|
+
end
|
46
|
+
|
47
|
+
def created
|
48
|
+
(@current - @last).map { |path| [path, :created] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def deleted
|
52
|
+
(@last - @current).map { |path| [path, :deleted] }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'thor/group'
|
3
|
+
require 'patches/thor/core_ext/hash'
|
4
|
+
require 'patches/thor/group/symbolized_options'
|
5
|
+
|
6
|
+
module Adva
|
7
|
+
module Tasks
|
8
|
+
class Static
|
9
|
+
class Setup < Thor::Group
|
10
|
+
namespace 'adva:static:setup'
|
11
|
+
desc 'Setup a static version of your site'
|
12
|
+
class_option :source, :required => false, :banner => 'source directory (defaults to import)'
|
13
|
+
class_option :target, :required => false, :banner => 'source directory (defaults to export)'
|
14
|
+
class_option :host, :required => false, :banner => 'hostname of your site (defaults to example.org)'
|
15
|
+
class_option :title, :required => false, :banner => 'title of your site (defaults to the hostname)'
|
16
|
+
class_option :remote, :required => false, :banner => 'github repository url (defaults to none)'
|
17
|
+
|
18
|
+
def export
|
19
|
+
require 'config/environment'
|
20
|
+
Adva::Static::Setup.new(symbolized_options).run
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Import < Thor::Group
|
25
|
+
namespace 'adva:static:import'
|
26
|
+
desc 'Import a site from a directory'
|
27
|
+
class_option :source, :required => false
|
28
|
+
|
29
|
+
def import
|
30
|
+
require 'config/environment'
|
31
|
+
Adva::Static::Import.new(symbolized_options).run
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Export < Thor::Group
|
36
|
+
namespace 'adva:static:export'
|
37
|
+
desc 'Export a static version of a site'
|
38
|
+
class_option :target, :required => false
|
39
|
+
|
40
|
+
def export
|
41
|
+
require 'config/environment'
|
42
|
+
Adva::Static::Export.new(Rails.application, symbolized_options).run
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Update < Thor::Group
|
47
|
+
namespace 'adva:static:update'
|
48
|
+
desc 'Import and export a static version of a site'
|
49
|
+
class_option :source, :required => false
|
50
|
+
class_option :target, :required => false
|
51
|
+
|
52
|
+
def export
|
53
|
+
require 'config/environment'
|
54
|
+
Adva::Static::Import.new(symbolized_options).run
|
55
|
+
Adva::Static::Export.new(Rails.application, symbolized_options).run
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Server < Thor::Group
|
60
|
+
namespace 'adva:static:server'
|
61
|
+
desc 'Start the adva:static server and watcher'
|
62
|
+
class_option :root, :required => false, :default => 'export'
|
63
|
+
|
64
|
+
def server
|
65
|
+
ARGV.shift
|
66
|
+
Dir.chdir(symbolized_options[:root])
|
67
|
+
require "rack"
|
68
|
+
::Rack::Server.start
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|