nanoc 4.2.3 → 4.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 308b361e14b750d6171037c0167935bf806c7a5f
4
- data.tar.gz: bf2db4108dc18dd6fa2602a3451ed5a2636af11e
3
+ metadata.gz: d71e907ef0318683da05fb81149422628b700007
4
+ data.tar.gz: 8d6e28866ff7dc184b6738005c55e4f368c2f74c
5
5
  SHA512:
6
- metadata.gz: 820db95cb94318cca8afc1e6942f33e4131beb2bcc53eb0e4866079180dab65f6164e274117c53b88799b29bab3e069d245250b972a630bb5386dd3036f3a287
7
- data.tar.gz: bdff99a92e4a2ff4ea4c6486bc3341728986ef259f5fe9c270c80355bf49d38d1cae5cf2f1c9b3178bce2c573313a423faca57009b7717ca1332abcab01019f1
6
+ metadata.gz: ef183242bffb54bc9875275146912a1bc7f3bc84ed51fa77b032ba986952a8bd6895e18170b2580e6131e8deaee0591a2df94769917ae4bd829a566310f529e7
7
+ data.tar.gz: bf46729b4e0e8e682327b553460f9e3611b76ef74627441d3f1189d155bd6f978da4b3668dc08cf06fec6c00ad6dcdf694f6b2afe19814cc7436efda5b7df18a
data/Gemfile CHANGED
@@ -2,6 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'json', '~> 2.0'
6
+
5
7
  group :devel do
6
8
  gem 'contracts', '~> 0.14'
7
9
  gem 'coveralls', require: false
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nanoc (4.2.3)
4
+ nanoc (4.2.4)
5
5
  cri (~> 2.3)
6
6
  hamster (~> 3.0)
7
+ ref (~> 2.0)
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
@@ -38,9 +39,9 @@ GEM
38
39
  sass (>= 3.2, < 3.5)
39
40
  concurrent-ruby (1.0.2)
40
41
  contracts (0.14.0)
41
- coveralls (0.8.13)
42
- json (~> 1.8)
43
- simplecov (~> 0.11.0)
42
+ coveralls (0.8.14)
43
+ json (>= 1.8, < 3)
44
+ simplecov (~> 0.12.0)
44
45
  term-ansicolor (~> 1.3)
45
46
  thor (~> 0.19.1)
46
47
  tins (~> 1.6.0)
@@ -51,9 +52,9 @@ GEM
51
52
  diff-lcs (1.2.5)
52
53
  docile (1.1.5)
53
54
  erubis (2.7.0)
54
- excon (0.50.1)
55
+ excon (0.51.0)
55
56
  execjs (2.7.0)
56
- ffi (1.9.10)
57
+ ffi (1.9.14)
57
58
  fission (0.5.0)
58
59
  CFPropertyList (~> 2.2)
59
60
  fog (1.38.0)
@@ -93,12 +94,12 @@ GEM
93
94
  fog-atmos (0.1.0)
94
95
  fog-core
95
96
  fog-xml
96
- fog-aws (0.9.4)
97
+ fog-aws (0.10.0)
97
98
  fog-core (~> 1.38)
98
99
  fog-json (~> 1.0)
99
100
  fog-xml (~> 0.1)
100
101
  ipaddress (~> 0.8)
101
- fog-brightbox (0.10.1)
102
+ fog-brightbox (0.11.0)
102
103
  fog-core (~> 1.22)
103
104
  fog-json
104
105
  inflecto (~> 0.0.2)
@@ -107,7 +108,7 @@ GEM
107
108
  fog-json (~> 1.0)
108
109
  fog-xml (~> 0.1)
109
110
  ipaddress (~> 0.8)
110
- fog-core (1.41.0)
111
+ fog-core (1.42.0)
111
112
  builder
112
113
  excon (~> 0.49)
113
114
  formatador (~> 0.2)
@@ -127,7 +128,7 @@ GEM
127
128
  multi_json (~> 1.10)
128
129
  fog-local (0.3.0)
129
130
  fog-core (~> 1.27)
130
- fog-openstack (0.1.7)
131
+ fog-openstack (0.1.8)
131
132
  fog-core (>= 1.40)
132
133
  fog-json (>= 1.0)
133
134
  ipaddress (>= 0.8)
@@ -158,7 +159,7 @@ GEM
158
159
  fog-serverlove (0.1.2)
159
160
  fog-core
160
161
  fog-json
161
- fog-softlayer (1.1.2)
162
+ fog-softlayer (1.1.3)
162
163
  fog-core
163
164
  fog-json
164
165
  fog-storm_on_demand (0.1.1)
@@ -183,7 +184,7 @@ GEM
183
184
  fog-core
184
185
  nokogiri (~> 1.5, >= 1.5.11)
185
186
  formatador (0.2.5)
186
- fuubar (2.0.0)
187
+ fuubar (2.1.1)
187
188
  rspec (~> 3.0)
188
189
  ruby-progressbar (~> 1.4)
189
190
  guard (2.14.0)
@@ -209,7 +210,7 @@ GEM
209
210
  hashdiff (0.3.0)
210
211
  inflecto (0.0.2)
211
212
  ipaddress (0.8.3)
212
- json (1.8.3)
213
+ json (2.0.1)
213
214
  kramdown (1.11.1)
214
215
  less (2.6.0)
215
216
  commonjs (~> 0.2.7)
@@ -246,7 +247,7 @@ GEM
246
247
  pkg-config (1.1.7)
247
248
  posix-spawn (0.3.11)
248
249
  powerpack (0.1.1)
249
- pry (0.10.3)
250
+ pry (0.10.4)
250
251
  coderay (~> 1.1.0)
251
252
  method_source (~> 0.8.1)
252
253
  slop (~> 3.4)
@@ -265,16 +266,15 @@ GEM
265
266
  nokogiri (>= 1.4.1)
266
267
  trollop
267
268
  rdiscount (2.2.0.1)
268
- rdoc (4.2.2)
269
- json (~> 1.4)
269
+ rdoc (4.2.1)
270
270
  redcarpet (3.3.4)
271
271
  ref (2.0.0)
272
- rouge (2.0.2)
272
+ rouge (2.0.5)
273
273
  rspec (3.5.0)
274
274
  rspec-core (~> 3.5.0)
275
275
  rspec-expectations (~> 3.5.0)
276
276
  rspec-mocks (~> 3.5.0)
277
- rspec-core (3.5.0)
277
+ rspec-core (3.5.1)
278
278
  rspec-support (~> 3.5.0)
279
279
  rspec-expectations (3.5.0)
280
280
  diff-lcs (>= 1.2.0, < 2.0)
@@ -283,7 +283,7 @@ GEM
283
283
  diff-lcs (>= 1.2.0, < 2.0)
284
284
  rspec-support (~> 3.5.0)
285
285
  rspec-support (3.5.0)
286
- rubocop (0.41.1)
286
+ rubocop (0.41.2)
287
287
  parser (>= 2.3.1.1, < 3.0)
288
288
  powerpack (~> 0.1)
289
289
  rainbow (>= 1.99.1, < 3.0)
@@ -295,9 +295,9 @@ GEM
295
295
  safe_yaml (1.0.4)
296
296
  sass (3.4.22)
297
297
  shellany (0.0.1)
298
- simplecov (0.11.2)
298
+ simplecov (0.12.0)
299
299
  docile (~> 1.1.0)
300
- json (~> 1.8)
300
+ json (>= 1.8, < 3)
301
301
  simplecov-html (~> 0.10.0)
302
302
  simplecov-html (0.10.0)
303
303
  slim (3.0.7)
@@ -329,7 +329,7 @@ GEM
329
329
  hashdiff
330
330
  xml-simple (1.1.5)
331
331
  yajl-ruby (1.2.1)
332
- yard (0.8.7.6)
332
+ yard (0.9.5)
333
333
  yuicompressor (1.3.3)
334
334
 
335
335
  PLATFORMS
@@ -352,6 +352,7 @@ DEPENDENCIES
352
352
  guard-rake
353
353
  haml
354
354
  handlebars
355
+ json (~> 2.0)
355
356
  kramdown
356
357
  less (~> 2.0)
357
358
  listen
data/NEWS.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.2.4 (2016-07-24)
4
+
5
+ Fixes:
6
+
7
+ * Fixed `UnmetDependency` errors in postprocessor (#913, #917)
8
+
9
+ Enhancement:
10
+
11
+ * Sped up Nanoc by not releasing cache memory as quickly (#902)
12
+ * Let `internal_links` check also verify resource paths, such as scripts and images (#912) [Lorin Werthen]
13
+ * Improved error reporting for errors in the Rules file (#908, #914, #915, #916)
14
+ * Removed `win32console` support, as it’s deprecated and causing problems (#900, #918)
15
+
3
16
  ## 4.2.3 (2016-07-03)
4
17
 
5
18
  Fixes:
data/lib/nanoc.rb CHANGED
@@ -22,6 +22,7 @@ end
22
22
 
23
23
  # Load external dependencies
24
24
  require 'hamster'
25
+ require 'ref'
25
26
 
26
27
  # Load general requirements
27
28
  require 'digest'
data/lib/nanoc/base.rb CHANGED
@@ -25,6 +25,7 @@ require_relative 'base/core_ext'
25
25
  require_relative 'base/contracts_support'
26
26
 
27
27
  require_relative 'base/entities'
28
+ require_relative 'base/feature'
28
29
  require_relative 'base/repos'
29
30
  require_relative 'base/services'
30
31
  require_relative 'base/views'
@@ -191,8 +191,7 @@ module Nanoc::Int
191
191
  end
192
192
 
193
193
  # Find item reps to compile and compile them
194
- outdated_reps = @reps.select { |r| outdatedness_checker.outdated?(r) }
195
- selector = Nanoc::Int::ItemRepSelector.new(outdated_reps)
194
+ selector = Nanoc::Int::ItemRepSelector.new(@reps)
196
195
  selector.each do |rep|
197
196
  @stack = []
198
197
  compile_rep(rep)
@@ -0,0 +1,11 @@
1
+ module Nanoc
2
+ # @api private
3
+ module Feature
4
+ TRUES = %w(y yes 1 t true).freeze
5
+
6
+ def self.enabled?(name)
7
+ env_name = "NANOC_FEATURE_#{name.upcase}"
8
+ TRUES.include?(ENV.fetch(env_name, 'f').downcase)
9
+ end
10
+ end
11
+ end
@@ -15,9 +15,12 @@ module Nanoc::Int
15
15
  end
16
16
 
17
17
  def inspect
18
- @ref.inspect
19
- rescue WeakRef::RefError
20
- '<weak ref collected>'
18
+ obj = @ref.object
19
+ if obj
20
+ obj.inspect
21
+ else
22
+ '<garbage collected>'
23
+ end
21
24
  end
22
25
  end
23
26
 
@@ -75,17 +78,13 @@ module Nanoc::Int
75
78
 
76
79
  value = NONE
77
80
  if method_cache.key?(args)
78
- value =
79
- begin
80
- method_cache[args].ref.value
81
- rescue WeakRef::RefError
82
- NONE
83
- end
81
+ object = method_cache[args].ref.object
82
+ value = object ? object.value : NONE
84
83
  end
85
84
 
86
85
  if value.equal?(NONE)
87
86
  send(original_method_name, *args).tap do |r|
88
- method_cache[args] = Wrapper.new(WeakRef.new(Value.new(r)))
87
+ method_cache[args] = Wrapper.new(Ref::SoftReference.new(Value.new(r)))
89
88
  end
90
89
  else
91
90
  value
data/lib/nanoc/cli.rb CHANGED
@@ -6,13 +6,6 @@ rescue LoadError => e
6
6
  exit 1
7
7
  end
8
8
 
9
- if Nanoc.on_windows?
10
- begin
11
- require 'Win32/Console/ANSI'
12
- rescue LoadError
13
- end
14
- end
15
-
16
9
  # @api private
17
10
  module Nanoc::CLI
18
11
  module Commands
@@ -208,15 +201,7 @@ module Nanoc::CLI
208
201
 
209
202
  # @return [Boolean] true if color support is present, false if not
210
203
  def self.enable_ansi_colors?(io)
211
- unless io.tty?
212
- return false
213
- end
214
-
215
- if Nanoc.on_windows?
216
- return defined?(::Win32::Console::ANSI)
217
- end
218
-
219
- true
204
+ io.tty?
220
205
  end
221
206
 
222
207
  def self.after_setup_procs
@@ -14,6 +14,7 @@ IDENTICAL - The item was deemed outdated and has been recompiled, but the compil
14
14
  SKIP - The item was deemed not outdated and was therefore not recompiled
15
15
 
16
16
  EOS
17
+ flag nil, :profile, 'profile compilation' if Nanoc::Feature.enabled?('PROFILER')
17
18
 
18
19
  module Nanoc::CLI::Commands
19
20
  class Compile < ::Nanoc::CLI::CommandRunner
@@ -49,6 +50,18 @@ module Nanoc::CLI::Commands
49
50
  # @return [void]
50
51
  def stop
51
52
  end
53
+
54
+ # @api private
55
+ def start_safely
56
+ start
57
+ @_started = true
58
+ end
59
+
60
+ # @api private
61
+ def stop_safely
62
+ stop if @_started
63
+ @_started = false
64
+ end
52
65
  end
53
66
 
54
67
  # Generates diffs for every output file written
@@ -63,10 +76,10 @@ module Nanoc::CLI::Commands
63
76
  require 'tempfile'
64
77
  setup_diffs
65
78
  old_contents = {}
66
- Nanoc::Int::NotificationCenter.on(:will_write_rep) do |rep, path|
79
+ Nanoc::Int::NotificationCenter.on(:will_write_rep, self) do |rep, path|
67
80
  old_contents[rep] = File.file?(path) ? File.read(path) : nil
68
81
  end
69
- Nanoc::Int::NotificationCenter.on(:rep_written) do |rep, path, _is_created, _is_modified|
82
+ Nanoc::Int::NotificationCenter.on(:rep_written, self) do |rep, path, _is_created, _is_modified|
70
83
  unless rep.binary?
71
84
  new_contents = File.file?(path) ? File.read(path) : nil
72
85
  if old_contents[rep] && new_contents
@@ -80,6 +93,10 @@ module Nanoc::CLI::Commands
80
93
  # @see Listener#stop
81
94
  def stop
82
95
  super
96
+
97
+ Nanoc::Int::NotificationCenter.remove(:will_write_rep, self)
98
+ Nanoc::Int::NotificationCenter.remove(:rep_written, self)
99
+
83
100
  teardown_diffs
84
101
  end
85
102
 
@@ -350,6 +367,28 @@ module Nanoc::CLI::Commands
350
367
  end
351
368
  end
352
369
 
370
+ # Records a profile using StackProf
371
+ class StackProfProfiler < Listener
372
+ PROFILE_FILE = 'tmp/stackprof_profile'.freeze
373
+
374
+ # @see Listener#enable_for?
375
+ def self.enable_for?(command_runner)
376
+ command_runner.options.fetch(:profile, false)
377
+ end
378
+
379
+ # @see Listener#start
380
+ def start
381
+ require 'stackprof'
382
+ StackProf.start(mode: :cpu)
383
+ end
384
+
385
+ # @see Listener#stop
386
+ def stop
387
+ StackProf.stop
388
+ StackProf.results(PROFILE_FILE)
389
+ end
390
+ end
391
+
353
392
  attr_accessor :listener_classes
354
393
 
355
394
  def initialize(options, arguments, command)
@@ -388,6 +427,7 @@ module Nanoc::CLI::Commands
388
427
  Nanoc::CLI::Commands::Compile::TimingRecorder,
389
428
  Nanoc::CLI::Commands::Compile::GCController,
390
429
  Nanoc::CLI::Commands::Compile::FileActionPrinter,
430
+ Nanoc::CLI::Commands::Compile::StackProfProfiler,
391
431
  ]
392
432
  end
393
433
 
@@ -397,7 +437,7 @@ module Nanoc::CLI::Commands
397
437
  .select { |klass| klass.enable_for?(self) }
398
438
  .map { |klass| klass.new(reps: reps) }
399
439
 
400
- @listeners.each(&:start)
440
+ @listeners.each(&:start_safely)
401
441
  end
402
442
 
403
443
  def listeners
@@ -412,7 +452,7 @@ module Nanoc::CLI::Commands
412
452
  end
413
453
 
414
454
  def teardown_listeners
415
- @listeners.each(&:stop)
455
+ @listeners.each(&:stop_safely)
416
456
  end
417
457
 
418
458
  def reps
@@ -15,7 +15,10 @@ module Nanoc::Extra::Checking::Checks
15
15
  # TODO: de-duplicate this (duplicated in external links check)
16
16
  filenames = output_filenames.select { |f| File.extname(f) == '.html' }
17
17
  hrefs_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames, :internal).filenames_per_href
18
- hrefs_with_filenames.each_pair do |href, fns|
18
+ resource_uris_with_filenames = ::Nanoc::Extra::LinkCollector.new(filenames, :internal).filenames_per_resource_uri
19
+
20
+ uris = hrefs_with_filenames.merge(resource_uris_with_filenames)
21
+ uris.each_pair do |href, fns|
19
22
  fns.each do |filename|
20
23
  next if valid?(href, filename)
21
24
 
@@ -22,6 +22,10 @@ module Nanoc
22
22
  end
23
23
 
24
24
  def layout(_rep, layout_identifier, extra_filter_args = {})
25
+ unless layout_identifier.is_a?(String)
26
+ raise ArgumentError.new('The layout passed to #layout must be a string')
27
+ end
28
+
25
29
  unless @rule_memory.any_layouts?
26
30
  @rule_memory.add_snapshot(:pre, true, nil)
27
31
  end
@@ -12,6 +12,18 @@ module Nanoc::RuleDSL
12
12
  end
13
13
  end
14
14
 
15
+ class NoRuleMemoryForLayoutException < ::Nanoc::Error
16
+ def initialize(layout)
17
+ super("There is no layout rule specified for #{layout.inspect}")
18
+ end
19
+ end
20
+
21
+ class NoRuleMemoryForItemRepException < ::Nanoc::Error
22
+ def initialize(item)
23
+ super("There is no compilation rule specified for #{item.inspect}")
24
+ end
25
+ end
26
+
15
27
  # @api private
16
28
  attr_accessor :rules_collection
17
29
 
@@ -57,14 +69,16 @@ module Nanoc::RuleDSL
57
69
  #
58
70
  # @return [Nanoc::Int::RuleMemory]
59
71
  def new_rule_memory_for_rep(rep)
60
- # FIXME: What if #compilation_rule_for returns nil?
61
-
62
72
  dependency_tracker = Nanoc::Int::DependencyTracker::Null.new
63
73
  view_context = @site.compiler.create_view_context(dependency_tracker)
64
74
 
65
75
  executor = Nanoc::RuleDSL::RecordingExecutor.new(rep, @rules_collection, @site)
66
76
  rule = @rules_collection.compilation_rule_for(rep)
67
77
 
78
+ unless rule
79
+ raise NoRuleMemoryForItemRepException.new(rep)
80
+ end
81
+
68
82
  executor.snapshot(rep, :raw)
69
83
  executor.snapshot(rep, :pre, final: false)
70
84
  rule.apply_to(rep, executor: executor, site: @site, view_context: view_context)
@@ -83,7 +97,11 @@ module Nanoc::RuleDSL
83
97
  # @return [Nanoc::Int::RuleMemory]
84
98
  def new_rule_memory_for_layout(layout)
85
99
  res = @rules_collection.filter_for_layout(layout)
86
- # FIXME: what if res is nil?
100
+
101
+ unless res
102
+ raise NoRuleMemoryForLayoutException.new(layout)
103
+ end
104
+
87
105
  Nanoc::Int::RuleMemory.new(layout).tap do |rm|
88
106
  rm.add_filter(res[0], res[1])
89
107
  end
data/lib/nanoc/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Nanoc
2
2
  # The current Nanoc version.
3
- VERSION = '4.2.3'.freeze
3
+ VERSION = '4.2.4'.freeze
4
4
  end
data/nanoc.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.add_runtime_dependency('cri', '~> 2.3')
28
28
  s.add_runtime_dependency('hamster', '~> 3.0')
29
+ s.add_runtime_dependency('ref', '~> 2.0')
29
30
 
30
31
  s.add_development_dependency('bundler', '>= 1.7.10', '< 2.0')
31
32
  end
@@ -208,6 +208,21 @@ class Nanoc::Int::CompilerTest < Nanoc::TestCase
208
208
  end
209
209
  end
210
210
 
211
+ def test_compile_should_recompile_all_reps
212
+ Nanoc::CLI.run %w(create_site bar)
213
+
214
+ FileUtils.cd('bar') do
215
+ Nanoc::CLI.run %w(compile)
216
+
217
+ site = Nanoc::Int::SiteLoader.new.new_from_cwd
218
+ site.compile
219
+
220
+ # At this point, even the already compiled items in the previous pass
221
+ # should have their compiled content assigned, so this should work:
222
+ site.compiler.reps[site.items['/index.*']][0].compiled_content
223
+ end
224
+ end
225
+
211
226
  def test_disallow_multiple_snapshots_with_the_same_name
212
227
  # Create site
213
228
  Nanoc::CLI.run %w(create_site bar)
@@ -16,6 +16,21 @@ class Nanoc::Extra::Checking::Checks::InternalLinksTest < Nanoc::TestCase
16
16
  end
17
17
  end
18
18
 
19
+ def test_resource_uris
20
+ with_site do |site|
21
+ # Create files
22
+ FileUtils.mkdir_p('output')
23
+ File.open('output/bar.html', 'w') { |io| io.write('<link rel="stylesheet" href="/styledinges.css">') }
24
+
25
+ # Create check
26
+ check = Nanoc::Extra::Checking::Checks::InternalLinks.create(site)
27
+ check.run
28
+
29
+ # Test
30
+ assert check.issues.size == 1
31
+ end
32
+ end
33
+
19
34
  def test_valid?
20
35
  with_site do |site|
21
36
  # Create files
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.2.3
4
+ version: 4.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Defreyne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-03 00:00:00.000000000 Z
11
+ date: 2016-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cri
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: ref
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -123,6 +137,7 @@ files:
123
137
  - lib/nanoc/base/entities/snapshot_def.rb
124
138
  - lib/nanoc/base/error.rb
125
139
  - lib/nanoc/base/errors.rb
140
+ - lib/nanoc/base/feature.rb
126
141
  - lib/nanoc/base/memoization.rb
127
142
  - lib/nanoc/base/plugin_registry.rb
128
143
  - lib/nanoc/base/repos.rb
@@ -392,4 +407,3 @@ signing_key:
392
407
  specification_version: 4
393
408
  summary: A static-site generator with a focus on flexibility.
394
409
  test_files: []
395
- has_rdoc: