nanoc 4.0.0b3 → 4.0.0b4

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +4 -2
  4. data/NEWS.md +10 -0
  5. data/TODO.md +15 -0
  6. data/lib/nanoc/base.rb +4 -27
  7. data/lib/nanoc/base/checksummer.rb +69 -19
  8. data/lib/nanoc/base/compilation/compiler.rb +14 -12
  9. data/lib/nanoc/base/compilation/compiler_dsl.rb +2 -0
  10. data/lib/nanoc/base/compilation/filter.rb +4 -2
  11. data/lib/nanoc/base/compilation/outdatedness_checker.rb +7 -7
  12. data/lib/nanoc/base/compilation/rule.rb +5 -6
  13. data/lib/nanoc/base/compilation/rule_context.rb +16 -34
  14. data/lib/nanoc/base/compilation/rule_memory_calculator.rb +3 -3
  15. data/lib/nanoc/base/compilation/rules_collection.rb +4 -10
  16. data/lib/nanoc/base/context.rb +2 -0
  17. data/lib/nanoc/base/core_ext/array.rb +0 -10
  18. data/lib/nanoc/base/core_ext/hash.rb +0 -10
  19. data/lib/nanoc/base/core_ext/pathname.rb +0 -9
  20. data/lib/nanoc/base/core_ext/string.rb +0 -10
  21. data/lib/nanoc/base/entities.rb +5 -0
  22. data/lib/nanoc/base/entities/content.rb +86 -0
  23. data/lib/nanoc/base/entities/document.rb +56 -0
  24. data/lib/nanoc/base/{source_data → entities}/identifier.rb +12 -1
  25. data/lib/nanoc/base/entities/layout.rb +8 -0
  26. data/lib/nanoc/base/{pattern.rb → entities/pattern.rb} +0 -0
  27. data/lib/nanoc/base/errors.rb +2 -1
  28. data/lib/nanoc/base/result_data/item_rep.rb +13 -278
  29. data/lib/nanoc/base/services.rb +5 -0
  30. data/lib/nanoc/base/services/executor.rb +141 -0
  31. data/lib/nanoc/base/services/item_rep_writer.rb +40 -0
  32. data/lib/nanoc/base/{notification_center.rb → services/notification_center.rb} +0 -0
  33. data/lib/nanoc/base/services/recording_executor.rb +41 -0
  34. data/lib/nanoc/base/{temp_filename_factory.rb → services/temp_filename_factory.rb} +0 -0
  35. data/lib/nanoc/base/source_data/code_snippet.rb +0 -6
  36. data/lib/nanoc/base/source_data/data_source.rb +4 -3
  37. data/lib/nanoc/base/source_data/item.rb +23 -213
  38. data/lib/nanoc/base/source_data/site.rb +0 -1
  39. data/lib/nanoc/base/views.rb +18 -0
  40. data/lib/nanoc/base/views/config.rb +1 -1
  41. data/lib/nanoc/base/views/item.rb +8 -73
  42. data/lib/nanoc/base/views/item_rep.rb +9 -0
  43. data/lib/nanoc/base/views/item_rep_collection.rb +17 -0
  44. data/lib/nanoc/base/views/layout.rb +1 -40
  45. data/lib/nanoc/base/views/mixins/document.rb +76 -0
  46. data/lib/nanoc/base/views/mixins/mutable_document.rb +22 -0
  47. data/lib/nanoc/base/views/mutable_identifiable_collection.rb +1 -1
  48. data/lib/nanoc/base/views/mutable_item.rb +1 -18
  49. data/lib/nanoc/base/views/mutable_item_collection.rb +6 -2
  50. data/lib/nanoc/base/views/mutable_layout.rb +1 -8
  51. data/lib/nanoc/cli/commands/compile.rb +1 -2
  52. data/lib/nanoc/cli/commands/create-site.rb +5 -5
  53. data/lib/nanoc/cli/commands/show-data.rb +11 -1
  54. data/lib/nanoc/data_sources/filesystem.rb +17 -10
  55. data/lib/nanoc/helpers/capturing.rb +1 -2
  56. data/lib/nanoc/helpers/filtering.rb +13 -1
  57. data/lib/nanoc/helpers/rendering.rb +4 -2
  58. data/lib/nanoc/version.rb +1 -1
  59. data/test/base/core_ext/array_spec.rb +0 -7
  60. data/test/base/core_ext/hash_spec.rb +0 -13
  61. data/test/base/core_ext/pathname_spec.rb +0 -33
  62. data/test/base/core_ext/string_spec.rb +0 -10
  63. data/test/base/test_compiler_dsl.rb +3 -3
  64. data/test/base/test_data_source.rb +2 -2
  65. data/test/base/test_item.rb +5 -129
  66. data/test/base/test_item_rep.rb +26 -558
  67. data/test/base/test_layout.rb +2 -26
  68. data/test/base/test_rule.rb +3 -3
  69. data/test/base/test_rule_context.rb +34 -15
  70. data/test/data_sources/test_filesystem.rb +2 -2
  71. data/test/data_sources/test_filesystem_unified.rb +39 -33
  72. data/test/extra/checking/checks/test_html.rb +0 -1
  73. data/test/extra/checking/checks/test_mixed_content.rb +3 -3
  74. data/test/extra/deployers/test_fog.rb +24 -24
  75. data/test/filters/test_less.rb +4 -4
  76. data/test/filters/test_sass.rb +10 -5
  77. data/test/filters/test_xsl.rb +6 -0
  78. data/test/helpers/test_capturing.rb +0 -1
  79. data/test/helpers/test_filtering.rb +5 -19
  80. data/test/helpers/test_tagging.rb +6 -6
  81. metadata +18 -11
  82. data/lib/nanoc/base/compilation/item_rep_proxy.rb +0 -109
  83. data/lib/nanoc/base/compilation/item_rep_recorder_proxy.rb +0 -97
  84. data/lib/nanoc/base/source_data/layout.rb +0 -111
  85. data/test/base/checksummer_spec.rb +0 -256
  86. data/test/base/test_item_rep_recorder_proxy.rb +0 -17
@@ -1,8 +1,8 @@
1
1
  usage 'create-site [options] path'
2
2
  aliases :create_site, :cs
3
3
  summary 'create a site'
4
- description "Create a new site at the given path. The site will use the `filesystem` data source."
5
- flag nil, :force, "Force creation of new site. Disregards previous existence of site in destination"
4
+ description 'Create a new site at the given path. The site will use the `filesystem` data source.'
5
+ flag nil, :force, 'Force creation of new site. Disregards previous existence of site in destination'
6
6
 
7
7
  module Nanoc::CLI::Commands
8
8
  class CreateSite < ::Nanoc::CLI::CommandRunner
@@ -288,10 +288,10 @@ EOS
288
288
  path = arguments[0]
289
289
 
290
290
  # Check whether site exists
291
- if File.exist?(path) && (!File.directory?(path) || !(Dir.entries(path) - %w{ . .. }).empty?) && !options[:force]
291
+ if File.exist?(path) && (!File.directory?(path) || !(Dir.entries(path) - %w(. ..)).empty?) && !options[:force]
292
292
  raise Nanoc::Int::Errors::GenericTrivial,
293
- "The site was not created because '#{path}' already exists. " +
294
- "Re-run the command using --force to create the site anyway."
293
+ "The site was not created because '#{path}' already exists. " \
294
+ 'Re-run the command using --force to create the site anyway.'
295
295
  end
296
296
 
297
297
  # Setup notifications
@@ -64,8 +64,18 @@ module Nanoc::CLI::Commands
64
64
  puts "item #{item.identifier} depends on:"
65
65
  predecessors = dependency_tracker.objects_causing_outdatedness_of(item).sort_by { |i| i ? i.identifier : '' }
66
66
  predecessors.each do |pred|
67
+ type =
68
+ case pred
69
+ when Nanoc::Int::Layout
70
+ 'layout'
71
+ when Nanoc::Int::ItemRep
72
+ 'item rep'
73
+ when Nanoc::Int::Item
74
+ 'item'
75
+ end
76
+
67
77
  if pred
68
- puts " [ #{format '%6s', pred.type} ] #{pred.identifier}"
78
+ puts " [ #{format '%6s', type} ] #{pred.identifier}"
69
79
  else
70
80
  puts ' ( removed item )'
71
81
  end
@@ -45,7 +45,7 @@ module Nanoc::DataSources
45
45
  def load_objects(dir_name, kind, klass)
46
46
  res = []
47
47
 
48
- return [] if dir_name.nil?
48
+ return [] if dir_name.nil?
49
49
 
50
50
  all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
51
51
  content_exts.each do |content_ext|
@@ -73,10 +73,10 @@ module Nanoc::DataSources
73
73
  }.merge(meta)
74
74
 
75
75
  # Get identifier
76
- if meta_filename
77
- identifier = identifier_for_filename(meta_filename[dir_name.length..-1])
78
- elsif content_filename
76
+ if content_filename
79
77
  identifier = identifier_for_filename(content_filename[dir_name.length..-1])
78
+ elsif meta_filename
79
+ identifier = identifier_for_filename(meta_filename[dir_name.length..-1])
80
80
  else
81
81
  raise 'meta_filename and content_filename are both nil'
82
82
  end
@@ -93,12 +93,19 @@ module Nanoc::DataSources
93
93
  else
94
94
  raise 'meta_mtime and content_mtime are both nil'
95
95
  end
96
-
97
- # Create layout object
98
- res << klass.new(
99
- content_or_filename, attributes, identifier,
100
- binary: is_binary, mtime: mtime
101
- )
96
+ attributes[:mtime] = mtime
97
+
98
+ # Create content
99
+ full_content_filename = content_filename && File.expand_path(content_filename)
100
+ content =
101
+ if is_binary
102
+ Nanoc::Int::BinaryContent.new(full_content_filename)
103
+ else
104
+ Nanoc::Int::TextualContent.new(content_or_filename, filename: full_content_filename)
105
+ end
106
+
107
+ # Create object
108
+ res << klass.new(content, attributes, identifier)
102
109
  end
103
110
  end
104
111
 
@@ -113,8 +113,7 @@ module Nanoc::Helpers
113
113
  @site.unwrap.captures_store_compiled_items << item
114
114
  item.forced_outdated = true
115
115
  item.reps.each do |r|
116
- raw_content = item.raw_content
117
- r.content = { raw: raw_content, last: raw_content }
116
+ r.content_snapshots = { last: item.content }
118
117
  raise Nanoc::Int::Errors::UnmetDependency.new(r)
119
118
  end
120
119
  end
@@ -31,7 +31,19 @@ module Nanoc::Helpers
31
31
  # Find filter
32
32
  klass = Nanoc::Filter.named(filter_name)
33
33
  raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil?
34
- filter = klass.new(@item_rep.unwrap.assigns)
34
+
35
+ # Create filter
36
+ assigns = {
37
+ item: @item,
38
+ rep: @rep,
39
+ item_rep: @item_rep,
40
+ items: @items,
41
+ layouts: @layouts,
42
+ config: @config,
43
+ site: @site,
44
+ content: @content,
45
+ }
46
+ filter = klass.new(assigns)
35
47
 
36
48
  # Filter captured data
37
49
  Nanoc::Int::NotificationCenter.post(:filtering_started, @item_rep.unwrap, filter_name)
@@ -92,7 +92,7 @@ module Nanoc::Helpers
92
92
  item: @item,
93
93
  item_rep: @item_rep,
94
94
  items: @items,
95
- layout: Nanoc::LayoutView.new(layout),
95
+ layout: Nanoc::LayoutView.new(layout), # FIXME: this does not need to be rewrapped
96
96
  layouts: @layouts,
97
97
  config: @config,
98
98
  site: @site
@@ -114,7 +114,9 @@ module Nanoc::Helpers
114
114
  Nanoc::Int::NotificationCenter.post(:processing_started, layout)
115
115
 
116
116
  # Layout
117
- result = filter.setup_and_run(layout.raw_content, filter_args)
117
+ content = layout.unwrap.content
118
+ arg = content.binary? ? content.filename : content.string
119
+ result = filter.setup_and_run(arg, filter_args)
118
120
 
119
121
  # Append to erbout if we have a block
120
122
  if block_given?
data/lib/nanoc/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Nanoc
2
2
  # The current nanoc version.
3
- VERSION = '4.0.0b3'
3
+ VERSION = '4.0.0b4'
4
4
  end
@@ -38,10 +38,3 @@ describe 'Array#__nanoc_freeze_recursively' do
38
38
  assert_equal a, a[0]
39
39
  end
40
40
  end
41
-
42
- describe 'Array#__nanoc_checksum' do
43
- it 'should work' do
44
- expectation = 'CEUlNvu/3DUmlbtpFRiLHU8oHA0='
45
- [[:foo, 123]].__nanoc_checksum.must_equal expectation
46
- end
47
- end
@@ -44,16 +44,3 @@ describe 'Hash#__nanoc_freeze_recursively' do
44
44
  assert_equal a, a[:x]
45
45
  end
46
46
  end
47
-
48
- describe 'Hash#__nanoc_checksum' do
49
- it 'should work' do
50
- expectation = 'wy7gHokc700tqJ/BmJ+EK6/F0bc='
51
- { foo: 123 }.__nanoc_checksum.must_equal expectation
52
- end
53
-
54
- it 'should not sort keys' do
55
- a = { a: 1, c: 2, b: 3 }.__nanoc_checksum
56
- b = { a: 1, b: 3, c: 2 }.__nanoc_checksum
57
- a.wont_equal b
58
- end
59
- end
@@ -1,33 +0,0 @@
1
- describe 'Pathname#checksum' do
2
- it 'should work on empty files' do
3
- begin
4
- # Create file
5
- FileUtils.mkdir_p('tmp')
6
- File.open('tmp/myfile', 'w') { |io| io.write('') }
7
- timestamp = Time.at(1_234_569)
8
- File.utime(timestamp, timestamp, 'tmp/myfile')
9
-
10
- # Create checksum
11
- pathname = Pathname.new('tmp/myfile')
12
- pathname.__nanoc_checksum.must_equal 'oU+0fYgGm4EDTl+uErBv8rB9YhU='
13
- ensure
14
- FileUtils.rm_rf('tmp')
15
- end
16
- end
17
-
18
- it 'should work on all files' do
19
- begin
20
- # Create file
21
- FileUtils.mkdir_p('tmp')
22
- File.open('tmp/myfile', 'w') { |io| io.write('abc') }
23
- timestamp = Time.at(1_234_569)
24
- File.utime(timestamp, timestamp, 'tmp/myfile')
25
-
26
- # Create checksum
27
- pathname = Pathname.new('tmp/myfile')
28
- pathname.__nanoc_checksum.must_equal 'IAoqYXvcDheQjaYmZ8waPtEO8zU='
29
- ensure
30
- FileUtils.rm_rf('tmp')
31
- end
32
- end
33
- end
@@ -19,13 +19,3 @@ describe 'String#__nanoc_cleaned_identifier' do
19
19
  '/foo/bar//'.__nanoc_cleaned_identifier.must_equal '/foo/bar/'
20
20
  end
21
21
  end
22
-
23
- describe 'String#__nanoc_checksum' do
24
- it 'should work on empty strings' do
25
- ''.__nanoc_checksum.must_equal 'PfY7essFItpoXa1f6EuB/deyUmQ='
26
- end
27
-
28
- it 'should work on all strings' do
29
- 'abc'.__nanoc_checksum.must_equal 'NkkYRO+25f6psNSeCYykXKCg3C0='
30
- end
31
- end
@@ -60,7 +60,7 @@ class Nanoc::Int::CompilerDSLTest < Nanoc::TestCase
60
60
 
61
61
  # Apply preprocess blocks
62
62
  site.compiler.preprocess
63
- assert item[:preprocessed]
63
+ assert item.attributes[:preprocessed]
64
64
  end
65
65
  end
66
66
 
@@ -286,7 +286,7 @@ EOS
286
286
  def test_create_pattern_with_regex
287
287
  compiler_dsl = Nanoc::Int::CompilerDSL.new(nil, { string_pattern_type: 'glob' })
288
288
 
289
- pattern = compiler_dsl.create_pattern(%r<\A/foo/a*/>)
289
+ pattern = compiler_dsl.create_pattern(%r{\A/foo/a*/})
290
290
  assert pattern.match?('/foo/aaaa/')
291
291
  end
292
292
 
@@ -396,7 +396,7 @@ EOS
396
396
  assert_equal(expected.casefold?, actual.casefold?)
397
397
  assert_equal(expected.options, actual.options)
398
398
  assert('/foo/bar/' =~ actual)
399
- refute('/foo/' =~ actual)
399
+ refute('/foo/' =~ actual)
400
400
  end
401
401
 
402
402
  def test_dsl_has_no_access_to_compiler
@@ -34,7 +34,7 @@ class Nanoc::DataSourceTest < Nanoc::TestCase
34
34
  data_source = Nanoc::DataSource.new(nil, nil, nil, nil)
35
35
 
36
36
  item = data_source.new_item('stuff', { title: 'Stuff!' }, '/asdf/')
37
- assert_equal 'stuff', item.raw_content
37
+ assert_equal 'stuff', item.content.string
38
38
  assert_equal 'Stuff!', item.attributes[:title]
39
39
  assert_equal Nanoc::Identifier.new('/asdf/'), item.identifier
40
40
  end
@@ -43,7 +43,7 @@ class Nanoc::DataSourceTest < Nanoc::TestCase
43
43
  data_source = Nanoc::DataSource.new(nil, nil, nil, nil)
44
44
 
45
45
  layout = data_source.new_layout('stuff', { title: 'Stuff!' }, '/asdf/')
46
- assert_equal 'stuff', layout.raw_content
46
+ assert_equal 'stuff', layout.content.string
47
47
  assert_equal 'Stuff!', layout.attributes[:title]
48
48
  assert_equal Nanoc::Identifier.new('/asdf/'), layout.identifier
49
49
  end
@@ -16,120 +16,9 @@ class Nanoc::Int::ItemTest < Nanoc::TestCase
16
16
  assert_equal([:item, '/path/'], item.reference)
17
17
  end
18
18
 
19
- def test_lookup
20
- # Create item
21
- item = Nanoc::Int::Item.new(
22
- 'content',
23
- { one: 'one in item' },
24
- '/path/'
25
- )
26
-
27
- # Test finding one
28
- assert_equal('one in item', item[:one])
29
-
30
- # Test finding two
31
- assert_equal(nil, item[:two])
32
- end
33
-
34
- def test_set_attribute
35
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
36
- assert_equal nil, item[:motto]
37
-
38
- item[:motto] = 'More human than human'
39
- assert_equal 'More human than human', item[:motto]
40
- end
41
-
42
- def test_compiled_content_with_default_rep_and_default_snapshot
43
- # Mock rep
44
- rep = Object.new
45
- def rep.name
46
- :default
47
- end
48
- def rep.compiled_content(params)
49
- "content at #{params[:snapshot].inspect}"
50
- end
51
-
52
- # Mock item
53
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
54
- item.expects(:reps).returns([rep])
55
-
56
- # Check
57
- assert_equal 'content at nil', item.compiled_content
58
- end
59
-
60
- def test_compiled_content_with_custom_rep_and_default_snapshot
61
- # Mock reps
62
- rep = Object.new
63
- def rep.name
64
- :foo
65
- end
66
- def rep.compiled_content(params)
67
- "content at #{params[:snapshot].inspect}"
68
- end
69
-
70
- # Mock item
71
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
72
- item.expects(:reps).returns([rep])
73
-
74
- # Check
75
- assert_equal 'content at nil', item.compiled_content(rep: :foo)
76
- end
77
-
78
- def test_compiled_content_with_default_rep_and_custom_snapshot
79
- # Mock reps
80
- rep = Object.new
81
- def rep.name
82
- :default
83
- end
84
- def rep.compiled_content(params)
85
- "content at #{params[:snapshot].inspect}"
86
- end
87
-
88
- # Mock item
89
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
90
- item.expects(:reps).returns([rep])
91
-
92
- # Check
93
- assert_equal 'content at :blah', item.compiled_content(snapshot: :blah)
94
- end
95
-
96
- def test_compiled_content_with_custom_nonexistant_rep
97
- # Mock item
98
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
99
- item.expects(:reps).returns([])
100
-
101
- # Check
102
- assert_raises(Nanoc::Int::Errors::Generic) do
103
- item.compiled_content(rep: :lkasdhflahgwfe)
104
- end
105
- end
106
-
107
- def test_path_with_default_rep
108
- # Mock reps
109
- rep = mock
110
- rep.expects(:name).returns(:default)
111
- rep.expects(:path).returns('the correct path')
112
-
113
- # Mock item
114
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
115
- item.expects(:reps).returns([rep])
116
-
117
- # Check
118
- assert_equal 'the correct path', item.path
119
- end
120
-
121
- def test_path_with_custom_rep
122
- # Mock reps
123
- rep = mock
124
- rep.expects(:name).returns(:moo)
125
- rep.expects(:path).returns('the correct path')
126
-
127
- # Mock item
128
- item = Nanoc::Int::Item.new('foo', {}, '/foo')
129
- item.expects(:reps).returns([rep])
130
-
131
- # Check
132
- assert_equal 'the correct path', item.path(rep: :moo)
19
+ def test_attributes
20
+ item = Nanoc::Int::Item.new('content', { 'one' => 'one in item' }, '/path/')
21
+ assert_equal({ one: 'one in item' }, item.attributes)
133
22
  end
134
23
 
135
24
  def test_freeze_should_disallow_changes
@@ -137,24 +26,11 @@ class Nanoc::Int::ItemTest < Nanoc::TestCase
137
26
  item.freeze
138
27
 
139
28
  assert_raises_frozen_error do
140
- item[:abc] = '123'
29
+ item.attributes[:abc] = '123'
141
30
  end
142
31
 
143
32
  assert_raises_frozen_error do
144
- item[:a][:b] = '456'
33
+ item.attributes[:a][:b] = '456'
145
34
  end
146
35
  end
147
-
148
- def test_dump_and_load
149
- item = Nanoc::Int::Item.new(
150
- 'foobar',
151
- { a: { b: 123 } },
152
- '/foo/')
153
-
154
- item = Marshal.load(Marshal.dump(item))
155
-
156
- assert_equal Nanoc::Identifier.new('/foo/'), item.identifier
157
- assert_equal 'foobar', item.raw_content
158
- assert_equal({ a: { b: 123 } }, item.attributes)
159
- end
160
36
  end
@@ -1,16 +1,13 @@
1
1
  class Nanoc::Int::ItemRepTest < Nanoc::TestCase
2
- def test_created_modified_compiled
3
- # TODO: implement
4
- end
5
-
6
2
  def test_compiled_content_with_only_last_available
7
3
  # Create rep
8
4
  item = Nanoc::Int::Item.new(
9
5
  'blah blah blah', {}, '/',
10
- binary: false, mtime: Time.now - 500
11
6
  )
12
7
  rep = Nanoc::Int::ItemRep.new(item, nil)
13
- rep.instance_eval { @content = { last: 'last content' } }
8
+ rep.content_snapshots = {
9
+ last: Nanoc::Int::TextualContent.new('last content'),
10
+ }
14
11
  rep.expects(:compiled?).returns(true)
15
12
 
16
13
  # Check
@@ -21,10 +18,12 @@ class Nanoc::Int::ItemRepTest < Nanoc::TestCase
21
18
  # Create rep
22
19
  item = Nanoc::Int::Item.new(
23
20
  'blah blah blah', {}, '/',
24
- binary: false, mtime: Time.now - 500
25
21
  )
26
22
  rep = Nanoc::Int::ItemRep.new(item, nil)
27
- rep.instance_eval { @content = { pre: 'pre content', last: 'last content' } }
23
+ rep.content_snapshots = {
24
+ pre: Nanoc::Int::TextualContent.new('pre content'),
25
+ last: Nanoc::Int::TextualContent.new('last content'),
26
+ }
28
27
  rep.expects(:compiled?).returns(true)
29
28
 
30
29
  # Check
@@ -35,10 +34,12 @@ class Nanoc::Int::ItemRepTest < Nanoc::TestCase
35
34
  # Create rep
36
35
  item = Nanoc::Int::Item.new(
37
36
  'blah blah blah', {}, '/',
38
- binary: false, mtime: Time.now - 500
39
37
  )
40
38
  rep = Nanoc::Int::ItemRep.new(item, nil)
41
- rep.instance_eval { @content = { pre: 'pre content', last: 'last content' } }
39
+ rep.content_snapshots = {
40
+ pre: Nanoc::Int::TextualContent.new('pre content'),
41
+ last: Nanoc::Int::TextualContent.new('last content'),
42
+ }
42
43
  rep.expects(:compiled?).returns(true)
43
44
 
44
45
  # Check
@@ -49,10 +50,12 @@ class Nanoc::Int::ItemRepTest < Nanoc::TestCase
49
50
  # Create rep
50
51
  item = Nanoc::Int::Item.new(
51
52
  'blah blah blah', {}, '/',
52
- binary: false, mtime: Time.now - 500
53
53
  )
54
54
  rep = Nanoc::Int::ItemRep.new(item, nil)
55
- rep.instance_eval { @content = { pre: 'pre content', last: 'last content' } }
55
+ rep.content_snapshots = {
56
+ pre: Nanoc::Int::TextualContent.new('pre content'),
57
+ last: Nanoc::Int::TextualContent.new('last content'),
58
+ }
56
59
 
57
60
  # Check
58
61
  assert_raises Nanoc::Int::Errors::NoSuchSnapshot do
@@ -64,7 +67,6 @@ class Nanoc::Int::ItemRepTest < Nanoc::TestCase
64
67
  # Create rep
65
68
  item = Nanoc::Int::Item.new(
66
69
  'blah blah', {}, '/',
67
- binary: false
68
70
  )
69
71
  rep = Nanoc::Int::ItemRep.new(item, nil)
70
72
  rep.expects(:compiled?).returns(false)
@@ -79,11 +81,13 @@ class Nanoc::Int::ItemRepTest < Nanoc::TestCase
79
81
  # Create rep
80
82
  item = Nanoc::Int::Item.new(
81
83
  'blah blah', {}, '/',
82
- binary: false
83
84
  )
84
85
  rep = Nanoc::Int::ItemRep.new(item, nil)
85
86
  rep.expects(:compiled?).returns(false)
86
- rep.instance_eval { @content = { pre: 'pre!', last: 'last!' } }
87
+ rep.content_snapshots = {
88
+ pre: Nanoc::Int::TextualContent.new('pre!'),
89
+ last: Nanoc::Int::TextualContent.new('last!'),
90
+ }
87
91
 
88
92
  # Check
89
93
  assert_raises(Nanoc::Int::Errors::UnmetDependency) do
@@ -95,568 +99,32 @@ class Nanoc::Int::ItemRepTest < Nanoc::TestCase
95
99
  # Create rep
96
100
  item = Nanoc::Int::Item.new(
97
101
  'blah blah', {}, '/',
98
- binary: false
99
102
  )
100
103
  rep = Nanoc::Int::ItemRep.new(item, nil)
101
104
  rep.expects(:compiled?).returns(false)
102
105
  rep.snapshots = [[:pre, true]]
103
- rep.instance_eval { @content = { pre: 'pre!', post: 'post!', last: 'last!' } }
106
+ rep.content_snapshots = {
107
+ pre: Nanoc::Int::TextualContent.new('pre!'),
108
+ post: Nanoc::Int::TextualContent.new('post!'),
109
+ last: Nanoc::Int::TextualContent.new('last!'),
110
+ }
104
111
 
105
112
  # Check
106
113
  assert_equal 'pre!', rep.compiled_content(snapshot: :pre)
107
114
  end
108
115
 
109
- def test_compiled_content_with_final_pre_snapshot_in_layout
110
- # Mock layout
111
- layout = Nanoc::Int::Layout.new(
112
- %(BEFORE <%= @item_rep.compiled_content(snapshot: :pre) %> AFTER),
113
- {},
114
- '/somelayout/')
115
-
116
- # Create item and item rep
117
- item = Nanoc::Int::Item.new(
118
- 'blah blah', {}, '/',
119
- binary: false
120
- )
121
- rep = create_rep_for(item, :foo)
122
- rep.assigns = { item_rep: rep }
123
-
124
- # Run and check
125
- rep.layout(layout, :erb, {})
126
- assert_equal('BEFORE blah blah AFTER', rep.instance_eval { @content[:last] })
127
- end
128
-
129
- def test_filter
130
- # Mock site
131
- site = MiniTest::Mock.new
132
- site.expect(:items, [])
133
- site.expect(:config, [])
134
- site.expect(:layouts, [])
135
-
136
- # Mock item
137
- item = Nanoc::Int::Item.new(
138
- %(<%= '<%= "blah" %' + '>' %>), {}, '/',
139
- binary: false
140
- )
141
-
142
- # Create item rep
143
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
144
- item_rep.instance_eval do
145
- @content[:raw] = item.raw_content
146
- @content[:last] = @content[:raw]
147
- end
148
-
149
- # Filter once
150
- item_rep.assigns = {}
151
- item_rep.filter(:erb)
152
- assert_equal(%(<%= "blah" %>), item_rep.instance_eval { @content[:last] })
153
-
154
- # Filter twice
155
- item_rep.assigns = {}
156
- item_rep.filter(:erb)
157
- assert_equal(%(blah), item_rep.instance_eval { @content[:last] })
158
- end
159
-
160
- def test_layout
161
- # Mock layout
162
- layout = Nanoc::Int::Layout.new(%(<%= "blah" %>), {}, '/somelayout/')
163
-
164
- # Mock item
165
- item = Nanoc::Int::Item.new(
166
- 'blah blah', {}, '/',
167
- binary: false
168
- )
169
-
170
- # Create item rep
171
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
172
- item_rep.instance_eval do
173
- @content[:raw] = item.raw_content
174
- @content[:last] = @content[:raw]
175
- end
176
-
177
- # Layout
178
- item_rep.assigns = {}
179
- item_rep.layout(layout, :erb, {})
180
- assert_equal(%(blah), item_rep.instance_eval { @content[:last] })
181
- end
182
-
183
- def test_snapshot
184
- # Mock site
185
- site = MiniTest::Mock.new
186
- site.expect(:items, [])
187
- site.expect(:config, [])
188
- site.expect(:layouts, [])
189
-
190
- # Mock item
191
- item = Nanoc::Int::Item.new(
192
- %(<%= '<%= "blah" %' + '>' %>), {}, '/foobar/',
193
- binary: false
194
- )
195
-
196
- # Create item rep
197
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
198
- item_rep.instance_eval do
199
- @content[:raw] = item.raw_content
200
- @content[:last] = @content[:raw]
201
- end
202
-
203
- # Filter while taking snapshots
204
- item_rep.assigns = {}
205
- item_rep.snapshot(:foo)
206
- item_rep.filter(:erb)
207
- item_rep.snapshot(:bar)
208
- item_rep.filter(:erb)
209
- item_rep.snapshot(:qux)
210
-
211
- # Check snapshots
212
- assert_equal(%(<%= '<%= "blah" %' + '>' %>), item_rep.instance_eval { @content[:foo] })
213
- assert_equal(%(<%= "blah" %>), item_rep.instance_eval { @content[:bar] })
214
- assert_equal(%(blah), item_rep.instance_eval { @content[:qux] })
215
- end
216
-
217
- def test_snapshot_should_be_written
218
- # Mock item
219
- item = Nanoc::Int::Item.new(
220
- 'blah blah', {}, '/',
221
- binary: false
222
- )
223
-
224
- # Create rep
225
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
226
- item_rep.instance_eval { @content[:last] = 'Lorem ipsum, etc.' }
227
- item_rep.raw_paths = { moo: 'foo-moo.txt' }
228
-
229
- # Test non-final
230
- refute File.file?(item_rep.raw_path(snapshot: :moo))
231
- item_rep.snapshot(:moo, final: false)
232
- refute File.file?(item_rep.raw_path(snapshot: :moo))
233
-
234
- # Test final 1
235
- item_rep.snapshot(:moo, final: true)
236
- assert File.file?(item_rep.raw_path(snapshot: :moo))
237
- assert_equal 'Lorem ipsum, etc.', File.read(item_rep.raw_path(snapshot: :moo))
238
- FileUtils.rm_f(item_rep.raw_path(snapshot: :moo))
239
-
240
- # Test final 2
241
- item_rep.snapshot(:moo)
242
- assert File.file?(item_rep.raw_path(snapshot: :moo))
243
- assert_equal 'Lorem ipsum, etc.', File.read(item_rep.raw_path(snapshot: :moo))
244
- end
245
-
246
- def test_write_should_not_touch_identical_textual_files
247
- # Mock item
248
- item = Nanoc::Int::Item.new(
249
- 'blah blah', {}, '/',
250
- binary: false
251
- )
252
-
253
- # Create rep
254
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
255
- def item_rep.generate_diff; end
256
- item_rep.instance_eval { @content[:last] = 'Lorem ipsum, etc.' }
257
- item_rep.raw_paths[:last] = 'foo/bar/baz/quux.txt'
258
-
259
- # Write once
260
- item_rep.write
261
- a_long_time_ago = Time.now - 1_000_000
262
- File.utime(a_long_time_ago, a_long_time_ago, item_rep.raw_path)
263
-
264
- # Write again
265
- assert_equal a_long_time_ago.to_s, File.mtime(item_rep.raw_path).to_s
266
- item_rep.write
267
- assert_equal a_long_time_ago.to_s, File.mtime(item_rep.raw_path).to_s
268
- end
269
-
270
- def test_write
271
- # Mock item
272
- item = Nanoc::Int::Item.new(
273
- 'blah blah', {}, '/',
274
- binary: false
275
- )
276
-
277
- # Create rep
278
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
279
- item_rep.instance_eval { @content[:last] = 'Lorem ipsum, etc.' }
280
- item_rep.raw_paths[:last] = 'foo/bar/baz/quux.txt'
281
-
282
- # Write
283
- item_rep.write
284
-
285
- # Check
286
- assert(File.file?('foo/bar/baz/quux.txt'))
287
- assert_equal('Lorem ipsum, etc.', File.read('foo/bar/baz/quux.txt'))
288
- end
289
-
290
- def test_filter_text_to_binary
291
- # Mock item
292
- item = Nanoc::Int::Item.new(
293
- 'blah blah', {}, '/',
294
- binary: false
295
- )
296
-
297
- # Create rep
298
- rep = Nanoc::Int::ItemRep.new(item, :foo)
299
- def rep.assigns
300
- {}
301
- end
302
-
303
- # Create fake filter
304
- def rep.filter_named(_name)
305
- @filter ||= Class.new(::Nanoc::Filter) do
306
- type text: :binary
307
- def run(content, _params = {})
308
- File.open(output_filename, 'w') { |io| io.write(content) }
309
- end
310
- end
311
- end
312
-
313
- # Run
314
- rep.filter(:foo)
315
-
316
- # Check
317
- assert rep.binary?
318
- end
319
-
320
- def test_filter_with_textual_rep_and_binary_filter
321
- # Mock item
322
- item = Nanoc::Int::Item.new(
323
- 'blah blah', {}, '/',
324
- binary: false
325
- )
326
-
327
- # Create rep
328
- rep = Nanoc::Int::ItemRep.new(item, :foo)
329
- def rep.assigns
330
- {}
331
- end
332
-
333
- # Create fake filter
334
- def rep.filter_named(_name)
335
- @filter ||= Class.new(::Nanoc::Filter) do
336
- type :binary
337
- def run(content, _params = {})
338
- File.open(output_filename, 'w') { |io| io.write(content) }
339
- end
340
- end
341
- end
342
-
343
- # Run
344
- assert_raises ::Nanoc::Int::Errors::CannotUseBinaryFilter do
345
- rep.filter(:foo)
346
- end
347
- end
348
-
349
- def test_using_textual_filters_on_binary_reps_raises
350
- item = create_binary_item
351
- site = mock_and_stub(items: [item],
352
- layouts: [],
353
- config: []
354
- )
355
- item.stubs(:site).returns(site)
356
- rep = create_rep_for(item, :foo)
357
- create_textual_filter
358
-
359
- assert rep.binary?
360
- assert_raises(Nanoc::Int::Errors::CannotUseTextualFilter) { rep.filter(:text_filter) }
361
- end
362
-
363
- def test_writing_binary_reps_uses_content_in_last_filename
364
- require 'tempfile'
365
-
366
- in_filename = 'nanoc-in'
367
- out_filename = 'nanoc-out'
368
- file_content = 'Some content for this test'
369
- File.open(in_filename, 'w') { |io| io.write(file_content) }
370
-
371
- item = create_binary_item
372
- rep = create_rep_for(item, :foo)
373
- rep.temporary_filenames[:last] = in_filename
374
- rep.raw_paths[:last] = out_filename
375
-
376
- rep.write
377
-
378
- assert(File.exist?(out_filename))
379
- assert_equal(file_content, File.read(out_filename))
380
- end
381
-
382
- def test_converted_binary_rep_can_be_layed_out
383
- # Mock layout
384
- layout = Nanoc::Int::Layout.new(%(<%= "blah" %> <%= yield %>), {}, '/somelayout/')
385
-
386
- # Create item and item rep
387
- item = create_binary_item
388
- rep = create_rep_for(item, :foo)
389
- rep.assigns = { content: 'meh' }
390
-
391
- # Create filter
392
- Class.new(::Nanoc::Filter) do
393
- type binary: :text
394
- identifier :binary_to_text
395
- def run(content, _params = {})
396
- content + ' textified'
397
- end
398
- end
399
-
400
- # Run and check
401
- rep.filter(:binary_to_text)
402
- rep.layout(layout, :erb, {})
403
- assert_equal('blah meh', rep.instance_eval { @content[:last] })
404
- end
405
-
406
- def test_converted_binary_rep_can_be_filtered_with_textual_filters
407
- item = create_binary_item
408
- site = mock_and_stub(items: [item],
409
- layouts: [],
410
- config: []
411
- )
412
- item.stubs(:site).returns(site)
413
- rep = create_rep_for(item, :foo)
414
- rep.assigns = {}
415
- create_textual_filter
416
-
417
- assert rep.binary?
418
-
419
- def rep.filter_named(_name)
420
- Class.new(::Nanoc::Filter) do
421
- type binary: :text
422
- def run(_content, _params = {})
423
- 'Some textual content'
424
- end
425
- end
426
- end
427
- rep.filter(:binary_to_text)
428
- assert !rep.binary?
429
-
430
- def rep.filter_named(_name)
431
- Class.new(::Nanoc::Filter) do
432
- type :text
433
- def run(_content, _params = {})
434
- 'Some textual content'
435
- end
436
- end
437
- end
438
- rep.filter(:text_filter)
439
- assert !rep.binary?
440
- end
441
-
442
- def test_converted_binary_rep_cannot_be_filtered_with_binary_filters
443
- item = create_binary_item
444
- site = mock_and_stub(
445
- items: [item],
446
- layouts: [],
447
- config: []
448
- )
449
- item.stubs(:site).returns(site)
450
- rep = create_rep_for(item, :foo)
451
- rep.assigns = {}
452
- create_binary_filter
453
-
454
- assert rep.binary?
455
- def rep.filter_named(_name)
456
- @filter ||= Class.new(::Nanoc::Filter) do
457
- type binary: :text
458
- def run(_content, _params = {})
459
- 'Some textual content'
460
- end
461
- end
462
- end
463
- rep.filter(:binary_to_text)
464
- refute rep.binary?
465
- assert_raises(Nanoc::Int::Errors::CannotUseBinaryFilter) { rep.filter(:binary_filter) }
466
- end
467
-
468
- def test_new_content_should_be_frozen
469
- filter_class = Class.new(::Nanoc::Filter) do
470
- def run(content, _params = {})
471
- content.gsub!('foo', 'moo')
472
- content
473
- end
474
- end
475
-
476
- item = Nanoc::Int::Item.new('foo bar', {}, '/foo/')
477
- rep = Nanoc::Int::ItemRep.new(item, :default)
478
- rep.instance_eval { @filter_class = filter_class }
479
- def rep.filter_named(_name)
480
- @filter_class
481
- end
482
-
483
- assert_raises_frozen_error do
484
- rep.filter(:whatever)
485
- end
486
- end
487
-
488
- def test_filter_should_freeze_content
489
- filter_class = Class.new(::Nanoc::Filter) do
490
- def run(content, _params = {})
491
- content.gsub!('foo', 'moo')
492
- content
493
- end
494
- end
495
-
496
- item = Nanoc::Int::Item.new('foo bar', {}, '/foo/')
497
- rep = Nanoc::Int::ItemRep.new(item, :default)
498
- rep.instance_eval { @filter_class = filter_class }
499
- def rep.filter_named(_name)
500
- @filter_class
501
- end
502
-
503
- assert_raises_frozen_error do
504
- rep.filter(:erb)
505
- rep.filter(:whatever)
506
- end
507
- end
508
-
509
- def test_raw_path_should_generate_dependency
510
- items = [
511
- Nanoc::Int::Item.new('foo', {}, '/foo/'),
512
- Nanoc::Int::Item.new('bar', {}, '/bar/')
513
- ]
514
- item_reps = [
515
- Nanoc::Int::ItemRep.new(items[0], :default),
516
- Nanoc::Int::ItemRep.new(items[1], :default)
517
- ]
518
-
519
- dt = Nanoc::Int::DependencyTracker.new(items)
520
- dt.start
521
- Nanoc::Int::NotificationCenter.post(:visit_started, items[0])
522
- item_reps[1].raw_path
523
- Nanoc::Int::NotificationCenter.post(:visit_ended, items[0])
524
- dt.stop
525
-
526
- assert_equal [items[1]], dt.objects_causing_outdatedness_of(items[0])
527
- end
528
-
529
- def test_path_should_generate_dependency
530
- items = [
531
- Nanoc::Int::Item.new('foo', {}, '/foo/'),
532
- Nanoc::Int::Item.new('bar', {}, '/bar/')
533
- ]
534
- item_reps = [
535
- Nanoc::Int::ItemRep.new(items[0], :default),
536
- Nanoc::Int::ItemRep.new(items[1], :default)
537
- ]
538
-
539
- dt = Nanoc::Int::DependencyTracker.new(items)
540
- dt.start
541
- Nanoc::Int::NotificationCenter.post(:visit_started, items[0])
542
- item_reps[1].path
543
- Nanoc::Int::NotificationCenter.post(:visit_ended, items[0])
544
- dt.stop
545
-
546
- assert_equal [items[1]], dt.objects_causing_outdatedness_of(items[0])
547
- end
548
-
549
116
  def test_access_compiled_content_of_binary_item
550
- item = Nanoc::Int::Item.new('content/somefile.dat', {}, '/somefile/', binary: true)
117
+ content = Nanoc::Int::BinaryContent.new(File.expand_path('content/somefile.dat'))
118
+ item = Nanoc::Int::Item.new(content, {}, '/somefile/')
551
119
  item_rep = Nanoc::Int::ItemRep.new(item, :foo)
552
120
  assert_raises(Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem) do
553
121
  item_rep.compiled_content
554
122
  end
555
123
  end
556
124
 
557
- def test_write_should_calculate_is_modified_correctly_for_binary_items_new
558
- # Mock item
559
- FileUtils.mkdir_p('content')
560
- File.open('content/meow.dat', 'w') { |io| io.write('asdf') }
561
- item = Nanoc::Int::Item.new(
562
- 'content/meow.dat', {}, '/',
563
- binary: true
564
- )
565
-
566
- # Create rep
567
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
568
- FileUtils.mkdir_p('tmp')
569
- File.open('tmp/woof.dat', 'w') { |io| io.write('fdsa') }
570
- item_rep.instance_eval { @temporary_filenames[:last] = 'tmp/woof.dat' }
571
- item_rep.raw_paths[:last] = 'output/woof.dat'
572
-
573
- # Write
574
- notified = false
575
- Nanoc::Int::NotificationCenter.on(:rep_written, self) do |_rep, _raw_path, is_created, is_modified|
576
- notified = true
577
- assert is_created
578
- assert is_modified
579
- end
580
- item_rep.write
581
- assert notified
582
- Nanoc::Int::NotificationCenter.remove(:rep_written, self)
583
- end
584
-
585
- def test_write_should_calculate_is_modified_correctly_for_binary_items_existing
586
- # Mock item
587
- FileUtils.mkdir_p('content')
588
- File.open('content/meow.dat', 'w') { |io| io.write('asdf') }
589
- item = Nanoc::Int::Item.new(
590
- 'content/meow.dat', {}, '/',
591
- binary: true
592
- )
593
-
594
- # Create rep
595
- item_rep = Nanoc::Int::ItemRep.new(item, :foo)
596
- FileUtils.mkdir_p('tmp')
597
- File.open('tmp/woof.dat', 'w') { |io| io.write('fdsa') }
598
- FileUtils.mkdir_p('output')
599
- File.open('output/woof.dat', 'w') { |io| io.write('fdsa but different') }
600
- item_rep.instance_eval { @temporary_filenames[:last] = 'tmp/woof.dat' }
601
- item_rep.raw_paths[:last] = 'output/woof.dat'
602
-
603
- # Write
604
- notified = false
605
- Nanoc::Int::NotificationCenter.on(:rep_written, self) do |_rep, _raw_path, is_created, is_modified|
606
- notified = true
607
- refute is_created
608
- assert is_modified
609
- end
610
- item_rep.write
611
- assert notified
612
- Nanoc::Int::NotificationCenter.remove(:rep_written, self)
613
- end
614
-
615
125
  private
616
126
 
617
- def create_binary_item
618
- Nanoc::Int::Item.new(
619
- '/a/file/name.dat', {}, '/',
620
- binary: true
621
- )
622
- end
623
-
624
- def mock_and_stub(params)
625
- m = mock
626
- params.each do |method, return_value|
627
- m.stubs(method.to_sym).returns(return_value)
628
- end
629
- m
630
- end
631
-
632
127
  def create_rep_for(item, name)
633
128
  Nanoc::Int::ItemRep.new(item, name)
634
129
  end
635
-
636
- def create_textual_filter
637
- f = create_filter(:text)
638
- f.class_eval do
639
- def run(_content, _params = {})
640
- ''
641
- end
642
- end
643
- f
644
- end
645
-
646
- def create_binary_filter
647
- f = create_filter(:binary)
648
- f.class_eval do
649
- def run(content, _params = {})
650
- File.open(output_filename, 'w') { |io| io.write(content) }
651
- end
652
- end
653
- f
654
- end
655
-
656
- def create_filter(type)
657
- filter_klass = Class.new(Nanoc::Filter)
658
- filter_klass.type(type)
659
- Nanoc::Filter.register filter_klass, "#{type}_filter".to_sym
660
- filter_klass
661
- end
662
130
  end