slimmer 1.1.3 → 1.1.4beta2

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.
data/lib/slimmer.rb CHANGED
@@ -1,346 +1,25 @@
1
- require 'slimmer/template'
2
- require 'slimmer/railtie' if defined?(Rails)
3
-
4
1
  require 'nokogiri'
5
2
  require 'erb'
6
3
  require 'open-uri'
7
4
  require 'plek'
8
5
 
9
6
  module Slimmer
10
-
11
7
  TEMPLATE_HEADER = 'X-Slimmer-Template'
12
8
  SKIP_HEADER = 'X-Slimmer-Skip'
13
9
 
14
- class App
15
- def initialize(app, *args, &block)
16
- options = args.first || {}
17
- @app = app
18
-
19
- if options.key? :template_path
20
- raise "Template path should not be used. Use asset_host instead."
21
- end
22
-
23
- unless options[:asset_host]
24
- options[:asset_host] = Plek.current.find("assets")
25
- end
26
-
27
- @skin = Skin.new options[:asset_host], options[:cache_templates]
28
- end
29
-
30
- def call(env)
31
- response_array = @app.call(env)
32
- if response_array[1][SKIP_HEADER]
33
- response_array
34
- else
35
- rewrite_response(env, response_array)
36
- end
37
- end
38
-
39
- def on_success(request,body)
40
- @skin.success(request, body)
41
- end
42
-
43
- def admin(request,body)
44
- @skin.admin(request,body)
45
- end
46
-
47
- def on_error(request, status, body)
48
- @skin.error(request, '500', body)
49
- end
50
-
51
- def on_404(request,body)
52
- @skin.error(request, '404', body)
53
- end
54
-
55
- def s(body)
56
- return body.to_s unless body.respond_to?(:each)
57
- b = ""
58
- body.each {|a| b << a }
59
- b
60
- end
61
-
62
- def rewrite_response(env,triplet)
63
- status, headers, app_body = triplet
64
- source_request = Rack::Request.new(env)
65
- request = Rack::Request.new(headers)
66
- if headers['Content-Type'] =~ /text\/html/ || headers['content-type'] =~ /text\/html/
67
- case status.to_i
68
- when 200
69
- if headers[TEMPLATE_HEADER] == 'admin' || source_request.path =~ /^\/admin(\/|$)/
70
- rewritten_body = admin(request,s(app_body))
71
- else
72
- rewritten_body = on_success(request,s(app_body))
73
- end
74
- when 301, 302, 304
75
- rewritten_body = app_body
76
- when 404
77
- rewritten_body = on_404(request,s(app_body))
78
- else
79
- rewritten_body = on_error(request,status,s(app_body))
80
- end
81
- else
82
- rewritten_body = app_body
83
- end
84
- rewritten_body = [rewritten_body] unless rewritten_body.respond_to?(:each)
85
- [status, filter_headers(headers), rewritten_body]
86
- end
87
-
88
- def filter_headers(header_hash)
89
- valid_keys = ['vary', 'set-cookie', 'location', 'content-type', 'expires', 'cache-control']
90
- header_hash.keys.each do |key|
91
- header_hash.delete(key) unless valid_keys.include?(key.downcase)
92
- end
93
- header_hash
94
- end
95
- end
96
-
97
- class UrlRewriter
98
-
99
- def initialize(request)
100
- @request = request
101
- end
102
-
103
- def filter(src,dest)
104
- rewrite_document src
105
- end
106
-
107
- def rewrite_document(doc)
108
- rewrite_nodes doc.css('body img'),'src'
109
- rewrite_nodes doc.css('script'),'src'
110
- rewrite_nodes doc.css('link'),'href'
111
- end
112
-
113
- def rewrite_nodes(nodes,attr)
114
- nodes.each do |node|
115
- next unless node.attr(attr)
116
- node_uri = URI.parse(node.attr(attr))
117
- node.attribute(attr).value = rewrite_url(node_uri).to_s
118
- end
119
- end
120
-
121
- def rewrite_url(uri)
122
- unless uri.absolute?
123
- uri.scheme = @request.scheme
124
- if @request.host =~ /:/
125
- host,port = @request.host.split(":")
126
- uri.host = host
127
- uri.port = port
128
- else
129
- uri.host = @request.host
130
- uri.port = @request.port
131
- end
132
- end
133
- uri
134
- end
135
- end
136
-
137
- class TitleInserter
138
- def filter(src,dest)
139
- title = src.at_css('head title')
140
- head = dest.at_xpath('/html/head')
141
- if head && title
142
- insert_title(title,head)
143
- end
144
- end
145
-
146
- def insert_title(title, head)
147
- if head.at_css('title').nil?
148
- head.first_element_child.nil? ? head << title : head.first_element_child.before(title)
149
- else
150
- head.at_css('title').replace(title)
151
- end
152
- end
153
- end
154
-
155
- class SectionInserter
156
- def filter(src,dest)
157
- meta_name = dest.at_css('meta[name="x-section-name"]')
158
- meta_link = dest.at_css('meta[name="x-section-link"]')
159
- list = dest.at_css('nav[role=navigation] ol')
160
-
161
- # FIXME: Presumably this is meant to stop us adding a 'current section'
162
- # link if we're missing navigation, or x-section-* meta tags.
163
- # It doesn't work: #at_css will return a truthy object in any case.
164
- if meta_name && meta_link && list
165
- link_node = Nokogiri::XML::Node.new('a', dest)
166
- link_node['href'] = meta_link['content']
167
- link_node.content = meta_name['content']
168
-
169
- list_item = Nokogiri::XML::Node.new('li', dest)
170
- list_item.add_child(link_node)
171
-
172
- list.first_element_child.after(list_item)
173
- end
174
- end
175
- end
176
-
177
- class TagMover
178
- def filter(src,dest)
179
- move_tags(src, dest, 'script', :must_have => ['src'])
180
- move_tags(src, dest, 'link', :must_have => ['href'])
181
- move_tags(src, dest, 'meta', :must_have => ['name', 'content'], :keys => ['name', 'content', 'http-equiv'])
182
- end
183
-
184
- def include_tag?(node, min_attrs)
185
- min_attrs.inject(true) { |all_okay, attr_name| all_okay && node.has_attribute?(attr_name) }
186
- end
187
-
188
- def tag_fingerprint(node, attrs)
189
- attrs.collect do |attr_name|
190
- node.has_attribute?(attr_name) ? node.attr(attr_name) : nil
191
- end.compact.sort
192
- end
193
-
194
- def move_tags(src, dest, type, opts)
195
- comparison_attrs = opts[:keys] || opts[:must_have]
196
- min_attrs = opts[:must_have]
197
- already_there = dest.css(type).map { |node|
198
- tag_fingerprint(node, comparison_attrs)
199
- }.compact
200
-
201
- src.css(type).each do |node|
202
- if include_tag?(node, min_attrs) && !already_there.include?(tag_fingerprint(node, comparison_attrs))
203
- node.remove
204
- dest.at_xpath('/html/head') << node
205
- end
206
- end
207
- end
208
- end
209
-
210
- class BodyInserter
211
- def initialize(path='#wrapper')
212
- @path = path
213
- end
214
-
215
- def filter(src,dest)
216
- body = src.fragment(src.at_css(@path))
217
- dest.at_css(@path).replace(body)
218
- end
219
- end
220
-
221
- class BodyClassCopier
222
- def filter(src, dest)
223
- src_body_tag = src.at_css("body")
224
- dest_body_tag = dest.at_css('body')
225
- if src_body_tag.has_attribute?("class")
226
- combinded_classes = dest_body_tag.attr('class').to_s.split(/ +/)
227
- combinded_classes << src_body_tag.attr('class').to_s.split(/ +/)
228
- dest_body_tag.set_attribute("class", combinded_classes.join(' '))
229
- end
230
- end
231
- end
232
-
233
- class AdminTitleInserter
234
- def filter(src,dest)
235
- title = src.at_css('#site-title')
236
- head = dest.at_css('.gds-header h2')
237
- if head && title
238
- head.content = title.content
239
- title.remove
240
- end
241
- end
242
- end
243
-
244
- class FooterRemover
245
- def filter(src,dest)
246
- footer = src.at_css("#footer")
247
- footer.remove if footer
248
- end
249
- end
250
-
251
- class Skin
252
- attr_accessor :use_cache
253
- private :use_cache=, :use_cache
254
-
255
- attr_accessor :templated_cache
256
- private :templated_cache=, :templated_cache
257
-
258
- attr_accessor :asset_host
259
- private :asset_host=, :asset_host
260
-
261
- # TODO: Extract the cache to something we can pass in instead of using
262
- # true/false and an in-memory cache.
263
- def initialize asset_host, use_cache = false
264
- self.asset_host = asset_host
265
- self.templated_cache = {}
266
- self.use_cache = false
267
- end
268
-
269
- def template(template_name)
270
- return cached_template(template_name) if template_cached? template_name
271
- load_template template_name
272
- end
273
-
274
- def template_cached? name
275
- !cached_template(name).nil?
276
- end
277
-
278
- def cached_template name
279
- templated_cache[name]
280
- end
281
-
282
- def cache name, template
283
- return unless use_cache
284
- templated_cache[name] = template
285
- end
286
-
287
- def load_template template_name
288
- url = template_url template_name
289
- source = open(url, "r:UTF-8").read
290
- template = ERB.new(source).result binding
291
- cache template_name, template
292
- template
293
- end
294
-
295
- def template_url template_name
296
- host = asset_host.dup
297
- host += '/' unless host =~ /\/$/
298
- "#{host}templates/#{template_name}.html.erb"
299
- end
300
-
301
- def error(request, template_name, body)
302
- processors = [
303
- TitleInserter.new()
304
- ]
305
- process(processors, body, template(template_name))
306
- end
307
-
308
- def process(processors,body,template)
309
- src = Nokogiri::HTML.parse(body.to_s)
310
- dest = Nokogiri::HTML.parse(template)
311
-
312
- processors.each do |p|
313
- p.filter(src,dest)
314
- end
315
-
316
- return dest.to_html
317
- end
318
-
319
- def admin(request,body)
320
- processors = [
321
- TitleInserter.new(),
322
- TagMover.new(),
323
- AdminTitleInserter.new,
324
- FooterRemover.new,
325
- BodyInserter.new(),
326
- BodyClassCopier.new
327
- ]
328
- process(processors,body,template('admin'))
329
- end
330
-
331
- def success(request,body)
332
- processors = [
333
- TitleInserter.new(),
334
- TagMover.new(),
335
- BodyInserter.new(),
336
- BodyClassCopier.new,
337
- SectionInserter.new()
338
- ]
339
-
340
- template_name = request.env.has_key?(TEMPLATE_HEADER) ? request.env[TEMPLATE_HEADER] : 'wrapper'
341
- process(processors,body,template(template_name))
342
- end
343
- end
344
-
345
-
10
+ autoload :Version, 'slimmer/version'
11
+ autoload :Railtie, 'slimmer/railtie'
12
+ autoload :Skin, 'slimmer/skin'
13
+
14
+ autoload :Template, 'slimmer/template'
15
+ autoload :App, 'slimmer/app'
16
+ autoload :TitleInserter, 'slimmer/title_inserter'
17
+ autoload :AdminTitleInserter, 'slimmer/admin_title_inserter'
18
+ autoload :SectionInserter, 'slimmer/section_inserter'
19
+ autoload :TagMover, 'slimmer/tag_mover'
20
+ autoload :FooterRemover, 'slimmer/footer_remover'
21
+ autoload :BodyInserter, 'slimmer/body_inserter'
22
+ autoload :BodyClassCopier, 'slimmer/body_class_copier'
23
+ autoload :UrlRewriter, 'slimmer/url_rewriter'
24
+ autoload :HeaderContextInserter, 'slimmer/header_context_inserter'
346
25
  end
@@ -0,0 +1,12 @@
1
+ module Slimmer
2
+ class AdminTitleInserter
3
+ def filter(src,dest)
4
+ title = src.at_css('#site-title')
5
+ head = dest.at_css('.gds-header h2')
6
+ if head && title
7
+ head.content = title.content
8
+ title.remove
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,84 @@
1
+ module Slimmer
2
+ class App
3
+ def initialize(app, *args, &block)
4
+ options = args.first || {}
5
+ @app = app
6
+
7
+ if options.key? :template_path
8
+ raise "Template path should not be used. Use asset_host instead."
9
+ end
10
+
11
+ unless options[:asset_host]
12
+ options[:asset_host] = Plek.current.find("assets")
13
+ end
14
+
15
+ @skin = Skin.new options[:asset_host], options[:cache_templates]
16
+ end
17
+
18
+ def call(env)
19
+ response_array = @app.call(env)
20
+ if response_array[1][SKIP_HEADER]
21
+ response_array
22
+ else
23
+ rewrite_response(env, response_array)
24
+ end
25
+ end
26
+
27
+ def on_success(request,body)
28
+ @skin.success(request, body)
29
+ end
30
+
31
+ def admin(request,body)
32
+ @skin.admin(request,body)
33
+ end
34
+
35
+ def on_error(request, status, body)
36
+ @skin.error(request, '500', body)
37
+ end
38
+
39
+ def on_404(request,body)
40
+ @skin.error(request, '404', body)
41
+ end
42
+
43
+ def s(body)
44
+ return body.to_s unless body.respond_to?(:each)
45
+ b = ""
46
+ body.each {|a| b << a }
47
+ b
48
+ end
49
+
50
+ def rewrite_response(env,triplet)
51
+ status, headers, app_body = triplet
52
+ source_request = Rack::Request.new(env)
53
+ request = Rack::Request.new(headers)
54
+ if headers['Content-Type'] =~ /text\/html/ || headers['content-type'] =~ /text\/html/
55
+ case status.to_i
56
+ when 200
57
+ if headers[TEMPLATE_HEADER] == 'admin' || source_request.path =~ /^\/admin(\/|$)/
58
+ rewritten_body = admin(request,s(app_body))
59
+ else
60
+ rewritten_body = on_success(request,s(app_body))
61
+ end
62
+ when 301, 302, 304
63
+ rewritten_body = app_body
64
+ when 404
65
+ rewritten_body = on_404(request,s(app_body))
66
+ else
67
+ rewritten_body = on_error(request,status,s(app_body))
68
+ end
69
+ else
70
+ rewritten_body = app_body
71
+ end
72
+ rewritten_body = [rewritten_body] unless rewritten_body.respond_to?(:each)
73
+ [status, filter_headers(headers), rewritten_body]
74
+ end
75
+
76
+ def filter_headers(header_hash)
77
+ valid_keys = ['vary', 'set-cookie', 'location', 'content-type', 'expires', 'cache-control']
78
+ header_hash.keys.each do |key|
79
+ header_hash.delete(key) unless valid_keys.include?(key.downcase)
80
+ end
81
+ header_hash
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,13 @@
1
+ module Slimmer
2
+ class BodyClassCopier
3
+ def filter(src, dest)
4
+ src_body_tag = src.at_css("body")
5
+ dest_body_tag = dest.at_css('body')
6
+ if src_body_tag.has_attribute?("class")
7
+ combinded_classes = dest_body_tag.attr('class').to_s.split(/ +/)
8
+ combinded_classes << src_body_tag.attr('class').to_s.split(/ +/)
9
+ dest_body_tag.set_attribute("class", combinded_classes.join(' '))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module Slimmer
2
+ class BodyInserter
3
+ def initialize(path='#wrapper')
4
+ @path = path
5
+ end
6
+
7
+ def filter(src,dest)
8
+ body = src.fragment(src.at_css(@path))
9
+ dest.at_css(@path).replace(body)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Slimmer
2
+ class FooterRemover
3
+ def filter(src,dest)
4
+ footer = src.at_css("#footer")
5
+ footer.remove if footer
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,14 @@
1
+ module Slimmer
2
+ class HeaderContextInserter
3
+ def initialize(path='.header-context')
4
+ @path = path
5
+ end
6
+
7
+ def filter(src,dest)
8
+ if dest.at_css(@path) && replacement = src.at_css(@path)
9
+ header_context = src.fragment(replacement)
10
+ dest.at_css(@path).replace(header_context)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Slimmer
2
+ class SectionInserter
3
+ def filter(src,dest)
4
+ meta_name = dest.at_css('meta[name="x-section-name"]')
5
+ meta_link = dest.at_css('meta[name="x-section-link"]')
6
+ list = dest.at_css('nav[role=navigation] ol')
7
+
8
+ # FIXME: Presumably this is meant to stop us adding a 'current section'
9
+ # link if we're missing navigation, or x-section-* meta tags.
10
+ # It doesn't work: #at_css will return a truthy object in any case.
11
+ if meta_name && meta_link && list
12
+ link_node = Nokogiri::XML::Node.new('a', dest)
13
+ link_node['href'] = meta_link['content']
14
+ link_node.content = meta_name['content']
15
+
16
+ list_item = Nokogiri::XML::Node.new('li', dest)
17
+ list_item.add_child(link_node)
18
+
19
+ list.first_element_child.after(list_item)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,96 @@
1
+ module Slimmer
2
+ class Skin
3
+ attr_accessor :use_cache
4
+ private :use_cache=, :use_cache
5
+
6
+ attr_accessor :templated_cache
7
+ private :templated_cache=, :templated_cache
8
+
9
+ attr_accessor :asset_host
10
+ private :asset_host=, :asset_host
11
+
12
+ # TODO: Extract the cache to something we can pass in instead of using
13
+ # true/false and an in-memory cache.
14
+ def initialize asset_host, use_cache = false
15
+ self.asset_host = asset_host
16
+ self.templated_cache = {}
17
+ self.use_cache = false
18
+ end
19
+
20
+ def template(template_name)
21
+ return cached_template(template_name) if template_cached? template_name
22
+ load_template template_name
23
+ end
24
+
25
+ def template_cached? name
26
+ !cached_template(name).nil?
27
+ end
28
+
29
+ def cached_template name
30
+ templated_cache[name]
31
+ end
32
+
33
+ def cache name, template
34
+ return unless use_cache
35
+ templated_cache[name] = template
36
+ end
37
+
38
+ def load_template template_name
39
+ url = template_url template_name
40
+ source = open(url, "r:UTF-8").read
41
+ template = ERB.new(source).result binding
42
+ cache template_name, template
43
+ template
44
+ end
45
+
46
+ def template_url template_name
47
+ host = asset_host.dup
48
+ host += '/' unless host =~ /\/$/
49
+ "#{host}templates/#{template_name}.html.erb"
50
+ end
51
+
52
+ def error(request, template_name, body)
53
+ processors = [
54
+ TitleInserter.new()
55
+ ]
56
+ process(processors, body, template(template_name))
57
+ end
58
+
59
+ def process(processors,body,template)
60
+ src = Nokogiri::HTML.parse(body.to_s)
61
+ dest = Nokogiri::HTML.parse(template)
62
+
63
+ processors.each do |p|
64
+ p.filter(src,dest)
65
+ end
66
+
67
+ return dest.to_html
68
+ end
69
+
70
+ def admin(request,body)
71
+ processors = [
72
+ TitleInserter.new(),
73
+ TagMover.new(),
74
+ AdminTitleInserter.new,
75
+ FooterRemover.new,
76
+ BodyInserter.new(),
77
+ BodyClassCopier.new
78
+ ]
79
+ process(processors,body,template('admin'))
80
+ end
81
+
82
+ def success(request,body)
83
+ processors = [
84
+ TitleInserter.new(),
85
+ TagMover.new(),
86
+ BodyInserter.new(),
87
+ BodyClassCopier.new,
88
+ HeaderContextInserter.new(),
89
+ SectionInserter.new()
90
+ ]
91
+
92
+ template_name = request.env.has_key?(TEMPLATE_HEADER) ? request.env[TEMPLATE_HEADER] : 'wrapper'
93
+ process(processors,body,template(template_name))
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,34 @@
1
+ module Slimmer
2
+ class TagMover
3
+ def filter(src,dest)
4
+ move_tags(src, dest, 'script', :must_have => ['src'])
5
+ move_tags(src, dest, 'link', :must_have => ['href'])
6
+ move_tags(src, dest, 'meta', :must_have => ['name', 'content'], :keys => ['name', 'content', 'http-equiv'])
7
+ end
8
+
9
+ def include_tag?(node, min_attrs)
10
+ min_attrs.inject(true) { |all_okay, attr_name| all_okay && node.has_attribute?(attr_name) }
11
+ end
12
+
13
+ def tag_fingerprint(node, attrs)
14
+ attrs.collect do |attr_name|
15
+ node.has_attribute?(attr_name) ? node.attr(attr_name) : nil
16
+ end.compact.sort
17
+ end
18
+
19
+ def move_tags(src, dest, type, opts)
20
+ comparison_attrs = opts[:keys] || opts[:must_have]
21
+ min_attrs = opts[:must_have]
22
+ already_there = dest.css(type).map { |node|
23
+ tag_fingerprint(node, comparison_attrs)
24
+ }.compact
25
+
26
+ src.css(type).each do |node|
27
+ if include_tag?(node, min_attrs) && !already_there.include?(tag_fingerprint(node, comparison_attrs))
28
+ node.remove
29
+ dest.at_xpath('/html/head') << node
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ module Slimmer
2
+ class Skin
3
+ def load_template name
4
+ %q{
5
+ <html>
6
+ <head>
7
+ <title>Test Template</title>
8
+ <script src="http://static.dev.alphagov.co.uk/javascripts/libs/jquery/jquery-1.6.2.min.js"></script><!-- no defer on jquery -->
9
+ <script src="http://static.dev.alphagov.co.uk/javascripts/libs/jquery/jquery-ui-1.8.16.custom.min.js" defer></script>
10
+ <script src="http://static.dev.alphagov.co.uk/javascripts/libs/jquery/plugins/jquery.base64.js" defer></script>
11
+ <script src="http://static.dev.alphagov.co.uk/javascripts/search.js" defer></script>
12
+ <script src="http://static.dev.alphagov.co.uk/javascripts/devolution.js" defer></script>
13
+ <script src="http://static.dev.alphagov.co.uk/javascripts/popup.js" defer></script>
14
+ <script src="http://static.dev.alphagov.co.uk/javascripts/feedback.js" defer></script>
15
+ <script src="http://static.dev.alphagov.co.uk/javascripts/customisation-settings.js" defer></script>
16
+ </head>
17
+ <body></body>
18
+ </html>
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,19 @@
1
+ module Slimmer
2
+ class TitleInserter
3
+ def filter(src,dest)
4
+ title = src.at_css('head title')
5
+ head = dest.at_xpath('/html/head')
6
+ if head && title
7
+ insert_title(title,head)
8
+ end
9
+ end
10
+
11
+ def insert_title(title, head)
12
+ if head.at_css('title').nil?
13
+ head.first_element_child.nil? ? head << title : head.first_element_child.before(title)
14
+ else
15
+ head.at_css('title').replace(title)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ module Slimmer
2
+ class UrlRewriter
3
+ def initialize(request)
4
+ @request = request
5
+ end
6
+
7
+ def filter(src,dest)
8
+ rewrite_document src
9
+ end
10
+
11
+ def rewrite_document(doc)
12
+ rewrite_nodes doc.css('body img'),'src'
13
+ rewrite_nodes doc.css('script'),'src'
14
+ rewrite_nodes doc.css('link'),'href'
15
+ end
16
+
17
+ def rewrite_nodes(nodes,attr)
18
+ nodes.each do |node|
19
+ next unless node.attr(attr)
20
+ node_uri = URI.parse(node.attr(attr))
21
+ node.attribute(attr).value = rewrite_url(node_uri).to_s
22
+ end
23
+ end
24
+
25
+ def rewrite_url(uri)
26
+ unless uri.absolute?
27
+ uri.scheme = @request.scheme
28
+ if @request.host =~ /:/
29
+ host,port = @request.host.split(":")
30
+ uri.host = host
31
+ uri.port = port
32
+ else
33
+ uri.host = @request.host
34
+ uri.port = @request.port
35
+ end
36
+ end
37
+ uri
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module Slimmer
2
- VERSION = '1.1.3'
2
+ VERSION = '1.1.4beta2'
3
3
  end
@@ -6,7 +6,9 @@
6
6
  <body>
7
7
  <div class="content">
8
8
  <header><h1>I AM A TITLE</h1></header>
9
- <nav role="navigation"><ol><li>MySite</li></ol></nav>
9
+ <div class="header-context">
10
+ <nav role="navigation"><ol><li>MySite</li></ol></nav>
11
+ </div>
10
12
  <div id="wrapper" class="group">
11
13
  </div>
12
14
  </div>
@@ -0,0 +1,51 @@
1
+ require "test_helper"
2
+
3
+ class HeaderContextInserterTest < MiniTest::Unit::TestCase
4
+ def test_should_replace_contents_of_header_context_in_template
5
+ template = as_nokogiri %{
6
+ <html><body><div><div class="header-context"></div></div></body></html>
7
+ }
8
+ source = as_nokogiri %{
9
+ <html><body><nav></nav><div class="header-context"><p>this should be moved</p></div></body></html>
10
+ }
11
+
12
+ Slimmer::HeaderContextInserter.new.filter(source, template)
13
+ assert_in template, ".header-context", %{<p>this should be moved</p>}
14
+ end
15
+
16
+ def test_should_replace_classes_of_header_context_in_template
17
+ template = as_nokogiri %{
18
+ <html><body><div><div class="header-context template-class"></div></div></body></html>
19
+ }
20
+ source = as_nokogiri %{
21
+ <html><body><nav></nav><div class="header-context app-class"><p>this should be moved</p></div></body></html>
22
+ }
23
+
24
+ Slimmer::HeaderContextInserter.new.filter(source, template)
25
+ assert_in template, ".header-context.app-class", %{<p>this should be moved</p>}
26
+ assert_not_in template, ".header-context.template-class"
27
+ end
28
+
29
+ def test_should_do_nothing_if_no_header_context_was_provided
30
+ template = as_nokogiri %{
31
+ <html><body><div><div class="header-context">should not be removed</div></div></body></html>
32
+ }
33
+ source = as_nokogiri %{
34
+ <html><body><nav></nav></body></html>
35
+ }
36
+
37
+ Slimmer::HeaderContextInserter.new.filter(source, template)
38
+ assert_in template, ".header-context", %{should not be removed}
39
+ end
40
+
41
+ def test_should_do_nothing_if_no_header_context_was_present_in_the_template
42
+ template = as_nokogiri %{
43
+ <html><body><div></div></body></html>
44
+ }
45
+ source = as_nokogiri %{
46
+ <html><body><div><div class="header-context">should be ignored</div></div></body></html>
47
+ }
48
+
49
+ Slimmer::HeaderContextInserter.new.filter(source, template) # should not raise
50
+ end
51
+ end
data/test/test_helper.rb CHANGED
@@ -13,6 +13,10 @@ class MiniTest::Unit::TestCase
13
13
  def assert_in(template, selector, content, message=nil)
14
14
  assert_equal content, template.at_css(selector).inner_html.to_s, message
15
15
  end
16
+
17
+ def assert_not_in(template, selector, message="didn't exist to find #{selector}")
18
+ refute template.at_css(selector), message
19
+ end
16
20
  end
17
21
 
18
22
  class SlimmerIntegrationTest < MiniTest::Unit::TestCase
@@ -11,7 +11,6 @@ module TypicalUsage
11
11
  end
12
12
 
13
13
  class NormalResponseTest < SlimmerIntegrationTest
14
-
15
14
  given_response 200, %{
16
15
  <html>
17
16
  <head><title>The title of the page</title>
@@ -56,6 +55,28 @@ module TypicalUsage
56
55
  end
57
56
  end
58
57
 
58
+ class HeaderContextResponseTest < SlimmerIntegrationTest
59
+ given_response 200, %{
60
+ <html>
61
+ <head><title>The title of the page</title>
62
+ <meta name="something" content="yes">
63
+ <meta name="x-section-name" content="This section">
64
+ <meta name="x-section-link" content="/this_section">
65
+ <script src="blah.js"></script>
66
+ <link href="app.css" rel="stylesheet" type="text/css">
67
+ </head>
68
+ <body class="body_class">
69
+ <div class="header-context custom-class">app-specific header context</div>
70
+ <div id="wrapper">The body of the page</div>
71
+ </body>
72
+ </html>
73
+ }
74
+
75
+ def test_should_replace_the_header_context_using_the_app_response
76
+ assert_rendered_in_template ".header-context.custom-class", "app-specific header context"
77
+ end
78
+ end
79
+
59
80
  class Error500ResponseTest < SlimmerIntegrationTest
60
81
  include Rack::Test::Methods
61
82
 
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
5
- prerelease:
4
+ version: 1.1.4beta2
5
+ prerelease: 5
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ben Griffiths
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-11-24 00:00:00.000000000Z
13
+ date: 2011-11-29 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: nokogiri
17
- requirement: &70200486758560 !ruby/object:Gem::Requirement
17
+ requirement: &70348230280000 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.5.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *70200486758560
25
+ version_requirements: *70348230280000
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rack
28
- requirement: &70200486758020 !ruby/object:Gem::Requirement
28
+ requirement: &70348230276740 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 1.3.5
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *70200486758020
36
+ version_requirements: *70348230276740
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: plek
39
- requirement: &70200486757440 !ruby/object:Gem::Requirement
39
+ requirement: &70348230274580 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 0.1.8
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *70200486757440
47
+ version_requirements: *70348230274580
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: rake
50
- requirement: &70200486756460 !ruby/object:Gem::Requirement
50
+ requirement: &70348230264040 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 0.9.2.2
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *70200486756460
58
+ version_requirements: *70348230264040
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: rack-test
61
- requirement: &70200486755740 !ruby/object:Gem::Requirement
61
+ requirement: &70348230253520 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: 0.6.1
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *70200486755740
69
+ version_requirements: *70348230253520
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: mocha
72
- requirement: &70200486755240 !ruby/object:Gem::Requirement
72
+ requirement: &70348230212940 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ~>
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: 0.9.12
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *70200486755240
80
+ version_requirements: *70348230212940
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: webmock
83
- requirement: &70200486754780 !ruby/object:Gem::Requirement
83
+ requirement: &70348230198760 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ~>
@@ -88,7 +88,7 @@ dependencies:
88
88
  version: '1.7'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *70200486754780
91
+ version_requirements: *70348230198760
92
92
  description: Rack middleware for skinning pages using a specific template
93
93
  email:
94
94
  - bengriffiths@gmail.com
@@ -99,8 +99,20 @@ extra_rdoc_files: []
99
99
  files:
100
100
  - README.md
101
101
  - CHANGELOG.md
102
+ - lib/slimmer/admin_title_inserter.rb
103
+ - lib/slimmer/app.rb
104
+ - lib/slimmer/body_class_copier.rb
105
+ - lib/slimmer/body_inserter.rb
106
+ - lib/slimmer/footer_remover.rb
107
+ - lib/slimmer/header_context_inserter.rb
102
108
  - lib/slimmer/railtie.rb
109
+ - lib/slimmer/section_inserter.rb
110
+ - lib/slimmer/skin.rb
111
+ - lib/slimmer/tag_mover.rb
103
112
  - lib/slimmer/template.rb
113
+ - lib/slimmer/test.rb
114
+ - lib/slimmer/title_inserter.rb
115
+ - lib/slimmer/url_rewriter.rb
104
116
  - lib/slimmer/version.rb
105
117
  - lib/slimmer.rb
106
118
  - lib/tasks/slimmer.rake
@@ -109,6 +121,7 @@ files:
109
121
  - test/fixtures/500.html.erb
110
122
  - test/fixtures/wrapper.html.erb
111
123
  - test/processors/body_inserter_test.rb
124
+ - test/processors/header_context_inserter_test.rb
112
125
  - test/slimmer_test.rb
113
126
  - test/test_helper.rb
114
127
  - test/typical_usage_test.rb
@@ -127,9 +140,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
140
  required_rubygems_version: !ruby/object:Gem::Requirement
128
141
  none: false
129
142
  requirements:
130
- - - ! '>='
143
+ - - ! '>'
131
144
  - !ruby/object:Gem::Version
132
- version: '0'
145
+ version: 1.3.1
133
146
  requirements: []
134
147
  rubyforge_project: slimmer
135
148
  rubygems_version: 1.8.10
@@ -141,6 +154,7 @@ test_files:
141
154
  - test/fixtures/500.html.erb
142
155
  - test/fixtures/wrapper.html.erb
143
156
  - test/processors/body_inserter_test.rb
157
+ - test/processors/header_context_inserter_test.rb
144
158
  - test/slimmer_test.rb
145
159
  - test/test_helper.rb
146
160
  - test/typical_usage_test.rb