nanoc 3.7.5 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +2 -2
  3. data/Gemfile +1 -1
  4. data/NEWS.md +24 -3
  5. data/README.md +1 -1
  6. data/bin/nanoc +1 -1
  7. data/lib/nanoc/base/compilation/dependency_tracker.rb +1 -1
  8. data/lib/nanoc/base/compilation/item_rep_recorder_proxy.rb +1 -1
  9. data/lib/nanoc/base/compilation/rule.rb +15 -1
  10. data/lib/nanoc/base/result_data/item_rep.rb +27 -10
  11. data/lib/nanoc/base/source_data/item_array.rb +2 -0
  12. data/lib/nanoc/base/source_data/site.rb +1 -0
  13. data/lib/nanoc/cli.rb +16 -7
  14. data/lib/nanoc/cli/commands/autocompile.rb +1 -1
  15. data/lib/nanoc/cli/commands/create-site.rb +2 -2
  16. data/lib/nanoc/cli/commands/show-rules.rb +3 -6
  17. data/lib/nanoc/cli/commands/watch.rb +3 -3
  18. data/lib/nanoc/cli/error_handler.rb +1 -1
  19. data/lib/nanoc/cli/logger.rb +3 -5
  20. data/lib/nanoc/data_sources/filesystem.rb +2 -2
  21. data/lib/nanoc/extra/auto_compiler.rb +1 -1
  22. data/lib/nanoc/extra/checking/checks.rb +2 -0
  23. data/lib/nanoc/extra/checking/checks/mixed_content.rb +31 -0
  24. data/lib/nanoc/extra/deployers/fog.rb +24 -0
  25. data/lib/nanoc/extra/link_collector.rb +42 -9
  26. data/lib/nanoc/extra/pruner.rb +1 -1
  27. data/lib/nanoc/filters/colorize_syntax.rb +4 -4
  28. data/lib/nanoc/filters/pandoc.rb +23 -4
  29. data/lib/nanoc/filters/sass/sass_filesystem_importer.rb +4 -2
  30. data/lib/nanoc/helpers/blogging.rb +17 -6
  31. data/lib/nanoc/helpers/tagging.rb +1 -1
  32. data/lib/nanoc/helpers/text.rb +1 -1
  33. data/lib/nanoc/version.rb +1 -1
  34. data/tasks/test.rake +1 -1
  35. data/test/base/test_item_array.rb +8 -0
  36. data/test/base/test_item_rep.rb +51 -0
  37. data/test/base/test_item_rep_recorder_proxy.rb +19 -0
  38. data/test/base/test_rule.rb +10 -0
  39. data/test/cli/commands/test_create_site.rb +13 -0
  40. data/test/cli/test_cli.rb +30 -0
  41. data/test/extra/checking/checks/test_mixed_content.rb +188 -0
  42. data/test/extra/deployers/test_fog.rb +32 -0
  43. data/test/filters/test_pandoc.rb +17 -3
  44. data/test/filters/test_sass.rb +11 -0
  45. data/test/helper.rb +3 -3
  46. data/test/helpers/test_blogging.rb +30 -0
  47. metadata +6 -3
@@ -8,6 +8,10 @@ module Nanoc::Extra::Deployers
8
8
  # deploy:
9
9
  # public:
10
10
  # kind: fog
11
+ # bucket: nanoc-site
12
+ # cdn_id: XXXXXX
13
+ # preprod:
14
+ # kind: fog
11
15
  # provider: local
12
16
  # local_root: ~/myCloud
13
17
  # bucket: nanoc-site
@@ -25,6 +29,7 @@ module Nanoc::Extra::Deployers
25
29
  src = File.expand_path(source_path)
26
30
  bucket = config.delete(:bucket) || config.delete(:bucket_name)
27
31
  path = config.delete(:path)
32
+ cdn_id = config.delete(:cdn_id)
28
33
 
29
34
  config.delete(:kind)
30
35
 
@@ -33,6 +38,7 @@ module Nanoc::Extra::Deployers
33
38
 
34
39
  # Mock if necessary
35
40
  if self.dry_run?
41
+ puts 'Dry run - simulation'
36
42
  ::Fog.mock!
37
43
  end
38
44
 
@@ -51,6 +57,7 @@ module Nanoc::Extra::Deployers
51
57
 
52
58
  # Create bucket if necessary
53
59
  if should_create_bucket
60
+ puts 'Creating bucket'
54
61
  directory = connection.directories.create(key: bucket, prefix: path)
55
62
  end
56
63
 
@@ -63,6 +70,7 @@ module Nanoc::Extra::Deployers
63
70
  files += set
64
71
  end
65
72
  keys_to_destroy = files.all.map(&:key)
73
+ keys_to_invalidate = []
66
74
 
67
75
  # Upload all the files in the output folder to the clouds
68
76
  puts 'Uploading local files'
@@ -75,6 +83,7 @@ module Nanoc::Extra::Deployers
75
83
  body: File.open(file_path),
76
84
  public: true)
77
85
  keys_to_destroy.delete(key)
86
+ keys_to_invalidate.push(key)
78
87
  end
79
88
  end
80
89
 
@@ -84,6 +93,21 @@ module Nanoc::Extra::Deployers
84
93
  directory.files.get(key).destroy
85
94
  end
86
95
 
96
+ # invalidate CDN objects
97
+ if cdn_id
98
+ puts 'Invalidating CDN distribution'
99
+ keys_to_invalidate.concat(keys_to_destroy)
100
+ cdn = ::Fog::CDN.new(config)
101
+ # fog cannot mock CDN requests
102
+ unless self.dry_run?
103
+ distribution = cdn.get_distribution(cdn_id)
104
+ # usual limit per invalidation: 1000 objects
105
+ keys_to_invalidate.each_slice(1000) do |paths|
106
+ cdn.post_invalidation(distribution, paths)
107
+ end
108
+ end
109
+ end
110
+
87
111
  puts 'Done!'
88
112
  end
89
113
 
@@ -4,6 +4,17 @@ require 'set'
4
4
 
5
5
  module ::Nanoc::Extra
6
6
  class LinkCollector
7
+ URI_ATTRS = {
8
+ 'a' => :href,
9
+ 'audio' => :src,
10
+ 'form' => :action,
11
+ 'iframe' => :src,
12
+ 'img' => :src,
13
+ 'link' => :href,
14
+ 'script' => :src,
15
+ 'video' => :src
16
+ }
17
+
7
18
  def initialize(filenames, mode = nil)
8
19
  Nanoc::Extra::JRubyNokogiriWarner.check_and_warn
9
20
 
@@ -33,24 +44,46 @@ module ::Nanoc::Extra
33
44
  filenames_per_href
34
45
  end
35
46
 
47
+ def filenames_per_resource_uri
48
+ require 'nokogiri'
49
+ filenames_per_resource_uri = {}
50
+ @filenames.each do |filename|
51
+ resource_uris_in_file(filename).each do |resouce_uri|
52
+ filenames_per_resource_uri[resouce_uri] ||= Set.new
53
+ filenames_per_resource_uri[resouce_uri] << filename
54
+ end
55
+ end
56
+ filenames_per_resource_uri
57
+ end
58
+
36
59
  def external_href?(href)
37
60
  href =~ %r{^(\/\/|[a-z\-]+:)}
38
61
  end
39
62
 
40
63
  def hrefs_in_file(filename)
41
- hrefs_in_file = Set.new
42
- doc = Nokogiri::HTML(::File.read(filename))
43
- doc.css('a').each { |e| hrefs_in_file << e[:href] unless e[:href].nil? }
44
- doc.css('img').each { |e| hrefs_in_file << e[:src] }
64
+ uris_in_file filename, %w(a img)
65
+ end
66
+
67
+ def resource_uris_in_file(filename)
68
+ uris_in_file filename, %w(audio form img iframe link script video)
69
+ end
70
+
71
+ private
45
72
 
46
- # Convert protocol-relative urls
47
- # e.g. //example.com => http://example.com
48
- hrefs_in_file.map! { |href| href.gsub(/^\/\//, 'http://') }
73
+ def uris_in_file(filename, tag_names)
74
+ uris = Set.new
75
+ doc = Nokogiri::HTML(::File.read(filename))
76
+ tag_names.each do |tag_name|
77
+ attr = URI_ATTRS[tag_name]
78
+ doc.css(tag_name).each do |e|
79
+ uris << e[attr] unless e[attr].nil?
80
+ end
81
+ end
49
82
 
50
83
  # Strip fragment
51
- hrefs_in_file.map! { |href| href.gsub(/#.*$/, '') }
84
+ uris.map! { |href| href.gsub(/#.*$/, '') }
52
85
 
53
- hrefs_in_file.select(&@filter)
86
+ uris.select(&@filter)
54
87
  end
55
88
  end
56
89
  end
@@ -35,7 +35,7 @@ module Nanoc::Extra
35
35
  present_dirs = []
36
36
  Find.find(site.config[:output_dir] + '/') do |f|
37
37
  present_files << f if File.file?(f)
38
- present_dirs << f if File.directory?(f)
38
+ present_dirs << f if File.directory?(f)
39
39
  end
40
40
 
41
41
  # Remove stray files
@@ -29,7 +29,7 @@ module Nanoc::Filters
29
29
  #
30
30
  # * `:coderay` for [Coderay](http://coderay.rubychan.de/)
31
31
  # * `:pygmentize` for [pygmentize](http://pygments.org/docs/cmdline/), the
32
- # commandline frontend for [Pygments](http://pygments.org/)
32
+ # command-line frontend for [Pygments](http://pygments.org/)
33
33
  # * `:pygmentsrb` for [pygments.rb](https://github.com/tmm1/pygments.rb),
34
34
  # a Ruby interface for [Pygments](http://pygments.org/)
35
35
  # * `:simon_highlight` for [Highlight](http://www.andre-simon.de/doku/highlight/en/highlight.html)
@@ -201,7 +201,7 @@ module Nanoc::Filters
201
201
  end
202
202
 
203
203
  # Runs the content through [pygmentize](http://pygments.org/docs/cmdline/),
204
- # the commandline frontend for [Pygments](http://pygments.org/).
204
+ # the command-line frontend for [Pygments](http://pygments.org/).
205
205
  #
206
206
  # @api private
207
207
  #
@@ -216,7 +216,7 @@ module Nanoc::Filters
216
216
  check_availability('pygmentize', '-V')
217
217
 
218
218
  params[:encoding] ||= 'utf-8'
219
- params[:nowrap] ||= 'True'
219
+ params[:nowrap] ||= 'True'
220
220
 
221
221
  cmd = ['pygmentize', '-l', language, '-f', 'html']
222
222
  cmd << '-O' << params.map { |k, v| "#{k}=#{v}" }.join(',') unless params.empty?
@@ -246,7 +246,7 @@ module Nanoc::Filters
246
246
  args[:lexer] ||= language
247
247
  args[:options] ||= {}
248
248
  args[:options][:encoding] ||= 'utf-8'
249
- args[:options][:nowrap] ||= 'True'
249
+ args[:options][:nowrap] ||= 'True'
250
250
 
251
251
  Pygments.highlight(code, args)
252
252
  end
@@ -5,14 +5,33 @@ module Nanoc::Filters
5
5
  requires 'pandoc-ruby'
6
6
 
7
7
  # Runs the content through [Pandoc](http://johnmacfarlane.net/pandoc/)
8
- # using [PandocRuby](https://github.com/alphabetum/pandoc-ruby). Options
9
- # are passed on to PandocRuby.
8
+ # using [PandocRuby](https://github.com/alphabetum/pandoc-ruby).
9
+ #
10
+ # Arguments can be passed to PandocRuby in two ways:
11
+ #
12
+ # * Use the `:args` option. This approach is more flexible, since it
13
+ # allows passing an array instead of a hash.
14
+ #
15
+ # * Pass the arguments directly to the filter. With this approach, only
16
+ # hashes can be passed, which is more limiting than the `:args` approach.
17
+ #
18
+ # The `:args` approach is recommended.
19
+ #
20
+ # @example Passing arguments using `:arg`
21
+ #
22
+ # filter :pandoc, args: [:s, {:f => :markdown, :to => :html}, 'no-wrap', :toc]
23
+ #
24
+ # @example Passing arguments not using `:arg`
25
+ #
26
+ # filter :pandoc, :f => :markdown, :to => :html
10
27
  #
11
28
  # @param [String] content The content to filter
12
29
  #
13
30
  # @return [String] The filtered content
14
- def run(content, *params)
15
- PandocRuby.convert(content, *params)
31
+ def run(content, params = {})
32
+ args = params.key?(:args) ? params[:args] : params
33
+
34
+ PandocRuby.convert(content, *args)
16
35
  end
17
36
  end
18
37
  end
@@ -10,8 +10,10 @@ class ::Sass::Importers::Filesystem
10
10
 
11
11
  # Create dependency
12
12
  filter = options[:nanoc_current_filter]
13
- item = filter.imported_filename_to_item(full_filename)
14
- filter.depend_on([item]) unless item.nil?
13
+ if filter
14
+ item = filter.imported_filename_to_item(full_filename)
15
+ filter.depend_on([item]) unless item.nil?
16
+ end
15
17
 
16
18
  # Call original _find
17
19
  _orig_find(dir, name, options)
@@ -56,6 +56,7 @@ module Nanoc::Helpers
56
56
 
57
57
  attr_accessor :limit
58
58
  attr_accessor :relevant_articles
59
+ attr_accessor :preserve_order
59
60
  attr_accessor :content_proc
60
61
  attr_accessor :excerpt_proc
61
62
  attr_accessor :title
@@ -85,9 +86,13 @@ module Nanoc::Helpers
85
86
  protected
86
87
 
87
88
  def sorted_relevant_articles
88
- relevant_articles.sort_by do |a|
89
- attribute_to_time(a[:created_at])
90
- end.reverse.first(limit)
89
+ all = relevant_articles
90
+
91
+ unless @preserve_order
92
+ all = all.sort_by { |a| attribute_to_time(a[:created_at]) }
93
+ end
94
+
95
+ all.reverse.first(limit)
91
96
  end
92
97
 
93
98
  def last_article
@@ -172,7 +177,7 @@ module Nanoc::Helpers
172
177
  if a[:author_name] || a[:author_uri]
173
178
  xml.author do
174
179
  xml.name a[:author_name] || author_name
175
- xml.uri a[:author_uri] || author_uri
180
+ xml.uri a[:author_uri] || author_uri
176
181
  end
177
182
  end
178
183
 
@@ -268,8 +273,13 @@ module Nanoc::Helpers
268
273
  # @option params [Number] :limit (5) The maximum number of articles to
269
274
  # show
270
275
  #
271
- # @option params [Array] :articles (sorted_articles) A list of articles to
272
- # include in the feed
276
+ # @option params [Array] :articles (articles) A list of articles to include
277
+ # in the feed
278
+ #
279
+ # @option params [Boolean] :preserve_order (false) Whether or not the
280
+ # ordering of the list of articles should be preserved. If false, the
281
+ # articles will be sorted by `created_at`. If true, the list of articles
282
+ # will be used as-is, and should have the most recent articles last.
273
283
  #
274
284
  # @option params [Proc] :content_proc (->{ |article|
275
285
  # article.compiled_content(:snapshot => :pre) }) A proc that returns the
@@ -303,6 +313,7 @@ module Nanoc::Helpers
303
313
  # Fill builder
304
314
  builder.limit = params[:limit] || 5
305
315
  builder.relevant_articles = params[:articles] || articles || []
316
+ builder.preserve_order = params.fetch(:preserve_order, false)
306
317
  builder.content_proc = params[:content_proc] || ->(a) { a.compiled_content(snapshot: :pre) }
307
318
  builder.excerpt_proc = params[:excerpt_proc] || ->(a) { a[:excerpt] }
308
319
  builder.title = params[:title] || @item[:title] || @site.config[:title]
@@ -29,7 +29,7 @@ module Nanoc::Helpers
29
29
  #
30
30
  # @return [String] A hyperlinked list of tags for the given item
31
31
  def tags_for(item, params = {})
32
- base_url = params[:base_url] || 'http://technorati.com/tag/'
32
+ base_url = params[:base_url] || 'http://technorati.com/tag/'
33
33
  none_text = params[:none_text] || '(none)'
34
34
  separator = params[:separator] || ', '
35
35
 
@@ -18,7 +18,7 @@ module Nanoc::Helpers
18
18
  # @return [String] The excerpt of the given string
19
19
  def excerptize(string, params = {})
20
20
  # Initialize params
21
- params[:length] ||= 25
21
+ params[:length] ||= 25
22
22
  params[:omission] ||= '...'
23
23
 
24
24
  # Get excerpt
data/lib/nanoc/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Nanoc
4
4
  # The current nanoc version.
5
- VERSION = '3.7.5'
5
+ VERSION = '3.8.0'
6
6
  end
data/tasks/test.rake CHANGED
@@ -12,7 +12,7 @@ def run_tests(dir_glob)
12
12
  test_files = Dir["#{dir_glob}*_spec.rb"] + Dir["#{dir_glob}test_*.rb"]
13
13
  test_files.each { |f| require f }
14
14
 
15
- res = MiniTest::Unit.new.run(ENV['ARGS'].split)
15
+ res = Minitest.run(ENV['ARGS'].split)
16
16
  exit(res) if res != 0
17
17
  end
18
18
 
@@ -96,6 +96,14 @@ class Nanoc::ItemArrayTest < Nanoc::TestCase
96
96
  assert_nil @items.at('/tenthousand/')
97
97
  end
98
98
 
99
+ def test_regex
100
+ foo = Nanoc::Item.new('Item Foo', {}, '/foo/')
101
+ @items << foo
102
+
103
+ assert_equal [@one], @items[/n/]
104
+ assert_equal [@two, foo], @items[%r{o/}]
105
+ end
106
+
99
107
  def test_less_than_less_than
100
108
  assert_nil @items[2]
101
109
  assert_nil @items['/foo/']
@@ -77,6 +77,57 @@ class Nanoc::ItemRepTest < Nanoc::TestCase
77
77
  end
78
78
  end
79
79
 
80
+ def test_compiled_content_with_moving_pre_snapshot
81
+ # Create rep
82
+ item = Nanoc::Item.new(
83
+ 'blah blah', {}, '/',
84
+ binary: false
85
+ )
86
+ rep = Nanoc::ItemRep.new(item, nil)
87
+ rep.expects(:compiled?).returns(false)
88
+ rep.instance_eval { @content = { pre: 'pre!', last: 'last!' } }
89
+
90
+ # Check
91
+ assert_raises(Nanoc::Errors::UnmetDependency) do
92
+ rep.compiled_content(snapshot: :pre)
93
+ end
94
+ end
95
+
96
+ def test_compiled_content_with_non_moving_pre_snapshot
97
+ # Create rep
98
+ item = Nanoc::Item.new(
99
+ 'blah blah', {}, '/',
100
+ binary: false
101
+ )
102
+ rep = Nanoc::ItemRep.new(item, nil)
103
+ rep.expects(:compiled?).returns(false)
104
+ rep.snapshots = [[:pre, true]]
105
+ rep.instance_eval { @content = { pre: 'pre!', post: 'post!', last: 'last!' } }
106
+
107
+ # Check
108
+ assert_equal 'pre!', rep.compiled_content(snapshot: :pre)
109
+ end
110
+
111
+ def test_compiled_content_with_final_pre_snapshot_in_layout
112
+ # Mock layout
113
+ layout = Nanoc::Layout.new(
114
+ %(BEFORE <%= @item_rep.compiled_content(snapshot: :pre) %> AFTER),
115
+ {},
116
+ '/somelayout/')
117
+
118
+ # Create item and item rep
119
+ item = Nanoc::Item.new(
120
+ 'blah blah', {}, '/',
121
+ binary: false
122
+ )
123
+ rep = create_rep_for(item, :foo)
124
+ rep.assigns = { item_rep: rep }
125
+
126
+ # Run and check
127
+ rep.layout(layout, :erb, {})
128
+ assert_equal('BEFORE blah blah AFTER', rep.instance_eval { @content[:last] })
129
+ end
130
+
80
131
  def test_filter
81
132
  # Mock site
82
133
  site = MiniTest::Mock.new
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ class Nanoc::ItemRepRecorderProxyTest < Nanoc::TestCase
4
+ def test_double_names
5
+ proxy = Nanoc::ItemRepRecorderProxy.new(mock)
6
+
7
+ proxy.snapshot(:foo, stuff: :giraffe)
8
+ assert_raises(Nanoc::Errors::CannotCreateMultipleSnapshotsWithSameName) do
9
+ proxy.snapshot(:foo, stuff: :donkey)
10
+ end
11
+ end
12
+
13
+ def test_double_params
14
+ proxy = Nanoc::ItemRepRecorderProxy.new(mock)
15
+
16
+ proxy.snapshot(:foo)
17
+ proxy.snapshot(:bar)
18
+ end
19
+ end
@@ -12,4 +12,14 @@ class Nanoc::RuleTest < Nanoc::TestCase
12
12
  def test_apply_to
13
13
  # TODO: implement
14
14
  end
15
+
16
+ def test_matches
17
+ regexp = %r</(.*)/(.*)/>
18
+ identifier = '/anything/else/'
19
+ expected = ['anything', 'else']
20
+
21
+ rule = Nanoc::Rule.new(regexp, :string, Proc.new {})
22
+
23
+ assert_equal expected, rule.send(:matches, identifier)
24
+ end
15
25
  end