parklife 0.2.0
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/.github/workflows/tests.yml +62 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +103 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/rack/.gitignore +1 -0
- data/examples/rack/Gemfile +4 -0
- data/examples/rack/Gemfile.lock +47 -0
- data/examples/rack/Parkfile +23 -0
- data/examples/rails/.gitignore +26 -0
- data/examples/rails/Gemfile +5 -0
- data/examples/rails/Gemfile.lock +150 -0
- data/examples/rails/Parkfile +6 -0
- data/examples/rails/Rakefile +6 -0
- data/examples/rails/app/assets/config/manifest.js +2 -0
- data/examples/rails/app/assets/stylesheets/application.css +15 -0
- data/examples/rails/app/assets/stylesheets/global.css +4 -0
- data/examples/rails/app/controllers/application_controller.rb +2 -0
- data/examples/rails/app/controllers/posts_controller.rb +9 -0
- data/examples/rails/app/helpers/application_helper.rb +2 -0
- data/examples/rails/app/jobs/application_job.rb +2 -0
- data/examples/rails/app/models/application_record.rb +3 -0
- data/examples/rails/app/models/post.rb +5 -0
- data/examples/rails/app/views/layouts/application.html.erb +14 -0
- data/examples/rails/app/views/posts/index.html.erb +7 -0
- data/examples/rails/app/views/posts/show.html.erb +3 -0
- data/examples/rails/bin/bundle +3 -0
- data/examples/rails/bin/rails +4 -0
- data/examples/rails/bin/rake +4 -0
- data/examples/rails/bin/setup +33 -0
- data/examples/rails/bin/update +28 -0
- data/examples/rails/config/application.rb +35 -0
- data/examples/rails/config/boot.rb +3 -0
- data/examples/rails/config/database.yml +25 -0
- data/examples/rails/config/environment.rb +5 -0
- data/examples/rails/config/environments/development.rb +53 -0
- data/examples/rails/config/environments/production.rb +79 -0
- data/examples/rails/config/environments/test.rb +36 -0
- data/examples/rails/config/initializers/application_controller_renderer.rb +8 -0
- data/examples/rails/config/initializers/assets.rb +12 -0
- data/examples/rails/config/initializers/backtrace_silencers.rb +7 -0
- data/examples/rails/config/initializers/content_security_policy.rb +25 -0
- data/examples/rails/config/initializers/cookies_serializer.rb +5 -0
- data/examples/rails/config/initializers/filter_parameter_logging.rb +4 -0
- data/examples/rails/config/initializers/inflections.rb +16 -0
- data/examples/rails/config/initializers/mime_types.rb +4 -0
- data/examples/rails/config/initializers/wrap_parameters.rb +14 -0
- data/examples/rails/config/locales/en.yml +33 -0
- data/examples/rails/config/routes.rb +5 -0
- data/examples/rails/config.ru +5 -0
- data/examples/rails/data/1-hello-from-parklife.txt +3 -0
- data/examples/rails/data/2-hello-again.txt +3 -0
- data/examples/rails/data/3-magic-number.txt +3 -0
- data/examples/rails/db/migrate/20190507172823_create_posts.rb +11 -0
- data/examples/rails/db/schema.rb +24 -0
- data/examples/rails/db/seeds.rb +10 -0
- data/examples/rails/parklife-build +24 -0
- data/examples/rails/public/404.html +67 -0
- data/examples/rails/public/422.html +67 -0
- data/examples/rails/public/500.html +66 -0
- data/examples/rails/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/rails/public/apple-touch-icon.png +0 -0
- data/examples/rails/public/favicon.ico +0 -0
- data/examples/rails/public/robots.txt +1 -0
- data/examples/sinatra/.gitignore +1 -0
- data/examples/sinatra/Gemfile +4 -0
- data/examples/sinatra/Gemfile.lock +56 -0
- data/examples/sinatra/Parkfile +21 -0
- data/exe/parklife +3 -0
- data/lib/parklife/application.rb +39 -0
- data/lib/parklife/cli.rb +36 -0
- data/lib/parklife/config.rb +14 -0
- data/lib/parklife/crawler.rb +104 -0
- data/lib/parklife/errors.rb +25 -0
- data/lib/parklife/rails.rb +12 -0
- data/lib/parklife/route.rb +23 -0
- data/lib/parklife/route_set.rb +36 -0
- data/lib/parklife/utils.rb +20 -0
- data/lib/parklife/version.rb +3 -0
- data/lib/parklife.rb +8 -0
- data/parklife.gemspec +38 -0
- metadata +217 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The page you were looking for doesn't exist (404)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
.rails-default-error-page {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
.rails-default-error-page div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
.rails-default-error-page div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
.rails-default-error-page h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
.rails-default-error-page div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body class="rails-default-error-page">
|
58
|
+
<!-- This file lives in public/404.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
62
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
63
|
+
</div>
|
64
|
+
<p>If you are the application owner check the logs for more information.</p>
|
65
|
+
</div>
|
66
|
+
</body>
|
67
|
+
</html>
|
@@ -0,0 +1,67 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>The change you wanted was rejected (422)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
.rails-default-error-page {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
.rails-default-error-page div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
.rails-default-error-page div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
.rails-default-error-page h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
.rails-default-error-page div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body class="rails-default-error-page">
|
58
|
+
<!-- This file lives in public/422.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>The change you wanted was rejected.</h1>
|
62
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
63
|
+
</div>
|
64
|
+
<p>If you are the application owner check the logs for more information.</p>
|
65
|
+
</div>
|
66
|
+
</body>
|
67
|
+
</html>
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
.rails-default-error-page {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
.rails-default-error-page div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
.rails-default-error-page div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
.rails-default-error-page h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
.rails-default-error-page div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body class="rails-default-error-page">
|
58
|
+
<!-- This file lives in public/500.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>We're sorry, but something went wrong.</h1>
|
62
|
+
</div>
|
63
|
+
<p>If you are the application owner check the logs for more information.</p>
|
64
|
+
</div>
|
65
|
+
</body>
|
66
|
+
</html>
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
|
@@ -0,0 +1 @@
|
|
1
|
+
build
|
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../..
|
3
|
+
specs:
|
4
|
+
parklife (0.1.0)
|
5
|
+
capybara
|
6
|
+
nokogiri
|
7
|
+
thor
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.8.1)
|
13
|
+
public_suffix (>= 2.0.2, < 6.0)
|
14
|
+
capybara (3.38.0)
|
15
|
+
addressable
|
16
|
+
matrix
|
17
|
+
mini_mime (>= 0.1.3)
|
18
|
+
nokogiri (~> 1.8)
|
19
|
+
rack (>= 1.6.0)
|
20
|
+
rack-test (>= 0.6.3)
|
21
|
+
regexp_parser (>= 1.5, < 3.0)
|
22
|
+
xpath (~> 3.2)
|
23
|
+
matrix (0.4.2)
|
24
|
+
mini_mime (1.1.2)
|
25
|
+
mini_portile2 (2.8.1)
|
26
|
+
mustermann (1.0.3)
|
27
|
+
nokogiri (1.14.2)
|
28
|
+
mini_portile2 (~> 2.8.0)
|
29
|
+
racc (~> 1.4)
|
30
|
+
public_suffix (5.0.1)
|
31
|
+
racc (1.6.2)
|
32
|
+
rack (2.2.3)
|
33
|
+
rack-protection (2.0.5)
|
34
|
+
rack
|
35
|
+
rack-test (2.0.2)
|
36
|
+
rack (>= 1.3)
|
37
|
+
regexp_parser (2.7.0)
|
38
|
+
sinatra (2.0.5)
|
39
|
+
mustermann (~> 1.0)
|
40
|
+
rack (~> 2.0)
|
41
|
+
rack-protection (= 2.0.5)
|
42
|
+
tilt (~> 2.0)
|
43
|
+
thor (1.2.1)
|
44
|
+
tilt (2.0.9)
|
45
|
+
xpath (3.2.0)
|
46
|
+
nokogiri (~> 1.8)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
ruby
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
parklife!
|
53
|
+
sinatra
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
1.17.2
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'parklife'
|
2
|
+
require 'sinatra'
|
3
|
+
|
4
|
+
get '/' do
|
5
|
+
'<a href="/hello/foo">foo</foo>, <a href="/hello/bar">bar</foo>, <a href="/hello/baz">baz</foo>'
|
6
|
+
end
|
7
|
+
|
8
|
+
get '/hello/:name' do
|
9
|
+
"Hello #{params[:name]}"
|
10
|
+
end
|
11
|
+
|
12
|
+
Parklife.application.configure do |config|
|
13
|
+
config.build_dir = 'build'
|
14
|
+
config.rack_app = Sinatra::Application
|
15
|
+
end
|
16
|
+
|
17
|
+
Parklife.application.routes do
|
18
|
+
root crawl: true
|
19
|
+
|
20
|
+
get '/hello/other'
|
21
|
+
end
|
data/exe/parklife
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'parklife/config'
|
3
|
+
require 'parklife/crawler'
|
4
|
+
require 'parklife/errors'
|
5
|
+
require 'parklife/route_set'
|
6
|
+
|
7
|
+
module Parklife
|
8
|
+
class Application
|
9
|
+
attr_reader :config, :crawler
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@config = Config.new
|
13
|
+
@route_set = RouteSet.new
|
14
|
+
@crawler = Crawler.new(config, @route_set)
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
raise BuildDirNotDefinedError if config.build_dir.nil?
|
19
|
+
raise RackAppNotDefinedError if config.rack_app.nil?
|
20
|
+
|
21
|
+
FileUtils.rm_rf(config.build_dir)
|
22
|
+
Dir.mkdir(config.build_dir)
|
23
|
+
|
24
|
+
crawler.start
|
25
|
+
end
|
26
|
+
|
27
|
+
def configure
|
28
|
+
yield config
|
29
|
+
end
|
30
|
+
|
31
|
+
def routes(&block)
|
32
|
+
if block_given?
|
33
|
+
@route_set.instance_eval(&block)
|
34
|
+
else
|
35
|
+
@route_set
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/parklife/cli.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Parklife
|
4
|
+
class CLI < Thor
|
5
|
+
desc 'build', 'create a production build'
|
6
|
+
def build
|
7
|
+
application.build
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'routes', 'list all defined routes'
|
11
|
+
def routes
|
12
|
+
application.routes.each do |route|
|
13
|
+
print route.path
|
14
|
+
print "\tcrawl=true" if route.crawl
|
15
|
+
puts
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def application
|
21
|
+
@application ||= begin
|
22
|
+
# Reach inside the consuming app's directory to load Parklife and
|
23
|
+
# apply its config. It's only at this point that the
|
24
|
+
# Parklife::Application is defined.
|
25
|
+
load discover_Parkfile(Dir.pwd)
|
26
|
+
|
27
|
+
Parklife.application.config.reporter = $stdout
|
28
|
+
Parklife.application
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def discover_Parkfile(dir)
|
33
|
+
File.expand_path('Parkfile', dir)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Parklife
|
4
|
+
class Config
|
5
|
+
attr_accessor :base, :build_dir, :nested_index, :on_404, :rack_app,
|
6
|
+
:reporter
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
self.nested_index = true
|
10
|
+
self.on_404 = :error
|
11
|
+
self.reporter = StringIO.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'capybara'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'parklife/route'
|
4
|
+
require 'parklife/utils'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Parklife
|
8
|
+
class Crawler
|
9
|
+
attr_reader :config, :route_set
|
10
|
+
|
11
|
+
def initialize(config, route_set)
|
12
|
+
@config = config
|
13
|
+
@route_set = route_set
|
14
|
+
|
15
|
+
Capybara.register_driver :parklife do |app|
|
16
|
+
Capybara::RackTest::Driver.new(app, follow_redirects: false)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
Capybara.app_host = config.base if config.base
|
22
|
+
Capybara.save_path = config.build_dir
|
23
|
+
|
24
|
+
@routes = route_set.to_a
|
25
|
+
@visited = Set.new
|
26
|
+
|
27
|
+
while route = @routes.shift
|
28
|
+
process_route(route)
|
29
|
+
config.reporter.print '.'
|
30
|
+
end
|
31
|
+
|
32
|
+
config.reporter.puts
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def process_route(route)
|
37
|
+
already_processed = if route.crawl
|
38
|
+
# No need to re-process an already-crawled route (but do re-process
|
39
|
+
# a route that has been visited but not crawled).
|
40
|
+
@visited.include?(route)
|
41
|
+
else
|
42
|
+
# This route isn't being crawled so there's no need to re-process
|
43
|
+
# it if it has already been visited or crawled.
|
44
|
+
crawled_route = Route.new(route.path, crawl: true)
|
45
|
+
@visited.include?(route) || @visited.include?(crawled_route)
|
46
|
+
end
|
47
|
+
|
48
|
+
return if already_processed
|
49
|
+
|
50
|
+
session.visit(route.path)
|
51
|
+
|
52
|
+
case session.status_code
|
53
|
+
when 200
|
54
|
+
# Continue processing the route.
|
55
|
+
when 404
|
56
|
+
case config.on_404
|
57
|
+
when :error
|
58
|
+
raise HTTPError.new(path: route.path, status: 404)
|
59
|
+
when :warn
|
60
|
+
$stderr.puts HTTPError.new(path: route.path, status: 404).message
|
61
|
+
end
|
62
|
+
else
|
63
|
+
raise HTTPError.new(path: route.path, status: session.status_code)
|
64
|
+
end
|
65
|
+
|
66
|
+
session.save_page(
|
67
|
+
Utils::build_path_for(
|
68
|
+
dir: config.build_dir,
|
69
|
+
path: route.path,
|
70
|
+
index: config.nested_index,
|
71
|
+
)
|
72
|
+
)
|
73
|
+
|
74
|
+
@visited << route
|
75
|
+
|
76
|
+
if route.crawl
|
77
|
+
scan_for_links(session.html) do |route|
|
78
|
+
# Don't revisit the route if it has already been visited with
|
79
|
+
# crawl=true but do revisit if it wasn't crawled (the passed route
|
80
|
+
# will always have crawl=true).
|
81
|
+
next if @visited.include?(route)
|
82
|
+
|
83
|
+
@routes << route
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def scan_for_links(html)
|
89
|
+
doc = Nokogiri::HTML.parse(html)
|
90
|
+
doc.css('a').each do |a|
|
91
|
+
uri = URI.parse(a[:href])
|
92
|
+
|
93
|
+
# Don't visit a page that belongs to a different domain.
|
94
|
+
next if uri.host
|
95
|
+
|
96
|
+
yield Route.new(uri.path, crawl: true)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def session
|
101
|
+
@session ||= Capybara::Session.new(:parklife, config.rack_app)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Parklife
|
2
|
+
Error = Class.new(StandardError)
|
3
|
+
BuildDirNotDefinedError = Class.new(Error)
|
4
|
+
RackAppNotDefinedError = Class.new(Error)
|
5
|
+
|
6
|
+
class HTTPError < Error
|
7
|
+
def initialize(path:, status:)
|
8
|
+
@path = path
|
9
|
+
@status = status
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
%Q(#{status} response from path "#{path}")
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
attr_reader :path, :status
|
18
|
+
end
|
19
|
+
|
20
|
+
class RailsNotDefinedError < Error
|
21
|
+
def message
|
22
|
+
'Expected Rails to be defined, require config/environment before parklife.'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'parklife/errors'
|
2
|
+
|
3
|
+
raise Parklife::RailsNotDefinedError unless defined?(Rails)
|
4
|
+
|
5
|
+
require 'parklife'
|
6
|
+
|
7
|
+
# Allow use of the consuming Rails application's route helpers from within the
|
8
|
+
# block when defining Parklife routes.
|
9
|
+
Parklife::RouteSet.include(Rails.application.routes.url_helpers)
|
10
|
+
|
11
|
+
Parklife.application.config.build_dir = Rails.root.join('build')
|
12
|
+
Parklife.application.config.rack_app = Rails.application
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Parklife
|
2
|
+
class Route
|
3
|
+
attr_reader :crawl, :path
|
4
|
+
|
5
|
+
def initialize(path, crawl:)
|
6
|
+
@path = path
|
7
|
+
@crawl = crawl
|
8
|
+
end
|
9
|
+
|
10
|
+
def ==(other)
|
11
|
+
path == other.path && crawl == other.crawl
|
12
|
+
end
|
13
|
+
alias_method :eql?, :==
|
14
|
+
|
15
|
+
def hash
|
16
|
+
[self.class, path, crawl].hash
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
%(<#{self.class.name} path="#{path}" crawl="#{crawl}">)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'set'
|
2
|
+
require_relative 'route'
|
3
|
+
|
4
|
+
module Parklife
|
5
|
+
class RouteSet
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
attr_reader :routes
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@routes = Set.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def each
|
15
|
+
routes.each do |path|
|
16
|
+
yield path
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def get(path, crawl: false)
|
21
|
+
routes << Route.new(path, crawl: crawl)
|
22
|
+
end
|
23
|
+
|
24
|
+
def root(crawl: false)
|
25
|
+
get('/', crawl: crawl)
|
26
|
+
end
|
27
|
+
|
28
|
+
def size
|
29
|
+
routes.size
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_a
|
33
|
+
routes.to_a
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Parklife
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def build_path_for(dir:, path:, index: true)
|
6
|
+
path = path.gsub(/^\/|\/$/, '')
|
7
|
+
|
8
|
+
if File.extname(path).empty?
|
9
|
+
if index
|
10
|
+
File.join(dir, path, 'index.html')
|
11
|
+
else
|
12
|
+
name = path.empty? ? 'index.html' : "#{path}.html"
|
13
|
+
File.join(dir, name)
|
14
|
+
end
|
15
|
+
else
|
16
|
+
File.join(dir, path)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/parklife.rb
ADDED
data/parklife.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'parklife/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'parklife'
|
7
|
+
spec.version = Parklife::VERSION
|
8
|
+
spec.authors = ['Ben Pickles']
|
9
|
+
spec.email = ['spideryoung@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Convert a Rack app into a static HTML site.'
|
12
|
+
spec.homepage = 'https://github.com/benpickles/parklife'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
16
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
17
|
+
spec.metadata = {
|
18
|
+
'changelog_uri' => 'https://github.com/benpickles/parklife/blob/main/CHANGELOG.md',
|
19
|
+
'source_code_uri' => 'https://github.com/benpickles/parklife',
|
20
|
+
}
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
end
|
27
|
+
spec.bindir = 'exe'
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
|
31
|
+
spec.add_dependency 'capybara'
|
32
|
+
spec.add_dependency 'nokogiri'
|
33
|
+
spec.add_dependency 'thor'
|
34
|
+
|
35
|
+
spec.add_development_dependency 'bundler'
|
36
|
+
spec.add_development_dependency 'rake'
|
37
|
+
spec.add_development_dependency 'rspec'
|
38
|
+
end
|