bookwatch 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bookwatch.gemspec +40 -0
- data/install_bin/bookwatch +5 -0
- data/lib/bookwatch/cli.rb +109 -0
- data/lib/bookwatch/code_example_reader.rb +95 -0
- data/lib/bookwatch/colorizer.rb +16 -0
- data/lib/bookwatch/commands/bind.rb +119 -0
- data/lib/bookwatch/commands/collection.rb +181 -0
- data/lib/bookwatch/commands/components/bind/directory_preparer.rb +33 -0
- data/lib/bookwatch/commands/components/bind/layout_preparer.rb +27 -0
- data/lib/bookwatch/commands/components/command_options.rb +45 -0
- data/lib/bookwatch/commands/components/imprint/directory_preparer.rb +24 -0
- data/lib/bookwatch/commands/generate.rb +92 -0
- data/lib/bookwatch/commands/imprint.rb +55 -0
- data/lib/bookwatch/commands/punch.rb +33 -0
- data/lib/bookwatch/commands/update_local_doc_repos.rb +42 -0
- data/lib/bookwatch/commands/watch.rb +100 -0
- data/lib/bookwatch/config/checkers/archive_menu_checker.rb +29 -0
- data/lib/bookwatch/config/checkers/ditamap_presence_checker.rb +27 -0
- data/lib/bookwatch/config/checkers/duplicate_section_name_checker.rb +33 -0
- data/lib/bookwatch/config/checkers/products_checker.rb +34 -0
- data/lib/bookwatch/config/checkers/repository_name_presence_checker.rb +34 -0
- data/lib/bookwatch/config/checkers/required_keys_checker.rb +18 -0
- data/lib/bookwatch/config/checkers/section_presence_checker.rb +15 -0
- data/lib/bookwatch/config/configuration.rb +119 -0
- data/lib/bookwatch/config/configuration_decorator.rb +54 -0
- data/lib/bookwatch/config/dita_config_generator.rb +61 -0
- data/lib/bookwatch/config/fetcher.rb +64 -0
- data/lib/bookwatch/config/imprint/configuration.rb +24 -0
- data/lib/bookwatch/config/product_config.rb +34 -0
- data/lib/bookwatch/config/section_config.rb +85 -0
- data/lib/bookwatch/config/validator.rb +33 -0
- data/lib/bookwatch/config/yaml_loader.rb +34 -0
- data/lib/bookwatch/css_link_checker.rb +67 -0
- data/lib/bookwatch/directory_helpers.rb +15 -0
- data/lib/bookwatch/dita_command_creator.rb +95 -0
- data/lib/bookwatch/dita_html_for_middleman_formatter.rb +45 -0
- data/lib/bookwatch/errors/programmer_mistake.rb +5 -0
- data/lib/bookwatch/html_document_manipulator.rb +21 -0
- data/lib/bookwatch/ingest/cloner_factory.rb +26 -0
- data/lib/bookwatch/ingest/destination_directory.rb +21 -0
- data/lib/bookwatch/ingest/git_accessor.rb +102 -0
- data/lib/bookwatch/ingest/git_cloner.rb +36 -0
- data/lib/bookwatch/ingest/local_filesystem_cloner.rb +66 -0
- data/lib/bookwatch/ingest/missing_working_copy.rb +27 -0
- data/lib/bookwatch/ingest/repo_identifier.rb +45 -0
- data/lib/bookwatch/ingest/section_repository.rb +49 -0
- data/lib/bookwatch/ingest/update_failure.rb +15 -0
- data/lib/bookwatch/ingest/update_success.rb +12 -0
- data/lib/bookwatch/ingest/working_copy.rb +36 -0
- data/lib/bookwatch/local_filesystem_accessor.rb +122 -0
- data/lib/bookwatch/middleman_runner.rb +48 -0
- data/lib/bookwatch/postprocessing/link_checker.rb +125 -0
- data/lib/bookwatch/postprocessing/redirection.rb +38 -0
- data/lib/bookwatch/preprocessing/dita_html_preprocessor.rb +91 -0
- data/lib/bookwatch/preprocessing/dita_pdf_preprocessor.rb +48 -0
- data/lib/bookwatch/preprocessing/link_to_site_gen_dir.rb +39 -0
- data/lib/bookwatch/preprocessing/preprocessor.rb +26 -0
- data/lib/bookwatch/server_director.rb +30 -0
- data/lib/bookwatch/sheller.rb +52 -0
- data/lib/bookwatch/streams/colorized_stream.rb +25 -0
- data/lib/bookwatch/streams/filter_stream.rb +22 -0
- data/lib/bookwatch/subnav/navigation_entries_from_html_toc.rb +59 -0
- data/lib/bookwatch/subnav/navigation_entries_from_markdown_root.rb +116 -0
- data/lib/bookwatch/subnav/pdf_config_creator.rb +50 -0
- data/lib/bookwatch/subnav/subnav_generator.rb +28 -0
- data/lib/bookwatch/subnav/subnav_generator_factory.rb +29 -0
- data/lib/bookwatch/subnav/template_creator.rb +71 -0
- data/lib/bookwatch/terminal.rb +19 -0
- data/lib/bookwatch/values/output_locations.rb +91 -0
- data/lib/bookwatch/values/product_info.rb +11 -0
- data/lib/bookwatch/values/section.rb +58 -0
- data/lib/bookwatch/values/subnav_template.rb +4 -0
- data/lib/bookwatch/values/user_message.rb +15 -0
- data/master_middleman/archive_drop_down_menu.rb +50 -0
- data/master_middleman/bookwatch_helpers.rb +259 -0
- data/master_middleman/compass_runner.rb +0 -0
- data/master_middleman/config.rb +34 -0
- data/master_middleman/quicklinks_renderer.rb +80 -0
- data/master_middleman/source/javascripts/all.js +2 -0
- data/master_middleman/source/javascripts/book.js +1 -0
- data/master_middleman/source/javascripts/bookwatch.js +103 -0
- data/master_middleman/source/layouts/_additional-scripts.erb +0 -0
- data/master_middleman/source/layouts/_book-footer.erb +0 -0
- data/master_middleman/source/layouts/_book-search.erb +0 -0
- data/master_middleman/source/layouts/_book-title.erb +3 -0
- data/master_middleman/source/layouts/_header.erb +34 -0
- data/master_middleman/source/layouts/_local-header.erb +0 -0
- data/master_middleman/source/layouts/_page-footer.erb +1 -0
- data/master_middleman/source/layouts/_title.erb +5 -0
- data/master_middleman/source/layouts/layout.erb +69 -0
- data/master_middleman/source/stylesheets/all.css.scss +3 -0
- data/master_middleman/source/stylesheets/base.scss +380 -0
- data/master_middleman/source/stylesheets/book-styles.css.scss +0 -0
- data/master_middleman/source/stylesheets/layout-styles.scss +0 -0
- data/master_middleman/source/stylesheets/partials/_book-base-values.scss +0 -0
- data/master_middleman/source/stylesheets/partials/_book-vars.scss +0 -0
- data/master_middleman/source/stylesheets/partials/_default.scss +300 -0
- data/master_middleman/source/stylesheets/partials/_footer.scss +64 -0
- data/master_middleman/source/stylesheets/partials/_header.scss +419 -0
- data/master_middleman/source/stylesheets/partials/_layout-vars.scss +0 -0
- data/master_middleman/source/stylesheets/partials/_mixins.scss +53 -0
- data/master_middleman/source/stylesheets/partials/_reset.scss +233 -0
- data/master_middleman/source/stylesheets/partials/_search.scss +78 -0
- data/master_middleman/source/stylesheets/partials/_sidenav.scss +191 -0
- data/master_middleman/source/stylesheets/partials/_syntax-highlight.scss +64 -0
- data/master_middleman/source/stylesheets/partials/_vars.scss +64 -0
- data/master_middleman/source/stylesheets/print.css.scss +58 -0
- data/master_middleman/source/subnavs/_default.erb +0 -0
- data/master_middleman/source/subnavs/_nav-links.erb +10 -0
- data/master_middleman/source/subnavs/_subnav_template.erb +8 -0
- data/master_middleman/subdirectory_aware_assets.rb +47 -0
- data/template_app/Gemfile +10 -0
- data/template_app/Gemfile.lock +43 -0
- data/template_app/config.ru +9 -0
- data/template_app/lib/rack_static_if_exists.rb +19 -0
- data/template_app/lib/search/handler.rb +47 -0
- data/template_app/lib/search/hit.rb +21 -0
- data/template_app/lib/search/query.rb +74 -0
- data/template_app/lib/search/renderer.rb +29 -0
- data/template_app/lib/server.rb +52 -0
- data/template_app/mail_sender.rb +69 -0
- data/template_app/rack_app.rb +110 -0
- data/template_app/search-results.html.erb +75 -0
- data/template_app/search.yml +22 -0
- metadata +491 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
/*
|
2
|
+
* FILE: _syntax-highlight.scss
|
3
|
+
======================================================================== */
|
4
|
+
pre .hll { background-color: #ffffcc; }
|
5
|
+
pre .c { color: #666666; font-style: italic; } // Comment
|
6
|
+
pre .err { color: #FF0000; background-color: #FFAAAA; } // Error
|
7
|
+
pre .k { color: #228899; font-weight: bold; } // Keyword
|
8
|
+
pre .o { color: #333333; } // Operator
|
9
|
+
pre .cm { color: #666666; font-style: italic; } // Comment.Multiline
|
10
|
+
pre .cp { color: #557799; } // Comment.Preproc
|
11
|
+
pre .c1 { color: #666666; font-style: italic; } // Comment.Single
|
12
|
+
pre .cs { color: #cc0000; font-weight: bold; font-style: italic; } // Comment.Special
|
13
|
+
pre .gd { color: #A00000; } // Generic.Deleted
|
14
|
+
pre .ge { font-style: italic; } // Generic.Emph
|
15
|
+
pre .gr { color: #FF0000; } // Generic.Error
|
16
|
+
pre .gh { color: #000080; font-weight: bold; } // Generic.Heading
|
17
|
+
pre .gi { color: #00A000; } // Generic.Inserted
|
18
|
+
pre .go { color: #888888; } // Generic.Output
|
19
|
+
pre .gp { color: #c65d09; font-weight: bold; } // Generic.Prompt
|
20
|
+
pre .gs { font-weight: bold; } // Generic.Strong
|
21
|
+
pre .gu { color: #800080; font-weight: bold; } // Generic.Subheading
|
22
|
+
pre .gt { color: #0044DD; } // Generic.Traceback
|
23
|
+
pre .kc { color: #228899; font-weight: bold; } // Keyword.Constant
|
24
|
+
pre .kd { color: #228899; font-weight: bold; } // Keyword.Declaration
|
25
|
+
pre .kn { color: #228899; font-weight: bold; } // Keyword.Namespace
|
26
|
+
pre .kp { color: #0088ff; font-weight: bold; } // Keyword.Pseudo
|
27
|
+
pre .kr { color: #228899; font-weight: bold; } // Keyword.Reserved
|
28
|
+
pre .kt { color: #6666ff; font-weight: bold; } // Keyword.Type
|
29
|
+
pre .m { color: #6600EE; font-weight: bold; } // Literal.Number
|
30
|
+
// pre .s { background-color: #F5F5FF; } // Literal.String
|
31
|
+
pre .na { color: #000077; } // Name.Attribute
|
32
|
+
pre .nb { color: #007722; } // Name.Builtin
|
33
|
+
pre .nc { color: #ee99ee; font-weight: bold; } // Name.Class
|
34
|
+
pre .no { color: #41AA9E; font-weight: bold; } // Name.Constant
|
35
|
+
pre .nd { color: #555555; font-weight: bold; } // Name.Decorator
|
36
|
+
pre .ni { color: #880000; } // Name.Entity
|
37
|
+
pre .ne { color: #FF0000; font-weight: bold; } // Name.Exception
|
38
|
+
pre .nf { color: #3E37DA; font-weight: bold; } // Name.Function
|
39
|
+
pre .nl { color: #997700; font-weight: bold; } // Name.Label
|
40
|
+
pre .nn { color: #0e84b5; font-weight: bold; } // Name.Namespace
|
41
|
+
pre .nt { color: #007700; } // Name.Tag
|
42
|
+
pre .nv { color: #003366; } // Name.Variable
|
43
|
+
pre .ow { color: #000000; font-weight: bold; } // Operator.Word
|
44
|
+
pre .w { color: #bbbbbb; } // Text.Whitespace
|
45
|
+
pre .mf { color: #6600EE; font-weight: bold; } // Literal.Number.Float
|
46
|
+
pre .mh { color: #005588; font-weight: bold; } // Literal.Number.Hex
|
47
|
+
pre .mi { color: #6666ff; font-weight: bold; } // Literal.Number.Integer
|
48
|
+
pre .mo { color: #4400EE; font-weight: bold; } // Literal.Number.Oct
|
49
|
+
pre .sb { background-color: #F5F5FF; } // Literal.String.Backtick
|
50
|
+
pre .sc { color: #8888FF; } // Literal.String.Char
|
51
|
+
pre .sd { color: #DD4422; } // Literal.String.Doc
|
52
|
+
pre .s2 { background-color: rgba(211, 211, 211, 0.2); } // Literal.String.Double
|
53
|
+
pre .se { color: #666666; font-weight: bold; background-color: #F5F5FF; } // Literal.String.Escape
|
54
|
+
pre .sh { background-color: #F5F5FF; } // Literal.String.Heredoc
|
55
|
+
pre .si { background-color: #eeeeee; } // Literal.String.Interpol
|
56
|
+
pre .sx { color: #ff8888; background-color: #F5F5FF; } // Literal.String.Other
|
57
|
+
pre .sr { color: #000000; background-color: #F5F5FF; } // Literal.String.Regex
|
58
|
+
pre .s1 { background-color: rgba(211, 211, 211, 0.2); } // Literal.String.Single
|
59
|
+
pre .ss { color: #D60F3F; } // Literal.String.Symbol
|
60
|
+
pre .bp { color: #007722; } // Name.Builtin.Pseudo
|
61
|
+
pre .vc { color: #ccccff; } // Name.Variable.Class
|
62
|
+
pre .vg { color: #ff8844; } // Name.Variable.Global
|
63
|
+
pre .vi { color: #aaaaff; } // Name.Variable.Instance
|
64
|
+
pre .il { color: #6666ff; font-weight: bold; } // Literal.Number.Integer.Long
|
@@ -0,0 +1,64 @@
|
|
1
|
+
$em-base: 16px !default;
|
2
|
+
|
3
|
+
// ~BEGIN BASE VALUES
|
4
|
+
// ===================================================
|
5
|
+
$font-primary: 'Source Sans Pro', sans-serif;
|
6
|
+
$font-size-xxl: 2.441em;
|
7
|
+
$font-size-xl: 1.953em;
|
8
|
+
$font-size-l: 1.563em;
|
9
|
+
$font-size-m: 1.25em;
|
10
|
+
$font-size-s: 1em;
|
11
|
+
$font-size-xs: 0.8em;
|
12
|
+
|
13
|
+
$gray1: #424242;
|
14
|
+
$gray2: #686868;
|
15
|
+
$gray3: #b5b5b5;
|
16
|
+
$gray4: #eceae9;
|
17
|
+
$gray5: #f8f8f8;
|
18
|
+
$gray6: #fdfdfd;
|
19
|
+
|
20
|
+
// Book-specific overrides
|
21
|
+
@import "book-base-values.scss";
|
22
|
+
|
23
|
+
$color-accent: $gray6;
|
24
|
+
$color-accent-bright: $gray6;
|
25
|
+
$color-accent-bright-contrast: #000000;
|
26
|
+
|
27
|
+
// text colors
|
28
|
+
$color-header: $gray2;
|
29
|
+
$color-text: $gray1;
|
30
|
+
$color-text-dim: $gray3;
|
31
|
+
|
32
|
+
// link colors
|
33
|
+
$color-link: #b5292e;
|
34
|
+
$color-link-border: #154896;
|
35
|
+
|
36
|
+
// border colors
|
37
|
+
$color-border-row: $gray5;
|
38
|
+
$color-border: $gray4;
|
39
|
+
$color-border-light: $gray4;
|
40
|
+
$color-border-superlight: $gray4;
|
41
|
+
$color-border-dark: $gray3;
|
42
|
+
$color-border-tip: $gray1;
|
43
|
+
|
44
|
+
// background-colors
|
45
|
+
$color-bg: $gray5;
|
46
|
+
$color-bg-light: $gray6;
|
47
|
+
$color-bg-panel: $gray5;
|
48
|
+
$color-bg-header: #b625ff;
|
49
|
+
$color-bg-dark: #523f74;
|
50
|
+
|
51
|
+
// ~SIZES
|
52
|
+
// ===================================================
|
53
|
+
$radius: 3px;
|
54
|
+
$sidenav-wide: 278px/$em-base + 0em;
|
55
|
+
$sidenav-widest: 367px/$em-base + 0em;
|
56
|
+
|
57
|
+
// ~BREAKPOINTS
|
58
|
+
// ===================================================
|
59
|
+
$bp-superwide: 1340px/$em-base + 0em;
|
60
|
+
$bp-widest: 900px/$em-base + 0em;
|
61
|
+
$bp-wide: 700px/$em-base + 0em;
|
62
|
+
|
63
|
+
@import "layout-vars.scss";
|
64
|
+
@import "book-vars.scss";
|
@@ -0,0 +1,58 @@
|
|
1
|
+
/*
|
2
|
+
* FILE: print.scss
|
3
|
+
======================================================================== */
|
4
|
+
@import "partials/_vars.scss";
|
5
|
+
|
6
|
+
// Book-specific overrides
|
7
|
+
@import "partials/_book-vars.scss";
|
8
|
+
|
9
|
+
pre code, p code, .terminal {
|
10
|
+
font-family: "DejaVu Sans Mono";
|
11
|
+
page-break-inside: avoid;
|
12
|
+
}
|
13
|
+
|
14
|
+
ol li {
|
15
|
+
margin-left: 15px;
|
16
|
+
}
|
17
|
+
|
18
|
+
.wrap {
|
19
|
+
width: 2400px;
|
20
|
+
|
21
|
+
.container {
|
22
|
+
margin-left: 0;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
li p code {
|
27
|
+
width: auto;
|
28
|
+
display: inline;
|
29
|
+
}
|
30
|
+
|
31
|
+
note {
|
32
|
+
overflow: visible;
|
33
|
+
page-break-inside: avoid !important;
|
34
|
+
page-break-before: avoid !important;
|
35
|
+
}
|
36
|
+
|
37
|
+
pre, pre code {
|
38
|
+
overflow: visible;
|
39
|
+
}
|
40
|
+
|
41
|
+
.pivotalcf .logo {
|
42
|
+
background: none;
|
43
|
+
padding-left: 0;
|
44
|
+
}
|
45
|
+
|
46
|
+
.quick-links,
|
47
|
+
.nav-container,
|
48
|
+
.header,
|
49
|
+
.site-footer-links,
|
50
|
+
#feedback {
|
51
|
+
display: none;
|
52
|
+
}
|
53
|
+
|
54
|
+
h1 {
|
55
|
+
margin-top: 0;
|
56
|
+
font-size: $font-size-l;
|
57
|
+
}
|
58
|
+
|
File without changes
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class SubdirectoryAwareAssets < ::Middleman::Extension
|
2
|
+
def initialize(app, options_hash={}, &block)
|
3
|
+
super
|
4
|
+
|
5
|
+
# After compass is setup, make it use the registered cache buster
|
6
|
+
app.compass_config do |config|
|
7
|
+
config.relative_assets = true
|
8
|
+
end if app.respond_to?(:compass_config)
|
9
|
+
end
|
10
|
+
|
11
|
+
helpers do
|
12
|
+
def asset_url(path, prefix="", options={})
|
13
|
+
url = super(path, prefix, options)
|
14
|
+
|
15
|
+
unless global_asset_at? url
|
16
|
+
current_dir = Pathname('/' + current_resource.destination_path)
|
17
|
+
url = Pathname(url).relative_path_from(current_dir.dirname).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
# middleman assumes your assets live at the top level, but they may be in the nested repos instead
|
21
|
+
# here we start at top level and dive down until we find the real asset
|
22
|
+
current_page_path_parts = current_resource.destination_path.split('/')
|
23
|
+
current_page_path_parts.pop
|
24
|
+
current_page_dir = File.join('source', current_page_path_parts, '')
|
25
|
+
while (!File.exists? "#{current_page_dir}#{url}") && url.match(/^\.\.\//) do
|
26
|
+
url = url.gsub(/^\.\.\//, "")
|
27
|
+
end
|
28
|
+
|
29
|
+
url
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def global_asset_at?(path)
|
35
|
+
path.include?('//') ||
|
36
|
+
path.start_with?('data:') ||
|
37
|
+
!current_resource ||
|
38
|
+
path.index('.css') ||
|
39
|
+
path.index('.js')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
::Middleman::Extensions.register(:subdirectory_aware_assets, SubdirectoryAwareAssets)
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
elasticsearch (1.0.17)
|
5
|
+
elasticsearch-api (= 1.0.17)
|
6
|
+
elasticsearch-transport (= 1.0.17)
|
7
|
+
elasticsearch-api (1.0.17)
|
8
|
+
multi_json
|
9
|
+
elasticsearch-transport (1.0.17)
|
10
|
+
faraday
|
11
|
+
multi_json
|
12
|
+
faraday (0.9.2)
|
13
|
+
multipart-post (>= 1.2, < 3)
|
14
|
+
libv8 (3.16.14.7)
|
15
|
+
mimemagic (0.3.0)
|
16
|
+
multi_json (1.11.2)
|
17
|
+
multipart-post (2.0.0)
|
18
|
+
puma (3.3.0)
|
19
|
+
rack (1.5.2)
|
20
|
+
rack-rewrite (1.5.0)
|
21
|
+
ref (1.0.5)
|
22
|
+
sendgrid-ruby (1.1.5)
|
23
|
+
faraday (~> 0.9)
|
24
|
+
mimemagic
|
25
|
+
smtpapi (~> 0.1)
|
26
|
+
smtpapi (0.1.0)
|
27
|
+
therubyracer (0.12.1)
|
28
|
+
libv8 (~> 3.16.14.0)
|
29
|
+
ref
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
elasticsearch
|
36
|
+
puma
|
37
|
+
rack
|
38
|
+
rack-rewrite
|
39
|
+
sendgrid-ruby (< 3.0)
|
40
|
+
therubyracer
|
41
|
+
|
42
|
+
BUNDLED WITH
|
43
|
+
1.11.2
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require './rack_app'
|
3
|
+
require './mail_sender'
|
4
|
+
|
5
|
+
mail_client = Bookwatch::MailSender.new(ENV['SENDGRID_USERNAME'],
|
6
|
+
ENV['SENDGRID_API_KEY'],
|
7
|
+
{to: ENV['FEEDBACK_TO'], from: ENV['FEEDBACK_FROM']})
|
8
|
+
|
9
|
+
run Bookwatch::RackApp.new(Pathname('redirects.rb'), mail_client).app
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Bookwatch
|
4
|
+
class RackStaticIfExists < Rack::Static
|
5
|
+
def can_serve(path)
|
6
|
+
file_exists?(path) || dir_with_index_exists?(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def file_exists?(path)
|
12
|
+
File.file?(File.join(@file_server.root, path))
|
13
|
+
end
|
14
|
+
|
15
|
+
def dir_with_index_exists?(path)
|
16
|
+
File.directory?(File.join(@file_server.root, path)) && !!@index && file_exists?(File.join(path, @index))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'elasticsearch'
|
2
|
+
require 'cgi'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative 'query'
|
6
|
+
require_relative 'renderer'
|
7
|
+
|
8
|
+
module Bookwatch
|
9
|
+
module Search
|
10
|
+
class Handler
|
11
|
+
def initialize(client_class = Elasticsearch::Client, environment = ENV)
|
12
|
+
@client_class = client_class
|
13
|
+
@environment = environment
|
14
|
+
@renderer = Renderer.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(request_env)
|
18
|
+
query = Query.new(extract_query_params(request_env['QUERY_STRING']))
|
19
|
+
query.get_results(client_class.new(url: elasticsearch_url))
|
20
|
+
|
21
|
+
[200, {'Content-Type' => 'text/html'}, [renderer.render_results(query)]]
|
22
|
+
rescue Exception => e
|
23
|
+
puts e.message
|
24
|
+
puts e.backtrace.join("\n")
|
25
|
+
|
26
|
+
[500, {'Content-Type' => 'text/plain'}, ['An error occurred']]
|
27
|
+
end
|
28
|
+
|
29
|
+
def extract_query_params(param_string)
|
30
|
+
parsed = CGI::parse(param_string || '')
|
31
|
+
parsed.keys.inject(Hash.new('')) do |params, key|
|
32
|
+
# CGI::parse returns values in an array
|
33
|
+
params[key] = parsed[key].first
|
34
|
+
params
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :renderer, :client_class, :environment
|
41
|
+
|
42
|
+
def elasticsearch_url
|
43
|
+
@elasticsearch_url ||= JSON.parse(environment['VCAP_SERVICES'])['searchly'][0]['credentials']['uri']
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Bookwatch
|
2
|
+
module Search
|
3
|
+
class Hit
|
4
|
+
def initialize(attrs = {})
|
5
|
+
source_fields = attrs.fetch('_source')
|
6
|
+
@title = source_fields.fetch('title').sub(/\A(.+)\|.*\z/, '\\1').strip
|
7
|
+
@url = source_fields.fetch('url')
|
8
|
+
@product_name = source_fields['product_name']
|
9
|
+
@product_version = source_fields['product_version']
|
10
|
+
|
11
|
+
if attrs.has_key?('highlight')
|
12
|
+
@text = attrs.fetch('highlight').fetch('text').first.strip
|
13
|
+
else
|
14
|
+
@text = source_fields.fetch('summary')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :title, :url, :text, :product_name, :product_version
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
require_relative 'hit'
|
4
|
+
|
5
|
+
module Bookwatch
|
6
|
+
module Search
|
7
|
+
class Query
|
8
|
+
attr_reader :search_term, :product_name, :product_version, :page_number, :result_list, :result_count, :last_page, :page_window
|
9
|
+
|
10
|
+
def initialize(params)
|
11
|
+
@search_term = params.fetch('q', '')
|
12
|
+
@product_name = params.fetch('product_name', nil)
|
13
|
+
@product_version = @product_name && params.fetch('product_version', nil)
|
14
|
+
@page_number = @search_term == '' ? 1 : [params['page'].to_i, 1].max
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_options
|
18
|
+
options = YAML.load_file(File.expand_path('../../../search.yml', __FILE__))
|
19
|
+
|
20
|
+
options['from'] = (page_number - 1) * 10
|
21
|
+
options['query']['bool']['should']['simple_query_string']['query'] = search_term
|
22
|
+
|
23
|
+
unless product_name.nil?
|
24
|
+
filters = [{
|
25
|
+
'term' => { 'product_name' => product_name }
|
26
|
+
}]
|
27
|
+
|
28
|
+
unless product_version.nil?
|
29
|
+
filters << {'term' => { 'product_version' => product_version }}
|
30
|
+
end
|
31
|
+
|
32
|
+
options['query']['bool']['filter'] = {
|
33
|
+
'bool' => {
|
34
|
+
'must' => filters
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
options
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_results(elasticsearch_client)
|
43
|
+
if search_term == ''
|
44
|
+
@result_count = 0
|
45
|
+
@result_list = []
|
46
|
+
@last_page = 1
|
47
|
+
else
|
48
|
+
results = elasticsearch_client.search index: 'searching', body: query_options
|
49
|
+
@result_count = results['hits']['total']
|
50
|
+
@result_list = results['hits']['hits'].map { |h| Hit.new(h) }
|
51
|
+
@last_page = (result_count / 10.0).ceil
|
52
|
+
end
|
53
|
+
@page_window = calculate_page_window
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def calculate_page_window
|
59
|
+
window_start = [page_number - 2, 1].max
|
60
|
+
window_end = [window_start + 4, last_page].min
|
61
|
+
window = (window_start .. window_end).to_a
|
62
|
+
|
63
|
+
if window.length < 5 && window.last == last_page && window.first != 1
|
64
|
+
window.unshift(window.first - 1)
|
65
|
+
if window.length < 5
|
66
|
+
window.unshift(window.first - 1)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
window
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|