slimmer 1.1.3 → 1.1.4beta2

Sign up to get free protection for your applications and to get access to all the features.
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