bookbindery 7.2.0 → 7.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bookbinder.gemspec +2 -2
- data/lib/bookbinder/commands/bind.rb +2 -1
- data/lib/bookbinder/commands/bind/directory_preparer.rb +2 -2
- data/lib/bookbinder/commands/bind/layout_preparer.rb +3 -1
- data/lib/bookbinder/commands/collection.rb +1 -0
- data/lib/bookbinder/commands/generate.rb +2 -0
- data/lib/bookbinder/commands/punch.rb +44 -0
- data/lib/bookbinder/commands/push_from_local.rb +21 -2
- data/lib/bookbinder/config/archive_menu_configuration.rb +1 -1
- data/lib/bookbinder/config/configuration.rb +1 -1
- data/lib/bookbinder/config/section_config.rb +4 -0
- data/lib/bookbinder/dita_html_for_middleman_formatter.rb +9 -3
- data/lib/bookbinder/ingest/git_accessor.rb +23 -0
- data/lib/bookbinder/ingest/section_repository.rb +2 -1
- data/lib/bookbinder/middleman_runner.rb +1 -0
- data/lib/bookbinder/preprocessing/dita_preprocessor.rb +1 -1
- data/lib/bookbinder/preprocessing/link_to_site_gen_dir.rb +2 -2
- data/lib/bookbinder/spider.rb +0 -3
- data/lib/bookbinder/values/section.rb +12 -4
- data/master_middleman/bookbinder_helpers.rb +8 -0
- data/template_app/config.ru +7 -3
- data/template_app/lib/rack_static.rb +15 -13
- data/template_app/lib/vienna_application.rb +45 -22
- data/template_app/mail_sender.rb +69 -0
- data/template_app/rack_app.rb +71 -16
- metadata +18 -17
- data/template_app/app.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88ea31dee49567678221b2f7ec08271dbee24d4d
|
4
|
+
data.tar.gz: dc7bc2856531c909147a8c215466e4ca78f29224
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7edb2efc47d478fa5908c40ad733bcfd1ab73b56af875dcbbc00c49768c6356f544b1c4ff5e378e3c9ff8623a598d8628c3e53d81d8ead55a023a0e42791054c
|
7
|
+
data.tar.gz: 5899419c8b5cd1ab847ba3dd9d556f2dbc68a4fc832deb1536b306fae063fe6bfe28705dccb894c5175d342c85bb4593025e54f0ce45eb4edebcd63ef691ea42
|
data/bookbinder.gemspec
CHANGED
@@ -2,7 +2,7 @@ require 'base64'
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'bookbindery'
|
5
|
-
s.version = '7.
|
5
|
+
s.version = '7.3.0'
|
6
6
|
s.summary = 'Markdown to Rackup application documentation generator'
|
7
7
|
s.description = 'A command line utility to be run in Book repositories to stitch together their constituent Markdown repos into a static-HTML-serving application'
|
8
8
|
s.authors = ['Mike Grafton', 'Lucas Marks', 'Gavin Morgan', 'Nikhil Gajwani', 'Dan Wendorf', 'Brenda Chan', 'Matthew Boedicker', 'Frank Kotsianas', 'Elena Sharma', 'Christa Hartsock']
|
@@ -23,13 +23,13 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_runtime_dependency 'middleman-syntax', ['~> 2.0']
|
24
24
|
s.add_runtime_dependency 'rouge', '!= 1.9.1'
|
25
25
|
s.add_runtime_dependency 'redcarpet', ['~> 3.2.3']
|
26
|
-
s.add_runtime_dependency 'vienna', ['= 0.4.1']
|
27
26
|
s.add_runtime_dependency 'anemone'
|
28
27
|
s.add_runtime_dependency 'css_parser'
|
29
28
|
s.add_runtime_dependency 'puma'
|
30
29
|
s.add_runtime_dependency 'rack-rewrite'
|
31
30
|
s.add_runtime_dependency 'therubyracer'
|
32
31
|
s.add_runtime_dependency 'git', '~> 1.2.8'
|
32
|
+
s.add_runtime_dependency 'sendgrid-ruby'
|
33
33
|
|
34
34
|
s.add_development_dependency 'license_finder'
|
35
35
|
s.add_development_dependency 'pry-byebug'
|
@@ -8,14 +8,14 @@ module Bookbinder
|
|
8
8
|
@fs = fs
|
9
9
|
end
|
10
10
|
|
11
|
-
def prepare_directories(config, gem_root, output_locations, cloner)
|
11
|
+
def prepare_directories(config, gem_root, output_locations, cloner, ref_override: nil)
|
12
12
|
fs.remove_directory(output_locations.output_dir)
|
13
13
|
fs.empty_directory(output_locations.final_app_dir)
|
14
14
|
|
15
15
|
copy_directory_from_gem(gem_root, 'template_app', output_locations.final_app_dir)
|
16
16
|
copy_directory_from_gem(gem_root, 'master_middleman', output_locations.site_generator_home)
|
17
17
|
|
18
|
-
LayoutPreparer.new(fs).prepare(output_locations, cloner, config)
|
18
|
+
LayoutPreparer.new(fs).prepare(output_locations, cloner, ref_override, config)
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
@@ -8,9 +8,11 @@ module Bookbinder
|
|
8
8
|
|
9
9
|
attr_reader :fs
|
10
10
|
|
11
|
-
def prepare(output_locations, cloner, config)
|
11
|
+
def prepare(output_locations, cloner, ref_override, config)
|
12
12
|
if config.has_option?('layout_repo')
|
13
|
+
|
13
14
|
cloned_repo = cloner.call(source_repo_name: config.layout_repo,
|
15
|
+
source_ref: ref_override || config.layout_repo_ref,
|
14
16
|
destination_parent_dir: Dir.mktmpdir)
|
15
17
|
|
16
18
|
fs.copy_contents(cloned_repo.path, output_locations.site_generator_home)
|
@@ -71,6 +71,7 @@ module Bookbinder
|
|
71
71
|
push_local_to_staging,
|
72
72
|
Commands::PushToProd.new(streams, logger, configuration_fetcher, Dir.mktmpdir),
|
73
73
|
Commands::RunPublishCI.new(bind, push_local_to_staging, build_and_push_tarball),
|
74
|
+
Commands::Punch.new(streams, configuration_fetcher, version_control_system),
|
74
75
|
Commands::UpdateLocalDocRepos.new(
|
75
76
|
streams,
|
76
77
|
configuration_fetcher,
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative '../errors/cli_error'
|
2
|
+
require_relative 'naming'
|
3
|
+
|
4
|
+
module Bookbinder
|
5
|
+
module Commands
|
6
|
+
class Punch
|
7
|
+
include Commands::Naming
|
8
|
+
|
9
|
+
def initialize(streams, configuration_fetcher, version_control_system)
|
10
|
+
@streams = streams
|
11
|
+
@configuration_fetcher = configuration_fetcher
|
12
|
+
@version_control_system = version_control_system
|
13
|
+
end
|
14
|
+
|
15
|
+
def usage
|
16
|
+
["punch <git tag>", "Apply the specified <git tag> to your book, sections, and layout repo"]
|
17
|
+
end
|
18
|
+
|
19
|
+
def run((tag, *))
|
20
|
+
raise CliError::InvalidArguments unless tag
|
21
|
+
|
22
|
+
urls(config).each do |url|
|
23
|
+
version_control_system.remote_tag(url, tag, 'HEAD')
|
24
|
+
end
|
25
|
+
|
26
|
+
streams[:success].puts 'Success!'
|
27
|
+
streams[:out].puts "#{config.book_repo} and its sections were tagged with #{tag}"
|
28
|
+
0
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :streams, :configuration_fetcher, :version_control_system
|
34
|
+
|
35
|
+
def urls(config)
|
36
|
+
[config.book_repo_url, config.layout_repo_url] + config.sections.map(&:repo_url).uniq
|
37
|
+
end
|
38
|
+
|
39
|
+
def config
|
40
|
+
@config ||= configuration_fetcher.fetch_config
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -8,6 +8,7 @@ module Bookbinder
|
|
8
8
|
include Commands::Naming
|
9
9
|
|
10
10
|
CredentialKeyError = Class.new(RuntimeError)
|
11
|
+
FeedbackConfigError = Class.new(RuntimeError)
|
11
12
|
REQUIRED_AWS_KEYS = %w(access_key secret_key green_builds_bucket)
|
12
13
|
REQUIRED_CF_KEYS = %w(username password api_endpoint organization app_name)
|
13
14
|
|
@@ -61,7 +62,7 @@ module Bookbinder
|
|
61
62
|
"push_local_to_#{environment}"
|
62
63
|
end
|
63
64
|
|
64
|
-
def
|
65
|
+
def creds_error_message
|
65
66
|
<<-ERROR
|
66
67
|
Cannot locate a specific key in credentials.yml.
|
67
68
|
Your credentials file should follow this format:
|
@@ -88,6 +89,19 @@ cloud_foundry:
|
|
88
89
|
ERROR
|
89
90
|
end
|
90
91
|
|
92
|
+
def mail_error_message
|
93
|
+
<<-ERROR
|
94
|
+
Your environment variables for sending feedback are not set.
|
95
|
+
|
96
|
+
To enable feedback, you must set the following variables in your environment:
|
97
|
+
|
98
|
+
export SENDGRID_USERNAME=<your-username>
|
99
|
+
export SENDGRID_API_KEY=<your-api-key>
|
100
|
+
export FEEDBACK_TO=<email-to-receive-feedback>
|
101
|
+
export FEEDBACK_FROM=<email-to-send-feedback>
|
102
|
+
ERROR
|
103
|
+
end
|
104
|
+
|
91
105
|
def validate
|
92
106
|
missing_keys = []
|
93
107
|
|
@@ -106,7 +120,12 @@ cloud_foundry:
|
|
106
120
|
missing_keys << key unless cf_creds.send(key)
|
107
121
|
end
|
108
122
|
|
109
|
-
raise CredentialKeyError.new
|
123
|
+
raise CredentialKeyError.new(creds_error_message) if missing_keys.any?
|
124
|
+
raise FeedbackConfigError.new(mail_error_message) if config.feedback_enabled && mail_vars_absent?
|
125
|
+
end
|
126
|
+
|
127
|
+
def mail_vars_absent?
|
128
|
+
!(ENV['SENDGRID_USERNAME'] && ENV['SENDGRID_API_KEY'] && ENV['FEEDBACK_TO'] && ENV['FEEDBACK_FROM'])
|
110
129
|
end
|
111
130
|
end
|
112
131
|
end
|
@@ -24,7 +24,7 @@ module Bookbinder
|
|
24
24
|
|
25
25
|
def section_config(sections)
|
26
26
|
sections.reduce({}) {|config, section|
|
27
|
-
config_path = section.
|
27
|
+
config_path = section.path_to_repo_dir.join(config_filename)
|
28
28
|
archive_config = loader.load_key(config_path, 'archive_menu')
|
29
29
|
if archive_config
|
30
30
|
config.merge(section.desired_directory_name => archive_config)
|
@@ -66,7 +66,7 @@ module Bookbinder
|
|
66
66
|
end
|
67
67
|
|
68
68
|
CONFIG_REQUIRED_KEYS = %w(book_repo public_host)
|
69
|
-
CONFIG_OPTIONAL_KEYS = %w(archive_menu book_repo_url cred_repo cred_repo_url layout_repo layout_repo_url sections subnav_exclusions)
|
69
|
+
CONFIG_OPTIONAL_KEYS = %w(archive_menu book_repo_url cred_repo cred_repo_url layout_repo layout_repo_ref layout_repo_url sections subnav_exclusions feedback_enabled)
|
70
70
|
|
71
71
|
CONFIG_REQUIRED_KEYS.each do |method_name|
|
72
72
|
define_method(method_name) do
|
@@ -10,17 +10,20 @@ module Bookbinder
|
|
10
10
|
all_files_with_ext = file_system_accessor.find_files_with_ext('html', src)
|
11
11
|
|
12
12
|
all_files_with_ext.map do |filepath|
|
13
|
-
file_text = file_system_accessor.read
|
13
|
+
file_text = file_system_accessor.read(filepath)
|
14
|
+
|
14
15
|
file_title_text = html_document_manipulator.read_html_in_tag(document: file_text,
|
15
16
|
tag: 'title')
|
16
17
|
|
17
18
|
file_body_text = html_document_manipulator.read_html_in_tag(document: file_text,
|
18
19
|
tag: 'body')
|
19
20
|
|
21
|
+
cleansed_body_text = revert_to_erb(file_body_text)
|
22
|
+
|
20
23
|
relative_path_to_file = file_system_accessor.relative_path_from(src, filepath)
|
21
24
|
new_filepath = File.join dest, "#{relative_path_to_file}.erb"
|
22
25
|
|
23
|
-
output_text = frontmatter(file_title_text) +
|
26
|
+
output_text = frontmatter(file_title_text) + cleansed_body_text
|
24
27
|
|
25
28
|
file_system_accessor.write(to: new_filepath, text: output_text)
|
26
29
|
end
|
@@ -34,6 +37,9 @@ module Bookbinder
|
|
34
37
|
sanitized_title = title.gsub('"', '\"')
|
35
38
|
"---\ntitle: \"#{sanitized_title}\"\ndita: true\n---\n"
|
36
39
|
end
|
37
|
-
end
|
38
40
|
|
41
|
+
def revert_to_erb(text)
|
42
|
+
text.gsub('<%', '<%').gsub('%>', '%>')
|
43
|
+
end
|
44
|
+
end
|
39
45
|
end
|
@@ -39,6 +39,29 @@ module Bookbinder
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
def remote_tag(url, tagname, commit_or_object)
|
43
|
+
Dir.mktmpdir do |dir|
|
44
|
+
path = Pathname(dir)
|
45
|
+
git = cached_clone(url, temp_name("tag"), path)
|
46
|
+
git.config('user.name', 'Bookbinder')
|
47
|
+
git.config('user.email', 'bookbinder@cloudfoundry.org')
|
48
|
+
begin
|
49
|
+
git.add_tag(tagname, "origin/#{commit_or_object}",
|
50
|
+
message: 'Tagged by Bookbinder')
|
51
|
+
rescue Git::GitExecuteError => e
|
52
|
+
case e.message
|
53
|
+
when /already exists/
|
54
|
+
raise TagExists
|
55
|
+
when /as a valid ref/
|
56
|
+
raise InvalidTagRef
|
57
|
+
else
|
58
|
+
raise
|
59
|
+
end
|
60
|
+
end
|
61
|
+
git.push("origin", "master", tags: true)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
42
65
|
def author_date(path)
|
43
66
|
Pathname(path).dirname.ascend do |current_dir|
|
44
67
|
if current_dir.to_s.include?(source_dir_name) && current_dir.entries.include?(Pathname(".git"))
|
@@ -25,6 +25,7 @@ module Bookbinder
|
|
25
25
|
template_variables: config.template_variables,
|
26
26
|
local_repo_dir: local_repo_dir,
|
27
27
|
workspace: output_locations.workspace_dir,
|
28
|
+
feedback_enabled: config.feedback_enabled
|
28
29
|
}
|
29
30
|
fs.write(to: "bookbinder_config.yml", text: YAML.dump(config))
|
30
31
|
sheller.run_command({'MM_ROOT' => output_locations.master_dir.to_s},
|
@@ -38,7 +38,7 @@ module Bookbinder
|
|
38
38
|
end
|
39
39
|
|
40
40
|
dita_formatter.format_html(section_html_dir(section), formatted_dir(section))
|
41
|
-
copy_images(section.
|
41
|
+
copy_images(section.path_to_repo_dir, formatted_dir(section))
|
42
42
|
fs.copy_contents(formatted_dir(section), source_for_site_gen_dir(section))
|
43
43
|
end
|
44
44
|
end
|
@@ -10,13 +10,13 @@ module Bookbinder
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def applicable_to?(section)
|
13
|
-
filesystem.file_exist?(section.
|
13
|
+
filesystem.file_exist?(section.path_to_repo_dir)
|
14
14
|
end
|
15
15
|
|
16
16
|
def preprocess(sections, output_locations, config: nil, **_)
|
17
17
|
sections.each do |section|
|
18
18
|
filesystem.link_creating_intermediate_dirs(
|
19
|
-
section.
|
19
|
+
section.path_to_repo_dir,
|
20
20
|
output_locations.source_for_site_generator.join(section.destination_directory)
|
21
21
|
)
|
22
22
|
end
|
data/lib/bookbinder/spider.rb
CHANGED
@@ -79,9 +79,6 @@ Found #{broken_links.count} broken links!
|
|
79
79
|
Anemone.crawl(url) do |anemone|
|
80
80
|
dont_visit_fragments(anemone)
|
81
81
|
anemone.on_every_page do |page|
|
82
|
-
if $sitemap_debug
|
83
|
-
puts "\n\n********** Visiting page: #{page.url}\n\n#{page.body}"
|
84
|
-
end
|
85
82
|
broken, working = sieve.links_from Stabilimentum.new(page), is_first_pass
|
86
83
|
broken_links.concat broken
|
87
84
|
sitemap.concat working
|
@@ -7,9 +7,10 @@ module Bookbinder
|
|
7
7
|
:desired_directory_name,
|
8
8
|
:subnav_templ,
|
9
9
|
:desired_subnav_name,
|
10
|
-
:preprocessor_config
|
11
|
-
|
12
|
-
|
10
|
+
:preprocessor_config,
|
11
|
+
:at_repo_path) do
|
12
|
+
def path_to_repo_dir
|
13
|
+
at_repo_path.nil? ? path_to_repository : path_to_repository.join(at_repo_path)
|
13
14
|
end
|
14
15
|
|
15
16
|
def subnav_template
|
@@ -30,12 +31,19 @@ module Bookbinder
|
|
30
31
|
end
|
31
32
|
|
32
33
|
def path_to_preprocessor_attribute(attr)
|
33
|
-
|
34
|
+
path_to_repo_dir.join(preprocessor_config[attr]) if preprocessor_config[attr]
|
34
35
|
rescue NoMethodError => e
|
35
36
|
raise Errors::ProgrammerMistake.new(
|
36
37
|
"path_to_preprocessor_attribute assumes preprocessor_config is available, got nil.\n" +
|
37
38
|
"Original exception:\n\n#{e.inspect}\n\n#{e.backtrace.join("\n")}"
|
38
39
|
)
|
39
40
|
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def path_to_repository
|
45
|
+
Pathname(self[:path_to_repository].to_s)
|
46
|
+
end
|
47
|
+
|
40
48
|
end
|
41
49
|
end
|
@@ -54,6 +54,14 @@ module Bookbinder
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
def exclude_feedback
|
58
|
+
current_page.add_metadata({page: {feedback_disabled: true}})
|
59
|
+
end
|
60
|
+
|
61
|
+
def yield_for_feedback
|
62
|
+
partial 'layouts/feedback' if config[:feedback_enabled] && !current_page.metadata[:page][:feedback_disabled]
|
63
|
+
end
|
64
|
+
|
57
65
|
def mermaid_diagram(&blk)
|
58
66
|
escaped_text = capture(&blk).gsub('-','\-')
|
59
67
|
|
data/template_app/config.ru
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require 'pathname'
|
2
|
-
require 'vienna'
|
3
2
|
require './rack_app'
|
4
|
-
require './
|
5
|
-
|
3
|
+
require './mail_sender'
|
4
|
+
|
5
|
+
mail_client = Bookbinder::MailSender.new(ENV['SENDGRID_USERNAME'],
|
6
|
+
ENV['SENDGRID_API_KEY'],
|
7
|
+
{to: ENV['FEEDBACK_TO'], from: ENV['FEEDBACK_FROM']})
|
8
|
+
|
9
|
+
run Bookbinder::RackApp.new(Pathname('redirects.rb'), mail_client).app
|
@@ -1,21 +1,23 @@
|
|
1
1
|
require 'rack'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Bookbinder
|
4
|
+
class RackStatic < Rack::Static
|
5
|
+
def route_file(path)
|
6
|
+
@urls.kind_of?(Array) && @urls.any? { |url| url.index(path) == 0 }
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def overwrite_file_path(path)
|
10
|
+
path_has_trailing_slash?(path) && we_can_serve_index_at_path?(path)
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
+
private
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def path_has_trailing_slash?(path)
|
16
|
+
!((path =~ /\/$/).nil?)
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
19
|
+
def we_can_serve_index_at_path?(path)
|
20
|
+
@index && File.exists?(File.join @file_server.root, path, @index)
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
@@ -1,28 +1,51 @@
|
|
1
|
-
|
1
|
+
require_relative './rack_static'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
index: 'index.html',
|
10
|
-
header_rules: [[:all, {'Cache-Control' => 'public, max-age=3600'}]]
|
11
|
-
}
|
12
|
-
run Vienna::NotFound.new("#{root}/404.html")
|
3
|
+
module Bookbinder
|
4
|
+
module Vienna
|
5
|
+
class << self
|
6
|
+
def call(env)
|
7
|
+
Application.new.call(env)
|
8
|
+
end
|
13
9
|
end
|
14
|
-
end
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
class Application
|
12
|
+
def initialize(root = 'public')
|
13
|
+
@app = Rack::Builder.new do
|
14
|
+
use RackStatic, {
|
15
|
+
urls: Dir.glob("#{root}/**/*").map { |fn| fn.gsub(/^#{root}/, '')},
|
16
|
+
root: root,
|
17
|
+
index: 'index.html',
|
18
|
+
header_rules: [[:all, {'Cache-Control' => 'public, max-age=3600'}]]
|
19
|
+
}
|
20
|
+
run NotFound.new("#{root}/404.html")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(env)
|
25
|
+
if env['PATH_INFO'] != '/' && env['PATH_INFO'] =~ MATCH
|
26
|
+
env['PATH_INFO'] += '/'
|
27
|
+
[301, {'Location' => Rack::Request.new(env).url, 'Content-Type' => ''}, []]
|
28
|
+
else
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
# regexp to match strings without periods that start and end with a slash
|
35
|
+
MATCH = %r{^/([^.]*)[^/]$}
|
22
36
|
end
|
23
|
-
end
|
24
37
|
|
25
|
-
|
26
|
-
|
27
|
-
|
38
|
+
class NotFound
|
39
|
+
def initialize(path = '')
|
40
|
+
@path = path
|
41
|
+
end
|
42
|
+
|
43
|
+
def call(env)
|
44
|
+
content = File.exist?(@path) ? File.read(@path) : 'Not Found'
|
45
|
+
length = content.length.to_s
|
46
|
+
|
47
|
+
[404, {'Content-Type' => 'text/html', 'Content-Length' => length}, [content]]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
28
51
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'sendgrid-ruby'
|
2
|
+
require 'rack/response'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Bookbinder
|
6
|
+
class MailSender
|
7
|
+
def initialize(username, api_key, config={})
|
8
|
+
@config = config
|
9
|
+
@client = SendGrid::Client.new(api_user: username, api_key: api_key, raise_exceptions: false)
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_mail(params)
|
13
|
+
params = whitelisted_params(params)
|
14
|
+
@subject = assemble_subject(params[:page_url])
|
15
|
+
@body = assemble_body(**params)
|
16
|
+
@mail = SendGrid::Mail.new(merged_options)
|
17
|
+
|
18
|
+
response = client.send(@mail)
|
19
|
+
Rack::Response.new(response.body, response.code, response.headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
def assemble_subject(page_url)
|
23
|
+
"[Feedback] New feedback submitted for #{URI.parse(page_url).host}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def assemble_body(helpful: nil, comments: nil, date: nil, page_url: nil)
|
27
|
+
<<-EOT
|
28
|
+
Dear docs writer,
|
29
|
+
|
30
|
+
You just received new feedback.
|
31
|
+
|
32
|
+
The sender thought the document was #{helpfulness(helpful)}.
|
33
|
+
|
34
|
+
Date: #{date}
|
35
|
+
|
36
|
+
Page URL: #{page_url}
|
37
|
+
|
38
|
+
Comments:
|
39
|
+
#{comments || "None given"}
|
40
|
+
|
41
|
+
Happy editing!
|
42
|
+
EOT
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :config, :client
|
48
|
+
|
49
|
+
def whitelist
|
50
|
+
%w{helpful comments date page_url}
|
51
|
+
end
|
52
|
+
|
53
|
+
def whitelisted_params(params)
|
54
|
+
{}.tap do |hash|
|
55
|
+
params.each do |key, value|
|
56
|
+
hash[key.to_sym] = value if whitelist.include?(key)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def merged_options
|
62
|
+
config.merge({text: @body, subject: @subject})
|
63
|
+
end
|
64
|
+
|
65
|
+
def helpfulness(was_helpful)
|
66
|
+
was_helpful ? 'helpful' : 'not helpful'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/template_app/rack_app.rb
CHANGED
@@ -1,43 +1,82 @@
|
|
1
|
+
require 'rack'
|
1
2
|
require 'rack/rewrite'
|
2
|
-
|
3
|
-
require 'vienna'
|
3
|
+
require_relative './lib/vienna_application'
|
4
4
|
|
5
5
|
module Bookbinder
|
6
6
|
class RackApp
|
7
|
-
def initialize(redirect_pathname, auth_required: true)
|
7
|
+
def initialize(redirect_pathname, mail_client=nil, auth_required: true)
|
8
8
|
@redirect_pathname = redirect_pathname
|
9
|
+
@mail_client = mail_client
|
9
10
|
@auth_required = auth_required
|
10
11
|
end
|
11
12
|
|
12
13
|
def app
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
path = redirect_pathname
|
15
|
+
client = mail_client
|
16
|
+
auth = auth_required
|
17
|
+
Rack::Builder.new(Vienna) do
|
18
|
+
use ResolveRedirects, path
|
19
|
+
use AuthorizeUser, auth
|
20
|
+
map '/api/feedback' do
|
21
|
+
use MailFeedback, client
|
22
|
+
run Vienna::NotFound.new('public/404.html')
|
23
|
+
end
|
19
24
|
end
|
20
25
|
end
|
21
26
|
|
22
27
|
private
|
23
28
|
|
24
|
-
attr_reader :redirect_pathname, :auth_required
|
29
|
+
attr_reader :redirect_pathname, :mail_client, :auth_required
|
30
|
+
end
|
31
|
+
|
32
|
+
class ResolveRedirects
|
33
|
+
def initialize(app, redirect_pathname)
|
34
|
+
@app = app
|
35
|
+
@redirect_pathname = redirect_pathname
|
36
|
+
end
|
25
37
|
|
26
|
-
def
|
38
|
+
def rack_app
|
27
39
|
if redirect_pathname.exist?
|
28
40
|
p = redirect_pathname
|
29
|
-
Rack::Rewrite.new(
|
41
|
+
Rack::Rewrite.new(@app) { eval(p.read) }
|
30
42
|
else
|
31
|
-
|
43
|
+
@app
|
32
44
|
end
|
33
45
|
end
|
34
46
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
47
|
+
def call(env)
|
48
|
+
rack_app.call(env)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_reader :redirect_pathname
|
54
|
+
end
|
55
|
+
|
56
|
+
class AuthorizeUser
|
57
|
+
def initialize(app, auth_required)
|
58
|
+
@app = app
|
59
|
+
@auth_required = auth_required
|
60
|
+
end
|
61
|
+
|
62
|
+
def rack_app
|
63
|
+
if auth_required && site_username && site_password
|
64
|
+
Rack::Auth::Basic.new(@app) do |username, password|
|
65
|
+
[username, password] == [site_username, site_password]
|
66
|
+
end
|
67
|
+
else
|
68
|
+
@app
|
38
69
|
end
|
39
70
|
end
|
40
71
|
|
72
|
+
def call(env)
|
73
|
+
rack_app.call(env)
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
attr_accessor :auth_required
|
79
|
+
|
41
80
|
def site_username
|
42
81
|
ENV['SITE_AUTH_USERNAME']
|
43
82
|
end
|
@@ -46,4 +85,20 @@ module Bookbinder
|
|
46
85
|
ENV['SITE_AUTH_PASSWORD']
|
47
86
|
end
|
48
87
|
end
|
88
|
+
|
89
|
+
class MailFeedback
|
90
|
+
def initialize(app, client)
|
91
|
+
@app = app
|
92
|
+
@client = client
|
93
|
+
end
|
94
|
+
|
95
|
+
def call(env)
|
96
|
+
request = Rack::Request.new(env)
|
97
|
+
if request.post? && @client
|
98
|
+
@client.send_mail(request.params)
|
99
|
+
else
|
100
|
+
@app.call(env)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
49
104
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bookbindery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.
|
4
|
+
version: 7.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Grafton
|
@@ -17,7 +17,7 @@ authors:
|
|
17
17
|
autorequire:
|
18
18
|
bindir: install_bin
|
19
19
|
cert_chain: []
|
20
|
-
date: 2015-
|
20
|
+
date: 2015-11-11 00:00:00.000000000 Z
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
23
|
name: fog-aws
|
@@ -131,20 +131,6 @@ dependencies:
|
|
131
131
|
- - "~>"
|
132
132
|
- !ruby/object:Gem::Version
|
133
133
|
version: 3.2.3
|
134
|
-
- !ruby/object:Gem::Dependency
|
135
|
-
name: vienna
|
136
|
-
requirement: !ruby/object:Gem::Requirement
|
137
|
-
requirements:
|
138
|
-
- - '='
|
139
|
-
- !ruby/object:Gem::Version
|
140
|
-
version: 0.4.1
|
141
|
-
type: :runtime
|
142
|
-
prerelease: false
|
143
|
-
version_requirements: !ruby/object:Gem::Requirement
|
144
|
-
requirements:
|
145
|
-
- - '='
|
146
|
-
- !ruby/object:Gem::Version
|
147
|
-
version: 0.4.1
|
148
134
|
- !ruby/object:Gem::Dependency
|
149
135
|
name: anemone
|
150
136
|
requirement: !ruby/object:Gem::Requirement
|
@@ -229,6 +215,20 @@ dependencies:
|
|
229
215
|
- - "~>"
|
230
216
|
- !ruby/object:Gem::Version
|
231
217
|
version: 1.2.8
|
218
|
+
- !ruby/object:Gem::Dependency
|
219
|
+
name: sendgrid-ruby
|
220
|
+
requirement: !ruby/object:Gem::Requirement
|
221
|
+
requirements:
|
222
|
+
- - ">="
|
223
|
+
- !ruby/object:Gem::Version
|
224
|
+
version: '0'
|
225
|
+
type: :runtime
|
226
|
+
prerelease: false
|
227
|
+
version_requirements: !ruby/object:Gem::Requirement
|
228
|
+
requirements:
|
229
|
+
- - ">="
|
230
|
+
- !ruby/object:Gem::Version
|
231
|
+
version: '0'
|
232
232
|
- !ruby/object:Gem::Dependency
|
233
233
|
name: license_finder
|
234
234
|
requirement: !ruby/object:Gem::Requirement
|
@@ -309,6 +309,7 @@ files:
|
|
309
309
|
- lib/bookbinder/commands/generate.rb
|
310
310
|
- lib/bookbinder/commands/help.rb
|
311
311
|
- lib/bookbinder/commands/naming.rb
|
312
|
+
- lib/bookbinder/commands/punch.rb
|
312
313
|
- lib/bookbinder/commands/push_from_local.rb
|
313
314
|
- lib/bookbinder/commands/push_to_prod.rb
|
314
315
|
- lib/bookbinder/commands/run_publish_ci.rb
|
@@ -387,12 +388,12 @@ files:
|
|
387
388
|
- lib/bookbinder/values/section.rb
|
388
389
|
- lib/bookbinder/values/subnav_template.rb
|
389
390
|
- lib/bookbinder/values/user_message.rb
|
390
|
-
- template_app/app.rb
|
391
391
|
- template_app/config.ru
|
392
392
|
- template_app/Gemfile
|
393
393
|
- template_app/Gemfile.lock
|
394
394
|
- template_app/lib/rack_static.rb
|
395
395
|
- template_app/lib/vienna_application.rb
|
396
|
+
- template_app/mail_sender.rb
|
396
397
|
- template_app/rack_app.rb
|
397
398
|
- master_middleman/archive_drop_down_menu.rb
|
398
399
|
- master_middleman/bookbinder_helpers.rb
|
data/template_app/app.rb
DELETED