nanoc-core 4.11.1

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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/NEWS.md +3 -0
  3. data/README.md +3 -0
  4. data/lib/nanoc/core/binary_content.rb +12 -0
  5. data/lib/nanoc/core/checksummer.rb +287 -0
  6. data/lib/nanoc/core/code_snippet.rb +57 -0
  7. data/lib/nanoc/core/configuration-schema.json +122 -0
  8. data/lib/nanoc/core/configuration.rb +206 -0
  9. data/lib/nanoc/core/content.rb +46 -0
  10. data/lib/nanoc/core/context.rb +70 -0
  11. data/lib/nanoc/core/contracts_support.rb +131 -0
  12. data/lib/nanoc/core/core_ext/array.rb +54 -0
  13. data/lib/nanoc/core/core_ext/hash.rb +58 -0
  14. data/lib/nanoc/core/core_ext/string.rb +20 -0
  15. data/lib/nanoc/core/data_source.rb +170 -0
  16. data/lib/nanoc/core/directed_graph.rb +195 -0
  17. data/lib/nanoc/core/document.rb +124 -0
  18. data/lib/nanoc/core/error.rb +9 -0
  19. data/lib/nanoc/core/identifiable_collection.rb +142 -0
  20. data/lib/nanoc/core/identifier.rb +218 -0
  21. data/lib/nanoc/core/item.rb +11 -0
  22. data/lib/nanoc/core/item_collection.rb +15 -0
  23. data/lib/nanoc/core/item_rep.rb +92 -0
  24. data/lib/nanoc/core/layout.rb +11 -0
  25. data/lib/nanoc/core/layout_collection.rb +15 -0
  26. data/lib/nanoc/core/lazy_value.rb +38 -0
  27. data/lib/nanoc/core/notification_center.rb +97 -0
  28. data/lib/nanoc/core/pattern.rb +37 -0
  29. data/lib/nanoc/core/processing_action.rb +23 -0
  30. data/lib/nanoc/core/processing_actions/filter.rb +40 -0
  31. data/lib/nanoc/core/processing_actions/layout.rb +40 -0
  32. data/lib/nanoc/core/processing_actions/snapshot.rb +50 -0
  33. data/lib/nanoc/core/processing_actions.rb +12 -0
  34. data/lib/nanoc/core/regexp_pattern.rb +28 -0
  35. data/lib/nanoc/core/snapshot_def.rb +22 -0
  36. data/lib/nanoc/core/string_pattern.rb +29 -0
  37. data/lib/nanoc/core/temp_filename_factory.rb +53 -0
  38. data/lib/nanoc/core/textual_content.rb +41 -0
  39. data/lib/nanoc/core/version.rb +7 -0
  40. data/lib/nanoc/core.rb +60 -0
  41. data/lib/nanoc-core.rb +3 -0
  42. metadata +152 -0
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Represents a directed graph. It is used by the dependency tracker for
6
+ # storing and querying dependencies between items.
7
+ #
8
+ # @example Creating and using a directed graph
9
+ #
10
+ # # Create a graph with three vertices
11
+ # graph = Nanoc::Core::DirectedGraph.new(%w( a b c d e f g ))
12
+ #
13
+ # # Add edges
14
+ # graph.add_edge('a', 'b')
15
+ # graph.add_edge('b', 'c')
16
+ # graph.add_edge('b', 'f')
17
+ # graph.add_edge('b', 'g')
18
+ # graph.add_edge('c', 'd')
19
+ # graph.add_edge('d', 'e')
20
+ #
21
+ # # Get (direct) predecessors
22
+ # graph.direct_predecessors_of('b').sort
23
+ # # => %w( a )
24
+ # graph.predecessors_of('e').sort
25
+ # # => %w( a b c d )
26
+ #
27
+ # # Modify edges
28
+ # graph.delete_edges_to('c')
29
+ #
30
+ # # Get (direct) predecessors again
31
+ # graph.direct_predecessors_of('e').sort
32
+ # # => %w( d )
33
+ # graph.predecessors_of('e').sort
34
+ # # => %w( c d )
35
+ class DirectedGraph
36
+ # @group Creating a graph
37
+
38
+ # Creates a new directed graph with the given vertices.
39
+ def initialize(vertices)
40
+ @vertices = {}
41
+ @next_vertex_idx = 0
42
+ vertices.each do |v|
43
+ @vertices[v] = @next_vertex_idx.tap { @next_vertex_idx += 1 }
44
+ end
45
+
46
+ @to_graph = {}
47
+
48
+ @edge_props = {}
49
+
50
+ invalidate_caches
51
+ end
52
+
53
+ def inspect
54
+ s = []
55
+
56
+ @vertices.each_pair do |v2, _|
57
+ direct_predecessors_of(v2).each do |v1|
58
+ s << [v1.inspect + ' -> ' + v2.inspect + ' props=' + @edge_props[[v1, v2]].inspect]
59
+ end
60
+ end
61
+
62
+ self.class.to_s + '(' + s.join(', ') + ')'
63
+ end
64
+
65
+ # @group Modifying the graph
66
+
67
+ # Adds an edge from the first vertex to the second vertex.
68
+ #
69
+ # @param from Vertex where the edge should start
70
+ #
71
+ # @param to Vertex where the edge should end
72
+ #
73
+ # @return [void]
74
+ def add_edge(from, to, props: nil)
75
+ add_vertex(from)
76
+ add_vertex(to)
77
+
78
+ @to_graph[to] ||= Set.new
79
+ @to_graph[to] << from
80
+
81
+ if props
82
+ @edge_props[[from, to]] = props
83
+ end
84
+
85
+ invalidate_caches
86
+ end
87
+
88
+ # Adds the given vertex to the graph.
89
+ #
90
+ # @param vertex The vertex to add to the graph
91
+ #
92
+ # @return [void]
93
+ def add_vertex(vertex)
94
+ return if @vertices.key?(vertex)
95
+
96
+ @vertices[vertex] = @next_vertex_idx.tap { @next_vertex_idx += 1 }
97
+ end
98
+
99
+ # Deletes all edges going to the given vertex.
100
+ #
101
+ # @param to Vertex to which all edges should be removed
102
+ #
103
+ # @return [void]
104
+ def delete_edges_to(to)
105
+ return if @to_graph[to].nil?
106
+
107
+ @to_graph[to].each do |from|
108
+ @edge_props.delete([from, to])
109
+ end
110
+ @to_graph.delete(to)
111
+
112
+ invalidate_caches
113
+ end
114
+
115
+ # @group Querying the graph
116
+
117
+ # Returns the direct predecessors of the given vertex, i.e. the vertices
118
+ # x where there is an edge from x to the given vertex y.
119
+ #
120
+ # @param to The vertex of which the predecessors should be calculated
121
+ #
122
+ # @return [Array] Direct predecessors of the given vertex
123
+ def direct_predecessors_of(to)
124
+ @to_graph.fetch(to, Set.new)
125
+ end
126
+
127
+ # Returns the predecessors of the given vertex, i.e. the vertices x for
128
+ # which there is a path from x to the given vertex y.
129
+ #
130
+ # @param to The vertex of which the predecessors should be calculated
131
+ #
132
+ # @return [Array] Predecessors of the given vertex
133
+ def predecessors_of(to)
134
+ @predecessors[to] ||= recursively_find_vertices(to, :direct_predecessors_of)
135
+ end
136
+
137
+ def props_for(from, to)
138
+ @edge_props[[from, to]]
139
+ end
140
+
141
+ # @return [Array] The list of all vertices in this graph.
142
+ def vertices
143
+ @vertices.keys.sort_by { |v| @vertices[v] }
144
+ end
145
+
146
+ # Returns an array of tuples representing the edges. The result of this
147
+ # method may take a while to compute and should be cached if possible.
148
+ #
149
+ # @return [Array] The list of all edges in this graph.
150
+ def edges
151
+ result = []
152
+ @vertices.each_pair do |v2, i2|
153
+ direct_predecessors_of(v2).map { |v1| [@vertices[v1], v1] }.each do |i1, v1|
154
+ result << [i1, i2, @edge_props[[v1, v2]]]
155
+ end
156
+ end
157
+ result
158
+ end
159
+
160
+ private
161
+
162
+ # Invalidates cached data. This method should be called when the internal
163
+ # graph representation is changed.
164
+ def invalidate_caches
165
+ @predecessors = {}
166
+ end
167
+
168
+ # Recursively finds vertices, starting at the vertex start, using the
169
+ # given method, which should be a symbol to a method that takes a vertex
170
+ # and returns related vertices (e.g. predecessors, successors).
171
+ def recursively_find_vertices(start, method)
172
+ all_vertices = Set.new
173
+
174
+ processed_vertices = Set.new
175
+ unprocessed_vertices = [start]
176
+
177
+ until unprocessed_vertices.empty?
178
+ # Get next unprocessed vertex
179
+ vertex = unprocessed_vertices.pop
180
+ next if processed_vertices.include?(vertex)
181
+
182
+ processed_vertices << vertex
183
+
184
+ # Add predecessors of this vertex
185
+ send(method, vertex).each do |v|
186
+ all_vertices << v unless all_vertices.include?(v)
187
+ unprocessed_vertices << v
188
+ end
189
+ end
190
+
191
+ all_vertices
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class Document
6
+ include Nanoc::Core::ContractsSupport
7
+
8
+ # @return [Nanoc::Core::Content]
9
+ attr_reader :content
10
+
11
+ # @return [Hash]
12
+ def attributes
13
+ @attributes.value
14
+ end
15
+
16
+ # @return [Nanoc::Core::Identifier]
17
+ attr_reader :identifier
18
+
19
+ # @return [String, nil]
20
+ attr_accessor :checksum_data
21
+
22
+ # @return [String, nil]
23
+ attr_accessor :content_checksum_data
24
+
25
+ # @return [String, nil]
26
+ attr_accessor :attributes_checksum_data
27
+
28
+ c_content = C::Or[String, Nanoc::Core::Content]
29
+ c_attributes = C::Or[Hash, Proc]
30
+ c_identifier = C::Or[String, Nanoc::Core::Identifier]
31
+ c_checksum_data = C::KeywordArgs[
32
+ checksum_data: C::Optional[C::Maybe[String]],
33
+ content_checksum_data: C::Optional[C::Maybe[String]],
34
+ attributes_checksum_data: C::Optional[C::Maybe[String]],
35
+ ]
36
+
37
+ contract c_content, c_attributes, c_identifier, c_checksum_data => C::Any
38
+ # @param [String, Nanoc::Core::Content] content
39
+ #
40
+ # @param [Hash, Proc] attributes
41
+ #
42
+ # @param [String, Nanoc::Core::Identifier] identifier
43
+ #
44
+ # @param [String, nil] checksum_data
45
+ #
46
+ # @param [String, nil] content_checksum_data
47
+ #
48
+ # @param [String, nil] attributes_checksum_data
49
+ def initialize(content, attributes, identifier, checksum_data: nil, content_checksum_data: nil, attributes_checksum_data: nil)
50
+ @content = Nanoc::Core::Content.create(content)
51
+ @attributes = Nanoc::Core::LazyValue.new(attributes).map(&:__nanoc_symbolize_keys_recursively)
52
+ @identifier = Nanoc::Core::Identifier.from(identifier)
53
+
54
+ @checksum_data = checksum_data
55
+ @content_checksum_data = content_checksum_data
56
+ @attributes_checksum_data = attributes_checksum_data
57
+ end
58
+
59
+ contract C::None => self
60
+ # @return [void]
61
+ def freeze
62
+ super
63
+ @content.freeze
64
+ @attributes.freeze
65
+ self
66
+ end
67
+
68
+ contract String => self
69
+ def with_identifier_prefix(prefix)
70
+ other = dup
71
+ other.identifier = @identifier.prefix(prefix)
72
+ other
73
+ end
74
+
75
+ contract C::None => String
76
+ # @abstract
77
+ #
78
+ # @return Unique reference to this object
79
+ def reference
80
+ raise NotImplementedError
81
+ end
82
+
83
+ contract C::Or[Nanoc::Core::Identifier, String] => Nanoc::Core::Identifier
84
+ def identifier=(new_identifier)
85
+ @identifier = Nanoc::Core::Identifier.from(new_identifier)
86
+ end
87
+
88
+ contract Nanoc::Core::Content => C::Any
89
+ def content=(new_content)
90
+ @content = new_content
91
+
92
+ @checksum_data = nil
93
+ @content_checksum_data = nil
94
+ end
95
+
96
+ def set_attribute(key, value)
97
+ attributes[key] = value
98
+
99
+ @checksum_data = nil
100
+ @attributes_checksum_data = nil
101
+ end
102
+
103
+ contract C::None => String
104
+ def inspect
105
+ "<#{self.class} identifier=\"#{identifier}\">"
106
+ end
107
+
108
+ contract C::None => C::Num
109
+ def hash
110
+ self.class.hash ^ identifier.hash
111
+ end
112
+
113
+ contract C::Any => C::Bool
114
+ def ==(other)
115
+ other.respond_to?(:identifier) && identifier == other.identifier
116
+ end
117
+
118
+ contract C::Any => C::Bool
119
+ def eql?(other)
120
+ other.is_a?(self.class) && identifier == other.identifier
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Generic error. Superclass for all Nanoc-specific errors.
6
+ class Error < ::StandardError
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class IdentifiableCollection
6
+ DDMemoize.activate(self)
7
+
8
+ include Nanoc::Core::ContractsSupport
9
+ include Enumerable
10
+
11
+ extend Forwardable
12
+
13
+ def_delegator :@objects, :each
14
+ def_delegator :@objects, :size
15
+
16
+ def initialize(*)
17
+ raise 'IdentifiableCollection is abstract and cannot be instantiated'
18
+ end
19
+
20
+ contract C::Or[Hash, C::Named['Nanoc::Core::Configuration']], C::IterOf[C::RespondTo[:identifier]], C::Maybe[String] => C::Any
21
+ def initialize_basic(config, objects = [], name = nil)
22
+ @config = config
23
+ @objects = Hamster::Vector.new(objects)
24
+ @name = name
25
+ end
26
+
27
+ contract C::None => String
28
+ def inspect
29
+ "<#{self.class}>"
30
+ end
31
+
32
+ contract C::None => self
33
+ def freeze
34
+ @objects.freeze
35
+ @objects.each(&:freeze)
36
+ build_mapping
37
+ super
38
+ end
39
+
40
+ contract C::Any => C::Maybe[C::RespondTo[:identifier]]
41
+ def [](arg)
42
+ if frozen?
43
+ get_memoized(arg)
44
+ else
45
+ get_unmemoized(arg)
46
+ end
47
+ end
48
+
49
+ contract C::Any => C::IterOf[C::RespondTo[:identifier]]
50
+ def find_all(arg)
51
+ if frozen?
52
+ find_all_memoized(arg)
53
+ else
54
+ find_all_unmemoized(arg)
55
+ end
56
+ end
57
+
58
+ contract C::None => C::ArrayOf[C::RespondTo[:identifier]]
59
+ def to_a
60
+ @objects.to_a
61
+ end
62
+
63
+ contract C::None => C::Bool
64
+ def empty?
65
+ @objects.empty?
66
+ end
67
+
68
+ contract C::RespondTo[:identifier] => self
69
+ def add(obj)
70
+ self.class.new(@config, @objects.add(obj))
71
+ end
72
+
73
+ contract C::Func[C::RespondTo[:identifier] => C::Any] => self
74
+ def reject(&block)
75
+ self.class.new(@config, @objects.reject(&block))
76
+ end
77
+
78
+ contract C::Any => C::Maybe[C::RespondTo[:identifier]]
79
+ def object_with_identifier(identifier)
80
+ if frozen?
81
+ @mapping[identifier.to_s]
82
+ else
83
+ @objects.find { |i| i.identifier == identifier }
84
+ end
85
+ end
86
+
87
+ protected
88
+
89
+ contract C::Any => C::Maybe[C::RespondTo[:identifier]]
90
+ def get_unmemoized(arg)
91
+ case arg
92
+ when Nanoc::Core::Identifier
93
+ object_with_identifier(arg)
94
+ when String
95
+ object_with_identifier(arg) || object_matching_glob(arg)
96
+ when Regexp
97
+ @objects.find { |i| i.identifier.to_s =~ arg }
98
+ else
99
+ raise ArgumentError, "don’t know how to fetch objects by #{arg.inspect}"
100
+ end
101
+ end
102
+
103
+ contract C::Any => C::Maybe[C::RespondTo[:identifier]]
104
+ memoized def get_memoized(arg)
105
+ get_unmemoized(arg)
106
+ end
107
+
108
+ contract C::Any => C::IterOf[C::RespondTo[:identifier]]
109
+ def find_all_unmemoized(arg)
110
+ pat = Nanoc::Core::Pattern.from(arg)
111
+ select { |i| pat.match?(i.identifier) }
112
+ end
113
+
114
+ contract C::Any => C::IterOf[C::RespondTo[:identifier]]
115
+ memoized def find_all_memoized(arg)
116
+ find_all_unmemoized(arg)
117
+ end
118
+
119
+ def object_matching_glob(glob)
120
+ if use_globs?
121
+ pat = Nanoc::Core::Pattern.from(glob)
122
+ @objects.find { |i| pat.match?(i.identifier) }
123
+ else
124
+ nil
125
+ end
126
+ end
127
+
128
+ def build_mapping
129
+ @mapping = {}
130
+ @objects.each do |object|
131
+ @mapping[object.identifier.to_s] = object
132
+ end
133
+ @mapping.freeze
134
+ end
135
+
136
+ contract C::None => C::Bool
137
+ def use_globs?
138
+ @config[:string_pattern_type] == 'glob'
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class Identifier
6
+ include Comparable
7
+ include Nanoc::Core::ContractsSupport
8
+
9
+ class InvalidIdentifierError < ::Nanoc::Core::Error
10
+ def initialize(string)
11
+ super("Invalid identifier (does not start with a slash): #{string.inspect}")
12
+ end
13
+ end
14
+
15
+ class InvalidFullIdentifierError < ::Nanoc::Core::Error
16
+ def initialize(string)
17
+ super("Invalid full identifier (ends with a slash): #{string.inspect}")
18
+ end
19
+ end
20
+
21
+ class InvalidTypeError < ::Nanoc::Core::Error
22
+ def initialize(type)
23
+ super("Invalid type for identifier: #{type.inspect} (can be :full or :legacy)")
24
+ end
25
+ end
26
+
27
+ class InvalidPrefixError < ::Nanoc::Core::Error
28
+ def initialize(string)
29
+ super("Invalid prefix (does not start with a slash): #{string.inspect}")
30
+ end
31
+ end
32
+
33
+ class UnsupportedLegacyOperationError < ::Nanoc::Core::Error
34
+ def initialize
35
+ super('Cannot use this method on legacy identifiers')
36
+ end
37
+ end
38
+
39
+ class NonCoercibleObjectError < ::Nanoc::Core::Error
40
+ def initialize(obj)
41
+ super("#{obj.inspect} cannot be converted into a Nanoc::Core::Identifier")
42
+ end
43
+ end
44
+
45
+ contract C::Any => self
46
+ def self.from(obj)
47
+ case obj
48
+ when Nanoc::Core::Identifier
49
+ obj
50
+ when String
51
+ Nanoc::Core::Identifier.new(obj)
52
+ else
53
+ raise NonCoercibleObjectError.new(obj)
54
+ end
55
+ end
56
+
57
+ contract String, C::KeywordArgs[type: C::Optional[Symbol]] => C::Any
58
+ def initialize(string, type: :full)
59
+ @type = type
60
+
61
+ case @type
62
+ when :legacy
63
+ @string = "/#{string}/".gsub(/^\/+|\/+$/, '/').freeze
64
+ when :full
65
+ raise InvalidIdentifierError.new(string) if string !~ /\A\//
66
+ raise InvalidFullIdentifierError.new(string) if string =~ /\/\z/
67
+
68
+ @string = string.dup.freeze
69
+ else
70
+ raise InvalidTypeError.new(@type)
71
+ end
72
+ end
73
+
74
+ contract C::Any => C::Bool
75
+ def ==(other)
76
+ case other
77
+ when Nanoc::Core::Identifier, String
78
+ to_s == other.to_s
79
+ else
80
+ false
81
+ end
82
+ end
83
+
84
+ contract C::Any => C::Bool
85
+ def eql?(other)
86
+ other.is_a?(self.class) && to_s == other.to_s
87
+ end
88
+
89
+ contract C::None => C::Num
90
+ def hash
91
+ self.class.hash ^ to_s.hash
92
+ end
93
+
94
+ contract C::Any => C::Maybe[C::Num]
95
+ def =~(other)
96
+ Nanoc::Core::Pattern.from(other).match?(to_s) ? 0 : nil
97
+ end
98
+
99
+ contract C::Any => C::Bool
100
+ def match?(other)
101
+ Nanoc::Core::Pattern.from(other).match?(to_s)
102
+ end
103
+
104
+ contract C::Any => C::Num
105
+ def <=>(other)
106
+ to_s <=> other.to_s
107
+ end
108
+
109
+ contract C::None => C::Bool
110
+ # Whether or not this is a full identifier (i.e.includes the extension).
111
+ def full?
112
+ @type == :full
113
+ end
114
+
115
+ contract C::None => C::Bool
116
+ # Whether or not this is a legacy identifier (i.e. does not include the extension).
117
+ def legacy?
118
+ @type == :legacy
119
+ end
120
+
121
+ contract C::None => String
122
+ # @return [String]
123
+ def chop
124
+ to_s.chop
125
+ end
126
+
127
+ contract String => String
128
+ # @return [String]
129
+ def +(other)
130
+ to_s + other
131
+ end
132
+
133
+ contract String => self
134
+ # @return [Nanoc::Core::Identifier]
135
+ def prefix(string)
136
+ unless /\A\//.match?(string)
137
+ raise InvalidPrefixError.new(string)
138
+ end
139
+
140
+ Nanoc::Core::Identifier.new(string.sub(/\/+\z/, '') + @string, type: @type)
141
+ end
142
+
143
+ contract C::None => String
144
+ # The identifier, as string, with the last extension removed
145
+ def without_ext
146
+ unless full?
147
+ raise UnsupportedLegacyOperationError
148
+ end
149
+
150
+ extname = File.extname(@string)
151
+
152
+ if !extname.empty?
153
+ @string[0..-extname.size - 1]
154
+ else
155
+ @string
156
+ end
157
+ end
158
+
159
+ contract C::None => C::Maybe[String]
160
+ # The extension, without a leading dot
161
+ def ext
162
+ unless full?
163
+ raise UnsupportedLegacyOperationError
164
+ end
165
+
166
+ s = File.extname(@string)
167
+ s && s[1..-1]
168
+ end
169
+
170
+ contract C::None => String
171
+ # The identifier, as string, with all extensions removed
172
+ def without_exts
173
+ extname = exts.join('.')
174
+ if !extname.empty?
175
+ @string[0..-extname.size - 2]
176
+ else
177
+ @string
178
+ end
179
+ end
180
+
181
+ contract C::None => C::ArrayOf[String]
182
+ # The list of extensions, without a leading dot
183
+ def exts
184
+ unless full?
185
+ raise UnsupportedLegacyOperationError
186
+ end
187
+
188
+ s = File.basename(@string)
189
+ s ? s.split('.', -1).drop(1) : []
190
+ end
191
+
192
+ contract C::None => C::ArrayOf[String]
193
+ def components
194
+ res = to_s.split('/')
195
+ if res.empty?
196
+ []
197
+ else
198
+ res[1..-1]
199
+ end
200
+ end
201
+
202
+ contract C::None => String
203
+ def to_s
204
+ @string
205
+ end
206
+
207
+ contract C::None => String
208
+ def to_str
209
+ @string
210
+ end
211
+
212
+ contract C::None => String
213
+ def inspect
214
+ "<Nanoc::Core::Identifier type=#{@type} #{to_s.inspect}>"
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class Item < ::Nanoc::Core::Document
6
+ def reference
7
+ "item:#{identifier}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class ItemCollection < IdentifiableCollection
6
+ def initialize(config, objects = [])
7
+ initialize_basic(config, objects, 'items')
8
+ end
9
+
10
+ def reference
11
+ 'items'
12
+ end
13
+ end
14
+ end
15
+ end