awestruct 0.5.6.beta8 → 0.5.6.beta9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -4
  3. data/Rakefile +1 -1
  4. data/awestruct.gemspec +22 -18
  5. data/lib/awestruct/cli/auto.rb +25 -21
  6. data/lib/awestruct/cli/generate.rb +3 -2
  7. data/lib/awestruct/cli/init.rb +2 -1
  8. data/lib/awestruct/cli/invoker.rb +4 -3
  9. data/lib/awestruct/cli/manifest.rb +41 -0
  10. data/lib/awestruct/cli/options.rb +10 -1
  11. data/lib/awestruct/cli/server.rb +36 -6
  12. data/lib/awestruct/config/default-site.yml +3 -0
  13. data/lib/awestruct/context.rb +17 -0
  14. data/lib/awestruct/context_helper.rb +25 -21
  15. data/lib/awestruct/deploy/s3_deploy.rb +41 -3
  16. data/lib/awestruct/engine.rb +64 -33
  17. data/lib/awestruct/extensions/disqus.rb +2 -2
  18. data/lib/awestruct/extensions/flattr.rb +1 -1
  19. data/lib/awestruct/extensions/pipeline.rb +30 -8
  20. data/lib/awestruct/extensions/relative.rb +3 -1
  21. data/lib/awestruct/extensions/sitemap.rb +6 -0
  22. data/lib/awestruct/extensions/sitemap.xml.haml +2 -1
  23. data/lib/awestruct/extensions/tagger.rb +7 -6
  24. data/lib/awestruct/extensions/template.atom.haml +1 -1
  25. data/lib/awestruct/frameworks/base_Gemfile +12 -1
  26. data/lib/awestruct/frameworks/bootstrap/base_index.html.haml +22 -20
  27. data/lib/awestruct/frameworks/bootstrap/base_layout.html.haml +9 -7
  28. data/lib/awestruct/handlers/asciidoctor_handler.rb +1 -1
  29. data/lib/awestruct/handlers/base_tilt_handler.rb +3 -1
  30. data/lib/awestruct/pipeline.rb +57 -7
  31. data/lib/awestruct/rack/generate.rb +43 -0
  32. data/lib/awestruct/util/exception_helper.rb +3 -0
  33. data/lib/awestruct/version.rb +1 -1
  34. data/spec/awestruct/cli/invoker_spec.rb +1 -1
  35. data/spec/awestruct/cli/options_spec.rb +14 -13
  36. data/spec/awestruct/cli/server_spec.rb +15 -0
  37. data/spec/awestruct/context_helper_spec.rb +5 -5
  38. data/spec/awestruct/engine_spec.rb +110 -1
  39. data/spec/awestruct/extensions/relative_spec.rb +22 -0
  40. data/spec/awestruct/handlers/asciidoc_handler_spec.rb +7 -1
  41. data/spec/awestruct/handlers/layout_handler_spec.rb +7 -7
  42. data/spec/awestruct/handlers/tilt_handler_spec.rb +1 -1
  43. data/spec/awestruct/pipeline_spec.rb +27 -2
  44. data/spec/awestruct/scm/git_spec.rb +4 -4
  45. data/spec/spec_helper.rb +6 -1
  46. data/spec/support/emmet_matchers.rb +1 -1
  47. data/spec/support/nokogiri_matchers.rb +1 -1
  48. data/spec/support/shared_handler_example.rb +66 -71
  49. data/spec/support/test-data/engine-generate-no-errors/_config/site.yml +26 -0
  50. data/spec/support/test-data/engine-generate-no-errors/_ext/pipeline.rb +8 -0
  51. data/spec/support/test-data/engine-generate-no-errors/_layouts/base.html.slim +6 -0
  52. data/spec/support/test-data/engine-generate-no-errors/index.html.slim +5 -0
  53. data/spec/support/test-data/engine-generate-with-errors/_config/site.yml +26 -0
  54. data/spec/support/test-data/engine-generate-with-errors/_ext/pipeline.rb +8 -0
  55. data/spec/support/test-data/engine-generate-with-errors/_layouts/base.html.slim +6 -0
  56. data/spec/support/test-data/engine-generate-with-errors/index.html.slim +6 -0
  57. data/spec/support/test-data/engine-yaml/_config/site.yml +9 -0
  58. data/spec/support/test-data/pipeline/_ext/extensions.rb +26 -0
  59. data/spec/support/test-data/pipeline/_ext/pipeline.rb +12 -0
  60. metadata +229 -72
@@ -1,8 +1,8 @@
1
- require 'rexml/document'
1
+ require 'oga'
2
+ require 'awestruct/util/exception_helper'
2
3
 
3
4
  module Awestruct
4
5
  module ContextHelper
5
- include REXML
6
6
 
7
7
  def html_to_text(str)
8
8
  str.gsub( /<[^>]+>/, '' ).gsub( /&nbsp;/, ' ' )
@@ -39,27 +39,31 @@ module Awestruct
39
39
  end
40
40
 
41
41
  def fully_qualify_urls(base_url, text)
42
- doc = Document.new text
43
- doc.context[:attribute_quote] = :quote # Set double-quote as the attribute value delimiter
42
+ begin
43
+ doc = Oga.parse_html text
44
44
 
45
- XPath.each(doc, "//a") do |a|
46
- a.attributes['href'] = fix_url( base_url, a.attributes['href'] ) if a.attributes['href']
47
- end
48
-
49
- XPath.each(doc, "//link") do |link|
50
- link.attributes['href'] = fix_url( base_url, link.attributes['href'] )
51
- end
52
-
53
- XPath.each(doc, "//img") do |img|
54
- img.attributes['src'] = fix_url( base_url, img.attributes['src'] )
55
- end
45
+ doc.each_node do |elem|
46
+ if elem.is_a?(Oga::XML::Element) && elem.html?
47
+ case elem.name
48
+ when 'a'
49
+ elem.set 'href', fix_url(base_url, elem.get('href')) if elem.get('href')
50
+ when 'link'
51
+ elem.set 'href', fix_url(base_url, elem.get('href')) if elem.get('href')
52
+ when 'img'
53
+ elem.set 'src', fix_url(base_url, elem.get('src')) if elem.get('src')
54
+ end
55
+ end
56
+ end
56
57
 
57
- if RUBY_VERSION.start_with? '1.8'
58
- doc.to_s
59
- else
60
- doc.to_s.tap do |d|
61
- d.force_encoding(text.encoding) if d.encoding != text.encoding
62
- end
58
+ doc.to_xml.tap do |d|
59
+ d.force_encoding(text.encoding) if d.encoding != text.encoding
60
+ end
61
+ rescue => e
62
+ Awestruct::ExceptionHelper.log_error e
63
+ $LOG.info %Q(If the error has to do with 'end of input' ensure none of the following tags have a closing tag:
64
+ #{Oga::XML::HTML_VOID_ELEMENTS.to_a.collect {|a| a.downcase}.uniq.join(', ')}) if $LOG.info?
65
+ $LOG.warn "Text being parsed:\n#{text}" if $LOG.warn?
66
+ text # returning the bad text, which hopefully will help find the cause
63
67
  end
64
68
  end
65
69
 
@@ -1,5 +1,4 @@
1
1
  require 'awestruct/deploy/base_deploy'
2
- require 'ruby-s3cmd'
3
2
 
4
3
  module Awestruct
5
4
  module Deploy
@@ -7,14 +6,53 @@ module Awestruct
7
6
  def initialize( site_config, deploy_config )
8
7
  super
9
8
  @bucket = deploy_config['bucket']
9
+ @metadata = deploy_config['metadata']
10
10
  end
11
11
 
12
12
  def publish_site
13
13
  $LOG.info "Syncing #{@site_path} to bucket #{@bucket}" if $LOG.info?
14
- s3cmd = RubyS3Cmd::S3Cmd.new
15
- s3cmd.sync("#{@site_path}/", @bucket)
14
+ if @metadata and !@metadata.empty?
15
+ @metadata.each do |fileType, headers|
16
+ # Build the add-header command because the s3cmd-dsl gem doesn't support multi-headers
17
+ headerCmd = ""
18
+ if headers && !headers.empty?
19
+ headers.each do |key, value|
20
+ headerCmd << add_header(key, value)
21
+ end
22
+ end
23
+ # If gzip is enabled, add 'Content-Encoding: gzip' on js, css and html files
24
+ if @gzip and ['js', 'css', 'html'].include? fileType
25
+ headerCmd << add_header("Content-Encoding", "gzip")
26
+ end
27
+ # Sync files of current type with specified headers
28
+ s3_sync(@site_path, @bucket, "*", "*.#{fileType}", headerCmd)
29
+ end
30
+ end
31
+ # If gzip is enabled, add 'Content-Encoding: gzip' on not processed js, css and html files
32
+ if @gzip
33
+ remainingFileType = ['js', 'css', 'html'].find_all { |fileType| !@metadata.keys.include? fileType }
34
+ remainingFileType.each do |fileType|
35
+ headerCmd = add_header("Content-Encoding", "gzip")
36
+ s3_sync(@site_path, @bucket, "*", "*.#{fileType}", headerCmd)
37
+ end
38
+ end
39
+ # Finally, sync others files
40
+ s3_sync(@site_path, @bucket)
16
41
  $LOG.info "DONE" if $LOG.info?
17
42
  end
43
+
44
+ def add_header(key, value)
45
+ " --add-header '#{key}:#{value}'"
46
+ end
47
+
48
+ def s3_sync(site_path, bucket, exclude = nil, include = nil, headersCmd = nil)
49
+ cmd="s3cmd sync '#{site_path}' '#{bucket}'"
50
+ cmd << " --exclude '#{exclude}'" if exclude
51
+ cmd << " --include '#{include}'" if include
52
+ cmd << " #{headersCmd}" if headersCmd
53
+ $LOG.info "Execute #{cmd}"
54
+ `#{cmd}`
55
+ end
18
56
  end
19
57
  end
20
58
  end
@@ -13,6 +13,7 @@ require 'awestruct/extensions/pipeline'
13
13
  require 'fileutils'
14
14
  require 'set'
15
15
  require 'date'
16
+ require 'erb'
16
17
 
17
18
  require 'compass'
18
19
  require 'parallel'
@@ -52,7 +53,7 @@ module Awestruct
52
53
  site.config
53
54
  end
54
55
 
55
- def run(profile, base_url, default_base_url, force=false)
56
+ def run(profile, base_url, default_base_url, force=false, generate=true)
56
57
  $LOG.debug 'adjust_load_path' if $LOG.debug?
57
58
  adjust_load_path
58
59
  $LOG.debug 'load_default_site_yaml' if $LOG.debug?
@@ -69,27 +70,32 @@ module Awestruct
69
70
  load_pages
70
71
  $LOG.debug 'execute_pipeline' if $LOG.debug?
71
72
  $LOG.info 'Excecuting pipeline...' if $LOG.info?
72
- execute_pipeline
73
+ execute_pipeline(false)
73
74
  $LOG.debug 'configure_compass' if $LOG.debug?
74
75
  configure_compass
75
76
  $LOG.debug 'set_urls' if $LOG.debug?
76
77
  set_urls( site.pages )
77
78
  $LOG.debug 'build_page_index' if $LOG.debug?
78
79
  build_page_index
79
- $LOG.debug 'generate_output' if $LOG.debug?
80
- $LOG.info 'Generating pages...' if $LOG.info?
81
- generate_output
82
- return 0
80
+
81
+ if ( generate )
82
+ $LOG.debug 'generate_output' if $LOG.debug?
83
+ $LOG.info 'Generating pages...' if $LOG.info?
84
+ generate_output
85
+ end
86
+ return Awestruct::ExceptionHelper::EXITCODES[:success]
83
87
  end
84
88
 
85
89
  def build_page_index
86
90
  site.pages_by_relative_source_path = {}
91
+ site.pages_by_output_path = {}
87
92
  site.pages.each do |p|
88
93
  # Add the layout to the set of dependencies
89
94
  p.dependencies.add_dependency(site.layouts.find_matching(p.layout, p.output_extension))
90
95
  if ( p.relative_source_path )
91
96
  site.pages_by_relative_source_path[ p.relative_source_path ] = p
92
97
  end
98
+ site.pages_by_output_path[ p.output_path ] = p
93
99
  end
94
100
  site.layouts.each do |p|
95
101
  # Add the layout to the set of dependencies
@@ -137,7 +143,7 @@ module Awestruct
137
143
  def load_site_yaml(yaml_path, profile = nil)
138
144
  if ( File.exist?( yaml_path ) )
139
145
  begin
140
- data = YAML.load( File.read( yaml_path, :encoding => 'bom|utf-8' ) )
146
+ data = YAML.load( ERB.new(File.read( yaml_path, :encoding => 'bom|utf-8' )).result )
141
147
  if ( profile )
142
148
  # JP: Interpolation now turned off by default, turn it per page if needed
143
149
  site.interpolate = false
@@ -167,7 +173,7 @@ module Awestruct
167
173
 
168
174
  def load_yaml(yaml_path)
169
175
  begin
170
- data = YAML.load( File.read( yaml_path ) )
176
+ data = YAML.load( ERB.new(File.read( yaml_path )).result )
171
177
  rescue Exception => e
172
178
  ExceptionHelper.log_building_error e, yaml_path
173
179
  ExceptionHelper.mark_failed
@@ -241,22 +247,31 @@ module Awestruct
241
247
  pipeline_file = File.join( ext_dir, 'pipeline.rb' )
242
248
  if ( File.exists?( pipeline_file ) )
243
249
  p = eval(File.read( pipeline_file ), nil, pipeline_file, 1)
250
+ p.before_all_extensions.each do |e|
251
+ pipeline.add_before_extension( e )
252
+ end
244
253
  p.extensions.each do |e|
245
254
  pipeline.extension( e )
246
255
  end
256
+ p.after_all_extensions.each do |e|
257
+ pipeline.add_after_extension( e )
258
+ end
247
259
  p.helpers.each do |h|
248
260
  pipeline.helper( h )
249
261
  end
250
262
  p.transformers.each do |t|
251
263
  pipeline.transformer( t )
252
264
  end
265
+ p.after_generation_extensions.each do |e|
266
+ pipeline.add_after_generation_extension( e )
267
+ end
253
268
  end
254
269
  end
255
270
 
256
- def execute_pipeline
271
+ def execute_pipeline(on_reload = false)
257
272
  FileUtils.mkdir_p( site.config.output_dir )
258
273
  FileUtils.mkdir_p( site.config.tmp_dir )
259
- pipeline.execute( site )
274
+ pipeline.execute( site, on_reload )
260
275
  end
261
276
 
262
277
  def configure_compass
@@ -343,27 +358,36 @@ module Awestruct
343
358
 
344
359
  def generate_output
345
360
  FileUtils.mkdir_p( site.config.output_dir )
346
- Parallel.each(@site.pages, :in_processes => Parallel.processor_count) do |page|
347
- generated_path = File.join( site.config.output_dir, page.output_path )
348
- if ( page.stale_output?( generated_path ) )
349
- generate_page( page, generated_path )
350
- else
351
- generate_page( page, generated_path, false )
361
+ return_value = [Awestruct::ExceptionHelper::EXITCODES[:success]]
362
+ begin
363
+ return_value = Parallel.map(@site.pages, site.generation) do |page|
364
+ generate_page( page )
352
365
  end
366
+ rescue Exception => e
367
+ return_value = [Awestruct::ExceptionHelper::EXITCODES[:generation_error]]
368
+ end
369
+
370
+ if return_value.nil? || return_value.include?(Awestruct::ExceptionHelper::EXITCODES[:generation_error])
371
+ $LOG.error 'An error occurred during output generation, all pages may not have completed during generation'
372
+ exit Awestruct::ExceptionHelper::EXITCODES[:generation_error]
353
373
  end
374
+ site.engine.pipeline.execute_after_generation(site)
354
375
  end
355
376
 
356
- def generate_page(page, generated_path, produce_output=true)
377
+ def generate_page(page, produce_output=true)
357
378
  if ( produce_output )
358
379
  $LOG.debug "Generating: #{generated_path}" if $LOG.debug? && config.verbose
359
- FileUtils.mkdir_p( File.dirname( generated_path ) )
360
380
 
361
381
  c = page.rendered_content
362
382
  c = site.engine.pipeline.apply_transformers( site, page, c )
363
383
 
384
+ generated_path = File.join( site.config.output_dir, page.output_path )
385
+ FileUtils.mkdir_p( File.dirname( generated_path ) )
386
+
364
387
  File.open( generated_path, 'wb' ) do |file|
365
388
  file << c
366
389
  end
390
+ return Awestruct::ExceptionHelper::EXITCODES[:generation_error] if c.include? 'Backtrace:'
367
391
  elsif ( site.config.track_dependencies )
368
392
  if page.dependencies.load!
369
393
  $LOG.debug "Cached: #{generated_path}" if $LOG.debug?
@@ -371,6 +395,7 @@ module Awestruct
371
395
  $LOG.debug "Analyzing: #{generated_path}" if $LOG.debug?
372
396
  page.rendered_content
373
397
  end
398
+ return Awestruct::ExceptionHelper::EXITCODES[:success]
374
399
  end
375
400
  end
376
401
 
@@ -395,6 +420,19 @@ module Awestruct
395
420
  generate_page_internal(page)
396
421
  end
397
422
 
423
+ regen_pages = page_dependencies( page )
424
+
425
+ $LOG.debug "Starting regeneration of content dependent pages:" if regen_pages.size > 0 && $LOG.debug?
426
+
427
+ regen_pages.each do |p|
428
+ $LOG.info "Regenerating page #{p.output_path}" if $LOG.info?
429
+ generate_page_internal(p)
430
+ end
431
+
432
+ regen_pages
433
+ end
434
+
435
+ def page_dependencies(page)
398
436
  regen_pages = Set.new [ page ]
399
437
  regen_pages.merge page.dependencies.dependents
400
438
 
@@ -411,35 +449,28 @@ module Awestruct
411
449
  end
412
450
 
413
451
  regen_pages.merge temp_set
414
-
415
- $LOG.debug "Starting regeneration of content dependent pages:" if regen_pages.size > 0 && $LOG.debug?
416
-
417
- regen_pages.each do |p|
418
- $LOG.info "Regenerating page #{p.output_path}" if $LOG.info?
419
- generate_page_internal(p)
420
- end
421
-
422
- regen_pages
423
452
  end
424
453
 
425
- def run_auto_for_non_page(file)
454
+ def run_auto_for_non_page(file, generate = true)
426
455
  if File.extname(file) == '.rb'
427
456
  load file
428
457
  end
429
458
  @pipeline = Pipeline.new
430
459
  load_yamls
431
460
  load_pipeline
432
- execute_pipeline
433
- site.pages.each do |p|
434
- generate_page_internal(p)
461
+ execute_pipeline(true)
462
+
463
+ if ( generate )
464
+ site.pages.each do |p|
465
+ generate_page_internal(p)
466
+ end
435
467
  end
436
468
  site.pages
437
469
  end
438
470
 
439
471
  def generate_page_internal(p)
440
472
  unless ( p.output_path.nil? || p.__is_layout || !p.stale_output?(p.output_path) )
441
- generated_path = File.join( site.config.output_dir, p.output_path )
442
- generate_page( p, generated_path )
473
+ generate_page( p )
443
474
  end
444
475
  end
445
476
 
@@ -24,7 +24,7 @@ module Awestruct
24
24
  var disqus_identifier = #{identifier};
25
25
  (function() {
26
26
  var dsq = document.createElement("script"); dsq.type = "text/javascript"; dsq.async = true;
27
- dsq.src = "http://#{site.disqus}.disqus.com/embed.js";
27
+ dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
28
28
  (document.getElementsByTagName("head")[0] || document.getElementsByTagName("body")[0]).appendChild(dsq);
29
29
  })();
30
30
  </script>
@@ -46,7 +46,7 @@ module Awestruct
46
46
  var disqus_shortname = '#{site.disqus}';
47
47
  (function () {
48
48
  var s = document.createElement('script'); s.async = true;
49
- s.src = "http://disqus.com/forums/#{site.disqus}/count.js";
49
+ s.src = '//' + disqus_shortname + '.disqus.com/count.js';
50
50
  (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
51
51
  }());
52
52
  </script>
@@ -12,7 +12,7 @@ module Awestruct
12
12
  html += %Q|var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];|
13
13
  html += %Q|s.type = 'text/javascript';\n|
14
14
  html += %Q|s.async = true;\n|
15
- html += %Q|s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto&uid=#{site.flattr_username}&category=text';\n|
15
+ html += %Q|s.src = '//api.flattr.com/js/0.6/load.js?mode=auto&uid=#{site.flattr_username}&category=text';\n|
16
16
  html += %Q|t.parentNode.insertBefore(s, t);\n|
17
17
  html += %Q|})(); /* ]]> */ </script>|
18
18
  html
@@ -11,29 +11,47 @@ end
11
11
 
12
12
  module Awestruct
13
13
  module Extensions
14
+ # Public. Extension declaration class, initialized by the end user to
15
+ # declare their extensions, helpers, transformers, etc.
14
16
  class Pipeline
15
17
 
16
- attr_reader :before_extensions
18
+ attr_reader :before_all_extensions
17
19
  attr_reader :extensions
18
- attr_reader :after_extensions
20
+ attr_reader :after_all_extensions
19
21
  attr_reader :helpers
20
22
  attr_reader :transformers
23
+ attr_reader :after_generation_extensions
21
24
 
22
25
  def initialize(&block)
23
- @extensions = []
24
- @helpers = []
25
- @transformers = []
26
+ @before_all_extensions = []
27
+ @extensions = []
28
+ @helpers = []
29
+ @transformers = []
30
+ @after_all_extensions = []
31
+ @after_generation_extensions = []
26
32
  begin
27
- instance_eval &block if block
33
+ instance_eval(&block) if block
28
34
  rescue Exception => e
29
35
  abort("Failed to initialize pipeline: #{e}")
30
36
  end
31
37
  end
32
38
 
39
+ def before_extensions(ext)
40
+ @before_all_extensions << ext
41
+ end
42
+
33
43
  def extension(ext)
34
- @extensions << ext
44
+ @extensions << ext if ext.respond_to?(:execute)
35
45
  # TC: why? transformer and extension?
36
- ext.transform(@transformers) if ext.respond_to?('transform')
46
+ transformer(ext) if ext.respond_to?(:transform)
47
+
48
+ @before_all_extensions << ext if ext.respond_to?(:before_extensions)
49
+ @after_all_extensions << ext if ext.respond_to?(:after_extensions)
50
+ @after_generation_extensions << ext if ext.respond_to?(:after_generation)
51
+ end
52
+
53
+ def after_extensions(ext)
54
+ @after_all_extensions << ext
37
55
  end
38
56
 
39
57
  def helper(helper)
@@ -44,6 +62,10 @@ module Awestruct
44
62
  @transformers << transformer
45
63
  end
46
64
 
65
+ def after_generation(ext)
66
+ @after_generation_extensions << ext
67
+ end
68
+
47
69
  def execute(site)
48
70
  extensions.each do |ext|
49
71
  ext.execute( site )
@@ -11,7 +11,9 @@ module Awestruct
11
11
  if href.start_with?("http://") || href.start_with?("https://")
12
12
  result = href
13
13
  else
14
- result = Pathname.new(href).relative_path_from(Pathname.new(File.dirname(p.output_path))).to_s
14
+ pathname = Pathname.new(href).relative_path_from(Pathname.new(File.dirname(p.output_path)))
15
+ result = pathname.to_s
16
+ result << '/' if pathname.extname.empty?
15
17
  end
16
18
  result
17
19
  rescue Exception => e