jeckyl 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/jeckyl.rb ADDED
@@ -0,0 +1,619 @@
1
+ #
2
+ # Author:: Robert Sharp
3
+ # Copyright:: Copyright (c) 2011 Robert Sharp
4
+ # License:: Open Software Licence v3.0
5
+ #
6
+ # This software is licensed for use under the Open Software Licence v. 3.0
7
+ # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
8
+ # and in the file copyright.txt. Under the terms of this licence, all derivative works
9
+ # must themselves be licensed under the Open Software Licence v. 3.0
10
+ #
11
+ #
12
+ # == JECKYL
13
+ #
14
+ require 'jeckyl/version'
15
+ require 'jeckyl/errors'
16
+
17
+ #
18
+ # The Jeckyl configurator module, which is just a wrapper. See {file:README.md Readme} for details.
19
+ #
20
+ module Jeckyl
21
+
22
+ #default location for all config files
23
+ ConfigRoot = '/etc/jermine'
24
+
25
+ # This is the main Jeckyl class from which to create specific application
26
+ # classes. For example, to create a new set of parameters, define a class as
27
+ #
28
+ # class MyConfig < Jeckyl::Config
29
+ #
30
+ # More details are available in the {file:README.md Readme} file
31
+ class Config < Hash
32
+
33
+ # create a configuration hash by evaluating the parameters defined in the given config file.
34
+ #
35
+ # @param [String] config_file string path to a ruby file,
36
+ # @param [Hash] opts contains the following options.
37
+ # @option opts [Boolean] :flag_errors_on_defaults will raise exceptions from checks during default
38
+ # evaluation - although why is not clear, so best not to use it.
39
+ # @option opts [Boolean] :local limits generated defaults to the direct class being evaluated
40
+ # and should only be set internally on this call
41
+ # @option opts [Boolean] :relax, if set to true will not check for parameter methods but instead
42
+ # add unknown methods to the hash unchecked.
43
+ #
44
+ # If no config file is given then the hash of options returned will only have
45
+ # the defaults defined for the given class.
46
+ #
47
+ #
48
+ def initialize(config_file=nil, opts={})
49
+ # do whatever a hash has to do
50
+ super()
51
+
52
+ flag_errors_on_defaults = opts[:flag_errors_on_defaults] || false
53
+ local = opts[:local] || false
54
+ @_relax = opts[:relax] || false
55
+
56
+ # somewhere to save the most recently set symbol
57
+ @_last_symbol = nil
58
+ # hash for comments accessed with the same symbol
59
+ @_comments = {}
60
+ # hash for input defaults
61
+ @_defaults={}
62
+ # save order in which methods are defined for generating config files
63
+ @_order = Array.new
64
+
65
+ # get the defaults defined in the config parser
66
+ get_defaults(:local=> local, :flag_errors => flag_errors_on_defaults)
67
+
68
+ return self if config_file.nil?
69
+
70
+ # remember where the config file itself is
71
+ self[:config_files] = [config_file]
72
+
73
+ # and finally get the values from the config file itself
74
+ self.instance_eval(File.read(config_file), config_file)
75
+
76
+ rescue SyntaxError => err
77
+ raise ConfigSyntaxError, err.message
78
+ rescue Errno::ENOENT
79
+ # duff file path so tell the caller
80
+ raise ConfigFileMissing, "#{config_file}"
81
+ end
82
+
83
+ # gives access to a hash containing an entry for each parameter and the comments
84
+ # defined by the class definitions - used internally by class methods
85
+ def comments
86
+ @_comments
87
+ end
88
+
89
+ # This contains an array of the parameter names - used internally by class methods
90
+ def order
91
+ @_order
92
+ end
93
+
94
+ # this contains a hash of the defaults for each parameter - used internally by class methods
95
+ def defaults
96
+ @_defaults
97
+ end
98
+
99
+ # a class method to check a given config file one item at a time
100
+ #
101
+ # This evaluates the given config file and reports if there are any errors to the
102
+ # report_file, which defaults to Stdout. Can only do the checking one error at a time.
103
+ #
104
+ # To use this method, it is necessary to write a script that calls it for the particular
105
+ # subclass.
106
+ #
107
+ # @param [String] config_file is the file to check
108
+ # @param [String] report_file is a file to write the report to, or stdout
109
+ # @return [Boolean] indicates if the check was OK or not
110
+ #
111
+ def self.check_config(config_file, report_file=nil)
112
+
113
+ # create myself to generate defaults, but nothing else
114
+ me = self.new
115
+
116
+ success = true
117
+ message = "No errors found in: #{config_file}"
118
+
119
+ begin
120
+ # evaluate the config file
121
+ me.instance_eval(File.read(config_file), config_file)
122
+
123
+ rescue Errno::ENOENT
124
+ message = "No such config file: #{config_file}"
125
+ success = false
126
+ rescue JeckylError => err
127
+ message = err.message
128
+ success = false
129
+ rescue SyntaxError => err
130
+ message = err.message
131
+ success = false
132
+ end
133
+
134
+ begin
135
+ if report_file.nil? then
136
+ puts message
137
+ else
138
+ File.open(report_file, "w") do |rfile|
139
+ rfile.puts message
140
+ end
141
+ end
142
+ return success
143
+ rescue Errno::ENOENT
144
+ raise ReportFileError, "Error with file: #{report_file}"
145
+ end
146
+
147
+ end
148
+
149
+ # a class method to generate a config file from the class definition
150
+ #
151
+ # This calls each of the parameter methods, and creates a commented template
152
+ # with the comments and default lines
153
+ #
154
+ # @param [Boolean] local when set to true will limit the parameters to those defined in the
155
+ # immediate class and excludes any ancestors.
156
+ #
157
+ def self.generate_config(local=false)
158
+ me = self.new(nil, :local => local)
159
+ # everything should now exist
160
+ me.order.each do |key|
161
+
162
+ if me.comments.has_key?(key) then
163
+ me.comments[key].each do |comment|
164
+ puts "# #{comment}"
165
+ end
166
+ end
167
+ def_value = me.defaults[key]
168
+ default = def_value.nil? ? '' : def_value.inspect
169
+
170
+ puts "##{key.to_s} #{default}"
171
+ puts ""
172
+ end
173
+ end
174
+
175
+ # extract only those parameters in a hash that are from the given class
176
+ #
177
+ # @param [Hash] full_config is the config from which to extract the intersecting options
178
+ # and it can be an instance of Jeckyl::Config or a hash
179
+ # @return [Hash] containing all of the intersecting parameters
180
+ #
181
+ # Note this returns a plain hash and not an instance of Jeckyl::Config
182
+ #
183
+ def self.intersection(full_config)
184
+ me = self.new # create the defaults for this class
185
+ my_hash = {}
186
+ me.order.each do |my_key|
187
+ if full_config.has_key?(my_key) then
188
+ my_hash[my_key] = full_config[my_key]
189
+ end
190
+ end
191
+ return my_hash
192
+ end
193
+
194
+ # return a list of descendant classes in the current context. This is provided to help
195
+ # find classes for the jeckyl utility, e.g. to generate a default config file
196
+ #
197
+ # @return [Array] classes that are descendants of this class, sorted with the least ancestral
198
+ # first
199
+ #
200
+ def self.descendants
201
+ descs = Array.new
202
+ ObjectSpace.each_object {|obj| descs << obj if obj.kind_of?(Class) && obj < self}
203
+ descs.sort! {|a,b| a < b ? -1 : 1}
204
+ return descs
205
+ end
206
+
207
+
208
+ # set the prefix to the parameter names that should be used for corresponding
209
+ # parameter methods defined for a subclass. Parameter names in config files
210
+ # are mapped onto parameter method by prefixing the methods with the results of
211
+ # this function. So, for a parameter named 'greeting', the parameter method used
212
+ # to check the parameter will be, by default, 'configure_greeting'.
213
+ #
214
+ # For example, to define parameter methods prefix with 'set' redefine this
215
+ # method to return 'set'. The greeting parameter method should then be called
216
+ # 'set_greeting'
217
+ #
218
+ def prefix
219
+ 'configure'
220
+ end
221
+
222
+ # Delete those parameters that are in the given hash from this instance of Jeckyl::Config.
223
+ # Useful for tailoring parameter sets to specific uses (e.g. removing logging parameters)
224
+ #
225
+ # @param [Hash] conf_to_remove which is a hash or an instance of Jeckyl::Config
226
+ #
227
+ def complement(conf_to_remove)
228
+ self.delete_if {|key, value| conf_to_remove.has_key?(key)}
229
+ end
230
+
231
+ # Read, check and merge another parameter file into this one, being of the same config class.
232
+ #
233
+ # @param [String] conf_file - path to file to parse
234
+ #
235
+ def merge(conf_file)
236
+
237
+ self[:config_files] << conf_file
238
+
239
+ # and finally get the values from the config file itself
240
+ self.instance_eval(File.read(conf_file), conf_file)
241
+
242
+ rescue SyntaxError => err
243
+ raise ConfigSyntaxError, err.message
244
+ rescue Errno::ENOENT
245
+ # duff file path so tell the caller
246
+ raise ConfigFileMissing, "#{conf_file}"
247
+ end
248
+
249
+
250
+ protected
251
+
252
+ # create a description for the current parameter, to be used when generating a config template
253
+ #
254
+ # @param [*String] being one or more string arguments that are used to generate config file templates
255
+ # and documents
256
+ def comment(*strings)
257
+ @_comments[@_last_symbol] = strings unless @_last_symbol.nil?
258
+ end
259
+
260
+ # set default value(s) for the current parameter.
261
+ #
262
+ # @param [Object] val - any valid object as expected by the parameter method
263
+ def default(val)
264
+ return if @_last_symbol.nil? || @_defaults.has_key?(@_last_symbol)
265
+ @_defaults[@_last_symbol] = val
266
+ end
267
+
268
+ # the following are all helper methods to parse values and raise exceptions if the values are not correct
269
+
270
+ # file helpers - meanings should be apparent
271
+
272
+ # check that the parameter is a directory and that the directory is writable
273
+ #
274
+ # Jeckyl checking method to be used in parameter methods to check the validity of
275
+ # given parameters, returning the parameter if valid or else raising an exception
276
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
277
+ # the parameter is not validly formed
278
+ #
279
+ # @param [String] - path
280
+ #
281
+ def a_writable_dir(path)
282
+ if FileTest.directory?(path) && FileTest.writable?(path) then
283
+ path
284
+ else
285
+ raise_config_error(path, "directory is not writable or does not exist")
286
+ end
287
+ end
288
+
289
+ # check parameter is a readable file
290
+ #
291
+ # Jeckyl checking method to be used in parameter methods to check the validity of
292
+ # given parameters, returning the parameter if valid or else raising an exception
293
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
294
+ # the parameter is not validly formed
295
+ #
296
+ # @param [String] - path to file
297
+ #
298
+ def a_readable_file(path)
299
+ if FileTest.readable?(path) then
300
+ path
301
+ else
302
+ raise_config_error(path, "file does not exist")
303
+ end
304
+ end
305
+
306
+ # simple type helpers
307
+
308
+ # check the parameter is of the required type
309
+ #
310
+ # Jeckyl checking method to be used in parameter methods to check the validity of
311
+ # given parameters, returning the parameter if valid or else raising an exception
312
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
313
+ # the parameter is not validly formed
314
+ #
315
+ # @param [Object] obj to check type of
316
+ # @param [Class] type, being a class constant such as Numeric, String
317
+ #
318
+ def a_type_of(obj, type)
319
+ if obj.kind_of?(type) then
320
+ obj
321
+ else
322
+ raise_config_error(obj, "value is not of required type: #{type}")
323
+ end
324
+ end
325
+
326
+ # check that the parameter is within the required range
327
+ #
328
+ # Jeckyl checking method to be used in parameter methods to check the validity of
329
+ # given parameters, returning the parameter if valid or else raising an exception
330
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
331
+ # the parameter is not validly formed
332
+ #
333
+ # @param [Numeric] val to check
334
+ # @param [Numeric] lower bound of range
335
+ # @param [Numeric] upper bound of range
336
+ #
337
+ def in_range(val, lower, upper)
338
+ raise_syntax_error("#{lower.to_s}..#{upper.to_s} is not a range") unless (lower .. upper).kind_of?(Range)
339
+ if (lower .. upper) === val then
340
+ val
341
+ else
342
+ raise_config_error(val, "value is not within required range: #{lower.to_s}..#{upper.to_s}")
343
+ end
344
+ end
345
+
346
+
347
+ # boolean helpers
348
+
349
+ # check parameter is a boolean, true or false but not strings "true" or "false"
350
+ #
351
+ # Jeckyl checking method to be used in parameter methods to check the validity of
352
+ # given parameters, returning the parameter if valid or else raising an exception
353
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
354
+ # the parameter is not validly formed
355
+ #
356
+ # @param [Boolean] val to check
357
+ #
358
+ def a_boolean(val)
359
+ if val.kind_of?(TrueClass) || val.kind_of?(FalseClass) then
360
+ val
361
+ else
362
+ raise_config_error(val, "Value is not a Boolean")
363
+ end
364
+ end
365
+
366
+ # check the parameter is a flag, being "true", "false", "yes", "no", "on", "off", or 1 , 0
367
+ # and return a proper boolean
368
+ #
369
+ # Jeckyl checking method to be used in parameter methods to check the validity of
370
+ # given parameters, returning the parameter if valid or else raising an exception
371
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
372
+ # the parameter is not validly formed
373
+ #
374
+ # @param [String] val to check
375
+ #
376
+ def a_flag(val)
377
+ val = val.downcase if val.kind_of?(String)
378
+ case val
379
+ when "true", "yes", "on", 1
380
+ true
381
+ when "false", "no", "off", 0
382
+ false
383
+ else
384
+ raise_config_error(val, "Cannot convert to Boolean")
385
+ end
386
+ end
387
+
388
+
389
+ # compound objects
390
+
391
+ # check the parameter is an array
392
+ #
393
+ # Jeckyl checking method to be used in parameter methods to check the validity of
394
+ # given parameters, returning the parameter if valid or else raising an exception
395
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
396
+ # the parameter is not validly formed
397
+ #
398
+ # @param [Array] ary to check
399
+ #
400
+ def an_array(ary)
401
+ if ary.kind_of?(Array) then
402
+ ary
403
+ else
404
+ raise_config_error(ary, "value is not an Array")
405
+ end
406
+ end
407
+
408
+ # check the parameter is an array and the array is of the required type
409
+ #
410
+ # Jeckyl checking method to be used in parameter methods to check the validity of
411
+ # given parameters, returning the parameter if valid or else raising an exception
412
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
413
+ # the parameter is not validly formed
414
+ #
415
+ # @param [Array] ary of values to check
416
+ # @param [Class] type being the class that the values must belong to
417
+ #
418
+ def an_array_of(ary, type)
419
+ raise_syntax_error("Provided a value that is a type: #{type.to_s}") unless type.class == Class
420
+ if ary.kind_of?(Array) then
421
+ ary.each do |element|
422
+ unless element.kind_of?(type) then
423
+ raise_config_error(element, "element of array is not of type: #{type}")
424
+ end
425
+ end
426
+ return ary
427
+ else
428
+ raise_config_error(ary, "value is not an Array")
429
+ end
430
+ end
431
+
432
+ # check the parameter is a hash
433
+ #
434
+ # Jeckyl checking method to be used in parameter methods to check the validity of
435
+ # given parameters, returning the parameter if valid or else raising an exception
436
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
437
+ # the parameter is not validly formed
438
+ #
439
+ # @param [Hash] hsh to check
440
+ #
441
+ def a_hash(hsh)
442
+ if hsh.kind_of?(Hash) then
443
+ true
444
+ else
445
+ raise_config_error(hsh, "value is not a Hash")
446
+ end
447
+ end
448
+
449
+ # strings and text and stuff
450
+
451
+ # check the parameter is a string
452
+ #
453
+ # Jeckyl checking method to be used in parameter methods to check the validity of
454
+ # given parameters, returning the parameter if valid or else raising an exception
455
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
456
+ # the parameter is not validly formed
457
+ #
458
+ # @param [String] str to check
459
+ #
460
+ def a_string(str)
461
+ if str.kind_of?(String) then
462
+ str
463
+ else
464
+ raise_config_error(str.to_s, "is not a String")
465
+ end
466
+ end
467
+
468
+ # check the parameter is a string and matches the required pattern
469
+ #
470
+ # Jeckyl checking method to be used in parameter methods to check the validity of
471
+ # given parameters, returning the parameter if valid or else raising an exception
472
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
473
+ # the parameter is not validly formed
474
+ #
475
+ # @param [String] str to match against the pattern
476
+ # @param [Regexp] pattern to match with
477
+ #
478
+ def a_matching_string(str, pattern)
479
+ raise_syntax_error("Attempt to pattern match without a Regexp") unless pattern.kind_of?(Regexp)
480
+ if pattern =~ a_string(str) then
481
+ str
482
+ else
483
+ raise_config_error(str, "does not match required pattern: #{pattern.source}")
484
+ end
485
+ end
486
+
487
+ # set membership - set is an array of members, usually symbols
488
+ #
489
+ # Jeckyl checking method to be used in parameter methods to check the validity of
490
+ # given parameters, returning the parameter if valid or else raising an exception
491
+ # which is either ConfigError if the parameter fails the check or ConfigSyntaxError if
492
+ # the parameter is not validly formed
493
+ #
494
+ # @param [Symbol] symb being the symbol to check
495
+ # @param [Array] set containing the valid symbols that symb should belong to
496
+ #
497
+ def a_member_of(symb, set)
498
+ raise_syntax_error("Sets to test membership must be arrays") unless set.kind_of?(Array)
499
+ if set.include?(symb) then
500
+ symb
501
+ else
502
+ raise_config_error(symb, "is not a member of: #{set.join(', ')}")
503
+ end
504
+ end
505
+
506
+
507
+ private
508
+
509
+ # decides what to do with parameters that have not been defined.
510
+ # unless @_relax then it will raise an exception. Otherwise it will create a key value pair
511
+ #
512
+ # This method also remembers the method name as the key to prevent the parsers etc from
513
+ # having to carry this around just to do things like report on it.
514
+ #
515
+ def method_missing(symb, parameter)
516
+
517
+ @_last_symbol = symb
518
+ #@parameter = parameter
519
+ method_to_call = ("#{self.prefix}_" + symb.to_s).to_sym
520
+ set_method = self.method(method_to_call)
521
+
522
+ self[@_last_symbol] = set_method.call(parameter)
523
+
524
+ rescue NameError
525
+ #raise if @@debug
526
+ # no parser method defined.
527
+ unless @_relax then
528
+ # not tolerable
529
+ raise UnknownParameter, format_error(symb, parameter, "Unknown parameter")
530
+ else
531
+ # feeling relaxed, so lets store it anyway.
532
+ self[symb] = parameter
533
+ end
534
+
535
+ end
536
+
537
+ # get_defaults
538
+ #
539
+ # calls each method with the specified prefix with no parameters so that the defaults
540
+ # defined for each will be passed back and used to set the hash before the
541
+ # config file is evaluated.
542
+ #
543
+ def get_defaults(opts={})
544
+ flag_errors = opts[:flag_errors]
545
+ local = opts[:local]
546
+
547
+ # go through all of the methods
548
+ self.class.instance_methods(!local).each do |method_name|
549
+ if md = /^#{self.prefix}_/.match(method_name) then
550
+
551
+ # its a prefixed method so call it
552
+
553
+ pref_method = self.method(method_name.to_sym)
554
+ # get the corresponding symbol for the hash
555
+ @_last_symbol = md.post_match.to_sym
556
+ @_order << @_last_symbol
557
+ # and call the method with no parameters, which will
558
+ # call the comment method and the default method where defined
559
+ # and thereby capture their values
560
+ begin
561
+ a_value = pref_method.call(1)
562
+ rescue Exception
563
+ # ignore any errors, which are bound to result from passing in 1
564
+ end
565
+ begin
566
+ # now set the actual default by calling the method again and passing
567
+ # the captured default, providing a result which may be different if the method transforms
568
+ # the parameter!
569
+ param = @_defaults[@_last_symbol]
570
+ self[@_last_symbol] = pref_method.call(param) unless param.nil?
571
+ rescue Exception
572
+ raise if flag_errors
573
+ # ignore any errors raised
574
+ end
575
+ end
576
+ end
577
+ end
578
+
579
+ # really private helpers that should not be needed unless the parser method
580
+ # is custom
581
+
582
+ protected
583
+
584
+ # helper method to format exception messages. A config error should be raised
585
+ # when the given parameter does not match the checks.
586
+ #
587
+ # The exception is raised in the caller's context to ensure backtraces are accurate.
588
+ #
589
+ # @param [Object] value - the object that caused the error
590
+ # @param [String] message to include in the exception
591
+ #
592
+ def raise_config_error(value, message)
593
+ raise ConfigError, format_error(@_last_symbol, value, message), caller
594
+ end
595
+
596
+ # helper method to format exception messages. A syntax error should be raised
597
+ # when the check method has been used incorrectly. See check methods for examples.
598
+ #
599
+ # The exception is raised in the caller's context to ensure backtraces are accurate.
600
+ #
601
+ # @param [String] message to include in the exception
602
+ #
603
+ def raise_syntax_error(message)
604
+ raise ConfigSyntaxError, message, caller
605
+ end
606
+
607
+ # helper method to format an error
608
+ def format_error(key, value, message)
609
+ "[#{key}]: #{value} - #{message}"
610
+ end
611
+
612
+ end
613
+
614
+ # define an alias for backwards compatitbility
615
+ # @deprecated Please use Jeckyl::Config
616
+ Options = Config
617
+
618
+ end
619
+
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'jeckyl/errors'
3
+ require 'jeckyl'
4
+ require File.expand_path(File.dirname(__FILE__) + '/../test/test_configurator')
5
+
6
+ conf_path = File.expand_path(File.dirname(__FILE__) + '/../test/conf.d')
7
+
8
+ report_path = File.expand_path(File.dirname(__FILE__) + '/../test/reports')
9
+
10
+ describe "Jeckyl Config Checker" do
11
+
12
+ # general tests
13
+
14
+ it "should checkout a simple config" do
15
+ conf_file = conf_path + '/jeckyl'
16
+ rep_file = report_path + '/ok.txt'
17
+ conf_ok = TestJeckyl.check_config(conf_file, rep_file)
18
+ conf_ok.should be_true
19
+ message = File.read(rep_file).chomp
20
+ message.should == "No errors found in: #{conf_file}"
21
+ end
22
+
23
+
24
+ it "should complain if the config file does not exist" do
25
+ conf_file = conf_path + "/never/likely/to/be/there"
26
+ rep_file = report_path + '/not_ok.txt'
27
+ conf_ok = TestJeckyl.check_config(conf_file, rep_file)
28
+ conf_ok.should be_false
29
+ message = File.read(rep_file).chomp
30
+ message.should == "No such config file: #{conf_file}"
31
+ end
32
+
33
+ it "should return false if the config file has a syntax error" do
34
+ conf_file = conf_path + "/syntax_error"
35
+ rep_file = report_path + '/not_ok.txt'
36
+ conf_ok = TestJeckyl.check_config(conf_file, rep_file)
37
+ conf_ok.should be_false
38
+ message = File.read(rep_file).chomp
39
+ message.should match(/^compile error/)
40
+ end
41
+
42
+ it "should return false if the config file has an error" do
43
+ conf_file = conf_path + "/not_a_bool"
44
+ rep_file = report_path + '/not_ok.txt'
45
+ conf_ok = TestJeckyl.check_config(conf_file, rep_file)
46
+ conf_ok.should be_false
47
+ message = File.read(rep_file).chomp
48
+ message.should match(/^\[debug\]:/)
49
+ end
50
+
51
+ it "should fail if it cannot write to the report file" do
52
+ conf_file = conf_path + "/jeckyl"
53
+ rep_file = report_path + '/no_such_directory/ok.txt'
54
+ lambda{conf_ok = TestJeckyl.check_config(conf_file, rep_file)}.should raise_error(Jeckyl::ReportFileError)
55
+ end
56
+
57
+ end