awestruct 0.5.6.beta8 → 0.5.6.beta9

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.
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