libis-tools 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +37 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +289 -0
  9. data/Rakefile +6 -0
  10. data/lib/libis-tools.rb +1 -0
  11. data/lib/libis/tools.rb +16 -0
  12. data/lib/libis/tools/assert.rb +41 -0
  13. data/lib/libis/tools/checksum.rb +84 -0
  14. data/lib/libis/tools/command.rb +40 -0
  15. data/lib/libis/tools/config.rb +160 -0
  16. data/lib/libis/tools/dc_record.rb +47 -0
  17. data/lib/libis/tools/extend/empty.rb +7 -0
  18. data/lib/libis/tools/extend/hash.rb +107 -0
  19. data/lib/libis/tools/extend/ostruct.rb +3 -0
  20. data/lib/libis/tools/extend/string.rb +85 -0
  21. data/lib/libis/tools/extend/struct.rb +29 -0
  22. data/lib/libis/tools/logger.rb +71 -0
  23. data/lib/libis/tools/mets_file.rb +575 -0
  24. data/lib/libis/tools/parameter.rb +172 -0
  25. data/lib/libis/tools/sharepoint_mapping.rb +118 -0
  26. data/lib/libis/tools/sharepoint_record.rb +260 -0
  27. data/lib/libis/tools/version.rb +5 -0
  28. data/lib/libis/tools/xml_document.rb +574 -0
  29. data/libis-tools.gemspec +39 -0
  30. data/spec/assert_spec.rb +65 -0
  31. data/spec/checksum_spec.rb +132 -0
  32. data/spec/command_spec.rb +68 -0
  33. data/spec/config_spec.rb +86 -0
  34. data/spec/data/test.data +9 -0
  35. data/spec/data/test.xml +8 -0
  36. data/spec/data/test.yml +1 -0
  37. data/spec/logger_spec.rb +107 -0
  38. data/spec/parameter_container_spec.rb +83 -0
  39. data/spec/parameter_spec.rb +139 -0
  40. data/spec/spec_helper.rb +12 -0
  41. data/spec/test.xsd +20 -0
  42. data/spec/xmldocument_spec.rb +413 -0
  43. data/test/test_helper.rb +7 -0
  44. data/test/webservices/test_ca_item_info.rb +59 -0
  45. data/test/webservices/test_ca_search.rb +35 -0
  46. metadata +244 -0
@@ -0,0 +1,84 @@
1
+ # encoding: utf-8
2
+
3
+ require 'digest'
4
+
5
+ module Libis
6
+ module Tools
7
+
8
+ # Common interface for checksum calculations.
9
+ #
10
+ # Supported checksum algortihms are MD5, RMD160 (not on JRuby), SHA-1, SHA-2 (256, 384 and 512-bit versions).
11
+ # All methods are available on the class and on the instance. The instance has to be initialized with a checksum
12
+ # algorithm and therefore the instance methods do not have to specify the checksum type.
13
+ #
14
+ 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]
19
+ end
20
+
21
+ # Create instance for a given checksum algorithm.
22
+ #
23
+ # @param [Symbol] type checksum algorithm; one of {#CHECKSUM_TYPES}
24
+ def initialize(type)
25
+ @hasher = self.class.get_hasher(type)
26
+ end
27
+
28
+ # Calculate binary digest of a file.
29
+ #
30
+ # @param [String] file_path_or_string path of the file to calculate the digest for
31
+ def digest(file_path_or_string)
32
+ @hasher.file(file_path_or_string).digest!
33
+ end
34
+
35
+ # Calculate the hexadecimal digest of a file.
36
+ # @param (see #digest)
37
+ def hexdigest(file_path_or_string)
38
+ @hasher.file(file_path_or_string).hexdigest!
39
+ end
40
+
41
+ # Calculate the base64 digest of a file.
42
+ # @param (see #digest)
43
+ def base64digest(file_path_or_string)
44
+ @hasher.file(file_path_or_string).base64digest!
45
+ end
46
+
47
+ # Calculate the binary digest of a file.
48
+ # @param (see #digest)
49
+ # @param (see #initialize)
50
+ def self.digest(file_path_or_string, type)
51
+ new(type).digest(file_path_or_string)
52
+ end
53
+
54
+ # Calculate the hexadecimal digest of a file.
55
+ # @param (see #digest)
56
+ # @param (see #initialize)
57
+ def self.hexdigest(file_path_or_string, type)
58
+ new(type).hexdigest(file_path_or_string)
59
+ end
60
+
61
+ # Calculate the base64 digest of a file.
62
+ # @param (see #digest)
63
+ # @param (see #initialize)
64
+ def self.base64digest(file_path_or_string, type)
65
+ new(type).base64digest(file_path_or_string)
66
+ end
67
+
68
+ # Instatiate a Digest instance for access to low-level functionality
69
+ # @param (see #initialize)
70
+ def self.get_hasher(type)
71
+ raise RuntimeError, "Checksum type '#{type}' not supported." unless CHECKSUM_TYPES.include? type
72
+ Digest(type).new
73
+ end
74
+
75
+ private
76
+
77
+ def hashit(file_path_or_string)
78
+ @hasher.file(file_path_or_string)
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ require 'open3'
3
+
4
+ module Libis
5
+ module Tools
6
+
7
+ module Command
8
+
9
+ # Run an external program and return status, stdout and stderr.
10
+ #
11
+ #
12
+ # @param [String] cmd program name
13
+ # @param [Array<String>] opts optional list of command line arguments
14
+ # @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
18
+ def self.run(cmd, *opts)
19
+ result = {}
20
+ begin
21
+ Open3.popen3(cmd, *opts) do |_, output, error, thread|
22
+ output = output.read
23
+ error = error.read
24
+ result[:out] = output.split("\n").map(&:chomp)
25
+ result[:err] = error.split("\n").map(&:chomp)
26
+ result[:status] = thread.value.exitstatus rescue nil
27
+ end
28
+
29
+ rescue Exception
30
+ # ignored
31
+
32
+ end
33
+
34
+ result
35
+
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,160 @@
1
+ # encoding: utf-8
2
+ require 'singleton'
3
+ require 'set'
4
+ require 'yaml'
5
+ require 'erb'
6
+ require 'logger'
7
+ require 'backports'
8
+
9
+ require 'libis/tools/extend/hash'
10
+
11
+ module Libis
12
+ module Tools
13
+
14
+ # The Config class is a convenience method for easy configuration maintenance and loading.
15
+ # It supports code defaults, loading configurations from multiple YAML files containing ERB statements.
16
+ # The Config class follows the Singleton pattern and behaves like a Hash/OpenStruct/HashWithIndifferentAccess.
17
+ # It also initializes a default Logger instance.
18
+ # The class also stores a system-wide {::Logger} instance that will be used by {::Libis::Tools::Logger}.
19
+ #
20
+ # The parameters can be accessed by getter/setter method or using the Hash syntax:
21
+ #
22
+ # require 'libis/tools/config'
23
+ # cfg = ::Libis::Tools::Config
24
+ # cfg['my_value'] = 10
25
+ # p cfg.instance.my_value # => 10
26
+ # cfg.instance.my_text = 'abc'
27
+ # p cfg[:my_text] # => 'abc'
28
+ # p cfg.logger.warn('message') # => W, [2015-03-16T12:51:01.180548 #28935] WARN -- : message
29
+ #
30
+ class Config
31
+ include Singleton
32
+
33
+ class << self
34
+
35
+ private
36
+
37
+ # For each configuration parameter, the value can be accessed via the class or the Singleton instance.
38
+ def method_missing(name, *args, &block)
39
+ instance.send(name, *args, &block)
40
+ end
41
+
42
+ end
43
+
44
+ # Load configuration parameters from a YAML file or Hash.
45
+ #
46
+ # The YAML file can contain ERB syntax values that will be evaluated at loading time. Multiple files can be
47
+ # loaded. Instead of a YAML file, a Hash can be passed. The file paths and Hashes are memorised and loaded again
48
+ # by the {#reload} methods.
49
+ # @param [String,Hash] file_or_hash
50
+ def <<(file_or_hash)
51
+ return if file_or_hash.nil?
52
+ hash = case file_or_hash
53
+ when Hash
54
+ @sources << file_or_hash
55
+ file_or_hash
56
+ when String
57
+ return unless File.exist?(file_or_hash)
58
+ @sources << File.absolute_path(file_or_hash)
59
+ data = ERB.new(open(file_or_hash).read).result
60
+ # noinspection RubyResolve
61
+ YAML.load(data).to_hash rescue {}
62
+ else
63
+ {}
64
+ end
65
+ @data.merge! hash.key_symbols_to_strings recursive: true
66
+ self
67
+ end
68
+
69
+ # Load all files and Hashes again. Will not reset the configuration parameters. Parameters set directly on the
70
+ # configuration are kept intact unless they also exist in the files or hashes in which case they will be overwritten.
71
+ def reload
72
+ sources = @sources.dup
73
+ @sources.clear
74
+ sources.each { |f| self << f }
75
+ self
76
+ end
77
+
78
+ # Load all files and Hashes again. All configuration parameters are first deleted which means that any parameters
79
+ # added directly (not via file or hash) will no longer be available. Parameters set explicitly that also exist in
80
+ # the files or hashes will be reset to the values in those files and hashes.
81
+ def reload!
82
+ @data.clear
83
+ reload
84
+ end
85
+
86
+ # Get the value of a parameter.
87
+ # @param [String, Symbol] name parameter name
88
+ # @return [Object] parameter value; nil if the parameter does not exist
89
+ def [](name)
90
+ @data.fetch(name.to_s) rescue nil
91
+ end
92
+
93
+ # Set the value of a parameter.
94
+ # If the parameter does not yet exist, it will be created.
95
+ # @param (see #[])
96
+ # @param [Object] value the new value for the parameter
97
+ # @return [Object] parameter value
98
+ def []=(name, value)
99
+ @data.store(name.to_s, value)
100
+ end
101
+
102
+ # Return the ::Logger instance.
103
+ def logger
104
+ @logger
105
+ end
106
+
107
+ # Set the ::Logger instance.
108
+ # @param [::Logger] my_logger new logger instance
109
+ def logger=(my_logger)
110
+ @logger = my_logger
111
+ end
112
+
113
+ # Set the ::Logger instance's formatter.
114
+ # If the supplied formatter is missing or nil, a default formatter will be applied. The default formatter prints
115
+ # log lines like this:
116
+ #
117
+ # <first char of severity>, [<timestamp>#<process-id>] <severity> -- <program_name> : <message>
118
+ #
119
+ # @param [Proc] formatter the formatter procedure or nil for default formatter
120
+ def set_log_formatter(formatter = nil)
121
+ logger.formatter = formatter || proc do |severity, time, progname, msg|
122
+ "%s, [%s#%d] %5s -- %s: %s\n" % [severity[0..0],
123
+ (time.strftime('%Y-%m-%dT%H:%M:%S.') << '%06d ' % time.usec),
124
+ $$, severity, progname, msg]
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ def method_missing(name, *args)
131
+ key = name.to_s
132
+ if name.to_s =~ /^(.*)(=)$/
133
+ key = $1
134
+ end
135
+ if @data.has_key?(key)
136
+ if key =~/^\w+$/ # not all key names are safe to use as method names
137
+ self.instance_eval <<-END
138
+ def #{key}
139
+ self['#{key}']
140
+ end
141
+ def #{name}=(value)
142
+ self['#{name}'] = value
143
+ end
144
+ END
145
+ end
146
+ end
147
+ ($2.nil? || $2.empty?) ? (@data.fetch(key) rescue nil) : @data.store(key, args.first)
148
+ end
149
+
150
+ def initialize
151
+ @data = Hash.new
152
+ @sources = Array.new
153
+ @logger = ::Logger.new(STDOUT)
154
+ set_log_formatter
155
+ end
156
+
157
+ end
158
+ end
159
+ end
160
+
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'xml_document'
4
+
5
+ module Libis
6
+ module Tools
7
+
8
+ class DCRecord < XmlDocument
9
+
10
+ # noinspection RubyResolve
11
+ def initialize(doc = nil)
12
+ super('utf-8')
13
+ case doc
14
+ when NilClass
15
+ build do |xml|
16
+ xml[:dc].record('xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
17
+ 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
18
+ 'xmlns:dcterms' => 'http://purl.org/dc/terms/') {
19
+ yield xml if block_given?
20
+ }
21
+ end
22
+ when ::Libis::Tools::XmlDocument
23
+ @document = doc.document.dup
24
+ when String
25
+ if File.exist?(doc)
26
+ # noinspection RubyResolve
27
+ load(doc)
28
+ else
29
+ parse(doc)
30
+ end
31
+ when Hash
32
+ self.from_hash(doc)
33
+ when IO
34
+ self.parse(doc.read)
35
+ else
36
+ raise ArgumentError, "Invalid argument: #{doc.inspect}"
37
+ end
38
+ end
39
+
40
+ def add(tag, value = nil, attributes = {})
41
+ add_node(tag, value, root, attributes)
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ class NilClass
4
+ def empty?
5
+ true
6
+ end
7
+ end
@@ -0,0 +1,107 @@
1
+ require 'backports/rails/hash'
2
+
3
+ class Hash
4
+
5
+ def cleanup
6
+ self.delete_if { |_,v| v.nil? || (v.respond_to?(:empty?) ? v.empty? : false) }
7
+ end unless method_defined? :cleanup
8
+
9
+ def recursive_cleanup
10
+ delete_proc = Proc.new do |_, v|
11
+ v.delete_if(&delete_proc) if v.kind_of?(Hash)
12
+ v.nil? || (v.respond_to?(:empty?) ? v.empty? : false)
13
+ end
14
+ self.delete_if &delete_proc
15
+ end unless method_defined? :recursive_cleanup
16
+
17
+ def recursive_merge(other_hash)
18
+ self.merge(other_hash) do |_, old_val, new_val|
19
+ if old_val.is_a? Hash
20
+ old_val.recursive_merge new_val
21
+ else
22
+ new_val
23
+ end
24
+ end
25
+ end unless method_defined? :recursive_merge
26
+
27
+ def recursive_merge!(other_hash)
28
+ self.merge!(other_hash) do |_, old_val, new_val|
29
+ if old_val.is_a? Hash
30
+ old_val.recursive_merge new_val
31
+ else
32
+ new_val
33
+ end
34
+ end
35
+ end unless method_defined? :recursive_merge!
36
+
37
+ def key_strings_to_symbols(opts = {})
38
+ opts = {resursive: false, upcase: false, downcase: false}.merge opts
39
+
40
+ r = Hash.new
41
+ self.each_pair do |k,v|
42
+
43
+ k = k.to_s if k.kind_of? Symbol
44
+ if k.kind_of? String
45
+ k = k.downcase if opts[:downcase]
46
+ k = k.upcase if opts[:upcase]
47
+ k = k.to_sym
48
+ end
49
+
50
+ if opts[:recursive]
51
+ case v
52
+ when Hash
53
+ v = v.key_strings_to_symbols opts
54
+ when Array
55
+ # noinspection RubyResolve
56
+ v = v.collect { |a| (a.kind_of? Hash) ? a.key_strings_to_symbols(opts) : Marshal.load(Marshal.dump(a)) }
57
+ else
58
+ # noinspection RubyResolve
59
+ v = Marshal.load(Marshal.dump(v))
60
+ end
61
+ end
62
+
63
+ r[k] = v
64
+
65
+ end
66
+
67
+ r
68
+ end unless method_defined? :key_strings_to_symbols
69
+
70
+ def key_symbols_to_strings!(opts = {})
71
+ self.replace self.key_symbols_to_strings opts
72
+ end unless method_defined? :key_symbols_to_strings!
73
+
74
+ def key_symbols_to_strings(opts = {})
75
+ opts = {resursive: false, upcase: false, downcase: false}.merge opts
76
+
77
+ r = Hash.new
78
+ self.each_pair do |k,v|
79
+
80
+ k = k.to_sym if k.kind_of? String
81
+ if k.kind_of? Symbol
82
+ k = k.to_s
83
+ k = k.downcase if opts[:downcase]
84
+ k = k.upcase if opts[:upcase]
85
+ end
86
+
87
+ if opts[:recursive]
88
+ case v
89
+ when Hash
90
+ v = v.key_symbols_to_strings(opts)
91
+ when Array
92
+ # noinspection RubyResolve
93
+ v = v.collect { |a| (a.kind_of? Hash) ? a.key_symbols_to_strings(opts) : Marshal.load(Marshal.dump(a)) }
94
+ else
95
+ # noinspection RubyResolve
96
+ v = Marshal.load(Marshal.dump(v))
97
+ end
98
+ end
99
+
100
+ r[k] = v
101
+
102
+ end
103
+
104
+ r
105
+ end unless method_defined? :key_symbols_to_strings
106
+
107
+ end