slimmer 1.2.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +14 -1
- data/lib/slimmer.rb +28 -17
- data/lib/slimmer/app.rb +9 -9
- data/lib/slimmer/headers.rb +32 -1
- data/lib/slimmer/{admin_title_inserter.rb → processors/admin_title_inserter.rb} +1 -1
- data/lib/slimmer/{body_class_copier.rb → processors/body_class_copier.rb} +1 -1
- data/lib/slimmer/{body_inserter.rb → processors/body_inserter.rb} +1 -1
- data/lib/slimmer/{conditional_comment_mover.rb → processors/conditional_comment_mover.rb} +1 -1
- data/lib/slimmer/{footer_remover.rb → processors/footer_remover.rb} +1 -1
- data/lib/slimmer/{google_analytics_configurator.rb → processors/google_analytics_configurator.rb} +3 -2
- data/lib/slimmer/{header_context_inserter.rb → processors/header_context_inserter.rb} +1 -1
- data/lib/slimmer/processors/logo_class_inserter.rb +20 -0
- data/lib/slimmer/processors/related_items_inserter.rb +20 -0
- data/lib/slimmer/{search_path_setter.rb → processors/search_path_setter.rb} +2 -4
- data/lib/slimmer/{section_inserter.rb → processors/section_inserter.rb} +1 -1
- data/lib/slimmer/{tag_mover.rb → processors/tag_mover.rb} +1 -1
- data/lib/slimmer/{title_inserter.rb → processors/title_inserter.rb} +1 -1
- data/lib/slimmer/skin.rb +28 -20
- data/lib/slimmer/template.rb +1 -1
- data/lib/slimmer/test.rb +5 -1
- data/lib/slimmer/test_template.rb +7 -1
- data/lib/slimmer/version.rb +1 -1
- data/test/fixtures/related.raw.html.erb +26 -22
- data/test/fixtures/wrapper.html.erb +2 -0
- data/test/headers_test.rb +56 -1
- data/test/processors/body_inserter_test.rb +4 -4
- data/test/{google_analytics_test.rb → processors/google_analytics_test.rb} +31 -0
- data/test/processors/header_context_inserter_test.rb +5 -5
- data/test/processors/logo_class_inserter_test.rb +55 -0
- data/test/processors/related_items_inserter_test.rb +61 -0
- data/test/{search_path_setter_test.rb → processors/search_path_setter_test.rb} +0 -0
- data/test/processors/section_inserter_test.rb +20 -5
- data/test/test_helper.rb +31 -22
- data/test/typical_usage_test.rb +101 -70
- metadata +60 -70
- data/lib/slimmer/related_items_inserter.rb +0 -36
- data/lib/slimmer/url_rewriter.rb +0 -40
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
|
+
# 2.0.0
|
2
|
+
|
3
|
+
Backwards-incompatible changes:
|
4
|
+
|
5
|
+
* Artefact has to be explicitly passed to slimmer.
|
6
|
+
* RelatedItemsInserter uses passed artefact instead of calling out to panopticon.
|
7
|
+
* Slimmer now strips all X-Slimmer-* HTTP headers from the final response.
|
8
|
+
|
9
|
+
Other changes
|
10
|
+
|
11
|
+
* new LogoClassInserter module - adds classes to the `#wrapper` element to control the appearence of the directgov and businesslink logos
|
12
|
+
* Rounded Corners!!! (it is 2.0 after all)
|
13
|
+
|
1
14
|
# 0.9.0
|
2
15
|
|
3
16
|
* Moved templates into slimmer rather than using separate static project
|
4
17
|
* Added railtie so that slimmer can be dropped into a rails app without configuration
|
5
|
-
* Began to write *gasp* tests!
|
18
|
+
* Began to write *gasp* tests!
|
data/lib/slimmer.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
#########################################
|
2
|
+
####### Look, Rounded corners.... It's 2.0 #####
|
3
|
+
#####################################################
|
4
|
+
#######################################################
|
5
|
+
|
1
6
|
require 'nokogiri'
|
2
7
|
require 'erb'
|
3
8
|
require 'open-uri'
|
@@ -5,33 +10,39 @@ require 'plek'
|
|
5
10
|
require 'null_logger'
|
6
11
|
require 'openssl'
|
7
12
|
|
13
|
+
require 'slimmer/version'
|
8
14
|
require 'slimmer/railtie' if defined? Rails
|
9
15
|
|
10
16
|
module Slimmer
|
11
|
-
TEMPLATE_HEADER = 'X-Slimmer-Template'
|
12
|
-
SKIP_HEADER = 'X-Slimmer-Skip'
|
13
|
-
SEARCH_PATH_HEADER = 'X-Slimmer-Search-Path'
|
14
17
|
|
15
|
-
autoload :Version, 'slimmer/version'
|
16
18
|
autoload :Railtie, 'slimmer/railtie'
|
17
19
|
autoload :Skin, 'slimmer/skin'
|
18
20
|
|
19
21
|
autoload :Template, 'slimmer/template'
|
20
22
|
autoload :App, 'slimmer/app'
|
21
|
-
autoload :
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
23
|
+
autoload :Headers, 'slimmer/headers'
|
24
|
+
|
25
|
+
module Processors
|
26
|
+
autoload :AdminTitleInserter, 'slimmer/processors/admin_title_inserter'
|
27
|
+
autoload :BodyClassCopier, 'slimmer/processors/body_class_copier'
|
28
|
+
autoload :BodyInserter, 'slimmer/processors/body_inserter'
|
29
|
+
autoload :ConditionalCommentMover, 'slimmer/processors/conditional_comment_mover'
|
30
|
+
autoload :FooterRemover, 'slimmer/processors/footer_remover'
|
31
|
+
autoload :GoogleAnalyticsConfigurator, 'slimmer/processors/google_analytics_configurator'
|
32
|
+
autoload :HeaderContextInserter, 'slimmer/processors/header_context_inserter'
|
33
|
+
autoload :LogoClassInserter, 'slimmer/processors/logo_class_inserter'
|
34
|
+
autoload :RelatedItemsInserter, 'slimmer/processors/related_items_inserter'
|
35
|
+
autoload :SearchPathSetter, 'slimmer/processors/search_path_setter'
|
36
|
+
autoload :SectionInserter, 'slimmer/processors/section_inserter'
|
37
|
+
autoload :TagMover, 'slimmer/processors/tag_mover'
|
38
|
+
autoload :TitleInserter, 'slimmer/processors/title_inserter'
|
39
|
+
end
|
34
40
|
|
35
41
|
class TemplateNotFoundException < StandardError; end
|
36
42
|
class CouldNotRetrieveTemplate < StandardError; end
|
37
43
|
end
|
44
|
+
|
45
|
+
#######################################################
|
46
|
+
#####################################################
|
47
|
+
#################################################
|
48
|
+
#########################################
|
data/lib/slimmer/app.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "gds_api/exceptions"
|
2
|
-
|
3
1
|
module Slimmer
|
4
2
|
class App
|
5
3
|
attr_accessor :logger
|
@@ -29,10 +27,10 @@ module Slimmer
|
|
29
27
|
response = Rack::Response.new(body, status, headers)
|
30
28
|
|
31
29
|
if response_can_be_rewritten?(response) && !skip_slimmer?(env, response)
|
32
|
-
rewrite_response(env, response)
|
33
|
-
else
|
34
|
-
[status, headers, body]
|
30
|
+
status, headers, body = rewrite_response(env, response)
|
35
31
|
end
|
32
|
+
|
33
|
+
[status, strip_slimmer_headers(headers), body]
|
36
34
|
end
|
37
35
|
|
38
36
|
def response_can_be_rewritten?(response)
|
@@ -53,7 +51,7 @@ module Slimmer
|
|
53
51
|
end
|
54
52
|
|
55
53
|
def skip_slimmer_header?(response)
|
56
|
-
!!response.headers[SKIP_HEADER]
|
54
|
+
!!response.headers[Headers::SKIP_HEADER]
|
57
55
|
end
|
58
56
|
|
59
57
|
def s(body)
|
@@ -74,7 +72,7 @@ module Slimmer
|
|
74
72
|
|
75
73
|
rewritten_body = case response.status
|
76
74
|
when 200
|
77
|
-
if response.headers[TEMPLATE_HEADER] == 'admin' || request.path =~ /^\/admin(\/|$)/
|
75
|
+
if response.headers[Headers::TEMPLATE_HEADER] == 'admin' || request.path =~ /^\/admin(\/|$)/
|
78
76
|
@skin.admin s(response.body)
|
79
77
|
else
|
80
78
|
@skin.success request, response, s(response.body)
|
@@ -90,8 +88,10 @@ module Slimmer
|
|
90
88
|
response.headers['Content-Length'] = content_length(response.body)
|
91
89
|
|
92
90
|
response.finish
|
93
|
-
|
94
|
-
|
91
|
+
end
|
92
|
+
|
93
|
+
def strip_slimmer_headers(headers)
|
94
|
+
headers.reject {|k, v| k =~ /\A#{Headers::HEADER_PREFIX}/ }
|
95
95
|
end
|
96
96
|
end
|
97
97
|
end
|
data/lib/slimmer/headers.rb
CHANGED
@@ -2,6 +2,8 @@ module Slimmer
|
|
2
2
|
module Headers
|
3
3
|
InvalidHeader = Class.new(RuntimeError)
|
4
4
|
|
5
|
+
HEADER_PREFIX = "X-Slimmer"
|
6
|
+
|
5
7
|
SLIMMER_HEADER_MAPPING = {
|
6
8
|
section: "Section",
|
7
9
|
need_id: "Need-ID",
|
@@ -12,12 +14,41 @@ module Slimmer
|
|
12
14
|
skip: "Skip",
|
13
15
|
}
|
14
16
|
|
17
|
+
TEMPLATE_HEADER = "#{HEADER_PREFIX}-Template"
|
18
|
+
SKIP_HEADER = "#{HEADER_PREFIX}-Skip"
|
19
|
+
SEARCH_PATH_HEADER = "#{HEADER_PREFIX}-Search-Path"
|
20
|
+
ARTEFACT_HEADER = "#{HEADER_PREFIX}-Artefact"
|
21
|
+
|
15
22
|
def set_slimmer_headers(hash)
|
16
23
|
raise InvalidHeader if (hash.keys - SLIMMER_HEADER_MAPPING.keys).any?
|
17
24
|
SLIMMER_HEADER_MAPPING.each do |hash_key, header_suffix|
|
18
25
|
value = hash[hash_key]
|
19
|
-
headers["
|
26
|
+
headers["#{HEADER_PREFIX}-#{header_suffix}"] = value.to_s if value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_slimmer_artefact(artefact_input)
|
31
|
+
if artefact_input.is_a?(Hash) or artefact_input.is_a?(OpenStruct)
|
32
|
+
artefact = artefact_input.dup
|
33
|
+
elsif artefact_input.respond_to?(:to_hash) # e.g. GdsApi::Response
|
34
|
+
artefact = artefact_input.to_hash.dup
|
35
|
+
end
|
36
|
+
|
37
|
+
if artefact.is_a?(Hash)
|
38
|
+
# Temporary deletions until the actions are removed from the API.
|
39
|
+
# The actions increase the size of the artefact significantly, and will
|
40
|
+
# only grow over time.
|
41
|
+
#
|
42
|
+
# We skip this when given an OpenStruct as they won't have actions etc...
|
43
|
+
artefact.delete("actions")
|
44
|
+
if artefact["related_items"]
|
45
|
+
artefact["related_items"].each do |ri|
|
46
|
+
ri["artefact"].delete("actions") if ri["artefact"]
|
47
|
+
end
|
48
|
+
end
|
20
49
|
end
|
50
|
+
|
51
|
+
headers[ARTEFACT_HEADER] = artefact.to_json
|
21
52
|
end
|
22
53
|
end
|
23
54
|
end
|
data/lib/slimmer/{google_analytics_configurator.rb → processors/google_analytics_configurator.rb}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "json"
|
2
2
|
|
3
|
-
module Slimmer
|
3
|
+
module Slimmer::Processors
|
4
4
|
class GoogleAnalyticsConfigurator
|
5
5
|
PAGE_LEVEL_EVENT = 3
|
6
6
|
HEADER_MAPPING = {
|
@@ -29,7 +29,8 @@ module Slimmer
|
|
29
29
|
def set_custom_var(slot, name, value)
|
30
30
|
return nil unless value
|
31
31
|
value.downcase!
|
32
|
-
"_gaq.push(#{JSON.dump([ "_setCustomVar", slot, name, value, PAGE_LEVEL_EVENT])})
|
32
|
+
response = "_gaq.push(#{JSON.dump([ "_setCustomVar", slot, name, value, PAGE_LEVEL_EVENT])});\n"
|
33
|
+
response + "GOVUK.Analytics.#{name} = \"#{value}\";"
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Slimmer
|
2
|
+
module Processors
|
3
|
+
class LogoClassInserter
|
4
|
+
LOGO_CLASSES = %w(businesslink directgov)
|
5
|
+
|
6
|
+
def initialize(artefact)
|
7
|
+
@artefact = artefact
|
8
|
+
end
|
9
|
+
|
10
|
+
def filter(source, dest)
|
11
|
+
return unless @artefact and @artefact["tag_ids"]
|
12
|
+
logo_classes = LOGO_CLASSES & @artefact["tag_ids"]
|
13
|
+
wrapper = dest.css('#wrapper')
|
14
|
+
logo_classes.each do |klass|
|
15
|
+
wrapper.add_class(klass)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Slimmer::Processors::RelatedItemsInserter
|
2
|
+
include ERB::Util
|
3
|
+
|
4
|
+
def initialize(related_block_template, artefact)
|
5
|
+
@related_block_template = related_block_template
|
6
|
+
@artefact = artefact
|
7
|
+
end
|
8
|
+
|
9
|
+
def filter(content_document, page_template)
|
10
|
+
if content_document.at_css('body.mainstream div#related-items')
|
11
|
+
page_template.at_css('body.mainstream div#related-items').replace(related_item_block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def related_item_block
|
16
|
+
artefact = @artefact
|
17
|
+
html = ERB.new(@related_block_template).result(binding)
|
18
|
+
Nokogiri::HTML.fragment(html)
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Slimmer
|
1
|
+
module Slimmer::Processors
|
4
2
|
class SearchPathSetter
|
5
3
|
def initialize(response)
|
6
4
|
@response = response
|
@@ -13,7 +11,7 @@ module Slimmer
|
|
13
11
|
end
|
14
12
|
|
15
13
|
def search_scope
|
16
|
-
@response.headers[SEARCH_PATH_HEADER]
|
14
|
+
@response.headers[Slimmer::Headers::SEARCH_PATH_HEADER]
|
17
15
|
end
|
18
16
|
end
|
19
17
|
end
|
data/lib/slimmer/skin.rb
CHANGED
@@ -92,8 +92,6 @@ module Slimmer
|
|
92
92
|
logger.debug "Slimmer: Processor #{p} started at #{processor_start_time}"
|
93
93
|
begin
|
94
94
|
p.filter(src,dest)
|
95
|
-
rescue GdsApi::TimedOutException
|
96
|
-
raise
|
97
95
|
rescue => e
|
98
96
|
logger.error "Slimmer: Failed while processing #{p}: #{[ e.message, e.backtrace ].flatten.join("\n")}"
|
99
97
|
end
|
@@ -111,39 +109,49 @@ module Slimmer
|
|
111
109
|
|
112
110
|
def admin(body)
|
113
111
|
processors = [
|
114
|
-
TitleInserter.new(),
|
115
|
-
TagMover.new(),
|
116
|
-
AdminTitleInserter.new,
|
117
|
-
FooterRemover.new,
|
118
|
-
BodyInserter.new(),
|
119
|
-
BodyClassCopier.new,
|
112
|
+
Processors::TitleInserter.new(),
|
113
|
+
Processors::TagMover.new(),
|
114
|
+
Processors::AdminTitleInserter.new,
|
115
|
+
Processors::FooterRemover.new,
|
116
|
+
Processors::BodyInserter.new(),
|
117
|
+
Processors::BodyClassCopier.new,
|
120
118
|
]
|
121
119
|
process(processors, body, template('admin'))
|
122
120
|
end
|
123
121
|
|
124
122
|
def success(source_request, response, body)
|
123
|
+
artefact = artefact_from_header(response)
|
125
124
|
processors = [
|
126
|
-
TitleInserter.new(),
|
127
|
-
TagMover.new(),
|
128
|
-
ConditionalCommentMover.new(),
|
129
|
-
BodyInserter.new(options[:wrapper_id] || 'wrapper'),
|
130
|
-
BodyClassCopier.new,
|
131
|
-
HeaderContextInserter.new(),
|
132
|
-
SectionInserter.new(),
|
133
|
-
GoogleAnalyticsConfigurator.new(response),
|
134
|
-
SearchPathSetter.new(response),
|
135
|
-
RelatedItemsInserter.new(template('related.raw'),
|
125
|
+
Processors::TitleInserter.new(),
|
126
|
+
Processors::TagMover.new(),
|
127
|
+
Processors::ConditionalCommentMover.new(),
|
128
|
+
Processors::BodyInserter.new(options[:wrapper_id] || 'wrapper'),
|
129
|
+
Processors::BodyClassCopier.new,
|
130
|
+
Processors::HeaderContextInserter.new(),
|
131
|
+
Processors::SectionInserter.new(),
|
132
|
+
Processors::GoogleAnalyticsConfigurator.new(response),
|
133
|
+
Processors::SearchPathSetter.new(response),
|
134
|
+
Processors::RelatedItemsInserter.new(template('related.raw'), artefact),
|
135
|
+
Processors::LogoClassInserter.new(artefact),
|
136
136
|
]
|
137
137
|
|
138
|
-
template_name = response.headers[TEMPLATE_HEADER] || 'wrapper'
|
138
|
+
template_name = response.headers[Headers::TEMPLATE_HEADER] || 'wrapper'
|
139
139
|
process(processors, body, template(template_name))
|
140
140
|
end
|
141
141
|
|
142
142
|
def error(template_name, body)
|
143
143
|
processors = [
|
144
|
-
TitleInserter.new()
|
144
|
+
Processors::TitleInserter.new()
|
145
145
|
]
|
146
146
|
process(processors, body, template(template_name))
|
147
147
|
end
|
148
|
+
|
149
|
+
def artefact_from_header(response)
|
150
|
+
if response.headers.include?(Headers::ARTEFACT_HEADER)
|
151
|
+
JSON.parse(response.headers[Headers::ARTEFACT_HEADER])
|
152
|
+
else
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
end
|
148
156
|
end
|
149
157
|
end
|
data/lib/slimmer/template.rb
CHANGED
data/lib/slimmer/test.rb
CHANGED
@@ -5,7 +5,11 @@ module Slimmer
|
|
5
5
|
class Skin
|
6
6
|
def load_template name
|
7
7
|
logger.debug "Slimmer: TEST MODE - Loading fixture template from #{__FILE__}"
|
8
|
-
|
8
|
+
if name == 'related.raw'
|
9
|
+
%{<div id="test-related-items"></div>}
|
10
|
+
else
|
11
|
+
Slimmer::TestTemplate::TEMPLATE
|
12
|
+
end
|
9
13
|
end
|
10
14
|
end
|
11
15
|
end
|
@@ -15,7 +15,13 @@ module Slimmer::TestTemplate
|
|
15
15
|
<script src="http://static.preview.alphagov.co.uk/static/customisation-settings.js" defer></script>
|
16
16
|
</head>
|
17
17
|
<body>
|
18
|
-
<
|
18
|
+
<nav role="navigation" class="header-context">
|
19
|
+
<ol class="group">
|
20
|
+
<li><a href="/">Home</a></li>
|
21
|
+
<li><a href="/browse">All sections</a></li>
|
22
|
+
</ol>
|
23
|
+
</nav>
|
24
|
+
|
19
25
|
<div id="wrapper"></div>
|
20
26
|
</body>
|
21
27
|
</html>
|
data/lib/slimmer/version.rb
CHANGED