libis-tools 0.9.20 → 0.9.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -233
  3. data/Rakefile +5 -0
  4. data/lib/libis/tools.rb +1 -0
  5. data/lib/libis/tools/assert.rb +11 -0
  6. data/lib/libis/tools/checksum.rb +22 -5
  7. data/lib/libis/tools/command.rb +24 -3
  8. data/lib/libis/tools/config.rb +61 -33
  9. data/lib/libis/tools/config_file.rb +0 -1
  10. data/lib/libis/tools/deep_struct.rb +10 -2
  11. data/lib/libis/tools/extend/empty.rb +2 -2
  12. data/lib/libis/tools/extend/hash.rb +37 -18
  13. data/lib/libis/tools/extend/kernel.rb +9 -0
  14. data/lib/libis/tools/extend/string.rb +17 -8
  15. data/lib/libis/tools/logger.rb +95 -44
  16. data/lib/libis/tools/metadata.rb +5 -1
  17. data/lib/libis/tools/metadata/dublin_core_record.rb +22 -4
  18. data/lib/libis/tools/metadata/field_format.rb +49 -9
  19. data/lib/libis/tools/metadata/fix_field.rb +5 -0
  20. data/lib/libis/tools/metadata/mapper.rb +2 -1
  21. data/lib/libis/tools/metadata/mappers/flandrica.rb +8 -1
  22. data/lib/libis/tools/metadata/mappers/kuleuven.rb +6 -2
  23. data/lib/libis/tools/metadata/marc21_record.rb +1 -0
  24. data/lib/libis/tools/metadata/marc_record.rb +31 -12
  25. data/lib/libis/tools/metadata/parser/basic_parser.rb +2 -0
  26. data/lib/libis/tools/metadata/parser/dublin_core_parser.rb +2 -1
  27. data/lib/libis/tools/metadata/parser/marc21_parser.rb +2 -1
  28. data/lib/libis/tools/metadata/parser/marc_format_parser.rb +2 -1
  29. data/lib/libis/tools/metadata/parser/marc_rules.rb +2 -1
  30. data/lib/libis/tools/metadata/parser/marc_select_parser.rb +2 -1
  31. data/lib/libis/tools/metadata/parser/patch.rb +1 -0
  32. data/lib/libis/tools/metadata/parser/subfield_criteria_parser.rb +2 -1
  33. data/lib/libis/tools/metadata/sharepoint_mapping.rb +1 -0
  34. data/lib/libis/tools/metadata/sharepoint_record.rb +2 -0
  35. data/lib/libis/tools/metadata/var_field.rb +8 -0
  36. data/lib/libis/tools/mets_dnx.rb +61 -0
  37. data/lib/libis/tools/mets_file.rb +87 -604
  38. data/lib/libis/tools/mets_objects.rb +534 -0
  39. data/lib/libis/tools/parameter.rb +144 -21
  40. data/lib/libis/tools/thread_safe.rb +31 -0
  41. data/lib/libis/tools/version.rb +1 -1
  42. data/lib/libis/tools/xml_document.rb +18 -24
  43. data/libis-tools.gemspec +6 -2
  44. data/spec/config_spec.rb +3 -4
  45. data/spec/logger_spec.rb +13 -30
  46. data/spec/mets_file_spec.rb +17 -17
  47. metadata +53 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b02d1a5db6228befa73d14fb34880fc50c1c3205
4
- data.tar.gz: 45cd0d340b39e2f585d0aeb25076b439132f2af7
3
+ metadata.gz: dc18f91c7fdaf317168eb91966d598fff6fd0252
4
+ data.tar.gz: 43d8f7c0f51f7e5e32b28680f53bb6f6dbb17658
5
5
  SHA512:
6
- metadata.gz: 660ddb441d546e37e4170cd236853dcf5273063873dee76647a79ee9c6f0386a9fdb010c8a91f4e1ce175060278ddb6e0b3153c8ec67ef47ccc232c2beace443
7
- data.tar.gz: 68d181a6f3c644f38561947f13dd138fb69973d12b3c213f0fee24b454f228e4840bdfec611ba1a8c1e9fc17b502faa31857fbd37ab4723745faec041e230436
6
+ metadata.gz: db2e5d1f92574eaefab41674b23aac4aa116b01206eb5eeb1304482de6dba8213a00b3c3ccfd79a83be1523b7d947d07112dd445ddadfeb781176f0fefacf65d
7
+ data.tar.gz: 5983fc775104c2d592fdeacdec3c86489215275ab9f138bf72211696e049e21f093e2f25e197a722d854962753652c469972e22a4dd123af58b9dd951ce8b782
data/README.md CHANGED
@@ -42,181 +42,64 @@ sections below for their names.
42
42
 
43
43
  ## Content
44
44
 
45
- ### assert
45
+ ### {Object#assert}
46
46
 
47
- The Object#assert method enables the assert functionality found in other languages.
48
- The method takes an argument that will be interpreted as a boolean expression and an optional message.
49
- If the boolean expression evaluates to false an AssertionFailure exception will be raised.
47
+ The {Object#assert} method enables the assert functionality found in other languages. Since it is defined on the
48
+ Object class, it is available on almost any class.
50
49
 
51
- Alternatively, if a block is passed to the method, the given block will be evaluated and the result will decide if the
52
- exception will be raised. In that case the first argument passed to the assert method is used as message and the second
53
- argument is ignored.
50
+ ### {::Libis::Tools::Checksum}
54
51
 
55
- The method will not evaluate the first parameter or block unless the general parameter $DEBUG evaluates to true. That
56
- means that special care should be taken that the expression does not generate any side effects that the program may rely
57
- on.
52
+ The {::Libis::Tools::Checksum} class offers a standardized interface for calculating checksums of file
53
+ contents in different formats.
58
54
 
59
- Examples:
55
+ ### {::Libis::Tools::Command}
60
56
 
61
- ```ruby
62
- require 'libis/tools/assert'
63
- assert(value > 0, 'value should be positive number')
64
- ```
65
- and using a code block:
66
-
67
- ```ruby
68
- require 'libis/tools/assert'
69
- assert 'database is not idle' do
70
- db = get_database
71
- db.status == :IDLE
72
- end
73
- ```
74
-
75
- ### Checksum
57
+ The {::Libis::Tools::Command} module offers a safe way to execute external commands and gives you access to the
58
+ exit status as well as standard output and standard error information. May have issues on older JRuby versions.
76
59
 
77
- The ::Libis::Tools::Checksum class offers a standardized interface for calculating checksums of file contents in
78
- different formats. The actual list of supported checksum formats is in ::Libis::Tools::Checksum.CHECKSUM_TYPES. It
79
- contains MD5, SHA-1 and SHA-2 (in 256-bit, 384-bit and 512-bit variants).
80
-
81
- There are two ways this can be used: using a class instance or using class methods. When a class instance is used, the
82
- desired checksum type has to be supplied when the instance is created. Each call to a checksum method will calculate the
83
- checksum and reset the digest to prevent future calls to be affected by the current result. When a class method is used
84
- besides the file name, the checksum type has to be supplied.
85
-
86
- The available methods on both instance and class level are:
87
-
88
- * digest: return the checksum as (binary) string.
89
- * hexdigest: return the checksum as hexadecimal encoded string.
90
- * base64digest: return the checksum as base64 encoded string.
91
-
92
- Examples:
93
-
94
- ```ruby
95
- require 'libis/tools/checksum'
96
- checksum = ::Libis::Tools::Checksum.new(:MD5)
97
- puts "Checksum: #{checksum.hexdigest(file_name)} (MD5, hex)"
98
- ```
99
-
100
- ```ruby
101
- require 'libis/tools/checksum'
102
- puts "Checksum: #{::Libis::Tools::Checksum.base64digest(file_name, :SHA384)} (SHA-2, 384 bit, base64)"
103
- ```
104
-
105
- ### Command
106
-
107
- This module allows to run an external command safely and returns it's output, error messages and status. The run method
108
- takes any number of arguments that will be used as command-line arguments. The method returns a Hash with:
109
-
110
- * :out => an array with lines that were printed on the external program's standard out.
111
- * :err => an array with lines that were printed on the external program's standard error.
112
- * :status => exit code returned by the external program.
113
-
114
- ```ruby
115
- require 'libis/tools/command'
116
- result = ::Libis::Tools::Command.run('ls', '-l', File.absolute_path(__FILE__))
117
- p result # => {out: [...], err: [...], status: 0}
118
- ```
119
-
120
- or:
121
-
122
- ```ruby
123
- require 'libis/tools/command'
124
- include ::Libis::Tools::Command
125
- result = run('ls', '-l', File.absolute_path(__FILE__))
126
- p result # => {out: [...], err: [...], status: 0}
127
- ```
60
+ ### {::Libis::Tools::DeepStruct}
128
61
 
129
- Note that the Command class uses Open3#popen3 internally. All arguments supplied to Command#run are passed to the popen3
130
- call. Unfortunately JRuby has some known issues with popen3. Please use and test carefully in JRuby environments.
62
+ A class that derives from OpenStruct through the RecursiveOpenStruct.
63
+ By wrapping a Hash recursively, it allows for easy access to the content by method names.
131
64
 
132
- ### DeepStruct
65
+ ### {::Libis::Tools::ConfigFile}
133
66
 
134
- A class that derives from OpenStruct through the RecursiveOpenStruct. A RecursiveOpenStruct is derived from stdlib's
135
- OpenStruct, but can be made recursive. DeepStruct enforces this behaviour and adds a clear! method.
67
+ A base class for {::Libis::Tools::Config}, but useable on it's own.
68
+ It extends the DeepStruct with loading from and saving to YAML files.
136
69
 
137
- ### ConfigFile
70
+ ### {::Libis::Tools::Config}
138
71
 
139
- A base class for Config, but useable on it's own. It extends the DeepStruct with << and >> methods that supports
140
- loading and saving of configuration values from and to files. Note that ERB commands will get lost during a round-trip.
141
-
142
- ```ruby
143
- require 'libis/tools/config_file'
144
- cfg_file = ::Libis::Tools::ConfigFile.new
145
- cfg_file << {foo: 'bar'}
146
- cfg_file.my_value = 10
147
- p cfg_file[:my_value] # => 10
148
- cfg_file{:my_text] = 'abc'
149
- p cfg_file['my_text'] # => 'abc'
150
- p cfg_file.to_hash # => { :foo => 'bar', 'my_value' => 10, :my_text => 'abc' }
151
- cfg >> 'my_config.yml'
152
- ```
153
- ### Config
72
+ This Singleton class is a convenience class for easy configuration maintenance and loading.
73
+ It also initializes a default logger.
154
74
 
155
- The Config class is a convenience class for easy configuration maintenance and loading. Based on ConfigFile and
156
- DeepStruc, it supports code defaults and loading configurations from multiple YAML files containing ERB statements.
157
- The Config class follows the Singleton pattern and behaves like a Hash/OpenStruct/HashWithIndifferentAccess with
158
- recursion over hashes and arrays. It also initializes a default Logger instance.
75
+ ### {::Libis::Tools::Logger}
159
76
 
160
- For each configuration parameter, the value can be accessed via the class or the Singleton instance through a method
161
- call or via the Hash operator using the parameter name either as a string or a symbol.
77
+ The ::Libis::Tools::Logger module adds support for logging functionality to any class.
162
78
 
163
- Examples:
79
+ ## {::Libis::Tools::Metadata}
164
80
 
165
- ```ruby
166
- require 'libis/tools/config'
167
- cfg = ::Libis::Tools::Config
168
- cfg << 'my_config.yml'
169
- cfg['my_value'] = 10
170
- p cfg.instance.my_value # => 10
171
- cfg.instance.my_text = 'abc'
172
- p cfg[:my_text] # => 'abc'
173
- p cfg.logger.warn('message') # => W, [2015-03-16T12:51:01.180548 #28935] WARN -- : message
174
- ```
81
+ This gem also provides some modules and classes that assist in working with metadata. There are classes that allow to
82
+ create and/or read metadata for MARC(21), Dublin Core and SharePoint. These classes all live in the
83
+ Libis::Tools::Metadata namespace.
175
84
 
176
- ### Logger
85
+ ### MARC
177
86
 
178
- The Logger module adds logging functionality to any class. Just include the ::Libis::Tools::Logger module and the
179
- methods debug, info, warn, error and fatal will be available to the class instance. Each method takes a message argument
180
- and optional extra parameters. The methods all call the message method with the logging level as first argument and the
181
- supplied arguments appended.
87
+ The classes {::Libis::Tools::Metadata::MarcRecord} and it's child class {::Libis::Tools::Metadata::Marc21Record} are
88
+ mainly built for reading MARC(21) records. Most of the class logic is in the base class
89
+ {::Libis::Tools::Metadata::MarcRecord MarcRecord}, which is incomplete and should be considered an abstract class.
182
90
 
183
- The default message method implementation uses the logger of ::Libis::Tools::Config. If extra parameters are supplied,
184
- the message will be used as a format specification with the extra parameters applied to it. If an 'appname' parameter is
185
- defined in the Config object, it will be used as program name by the logger, otherwise the class name is taken.
91
+ {::Libis::Tools::Metadata::Marc21Record Marc21Record} on the other hand only contains the logic to parse the XML data
92
+ into the internal structure. A {::Libis::Tools::Metadata::MarcRecord MarcRecord} is created by supplying it an XML node
93
+ (from Nokogiri or {::Libis::Tools::XmlDocument}) that contains child nodes with the MARC data of a single record.
186
94
 
187
- If the class defines a #options method that returns a Hash containing a :quiet key, the value for that key will be
188
- evaluated and if true debug, info and warning messages will not be printed.
95
+ The code will strip namespaces from the input in order to greatly simplify working with the XML.
189
96
 
190
- Example:
97
+ ## {::Libis::Tools::Parameter} and {::Libis::Tools::ParameterContainer}
191
98
 
192
- ```ruby
193
- require 'libis/tools/logger'
194
- class TestLogger
195
- include ::Libis::Tools::Logger
196
- attr_accessor :options, name
197
- def initialize
198
- @options = {}
199
- @name = nil
200
- end
201
- end
202
- tl = TestLogger.new
203
- tl.debug 'message'
204
- tl.options[:quiet] = true
205
- tl.warn 'message'
206
- ::Libis::Tools::Config.appname = 'TestApplication'
207
- tl.error 'huge error: [%d] %s', 1000, 'Exit'
208
- tl.name = 'TestClass'
209
- tl.options[:quiet] = false
210
- tl.info 'Running application: %s', ::Libis::Tools::Config.appname
211
- ```
212
- produces:
213
- <pre>
214
- D, [...] DEBUG -- TestLogger: message
215
- E, [...] ERROR -- TestApplication: huge error: [1000] Exit
216
- I, [...] INFO -- TestClass: Running application TestApplication
217
- </pre>
99
+ The class {::Libis::Tools::Parameter} and the {::Libis::Tools::ParameterContainer} module provide a simple framework for
100
+ instance variables that are type-safe and can easily be documented and provide defaults.
218
101
 
219
- ### XmlDocument
102
+ ### {::Libis::Tools::XmlDocument}
220
103
 
221
104
  Class that embodies most used features of Nokogiri, Nori and Gyoku in one convenience class. The Nokogiri document is
222
105
  stored in the class variable 'document' and can be accessed and manipulated directly - if required. The class supports
@@ -300,87 +183,7 @@ produces:
300
183
  <email>harry.potter@hogwarts.edu</email>
301
184
  <email>hpotter@JKRowling.com</email>
302
185
  </patron>
303
-
304
- ## Metadata
305
-
306
- This gem also provides some modules and classes that assist in working with metadata. There are classes that allow to
307
- create and/or read metadata for MARC(21), Dublin Core and SharePoint. These classes all live in the
308
- Libis::Tools::Metadata namespace.
309
-
310
- ### MARC
311
-
312
- The classes Libis::Tools::Metadata::MarcRecord and it's child class Libis::Tools::Metadata::Marc21Record are mainly
313
- built for reading MARC(21) records. Most of the class logic is in the base class MarcRecord, which is incomplete and
314
- should be considered an abstract class. Marc21Record on the other hand only contains the logic to parse the XML data
315
- into the internal structure. A MarcRecord is created by supplying it an XML node (from Nokogiri or
316
- Libis::Tools::XmlDocument) that contains child nodes with the MARC data of a single record. The code will strip
317
- namespaces from the input in order to greatly simplify working with the XML.
318
-
319
- ## Parameter
320
-
321
- The class ::Libis::Tools::Parameter and the ::Libis::Tools::ParameterContainer module provide a simple framework for
322
- instance variables that are type-safe and can easily be documented and provide defaults.
323
186
 
324
- To use these parameters a class should include the ::Libis::Tools::ParameterContainer module and add 'parameter'
325
- statements to the body of the class definition. It takes only one mandatory argument which is a Hash. The first entry is
326
- interpreted as '<name> => <default>'. The name for the parameter should be unique and the default value can be any value
327
- of type TrueClass, FalseClass, String, Integer, Float, Date, Time, DateTime, Array, Hash or NilClass.
328
-
329
- The second up to last Hash entries are optional properties for the parameter. These are:
330
-
331
- * datatype: the type of values the parameter will accept. Valid values are:
332
-
333
- * 'bool' or 'boolean'
334
- * 'string'
335
- * 'int'
336
- * 'float'
337
- * 'datetime'
338
- * 'array'
339
- * 'hash'
340
-
341
- Any other value will raise a RuntimeError when the parameter is used. The value is case-insensitive and if not present,
342
- the datatype will be derived from the default value with 'string' being the default for NilClass. In any case the
343
- parameter will try its best to convert supplied values to the proper data type. For instance, an Integer parameter will
344
- accept 3, 3.1415, '3' and Rational(10/3) as valid values and store them as the integer value 3. Likewise DateTime
345
- parameters will try to interprete date and time strings.
346
-
347
- * description: any descriptive text you want to add to clarify what this parameter is used for. Any tool can ask the
348
- class for its parameters and - for instance - can use this property to provide help in a GUI when asking the user for
349
- input.
350
-
351
- * constraint: adds a validation condition to the parameter. The condition value can be:
352
-
353
- * an array: only values that convert to a value in the list are considered valid.
354
- * a range: only values that convert to a value in the given range are considered valid.
355
- * a regular expression: only values that match the regular expression are considered valid.
356
- * a string: only values that are '==' to the constraint are considered valid.
357
-
358
- * frozen: if set to true, prevents the class instance to set the parameter to any value other than the default. Mostly
359
- useful when a derived class needs a parameter in the parent class to be set to a specific value. Setting a value on
360
- a frozen parameter with the 'parameter(name,value)' method throws a ::Libis::Tools::ParameterFrozenError. The '[]='
361
- method silently ignores the exception. In any case the default value will not be changed.
362
-
363
- * options: a hash with any additional properties that you want to associate to the parameter. Any key-value pair in this
364
- hash is added to the retrievable properties of the parameter. Likewise any property defined, that is not in the list of
365
- known properties is added to the options hash. In this aspect the ::Libis::Tools::Parameter class behaves much like an
366
- OpenStruct even though it is implemented as a Struct.
367
-
368
- Besides enabling the 'parameter' class method to define parameters, the ::Libis::Tools::ParameterContainer add the class
369
- method 'parameters' that will return a Hash with parameter names as keys and their respective parameter definitions as
370
- values. On each class instance the 'parameter' method is added and serves as both getter and setter for parameter values:
371
- With only one argument (the parameter name) it returns the current value for the parameter, but the optional second
372
- argument will cause the method to set the parameter value. If the parameter is not available or the given value is not
373
- a valid value for the parameter, the method will return the special constant ::Libis::ParameterContainer::NO_VALUE. The
374
- methods '[]' and '[]=' serve as aliases for the getter and setter calls.
375
-
376
- Additionally two protected methods are available on the instance:
377
- * 'parameters': returns the Hash that keeps track of the current parameter values for the instance.
378
- * 'get_parameter_defintion': retrieves the parameter definition from the instance's class for the given parameter name.
379
-
380
- Any class that derives from a class that included the ::Libis::Tools::ParameterContainer module will automatically
381
- inherit all parameter definitions from all of it's base classes and can override any of these parameter definitions e.g.
382
- to change the default values for the parameter.
383
-
384
187
  ## Contributing
385
188
 
386
189
  1. Fork it ( https://github.com/Kris-LIBIS/LIBIS_Tools/fork )
data/Rakefile CHANGED
@@ -1,6 +1,11 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'yard'
3
4
 
4
5
  RSpec::Core::RakeTask.new
5
6
 
7
+ YARD::Rake::YardocTask.new do |t|
8
+ t.files = ['lib/**/*.rb']
9
+ end
10
+
6
11
  task :default => :spec
data/lib/libis/tools.rb CHANGED
@@ -12,6 +12,7 @@ module Libis
12
12
  autoload :Logger, 'libis/tools/logger'
13
13
  autoload :MetsFile, 'libis/tools/mets_file'
14
14
  autoload :Parameter, 'libis/tools/parameter'
15
+ autoload :ThreadSafe, 'libis/tools/thread_safe'
15
16
  autoload :XmlDocument, 'libis/tools/xml_document'
16
17
 
17
18
  end
@@ -19,6 +19,17 @@ class Object
19
19
  #
20
20
  # Examples:
21
21
  #
22
+ # require 'libis/tools/assert'
23
+ # assert(value > 0, 'value should be positive number')
24
+ #
25
+ # # using a code block:
26
+ # require 'libis/tools/assert'
27
+ # assert 'database is not idle' do
28
+ # db = get_database
29
+ # db.status == :IDLE
30
+ # end
31
+ #
32
+ # # using $DEBUG:
22
33
  # $DEBUG = nil
23
34
  # assert false, 'assert 1' # nothing happens
24
35
  # $DEBUG = true
@@ -11,16 +11,33 @@ module Libis
11
11
  # All methods are available on the class and on the instance. The instance has to be initialized with a checksum
12
12
  # algorithm and therefore the instance methods do not have to specify the checksum type.
13
13
  #
14
+ # There are two ways this can be used: using a class instance or using class methods. When a class instance is used,
15
+ # the desired checksum type has to be supplied when the instance is created. Each call to a checksum method will
16
+ # calculate the checksum and reset the digest to prevent future calls to be affected by the current result. When a
17
+ # class method is used besides the file name, the checksum type has to be supplied.
18
+ #
19
+ # Examples:
20
+ #
21
+ # require 'libis/tools/checksum'
22
+ # checksum = ::Libis::Tools::Checksum.new(:MD5)
23
+ # puts "Checksum: #{checksum.hexdigest(file_name)} (MD5, hex)"
24
+ #
25
+ # require 'libis/tools/checksum'
26
+ # puts "Checksum: #{::Libis::Tools::Checksum.base64digest(file_name, :SHA384)} (SHA-2, 384 bit, base64)"
14
27
  class Checksum
15
- if defined? JRUBY_VERSION
16
- CHECKSUM_TYPES = [:MD5, :SHA1, :SHA256, :SHA384, :SHA512]
17
- else
18
- CHECKSUM_TYPES = [:MD5, :RMD160, :SHA1, :SHA256, :SHA384, :SHA512]
28
+ # All supported checksum types
29
+ CHECKSUM_TYPES = [:MD5, :SHA1, :SHA256, :SHA384, :SHA512]
30
+
31
+ # @!visibility private
32
+ # noinspection RubyResolve
33
+ unless defined? JRUBY_VERSION
34
+ checksum_types = CHECKSUM_TYPES
35
+ checksum_types << :RMD160
19
36
  end
20
37
 
21
38
  # Create instance for a given checksum algorithm.
22
39
  #
23
- # @param [Symbol] type checksum algorithm; one of {#CHECKSUM_TYPES}
40
+ # @param [Symbol] type checksum algorithm; one of {CHECKSUM_TYPES}
24
41
  def initialize(type)
25
42
  @hasher = self.class.get_hasher(type)
26
43
  end
@@ -4,6 +4,27 @@ require 'open3'
4
4
  module Libis
5
5
  module Tools
6
6
 
7
+ # This module allows to run an external command safely and returns it's output, error messages and status.
8
+ # The run method takes any number of arguments that will be used as command-line arguments. The method returns
9
+ # a Hash with:
10
+ # * :out => an array with lines that were printed on the external program's standard out.
11
+ # * :err => an array with lines that were printed on the external program's standard error.
12
+ # * :status => exit code returned by the external program.
13
+ #
14
+ # Examples:
15
+ #
16
+ # require 'libis/tools/command'
17
+ # result = ::Libis::Tools::Command.run('ls', '-l', File.absolute_path(__FILE__))
18
+ # p result # => {out: [...], err: [...], status: 0}
19
+ #
20
+ # require 'libis/tools/command'
21
+ # include ::Libis::Tools::Command
22
+ # result = run('ls', '-l', File.absolute_path(__FILE__))
23
+ # p result # => {out: [...], err: [...], status: 0}
24
+ #
25
+ # Note that the Command class uses Open3#popen3 internally. All arguments supplied to Command#run are passed to
26
+ # the popen3 call. Unfortunately some older JRuby versions have some known issues with popen3. Please use and
27
+ # test carefully in JRuby environments.
7
28
  module Command
8
29
 
9
30
  # Run an external program and return status, stdout and stderr.
@@ -12,9 +33,9 @@ module Libis
12
33
  # @param [String] cmd program name
13
34
  # @param [Array<String>] opts optional list of command line arguments
14
35
  # @return [Hash] a Hash with:
15
- # * +:status+ : the exit status of the command
16
- # * +:out+ : the stdout output of the command
17
- # * +:err+ : the stderr output of the command
36
+ # * :status (Integer) - the exit status of the command
37
+ # * :out (Array<String>) - the stdout output of the command
38
+ # * :err (Array<String>)- the stderr output of the command
18
39
  def self.run(cmd, *opts)
19
40
  result = {
20
41
  status: 999,
@@ -2,18 +2,19 @@
2
2
  require 'singleton'
3
3
  require 'yaml'
4
4
  require 'erb'
5
- require 'logger'
5
+ require 'logging'
6
6
 
7
- require 'libis/tools/config_file'
7
+ require_relative 'config_file'
8
8
 
9
9
  module Libis
10
10
  module Tools
11
11
 
12
- # The Config class is a convenience class for easy configuration maintenance, loading and saving.
13
- # It supports code defaults, loading configurations from multiple YAML files containing ERB statements.
14
- # The Config class follows the Singleton pattern and behaves like a Hash/OpenStruct/HashWithIndifferentAccess.
15
- # It also initializes a default Logger instance.
16
- # The class also stores a system-wide {::Logger} instance that will be used by {::Libis::Tools::Logger}.
12
+ # The Singleton Config class is a convenience class for easy configuration maintenance, loading and saving.
13
+ # It also initializes a default logger and supports creating extra loggers. The logging infrastructure is based on
14
+ # the {http://www.rubydoc.info/gems/logging/Logging ::Logging} gem and supports the {::Libis::Tools::Logger} class.
15
+ #
16
+ # For the configuration parameters, it supports code defaults, loading configurations from multiple YAML files
17
+ # containing ERB statements. The Config class behaves like a Hash/OpenStruct/HashWithIndifferentAccess.
17
18
  #
18
19
  # The parameters can be accessed by getter/setter method or using the Hash syntax:
19
20
  #
@@ -23,7 +24,7 @@ module Libis
23
24
  # p cfg.instance.my_value # => 10
24
25
  # cfg.instance.my_text = 'abc'
25
26
  # p cfg[:my_text] # => 'abc'
26
- # p cfg.logger.warn('message') # => W, [2015-03-16T12:51:01.180548 #28935] WARN -- : message
27
+ # p cfg.logger.warn('message') # => W, [2015-03-16T12:51:01.180548 #123.456] WARN : message
27
28
  #
28
29
  class Config
29
30
  include Singleton
@@ -33,6 +34,7 @@ module Libis
33
34
  private
34
35
 
35
36
  # For each configuration parameter, the value can be accessed via the class or the Singleton instance.
37
+ # The class diverts to the instance automatically.
36
38
  def method_missing(name, *args, &block)
37
39
  result = instance.send(name, *args, &block)
38
40
  self === result ? self : result
@@ -40,6 +42,7 @@ module Libis
40
42
 
41
43
  end
42
44
 
45
+ # Instance method that allows to access the configuration parameters by method.
43
46
  def method_missing(name, *args, &block)
44
47
  result = config.send(name, *args, &block)
45
48
  self === config ? self : result
@@ -50,8 +53,10 @@ module Libis
50
53
  # The file paths and Hashes are memorised and loaded again by the {#reload} methods.
51
54
  # @param [String,Hash] file_or_hash
52
55
  def <<(file_or_hash)
53
- @config.send('<<', (file_or_hash)) { |data| @sources << data }
54
- self
56
+ sync do
57
+ @config.send('<<', (file_or_hash)) { |data| @sources << data }
58
+ self
59
+ end
55
60
  end
56
61
 
57
62
  # Load all files and Hashes again.
@@ -59,10 +64,12 @@ module Libis
59
64
  # Will not reset the configuration parameters. Parameters set directly on the
60
65
  # configuration are kept intact unless they also exist in the files or hashes in which case they will be overwritten.
61
66
  def reload
62
- sources = @sources.dup
63
- @sources.clear
64
- sources.each { |f| self << f }
65
- self
67
+ sync do
68
+ sources = @sources.dup
69
+ @sources.clear
70
+ sources.each { |f| self << f }
71
+ self
72
+ end
66
73
  end
67
74
 
68
75
  # Clear data and load all files and Hashes again.
@@ -71,8 +78,10 @@ module Libis
71
78
  # added directly (not via file or hash) will no longer be available. Parameters set explicitly that also exist in
72
79
  # the files or hashes will be reset to the values in those files and hashes.
73
80
  def reload!
74
- @config.clear!
75
- reload
81
+ sync do
82
+ @config.clear!
83
+ reload
84
+ end
76
85
  end
77
86
 
78
87
  # Clear all data.
@@ -80,38 +89,57 @@ module Libis
80
89
  # Not only all configuration parameters are deleted, but also the memorized list of loaded files
81
90
  # and hashes are cleared and the logger configuration is reset to it's default status.
82
91
  def clear!
83
- @config.clear!
84
- @sources = Array.new
85
- @logger = ::Logger.new(STDOUT)
86
- set_log_formatter
87
- self
92
+ sync do
93
+ @config.clear!
94
+ @sources = Array.new
95
+ self.logger
96
+ self
97
+ end
88
98
  end
89
99
 
90
- # Set the ::Logger instance's formatter.
91
- # If the supplied formatter is missing or nil, a default formatter will be applied. The default formatter prints
92
- # log lines like this:
100
+ # Gets the default ::Logging formatter.
101
+ #
102
+ # This in an instance of a layout that prints in the default message format.
103
+ #
104
+ # The default layout prints log lines like this:
93
105
  #
94
- # <first char of severity>, [<timestamp>#<process-id>] <severity> -- <program_name> : <message>
106
+ # <first char of severity>, [<timestamp> #<process-id>.<thread-id] <severity> : <message>
95
107
  #
96
- # @param [Proc] formatter the formatter procedure or nil for default formatter
97
- def set_log_formatter(formatter = nil)
98
- self.logger.formatter = formatter || proc do |severity, time, progname, msg|
99
- "%s, [%s#%d] %5s -- %s: %s\n" % [severity[0..0],
100
- (time.strftime('%Y-%m-%dT%H:%M:%S.') << '%06d ' % time.usec),
101
- $$, severity, progname, msg]
108
+ def get_log_formatter
109
+ # noinspection RubyResolve
110
+ ::Logging::Layouts::Pattern.new(DEFAULT_LOG_LAYOUT_PARAMETERS)
111
+ end
112
+
113
+ def logger(name = nil)
114
+ sync do
115
+ name ||= :root
116
+ logger = ::Logging.logger[name]
117
+ logger.appenders = ::Logging.appenders.stdout(DEFAULT_LOG_LAYOUT_PARAMETERS) if logger.appenders.empty?
118
+ logger
102
119
  end
103
120
  end
104
121
 
105
- attr_accessor :logger, :config, :sources
122
+ attr_accessor :config, :sources
106
123
 
107
124
  protected
108
125
 
109
126
  def initialize(hash = nil, opts = {})
127
+ @mutex = ReentrantMutex.new
110
128
  @config = ConfigFile.new(hash, opts)
111
129
  self.clear!
112
130
  end
113
131
 
132
+ def sync(&block)
133
+ @mutex.synchronize(&block)
134
+ end
135
+
136
+ ::Logging::init
137
+ # noinspection RubyResolve
138
+ DEFAULT_LOG_LAYOUT_PARAMETERS = {
139
+ pattern: "%.1l, [%d #%p.%t] %#{::Logging::MAX_LEVEL_LENGTH}l : %m\n",
140
+ date_pattern: '%Y-%m-%dT%H:%M:%S.%L'
141
+ }
142
+
114
143
  end
115
144
  end
116
145
  end
117
-