slimmer 1.2.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGELOG.md +14 -1
  2. data/lib/slimmer.rb +28 -17
  3. data/lib/slimmer/app.rb +9 -9
  4. data/lib/slimmer/headers.rb +32 -1
  5. data/lib/slimmer/{admin_title_inserter.rb → processors/admin_title_inserter.rb} +1 -1
  6. data/lib/slimmer/{body_class_copier.rb → processors/body_class_copier.rb} +1 -1
  7. data/lib/slimmer/{body_inserter.rb → processors/body_inserter.rb} +1 -1
  8. data/lib/slimmer/{conditional_comment_mover.rb → processors/conditional_comment_mover.rb} +1 -1
  9. data/lib/slimmer/{footer_remover.rb → processors/footer_remover.rb} +1 -1
  10. data/lib/slimmer/{google_analytics_configurator.rb → processors/google_analytics_configurator.rb} +3 -2
  11. data/lib/slimmer/{header_context_inserter.rb → processors/header_context_inserter.rb} +1 -1
  12. data/lib/slimmer/processors/logo_class_inserter.rb +20 -0
  13. data/lib/slimmer/processors/related_items_inserter.rb +20 -0
  14. data/lib/slimmer/{search_path_setter.rb → processors/search_path_setter.rb} +2 -4
  15. data/lib/slimmer/{section_inserter.rb → processors/section_inserter.rb} +1 -1
  16. data/lib/slimmer/{tag_mover.rb → processors/tag_mover.rb} +1 -1
  17. data/lib/slimmer/{title_inserter.rb → processors/title_inserter.rb} +1 -1
  18. data/lib/slimmer/skin.rb +28 -20
  19. data/lib/slimmer/template.rb +1 -1
  20. data/lib/slimmer/test.rb +5 -1
  21. data/lib/slimmer/test_template.rb +7 -1
  22. data/lib/slimmer/version.rb +1 -1
  23. data/test/fixtures/related.raw.html.erb +26 -22
  24. data/test/fixtures/wrapper.html.erb +2 -0
  25. data/test/headers_test.rb +56 -1
  26. data/test/processors/body_inserter_test.rb +4 -4
  27. data/test/{google_analytics_test.rb → processors/google_analytics_test.rb} +31 -0
  28. data/test/processors/header_context_inserter_test.rb +5 -5
  29. data/test/processors/logo_class_inserter_test.rb +55 -0
  30. data/test/processors/related_items_inserter_test.rb +61 -0
  31. data/test/{search_path_setter_test.rb → processors/search_path_setter_test.rb} +0 -0
  32. data/test/processors/section_inserter_test.rb +20 -5
  33. data/test/test_helper.rb +31 -22
  34. data/test/typical_usage_test.rb +101 -70
  35. metadata +60 -70
  36. data/lib/slimmer/related_items_inserter.rb +0 -36
  37. data/lib/slimmer/url_rewriter.rb +0 -40
@@ -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!
@@ -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 :TitleInserter, 'slimmer/title_inserter'
22
- autoload :AdminTitleInserter, 'slimmer/admin_title_inserter'
23
- autoload :SectionInserter, 'slimmer/section_inserter'
24
- autoload :TagMover, 'slimmer/tag_mover'
25
- autoload :ConditionalCommentMover, 'slimmer/conditional_comment_mover'
26
- autoload :FooterRemover, 'slimmer/footer_remover'
27
- autoload :BodyInserter, 'slimmer/body_inserter'
28
- autoload :BodyClassCopier, 'slimmer/body_class_copier'
29
- autoload :UrlRewriter, 'slimmer/url_rewriter'
30
- autoload :HeaderContextInserter, 'slimmer/header_context_inserter'
31
- autoload :GoogleAnalyticsConfigurator, 'slimmer/google_analytics_configurator'
32
- autoload :RelatedItemsInserter, 'slimmer/related_items_inserter'
33
- autoload :SearchPathSetter, 'slimmer/search_path_setter'
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
+ #########################################
@@ -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
- rescue GdsApi::TimedOutException
94
- [503, {"Content-Type" => "text/plain"}, ["GDS API request timed out."]]
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
@@ -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["X-Slimmer-#{header_suffix}"] = value.to_s if value
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
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class AdminTitleInserter
3
3
  def filter(src,dest)
4
4
  title = src.at_css('#site-title')
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class BodyClassCopier
3
3
  def filter(src, dest)
4
4
  src_body_tag = src.at_css("body")
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class BodyInserter
3
3
  def initialize(source_id='wrapper', destination_id='wrapper')
4
4
  @source_selector = '#' + source_id
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class ConditionalCommentMover
3
3
  def filter(src, dest)
4
4
  src.xpath('//comment()').each do |comment|
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class FooterRemover
3
3
  def filter(src,dest)
4
4
  footer = src.at_css("#footer")
@@ -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
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class HeaderContextInserter
3
3
  def initialize(path='.header-context')
4
4
  @path = path
@@ -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
- require 'gds_api/helpers'
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
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class SectionInserter
3
3
  def filter(src,dest)
4
4
  meta_name = dest.at_css('meta[name="x-section-name"]')
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class TagMover
3
3
  def filter(src,dest)
4
4
  move_tags(src, dest, 'script', :must_have => ['src'])
@@ -1,4 +1,4 @@
1
- module Slimmer
1
+ module Slimmer::Processors
2
2
  class TitleInserter
3
3
  def filter(src,dest)
4
4
  title = src.at_css('head title')
@@ -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'), source_request),
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
@@ -7,7 +7,7 @@ module Slimmer
7
7
  module ClassMethods
8
8
  def slimmer_template template_name
9
9
  after_filter do
10
- response.headers[Slimmer::TEMPLATE_HEADER] = template_name.to_s
10
+ response.headers[Slimmer::Headers::TEMPLATE_HEADER] = template_name.to_s
11
11
  end
12
12
  end
13
13
  end
@@ -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
- Slimmer::TestTemplate::TEMPLATE
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
- <div class="header-context">Header</div>
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>
@@ -1,3 +1,3 @@
1
1
  module Slimmer
2
- VERSION = '1.2.5'
2
+ VERSION = '2.0.0'
3
3
  end