nanoc 4.0.0b3 → 4.0.0b4

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