bookbindery 7.2.0 → 7.3.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 +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