nanoc 4.8.18 → 4.8.19

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c6429f3ab75c70a0616b03c8658442b860f3238e626e3b7a41e21b93558bc6
4
- data.tar.gz: 329c3e49d8813363facc58726517a330821866d2ef6dac20279aa61800c0c617
3
+ metadata.gz: 77b1cd3570e8e94e5d17d18bf9954f0ac8d8a45e48f4e3d2f7d39cbdc8a2e3ff
4
+ data.tar.gz: a5a95e6ab7be8df7c016fdd659bac76734965ed9aed677110647ae7d0cfa19b8
5
5
  SHA512:
6
- metadata.gz: 3caf2cc5572e4ccc9615ab06487391f6d691e4dbe9bb08d600f20f2f20284c827a7ce1fd6325e7ed5cd785d16d19b6e77380a2134fa505dd27f11c91defb780b
7
- data.tar.gz: 71c7a650d30c2d29ba5a38b8fc2eee74596102506dbc44956a733f914d1c7b58b87bdd0ac5603788eae03758b3ef8ebae6c65c3102b3743e68e17354846b0f0d
6
+ metadata.gz: 5860e580a17a5ed72590cb7c81eb6b6e1649e5c2973a322ed1ca544af0faa4a37de23343151a6dbbe730cd022b9d58f54feae6546c7f243e7c359bc2e3f1ea2a
7
+ data.tar.gz: e8642774bd3d54418ae6c78392dedca7eecbc80b2d06d7e83aa9a579ab35074a06de192420476b1ca7dfb48e29f2c7fc3b809925faedb23cb5522b89f170b9f0
data/NEWS.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.8.19 (2018-01-01)
4
+
5
+ Enhancements:
6
+
7
+ * Made `write nil` skip routing rule (#1286)
8
+
3
9
  ## 4.8.18 (2017-12-28)
4
10
 
5
11
  Enhancements:
@@ -27,6 +27,11 @@ module Nanoc
27
27
  def self.on_windows?
28
28
  RUBY_PLATFORM =~ /windows|bccwin|cygwin|djgpp|mingw|mswin|wince/i
29
29
  end
30
+
31
+ # Similar to `nil` except that it can only be compared against using
32
+ # `UNDEFINED.equal?(x)`. Used in places where `nil` already has meaning, and
33
+ # thus cannot be used to mean the presence of nothing.
34
+ UNDEFINED = Object.new
30
35
  end
31
36
 
32
37
  # Load general requirements
@@ -51,7 +51,15 @@ module Nanoc::Int
51
51
 
52
52
  def respond_to_missing?(method, include_all)
53
53
  ivar_name = '@' + method.to_s
54
- instance_variable_defined?(ivar_name) || super
54
+
55
+ valid_ivar_name =
56
+ if defined?(Contracts)
57
+ ivar_name =~ /\A@[A-Za-z_]+\z/
58
+ else
59
+ true # probably good enough
60
+ end
61
+
62
+ (valid_ivar_name && instance_variable_defined?(ivar_name)) || super
55
63
  end
56
64
 
57
65
  def include(mod)
@@ -98,7 +98,7 @@ module Nanoc::Int
98
98
  StringUpdateBehavior
99
99
  when Nanoc::View
100
100
  UnwrapUpdateBehavior
101
- when Nanoc::RuleDSL::RuleContext
101
+ when Nanoc::RuleDSL::CompilationRuleContext
102
102
  RuleContextUpdateBehavior
103
103
  when Nanoc::Int::Context
104
104
  ContextUpdateBehavior
@@ -379,114 +379,13 @@ module Nanoc::DataSources
379
379
  end
380
380
  end
381
381
 
382
- # @return [ParseResult]
383
382
  def parse(content_filename, meta_filename)
384
- if meta_filename
385
- parse_with_separate_meta_filename(content_filename, meta_filename)
386
- else
387
- parse_with_frontmatter(content_filename)
388
- end
389
- end
390
-
391
- # @return [ParseResult]
392
- def parse_with_separate_meta_filename(content_filename, meta_filename)
393
- content = content_filename ? read(content_filename) : ''
394
- meta_raw = read(meta_filename)
395
- meta = parse_metadata(meta_raw, meta_filename)
396
- ParseResult.new(content: content, attributes: meta, attributes_data: meta_raw)
397
- end
398
-
399
- SEPARATOR = /(-{5}|-{3})/.source
400
-
401
- # @return [ParseResult]
402
- def parse_with_frontmatter(content_filename)
403
- data = read(content_filename)
404
-
405
- if data !~ /\A#{SEPARATOR}\s*$/
406
- return ParseResult.new(content: data, attributes: {}, attributes_data: '')
407
- end
408
-
409
- pieces = data.split(/^#{SEPARATOR}[ \t]*\r?\n?/, 3)
410
- if pieces.size < 4
411
- raise Errors::InvalidFormat.new(content_filename)
412
- end
413
-
414
- meta = parse_metadata(pieces[2], content_filename)
415
- content = pieces[4]
416
-
417
- ParseResult.new(content: content, attributes: meta, attributes_data: pieces[2])
418
- end
419
-
420
- # @return [Hash]
421
- def parse_metadata(data, filename)
422
- begin
423
- meta = YAML.load(data) || {}
424
- rescue => e
425
- raise Errors::UnparseableMetadata.new(filename, e)
426
- end
427
-
428
- verify_meta(meta, filename)
429
-
430
- meta
431
- end
432
-
433
- class ParseResult
434
- attr_reader :content
435
- attr_reader :attributes
436
- attr_reader :attributes_data
437
-
438
- def initialize(content:, attributes:, attributes_data:)
439
- @content = content
440
- @attributes = attributes
441
- @attributes_data = attributes_data
442
- end
443
- end
444
-
445
- def verify_meta(meta, filename)
446
- return if meta.is_a?(Hash)
447
-
448
- raise Errors::InvalidMetadata.new(filename, meta.class)
449
- end
450
-
451
- # Reads the content of the file with the given name and returns a string
452
- # in UTF-8 encoding. The original encoding of the string is derived from
453
- # the default external encoding, but this can be overridden by the
454
- # “encoding” configuration attribute in the data source configuration.
455
- def read(filename)
456
- # Read
457
- begin
458
- data = File.read(filename)
459
- rescue => e
460
- raise Errors::FileUnreadable.new(filename, e)
461
- end
462
-
463
- # Set original encoding, if any
464
- if @config && @config[:encoding]
465
- original_encoding = Encoding.find(@config[:encoding])
466
- data.force_encoding(@config[:encoding])
467
- else
468
- original_encoding = data.encoding
469
- end
470
-
471
- # Set encoding to UTF-8
472
- begin
473
- data.encode!('UTF-8')
474
- rescue
475
- raise Errors::InvalidEncoding.new(filename, original_encoding)
476
- end
477
-
478
- # Verify
479
- unless data.valid_encoding?
480
- raise Errors::InvalidEncoding.new(filename, original_encoding)
481
- end
482
-
483
- # Remove UTF-8 BOM (ugly)
484
- data.delete!("\xEF\xBB\xBF")
485
-
486
- data
383
+ parser = Parser.new(config: @config)
384
+ parser.call(content_filename, meta_filename)
487
385
  end
488
386
  end
489
387
  end
490
388
 
491
389
  require_relative 'filesystem/tools'
492
390
  require_relative 'filesystem/errors'
391
+ require_relative 'filesystem/parser'
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ class Nanoc::DataSources::Filesystem
5
+ class Parser
6
+ SEPARATOR = /(-{5}|-{3})/.source
7
+
8
+ class ParseResult
9
+ attr_reader :content
10
+ attr_reader :attributes
11
+ attr_reader :attributes_data
12
+
13
+ def initialize(content:, attributes:, attributes_data:)
14
+ @content = content
15
+ @attributes = attributes
16
+ @attributes_data = attributes_data
17
+ end
18
+ end
19
+
20
+ def initialize(config:)
21
+ @config = config
22
+ end
23
+
24
+ # @return [ParseResult]
25
+ def call(content_filename, meta_filename)
26
+ if meta_filename
27
+ parse_with_separate_meta_filename(content_filename, meta_filename)
28
+ else
29
+ parse_with_frontmatter(content_filename)
30
+ end
31
+ end
32
+
33
+ # @return [ParseResult]
34
+ def parse_with_separate_meta_filename(content_filename, meta_filename)
35
+ content = content_filename ? Tools.read_file(content_filename, config: @config) : ''
36
+ meta_raw = Tools.read_file(meta_filename, config: @config)
37
+ meta = parse_metadata(meta_raw, meta_filename)
38
+ ParseResult.new(content: content, attributes: meta, attributes_data: meta_raw)
39
+ end
40
+
41
+ # @return [ParseResult]
42
+ def parse_with_frontmatter(content_filename)
43
+ data = Tools.read_file(content_filename, config: @config)
44
+
45
+ if data !~ /\A#{SEPARATOR}\s*$/
46
+ return ParseResult.new(content: data, attributes: {}, attributes_data: '')
47
+ end
48
+
49
+ pieces = data.split(/^#{SEPARATOR}[ \t]*\r?\n?/, 3)
50
+ if pieces.size < 4
51
+ raise Errors::InvalidFormat.new(content_filename)
52
+ end
53
+
54
+ meta = parse_metadata(pieces[2], content_filename)
55
+ content = pieces[4]
56
+
57
+ ParseResult.new(content: content, attributes: meta, attributes_data: pieces[2])
58
+ end
59
+
60
+ # @return [Hash]
61
+ def parse_metadata(data, filename)
62
+ begin
63
+ meta = YAML.load(data) || {}
64
+ rescue => e
65
+ raise Errors::UnparseableMetadata.new(filename, e)
66
+ end
67
+
68
+ verify_meta(meta, filename)
69
+
70
+ meta
71
+ end
72
+
73
+ def verify_meta(meta, filename)
74
+ return if meta.is_a?(Hash)
75
+ raise Errors::InvalidMetadata.new(filename, meta.class)
76
+ end
77
+ end
78
+ end
@@ -148,5 +148,44 @@ class Nanoc::DataSources::Filesystem < Nanoc::DataSource
148
148
  end
149
149
  end
150
150
  module_function :resolve_symlink
151
+
152
+ # Reads the content of the file with the given name and returns a string
153
+ # in UTF-8 encoding. The original encoding of the string is derived from
154
+ # the default external encoding, but this can be overridden by the
155
+ # “encoding” configuration attribute in the data source configuration.
156
+ def read_file(filename, config:)
157
+ # Read
158
+ begin
159
+ data = File.read(filename)
160
+ rescue => e
161
+ raise Errors::FileUnreadable.new(filename, e)
162
+ end
163
+
164
+ # Set original encoding, if any
165
+ if config && config[:encoding]
166
+ original_encoding = Encoding.find(config[:encoding])
167
+ data.force_encoding(config[:encoding])
168
+ else
169
+ original_encoding = data.encoding
170
+ end
171
+
172
+ # Set encoding to UTF-8
173
+ begin
174
+ data.encode!('UTF-8')
175
+ rescue
176
+ raise Errors::InvalidEncoding.new(filename, original_encoding)
177
+ end
178
+
179
+ # Verify
180
+ unless data.valid_encoding?
181
+ raise Errors::InvalidEncoding.new(filename, original_encoding)
182
+ end
183
+
184
+ # Remove UTF-8 BOM (ugly)
185
+ data.delete!("\xEF\xBB\xBF")
186
+
187
+ data
188
+ end
189
+ module_function :read_file
151
190
  end
152
191
  end
@@ -1,10 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Nanoc
4
+ # @api private
5
+ module RuleDSL
6
+ end
7
+ end
8
+
3
9
  require_relative 'rule_dsl/compiler_dsl'
4
10
  require_relative 'rule_dsl/action_provider'
5
- require_relative 'rule_dsl/recording_executor'
6
- require_relative 'rule_dsl/rule_context'
11
+ require_relative 'rule_dsl/action_recorder'
7
12
  require_relative 'rule_dsl/action_sequence_calculator'
8
- require_relative 'rule_dsl/rule'
9
13
  require_relative 'rule_dsl/rules_collection'
10
14
  require_relative 'rule_dsl/rules_loader'
15
+
16
+ require_relative 'rule_dsl/rule_context'
17
+ require_relative 'rule_dsl/compilation_rule_context'
18
+ require_relative 'rule_dsl/routing_rule_context'
19
+
20
+ require_relative 'rule_dsl/rule'
21
+ require_relative 'rule_dsl/compilation_rule'
22
+ require_relative 'rule_dsl/routing_rule'
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Nanoc
4
4
  module RuleDSL
5
- class RecordingExecutor
5
+ class ActionRecorder
6
6
  include Nanoc::Int::ContractsSupport
7
7
 
8
8
  contract Nanoc::Int::ItemRep => C::Any
@@ -12,6 +12,7 @@ module Nanoc
12
12
  @any_layouts = false
13
13
  @last_snapshot = false
14
14
  @pre_snapshot = false
15
+ @skip_routing_rule = false
15
16
  end
16
17
 
17
18
  def inspect
@@ -36,10 +37,19 @@ module Nanoc
36
37
  @any_layouts = true
37
38
  end
38
39
 
39
- Pathlike = C::Maybe[C::Or[String, Nanoc::Identifier]]
40
- contract Symbol, C::KeywordArgs[path: C::Optional[Pathlike]] => nil
41
- def snapshot(snapshot_name, path: nil)
42
- @action_sequence_builder.add_snapshot(snapshot_name, path&.to_s)
40
+ MaybePathlike = C::Or[nil, Nanoc::UNDEFINED, String, Nanoc::Identifier]
41
+ contract Symbol, C::KeywordArgs[path: C::Optional[MaybePathlike]] => nil
42
+ def snapshot(snapshot_name, path: Nanoc::UNDEFINED)
43
+ @skip_routing_rule = path.nil?
44
+
45
+ path =
46
+ if Nanoc::UNDEFINED.equal?(path) || path.nil?
47
+ nil
48
+ else
49
+ path.to_s
50
+ end
51
+
52
+ @action_sequence_builder.add_snapshot(snapshot_name, path)
43
53
  case snapshot_name
44
54
  when :last
45
55
  @last_snapshot = true
@@ -59,6 +69,11 @@ module Nanoc
59
69
  @any_layouts
60
70
  end
61
71
 
72
+ contract C::None => C::Bool
73
+ def skip_routing_rule?
74
+ @skip_routing_rule
75
+ end
76
+
62
77
  contract C::None => C::Bool
63
78
  def last_snapshot?
64
79
  @last_snapshot
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nanoc::RuleDSL
4
- # Calculates action sequences for objects that can be run through a rule (item
5
- # representations and layouts).
6
- #
7
- # @api private
8
4
  class ActionSequenceCalculator
9
5
  DDMemoize.activate(self)
10
6
 
@@ -60,20 +56,20 @@ module Nanoc::RuleDSL
60
56
  view_context =
61
57
  Nanoc::ViewContextForPreCompilation.new(items: @site.items)
62
58
 
63
- executor = Nanoc::RuleDSL::RecordingExecutor.new(rep)
59
+ recorder = Nanoc::RuleDSL::ActionRecorder.new(rep)
64
60
  rule = @rules_collection.compilation_rule_for(rep)
65
61
 
66
62
  unless rule
67
63
  raise NoActionSequenceForItemRepException.new(rep)
68
64
  end
69
65
 
70
- executor.snapshot(:raw)
71
- rule.apply_to(rep, executor: executor, site: @site, view_context: view_context)
72
- executor.snapshot(:post) if executor.any_layouts?
73
- executor.snapshot(:last) unless executor.last_snapshot?
74
- executor.snapshot(:pre) unless executor.pre_snapshot?
66
+ recorder.snapshot(:raw)
67
+ rule.apply_to(rep, recorder: recorder, site: @site, view_context: view_context)
68
+ recorder.snapshot(:post) if recorder.any_layouts?
69
+ recorder.snapshot(:last) unless recorder.last_snapshot?
70
+ recorder.snapshot(:pre) unless recorder.pre_snapshot?
75
71
 
76
- copy_paths_from_routing_rules(compact_snapshots(executor.action_sequence), rep: rep)
72
+ copy_paths_from_routing_rules(compact_snapshots(recorder.action_sequence), rep: rep)
77
73
  end
78
74
 
79
75
  # @param [Nanoc::Int::Layout] layout
@@ -138,7 +134,6 @@ module Nanoc::RuleDSL
138
134
  basic_path =
139
135
  routing_rule.apply_to(
140
136
  rep,
141
- executor: nil,
142
137
  site: @site,
143
138
  view_context: view_context,
144
139
  )
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::RuleDSL
4
+ class CompilationRule < Rule
5
+ include Nanoc::Int::ContractsSupport
6
+
7
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[
8
+ site: Nanoc::Int::Site,
9
+ recorder: Nanoc::RuleDSL::ActionRecorder,
10
+ view_context: Nanoc::ViewContextForPreCompilation,
11
+ ] => C::Any
12
+ def apply_to(rep, site:, recorder:, view_context:)
13
+ context = Nanoc::RuleDSL::CompilationRuleContext.new(
14
+ rep: rep,
15
+ recorder: recorder,
16
+ site: site,
17
+ view_context: view_context,
18
+ )
19
+
20
+ context.instance_exec(matches(rep.item.identifier), &@block)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::RuleDSL
4
+ class CompilationRuleContext < RuleContext
5
+ include Nanoc::Int::ContractsSupport
6
+
7
+ contract C::KeywordArgs[
8
+ rep: Nanoc::Int::ItemRep,
9
+ site: Nanoc::Int::Site,
10
+ recorder: Nanoc::RuleDSL::ActionRecorder,
11
+ view_context: Nanoc::ViewContextForPreCompilation,
12
+ ] => C::Any
13
+ def initialize(rep:, site:, recorder:, view_context:)
14
+ @_recorder = recorder
15
+
16
+ super(rep: rep, site: site, view_context: view_context)
17
+ end
18
+
19
+ # Filters the current representation (calls {Nanoc::Int::ItemRep#filter} with
20
+ # the given arguments on the rep).
21
+ #
22
+ # @see Nanoc::Int::ItemRep#filter
23
+ #
24
+ # @param [Symbol] filter_name The name of the filter to run the item
25
+ # representations' content through
26
+ #
27
+ # @param [Hash] filter_args The filter arguments that should be passed to
28
+ # the filter's #run method
29
+ #
30
+ # @return [void]
31
+ def filter(filter_name, filter_args = {})
32
+ @_recorder.filter(filter_name, filter_args)
33
+ end
34
+
35
+ # Layouts the current representation (calls {Nanoc::Int::ItemRep#layout} with
36
+ # the given arguments on the rep).
37
+ #
38
+ # @see Nanoc::Int::ItemRep#layout
39
+ #
40
+ # @param [String] layout_identifier The identifier of the layout the item
41
+ # should be laid out with
42
+ #
43
+ # @return [void]
44
+ def layout(layout_identifier, extra_filter_args = nil)
45
+ @_recorder.layout(layout_identifier, extra_filter_args)
46
+ end
47
+
48
+ # Creates a snapshot of the current compiled item content. Calls
49
+ # {Nanoc::Int::ItemRep#snapshot} with the given arguments on the rep.
50
+ #
51
+ # @see Nanoc::Int::ItemRep#snapshot
52
+ #
53
+ # @param [Symbol] snapshot_name The name of the snapshot to create
54
+ #
55
+ # @param [String, nil] path
56
+ #
57
+ # @return [void]
58
+ def snapshot(snapshot_name, path: Nanoc::UNDEFINED)
59
+ @_recorder.snapshot(snapshot_name, path: path)
60
+ end
61
+
62
+ # Creates a snapshot named :last the current compiled item content, with
63
+ # the given path. This is a convenience method for {#snapshot}.
64
+ #
65
+ # @see #snapshot
66
+ #
67
+ # @param [String] path
68
+ #
69
+ # @return [void]
70
+ def write(arg)
71
+ @_write_snapshot_counter ||= 0
72
+ snapshot_name = "_#{@_write_snapshot_counter}".to_sym
73
+ @_write_snapshot_counter += 1
74
+
75
+ case arg
76
+ when String, Nanoc::Identifier, nil
77
+ snapshot(snapshot_name, path: arg)
78
+ when Hash
79
+ if arg.key?(:ext)
80
+ ext = arg[:ext].sub(/\A\./, '')
81
+ path = @item.identifier.without_exts + '.' + ext
82
+ snapshot(snapshot_name, path: path)
83
+ else
84
+ raise ArgumentError, 'Cannot call #write this way (need path or :ext)'
85
+ end
86
+ else
87
+ raise ArgumentError, 'Cannot call #write this way (need path or :ext)'
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nanoc::RuleDSL
4
- # Contains methods that will be executed by the site’s `Rules` file.
5
- #
6
- # @api private
7
4
  class CompilerDSL < Nanoc::Int::Context
8
5
  # The current rules filename.
9
6
  #
@@ -74,7 +71,7 @@ module Nanoc::RuleDSL
74
71
  def compile(identifier, rep: :default, &block)
75
72
  raise ArgumentError.new('#compile requires a block') unless block_given?
76
73
 
77
- rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, block)
74
+ rule = Nanoc::RuleDSL::CompilationRule.new(create_pattern(identifier), rep, block)
78
75
  @rules_collection.add_item_compilation_rule(rule)
79
76
  end
80
77
 
@@ -114,7 +111,7 @@ module Nanoc::RuleDSL
114
111
  def route(identifier, rep: :default, snapshot: :last, &block)
115
112
  raise ArgumentError.new('#route requires a block') unless block_given?
116
113
 
117
- rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, block, snapshot_name: snapshot)
114
+ rule = Nanoc::RuleDSL::RoutingRule.new(create_pattern(identifier), rep, block, snapshot_name: snapshot)
118
115
  @rules_collection.add_item_routing_rule(rule)
119
116
  end
120
117
 
@@ -173,7 +170,7 @@ module Nanoc::RuleDSL
173
170
  raise ArgumentError.new('#passthrough does not require a block') if block_given?
174
171
 
175
172
  compilation_block = proc {}
176
- compilation_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, compilation_block)
173
+ compilation_rule = Nanoc::RuleDSL::CompilationRule.new(create_pattern(identifier), rep, compilation_block)
177
174
  @rules_collection.add_item_compilation_rule(compilation_rule)
178
175
 
179
176
  # Create routing rule
@@ -188,7 +185,7 @@ module Nanoc::RuleDSL
188
185
  item[:extension].nil? || (item[:content_filename].nil? && item.identifier =~ %r{#{item[:extension]}/$}) ? item.identifier.chop : item.identifier.chop + '.' + item[:extension]
189
186
  end
190
187
  end
191
- routing_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, routing_block, snapshot_name: :last)
188
+ routing_rule = Nanoc::RuleDSL::RoutingRule.new(create_pattern(identifier), rep, routing_block, snapshot_name: :last)
192
189
  @rules_collection.add_item_routing_rule(routing_rule)
193
190
  end
194
191
 
@@ -213,10 +210,10 @@ module Nanoc::RuleDSL
213
210
  def ignore(identifier, rep: :default)
214
211
  raise ArgumentError.new('#ignore does not require a block') if block_given?
215
212
 
216
- compilation_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, proc {})
213
+ compilation_rule = Nanoc::RuleDSL::CompilationRule.new(create_pattern(identifier), rep, proc {})
217
214
  @rules_collection.add_item_compilation_rule(compilation_rule)
218
215
 
219
- routing_rule = Nanoc::RuleDSL::Rule.new(create_pattern(identifier), rep, proc {}, snapshot_name: :last)
216
+ routing_rule = Nanoc::RuleDSL::RoutingRule.new(create_pattern(identifier), rep, proc {}, snapshot_name: :last)
220
217
  @rules_collection.add_item_routing_rule(routing_rule)
221
218
  end
222
219
 
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::RuleDSL
4
+ class RoutingRule < Rule
5
+ include Nanoc::Int::ContractsSupport
6
+
7
+ contract C::None => C::Maybe[Symbol]
8
+ attr_reader :snapshot_name
9
+
10
+ contract Nanoc::Int::Pattern, Symbol, Proc, C::KeywordArgs[snapshot_name: C::Optional[Symbol]] => C::Any
11
+ def initialize(pattern, rep_name, block, snapshot_name: nil)
12
+ super(pattern, rep_name, block)
13
+
14
+ @snapshot_name = snapshot_name
15
+ end
16
+
17
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[
18
+ site: Nanoc::Int::Site,
19
+ view_context: Nanoc::ViewContextForPreCompilation,
20
+ ] => C::Any
21
+ def apply_to(rep, site:, view_context:)
22
+ context = Nanoc::RuleDSL::RoutingRuleContext.new(
23
+ rep: rep, site: site, view_context: view_context,
24
+ )
25
+
26
+ context.instance_exec(matches(rep.item.identifier), &@block)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::RuleDSL
4
+ class RoutingRuleContext < RuleContext
5
+ end
6
+ end
@@ -1,77 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nanoc::RuleDSL
4
- # Contains the processing information for a item.
5
- #
6
- # @api private
7
4
  class Rule
8
5
  include Nanoc::Int::ContractsSupport
9
6
 
10
- # @return [Symbol] The name of the representation that will be compiled
11
- # using this rule
7
+ contract C::None => Symbol
12
8
  attr_reader :rep_name
13
9
 
14
- # @return [Symbol] The name of the snapshot this rule will apply to.
15
- # Ignored for compilation rules, but used for routing rules.
16
- attr_reader :snapshot_name
17
-
10
+ contract C::None => Nanoc::Int::Pattern
18
11
  attr_reader :pattern
19
12
 
20
- # Creates a new item compilation rule with the given identifier regex,
21
- # compiler and block. The block will be called during compilation with the
22
- # item rep as its argument.
23
- #
24
- # @param [Nanoc::Int::Pattern] pattern
25
- #
26
- # @param [String, Symbol] rep_name The name of the item representation
27
- # where this rule can be applied to
28
- #
29
- # @param [Proc] block A block that will be called when matching items are
30
- # compiled
31
- #
32
- # @param [Symbol, nil] snapshot_name The name of the snapshot this rule will
33
- # apply to. Ignored for compilation rules, but used for routing rules.
34
- def initialize(pattern, rep_name, block, snapshot_name: nil)
13
+ contract Nanoc::Int::Pattern, Symbol, Proc => C::Any
14
+ def initialize(pattern, rep_name, block)
35
15
  @pattern = pattern
36
16
  @rep_name = rep_name.to_sym
37
- @snapshot_name = snapshot_name
38
17
  @block = block
39
18
  end
40
19
 
41
- # @param [Nanoc::Int::Item] item The item to check
42
- #
43
- # @return [Boolean] true if this rule can be applied to the given item
44
- # rep, false otherwise
20
+ contract Nanoc::Int::Item => C::Bool
45
21
  def applicable_to?(item)
46
22
  @pattern.match?(item.identifier)
47
23
  end
48
24
 
49
- contract Nanoc::Int::ItemRep, C::KeywordArgs[
50
- site: Nanoc::Int::Site,
51
- executor: C::Or[nil, Nanoc::Int::Executor, Nanoc::RuleDSL::RecordingExecutor],
52
- view_context: Nanoc::ViewContextForPreCompilation,
53
- ] => C::Any
54
- def apply_to(rep, site:, executor:, view_context:)
55
- # FIXME: allowing executor to be nil is ugly
56
-
57
- context = Nanoc::RuleDSL::RuleContext.new(
58
- rep: rep,
59
- executor: executor,
60
- site: site,
61
- view_context: view_context,
62
- )
63
-
64
- context.instance_exec(matches(rep.item.identifier), &@block)
65
- end
66
-
67
- protected
68
-
69
- # Matches the rule regexp against items identifier and gives back group
70
- # captures if any
71
- #
72
- # @param [String] identifier Identifier to capture groups for
73
- #
74
- # @return [nil, Array] Captured groups, if any
25
+ # @api private
26
+ contract Nanoc::Identifier => C::Or[nil, C::ArrayOf[String]]
75
27
  def matches(identifier)
76
28
  @pattern.captures(identifier)
77
29
  end
@@ -1,19 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nanoc::RuleDSL
4
- # Provides a context in which compilation and routing rules can be executed.
5
- # It provides access to the item representation that is being compiled or
6
- # routed.
7
- #
8
- # @api private
9
4
  class RuleContext < Nanoc::Int::Context
10
- # @param [Nanoc::Int::ItemRep] rep
11
- # @param [Nanoc::Int::Site] site
12
- # @param [Nanoc::Int::Executor, Nanoc::RuleDSL::RecordingExecutor] executor
13
- # @param [Nanoc::ViewContextForCompilation] view_context
14
- def initialize(rep:, site:, executor:, view_context:)
15
- @_executor = executor
5
+ include Nanoc::Int::ContractsSupport
16
6
 
7
+ contract C::KeywordArgs[
8
+ rep: Nanoc::Int::ItemRep,
9
+ site: Nanoc::Int::Site,
10
+ view_context: Nanoc::ViewContextForPreCompilation,
11
+ ] => C::Any
12
+ def initialize(rep:, site:, view_context:)
17
13
  super({
18
14
  item: Nanoc::BasicItemView.new(rep.item, view_context),
19
15
  rep: Nanoc::BasicItemRepView.new(rep, view_context),
@@ -23,77 +19,5 @@ module Nanoc::RuleDSL
23
19
  config: Nanoc::ConfigView.new(site.config, view_context),
24
20
  })
25
21
  end
26
-
27
- # Filters the current representation (calls {Nanoc::Int::ItemRep#filter} with
28
- # the given arguments on the rep).
29
- #
30
- # @see Nanoc::Int::ItemRep#filter
31
- #
32
- # @param [Symbol] filter_name The name of the filter to run the item
33
- # representations' content through
34
- #
35
- # @param [Hash] filter_args The filter arguments that should be passed to
36
- # the filter's #run method
37
- #
38
- # @return [void]
39
- def filter(filter_name, filter_args = {})
40
- @_executor.filter(filter_name, filter_args)
41
- end
42
-
43
- # Layouts the current representation (calls {Nanoc::Int::ItemRep#layout} with
44
- # the given arguments on the rep).
45
- #
46
- # @see Nanoc::Int::ItemRep#layout
47
- #
48
- # @param [String] layout_identifier The identifier of the layout the item
49
- # should be laid out with
50
- #
51
- # @return [void]
52
- def layout(layout_identifier, extra_filter_args = nil)
53
- @_executor.layout(layout_identifier, extra_filter_args)
54
- end
55
-
56
- # Creates a snapshot of the current compiled item content. Calls
57
- # {Nanoc::Int::ItemRep#snapshot} with the given arguments on the rep.
58
- #
59
- # @see Nanoc::Int::ItemRep#snapshot
60
- #
61
- # @param [Symbol] snapshot_name The name of the snapshot to create
62
- #
63
- # @param [String, nil] path
64
- #
65
- # @return [void]
66
- def snapshot(snapshot_name, path: nil)
67
- @_executor.snapshot(snapshot_name, path: path)
68
- end
69
-
70
- # Creates a snapshot named :last the current compiled item content, with
71
- # the given path. This is a convenience method for {#snapshot}.
72
- #
73
- # @see #snapshot
74
- #
75
- # @param [String] path
76
- #
77
- # @return [void]
78
- def write(arg)
79
- @_write_snapshot_counter ||= 0
80
- snapshot_name = "_#{@_write_snapshot_counter}".to_sym
81
- @_write_snapshot_counter += 1
82
-
83
- case arg
84
- when String, Nanoc::Identifier
85
- snapshot(snapshot_name, path: arg)
86
- when Hash
87
- if arg.key?(:ext)
88
- ext = arg[:ext].sub(/\A\./, '')
89
- path = @item.identifier.without_exts + '.' + ext
90
- snapshot(snapshot_name, path: path)
91
- else
92
- raise ArgumentError, 'Cannot call #write this way (need path or :ext)'
93
- end
94
- else
95
- raise ArgumentError, 'Cannot call #write this way (need path or :ext)'
96
- end
97
- end
98
22
  end
99
23
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Nanoc::RuleDSL
4
- # @api private
5
4
  class RulesLoader
6
5
  def initialize(config, rules_collection)
7
6
  @dsl = Nanoc::RuleDSL::CompilerDSL.new(rules_collection, config)
@@ -24,6 +24,20 @@ module Nanoc
24
24
  def skip_unless_have_command(cmd)
25
25
  skip "Could not find external command \"#{cmd}\"" unless command?(cmd)
26
26
  end
27
+
28
+ def sleep_until(max: 3.0)
29
+ start = Time.now
30
+ loop do
31
+ diff = (Time.now - start).to_f
32
+ if diff > max
33
+ raise "Waited for #{diff}s for condition to become true, but it never did"
34
+ end
35
+
36
+ break if yield
37
+
38
+ sleep 0.1
39
+ end
40
+ end
27
41
  end
28
42
 
29
43
  class HelperContext
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Nanoc
4
4
  # The current Nanoc version.
5
- VERSION = '4.8.18'
5
+ VERSION = '4.8.19'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.8.18
4
+ version: 4.8.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Defreyne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-28 00:00:00.000000000 Z
11
+ date: 2018-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -314,6 +314,7 @@ files:
314
314
  - lib/nanoc/data_sources.rb
315
315
  - lib/nanoc/data_sources/filesystem.rb
316
316
  - lib/nanoc/data_sources/filesystem/errors.rb
317
+ - lib/nanoc/data_sources/filesystem/parser.rb
317
318
  - lib/nanoc/data_sources/filesystem/tools.rb
318
319
  - lib/nanoc/deploying.rb
319
320
  - lib/nanoc/deploying/deployer.rb
@@ -375,9 +376,13 @@ files:
375
376
  - lib/nanoc/helpers/xml_sitemap.rb
376
377
  - lib/nanoc/rule_dsl.rb
377
378
  - lib/nanoc/rule_dsl/action_provider.rb
379
+ - lib/nanoc/rule_dsl/action_recorder.rb
378
380
  - lib/nanoc/rule_dsl/action_sequence_calculator.rb
381
+ - lib/nanoc/rule_dsl/compilation_rule.rb
382
+ - lib/nanoc/rule_dsl/compilation_rule_context.rb
379
383
  - lib/nanoc/rule_dsl/compiler_dsl.rb
380
- - lib/nanoc/rule_dsl/recording_executor.rb
384
+ - lib/nanoc/rule_dsl/routing_rule.rb
385
+ - lib/nanoc/rule_dsl/routing_rule_context.rb
381
386
  - lib/nanoc/rule_dsl/rule.rb
382
387
  - lib/nanoc/rule_dsl/rule_context.rb
383
388
  - lib/nanoc/rule_dsl/rules_collection.rb