longleaf 0.2.0.pre.1 → 0.3.0

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 (165) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +84 -0
  3. data/.gitignore +4 -2
  4. data/.rubocop.yml +42 -2
  5. data/.rubocop_todo.yml +390 -311
  6. data/.yardopts +1 -0
  7. data/Gemfile +16 -1
  8. data/README.md +67 -13
  9. data/Rakefile +6 -0
  10. data/bin/setup +16 -1
  11. data/docs/aboutlongleaf.md +28 -0
  12. data/docs/extra.css +32 -0
  13. data/docs/img/change-file.png +0 -0
  14. data/docs/img/ll-example-preserved.png +0 -0
  15. data/docs/index.md +19 -0
  16. data/docs/install.md +66 -0
  17. data/docs/ll-example/config-example-relative.yml +33 -0
  18. data/docs/ll-example/files-dir/LLexample-PDF.pdf +0 -0
  19. data/docs/ll-example/files-dir/LLexample-TOCHANGE.txt +15 -0
  20. data/docs/ll-example/files-dir/LLexample-tokeep.txt +10 -0
  21. data/docs/ll-example/metadata-dir/.gitkeep +0 -0
  22. data/docs/ll-example/replica-files/.gitkeep +0 -0
  23. data/docs/ll-example/replica-metadata/.gitkeep +0 -0
  24. data/docs/quickstart.md +270 -0
  25. data/docs/rdocs/Longleaf.html +135 -0
  26. data/docs/rdocs/Longleaf/AppFields.html +178 -0
  27. data/docs/rdocs/Longleaf/ApplicationConfigDeserializer.html +631 -0
  28. data/docs/rdocs/Longleaf/ApplicationConfigManager.html +610 -0
  29. data/docs/rdocs/Longleaf/ApplicationConfigValidator.html +238 -0
  30. data/docs/rdocs/Longleaf/CLI.html +909 -0
  31. data/docs/rdocs/Longleaf/ChecksumMismatchError.html +151 -0
  32. data/docs/rdocs/Longleaf/ConfigBuilder.html +1339 -0
  33. data/docs/rdocs/Longleaf/ConfigurationError.html +143 -0
  34. data/docs/rdocs/Longleaf/ConfigurationValidator.html +227 -0
  35. data/docs/rdocs/Longleaf/DeregisterCommand.html +420 -0
  36. data/docs/rdocs/Longleaf/DeregisterEvent.html +453 -0
  37. data/docs/rdocs/Longleaf/DeregistrationError.html +151 -0
  38. data/docs/rdocs/Longleaf/DigestHelper.html +419 -0
  39. data/docs/rdocs/Longleaf/EventError.html +147 -0
  40. data/docs/rdocs/Longleaf/EventNames.html +163 -0
  41. data/docs/rdocs/Longleaf/EventStatusTracking.html +656 -0
  42. data/docs/rdocs/Longleaf/FileCheckService.html +540 -0
  43. data/docs/rdocs/Longleaf/FileHelpers.html +520 -0
  44. data/docs/rdocs/Longleaf/FileRecord.html +716 -0
  45. data/docs/rdocs/Longleaf/FileSelector.html +901 -0
  46. data/docs/rdocs/Longleaf/FixityCheckService.html +691 -0
  47. data/docs/rdocs/Longleaf/IndexManager.html +1155 -0
  48. data/docs/rdocs/Longleaf/InvalidDigestAlgorithmError.html +143 -0
  49. data/docs/rdocs/Longleaf/InvalidStoragePathError.html +143 -0
  50. data/docs/rdocs/Longleaf/Logging.html +405 -0
  51. data/docs/rdocs/Longleaf/Logging/RedirectingLogger.html +1213 -0
  52. data/docs/rdocs/Longleaf/LongleafError.html +139 -0
  53. data/docs/rdocs/Longleaf/MDFields.html +193 -0
  54. data/docs/rdocs/Longleaf/MetadataBuilder.html +787 -0
  55. data/docs/rdocs/Longleaf/MetadataDeserializer.html +537 -0
  56. data/docs/rdocs/Longleaf/MetadataError.html +143 -0
  57. data/docs/rdocs/Longleaf/MetadataPersistenceManager.html +539 -0
  58. data/docs/rdocs/Longleaf/MetadataRecord.html +1411 -0
  59. data/docs/rdocs/Longleaf/MetadataSerializer.html +786 -0
  60. data/docs/rdocs/Longleaf/PreservationServiceError.html +147 -0
  61. data/docs/rdocs/Longleaf/PreserveCommand.html +410 -0
  62. data/docs/rdocs/Longleaf/PreserveEvent.html +491 -0
  63. data/docs/rdocs/Longleaf/RegisterCommand.html +428 -0
  64. data/docs/rdocs/Longleaf/RegisterEvent.html +628 -0
  65. data/docs/rdocs/Longleaf/RegisteredFileSelector.html +446 -0
  66. data/docs/rdocs/Longleaf/RegistrationError.html +151 -0
  67. data/docs/rdocs/Longleaf/ReindexCommand.html +576 -0
  68. data/docs/rdocs/Longleaf/RsyncReplicationService.html +1180 -0
  69. data/docs/rdocs/Longleaf/SequelIndexDriver.html +1978 -0
  70. data/docs/rdocs/Longleaf/ServiceCandidateFilesystemIterator.html +572 -0
  71. data/docs/rdocs/Longleaf/ServiceCandidateIndexIterator.html +532 -0
  72. data/docs/rdocs/Longleaf/ServiceCandidateLocator.html +333 -0
  73. data/docs/rdocs/Longleaf/ServiceClassCache.html +725 -0
  74. data/docs/rdocs/Longleaf/ServiceDateHelper.html +425 -0
  75. data/docs/rdocs/Longleaf/ServiceDefinition.html +683 -0
  76. data/docs/rdocs/Longleaf/ServiceDefinitionManager.html +371 -0
  77. data/docs/rdocs/Longleaf/ServiceDefinitionValidator.html +269 -0
  78. data/docs/rdocs/Longleaf/ServiceFields.html +173 -0
  79. data/docs/rdocs/Longleaf/ServiceManager.html +1229 -0
  80. data/docs/rdocs/Longleaf/ServiceMappingManager.html +410 -0
  81. data/docs/rdocs/Longleaf/ServiceMappingValidator.html +347 -0
  82. data/docs/rdocs/Longleaf/ServiceRecord.html +821 -0
  83. data/docs/rdocs/Longleaf/StorageLocation.html +985 -0
  84. data/docs/rdocs/Longleaf/StorageLocationManager.html +729 -0
  85. data/docs/rdocs/Longleaf/StorageLocationUnavailableError.html +143 -0
  86. data/docs/rdocs/Longleaf/StorageLocationValidator.html +373 -0
  87. data/docs/rdocs/Longleaf/StoragePathValidator.html +253 -0
  88. data/docs/rdocs/Longleaf/SystemConfigBuilder.html +441 -0
  89. data/docs/rdocs/Longleaf/SystemConfigFields.html +163 -0
  90. data/docs/rdocs/Longleaf/ValidateConfigCommand.html +451 -0
  91. data/docs/rdocs/Longleaf/ValidateMetadataCommand.html +408 -0
  92. data/docs/rdocs/_index.html +660 -0
  93. data/docs/rdocs/class_list.html +51 -0
  94. data/docs/rdocs/css/common.css +1 -0
  95. data/docs/rdocs/css/full_list.css +58 -0
  96. data/docs/rdocs/css/style.css +496 -0
  97. data/docs/rdocs/file.README.html +165 -0
  98. data/docs/rdocs/file_list.html +56 -0
  99. data/docs/rdocs/frames.html +17 -0
  100. data/docs/rdocs/index.html +165 -0
  101. data/docs/rdocs/js/app.js +303 -0
  102. data/docs/rdocs/js/full_list.js +216 -0
  103. data/docs/rdocs/js/jquery.js +4 -0
  104. data/docs/rdocs/method_list.html +2051 -0
  105. data/docs/rdocs/top-level-namespace.html +110 -0
  106. data/lib/longleaf/candidates/file_selector.rb +47 -15
  107. data/lib/longleaf/candidates/registered_file_selector.rb +67 -0
  108. data/lib/longleaf/candidates/service_candidate_filesystem_iterator.rb +29 -35
  109. data/lib/longleaf/candidates/service_candidate_index_iterator.rb +84 -0
  110. data/lib/longleaf/candidates/service_candidate_locator.rb +9 -4
  111. data/lib/longleaf/cli.rb +162 -80
  112. data/lib/longleaf/commands/deregister_command.rb +12 -11
  113. data/lib/longleaf/commands/preserve_command.rb +13 -8
  114. data/lib/longleaf/commands/register_command.rb +9 -6
  115. data/lib/longleaf/commands/reindex_command.rb +92 -0
  116. data/lib/longleaf/commands/validate_config_command.rb +27 -6
  117. data/lib/longleaf/commands/validate_metadata_command.rb +11 -9
  118. data/lib/longleaf/errors.rb +12 -12
  119. data/lib/longleaf/events/deregister_event.rb +13 -15
  120. data/lib/longleaf/events/event_status_tracking.rb +7 -7
  121. data/lib/longleaf/events/preserve_event.rb +24 -14
  122. data/lib/longleaf/events/register_event.rb +21 -35
  123. data/lib/longleaf/helpers/digest_helper.rb +4 -4
  124. data/lib/longleaf/helpers/service_date_helper.rb +5 -6
  125. data/lib/longleaf/indexing/index_manager.rb +101 -0
  126. data/lib/longleaf/indexing/sequel_index_driver.rb +324 -0
  127. data/lib/longleaf/logging.rb +4 -4
  128. data/lib/longleaf/logging/redirecting_logger.rb +20 -20
  129. data/lib/longleaf/models/app_fields.rb +2 -1
  130. data/lib/longleaf/models/file_record.rb +10 -6
  131. data/lib/longleaf/models/md_fields.rb +1 -1
  132. data/lib/longleaf/models/metadata_record.rb +22 -12
  133. data/lib/longleaf/models/service_definition.rb +3 -3
  134. data/lib/longleaf/models/service_fields.rb +1 -1
  135. data/lib/longleaf/models/service_record.rb +6 -5
  136. data/lib/longleaf/models/storage_location.rb +26 -7
  137. data/lib/longleaf/models/system_config_fields.rb +9 -0
  138. data/lib/longleaf/preservation_services/file_check_service.rb +58 -0
  139. data/lib/longleaf/preservation_services/fixity_check_service.rb +16 -14
  140. data/lib/longleaf/preservation_services/rsync_replication_service.rb +32 -31
  141. data/lib/longleaf/services/application_config_deserializer.rb +55 -18
  142. data/lib/longleaf/services/application_config_manager.rb +16 -4
  143. data/lib/longleaf/services/application_config_validator.rb +1 -2
  144. data/lib/longleaf/services/configuration_validator.rb +6 -4
  145. data/lib/longleaf/services/metadata_deserializer.rb +40 -38
  146. data/lib/longleaf/services/metadata_persistence_manager.rb +46 -0
  147. data/lib/longleaf/services/metadata_serializer.rb +23 -22
  148. data/lib/longleaf/services/service_class_cache.rb +15 -15
  149. data/lib/longleaf/services/service_definition_manager.rb +5 -6
  150. data/lib/longleaf/services/service_definition_validator.rb +5 -6
  151. data/lib/longleaf/services/service_manager.rb +37 -17
  152. data/lib/longleaf/services/service_mapping_manager.rb +9 -9
  153. data/lib/longleaf/services/service_mapping_validator.rb +9 -10
  154. data/lib/longleaf/services/storage_location_manager.rb +22 -8
  155. data/lib/longleaf/services/storage_location_validator.rb +11 -8
  156. data/lib/longleaf/services/storage_path_validator.rb +1 -1
  157. data/lib/longleaf/specs/config_builder.rb +30 -17
  158. data/lib/longleaf/specs/custom_matchers.rb +1 -1
  159. data/lib/longleaf/specs/file_helpers.rb +15 -14
  160. data/lib/longleaf/specs/metadata_builder.rb +91 -0
  161. data/lib/longleaf/specs/system_config_builder.rb +27 -0
  162. data/lib/longleaf/version.rb +1 -1
  163. data/longleaf.gemspec +17 -7
  164. data/mkdocs.yml +20 -0
  165. metadata +233 -22
@@ -8,42 +8,41 @@ module Longleaf
8
8
  # Validates application configuration of service to location mappings
9
9
  class ServiceMappingValidator < ConfigurationValidator
10
10
  AF ||= Longleaf::AppFields
11
-
11
+
12
12
  # Validates service mapping configuration to ensure that it is syntactically and referentially correct.
13
13
  # @param config [Hash] hash containing the application configuration
14
14
  def self.validate_config(config)
15
-
16
15
  assert("Configuration must be a hash, but a #{config.class} was provided", config.class == Hash)
17
16
  assert("Configuration must contain a root '#{AF::SERVICE_MAPPINGS}' key", config.key?(AF::SERVICE_MAPPINGS))
18
17
  mappings = config[AF::SERVICE_MAPPINGS]
19
18
  return if mappings.nil? || mappings.empty?
20
19
  assert("'#{AF::SERVICE_MAPPINGS}' must be an array of mappings", mappings.is_a?(Array))
21
-
20
+
22
21
  service_names = config[AF::SERVICES].keys
23
22
  location_names = config[AF::LOCATIONS].keys
24
-
25
- existing_paths = Array.new
23
+
26
24
  mappings.each do |mapping|
27
25
  assert("Mapping must be a hash, but received #{mapping.inspect} instead", mapping.is_a?(Hash))
28
-
26
+
29
27
  validate_mapping_field(AF::LOCATIONS, mapping, location_names)
30
28
  validate_mapping_field(AF::SERVICES, mapping, service_names)
31
29
  end
32
30
  end
33
-
34
- private
31
+
35
32
  def self.validate_mapping_field(field, mapping, valid_values)
36
33
  assert("Mapping must contain a '#{field}' field", mapping.key?(field))
37
34
  field_values = mapping[field]
38
35
  assert("Mapping '#{field}' field must be either a string or an array, but received '#{field_values.inspect}' instead",
39
36
  field_values.is_a?(Array) || field_values.is_a?(String))
40
37
  assert("Mapping must specify one or more value in the '#{field}' field", !field_values.empty?)
41
-
38
+
42
39
  check_values = field_values.is_a?(String) ? [field_values] : field_values
43
40
  check_values.each do |value|
44
41
  assert("Mapping '#{field}' specifies value '#{value}', but no #{field} with that name exist",
45
42
  valid_values.include?(value))
46
43
  end
47
44
  end
45
+
46
+ private_class_method :validate_mapping_field
48
47
  end
49
- end
48
+ end
@@ -6,10 +6,10 @@ module Longleaf
6
6
  # Manager which loads and provides access to {StorageLocation} objects
7
7
  class StorageLocationManager
8
8
  AF ||= Longleaf::AppFields
9
-
9
+
10
10
  # Hash mapping storage location names to {StorageLocation} objects
11
11
  attr_reader :locations
12
-
12
+
13
13
  # @param config [Hash] has representation of the application configuration
14
14
  def initialize(config)
15
15
  raise ArgumentError.new("Configuration must be provided") if config&.empty?
@@ -23,29 +23,42 @@ module Longleaf
23
23
  path: path,
24
24
  metadata_path: md_path,
25
25
  metadata_digests: md_digests)
26
-
26
+
27
27
  @locations[name] = location
28
28
  end
29
29
  @locations.freeze
30
30
  end
31
-
31
+
32
32
  # Get the {StorageLocation} object which should contain the given path
33
33
  # @return [Longleaf::StorageLocation] location containing the given path
34
34
  # or nil if the path is not contained by a registered location.
35
35
  def get_location_by_path(path)
36
36
  raise ArgumentError.new("Path parameter is required") if path.nil? || path.empty?
37
37
  @locations.each do |name, location|
38
- return location if path.start_with?(location.path)
38
+ return location if path.start_with?(location.path)
39
39
  end
40
-
40
+
41
41
  nil
42
42
  end
43
-
43
+
44
+ # Get the {StorageLocation} object which should contain the given metadata path
45
+ # @return [Longleaf::StorageLocation] location containing the given metadata path
46
+ # or nil if the path is not contained by a registered location.
47
+ def get_location_by_metadata_path(md_path)
48
+ raise ArgumentError.new("Metadata path parameter is required") if md_path.nil? || md_path.empty?
49
+ @locations.each do |name, location|
50
+ return location if md_path.start_with?(location.metadata_path)
51
+ end
52
+
53
+ nil
54
+ end
55
+
44
56
  # Raises a {StorageLocationUnavailableError} if the given path is not in a known storage location,
45
57
  # or if it is not within the expected location if provided
46
58
  # @param path [String] file path
47
59
  # @param expected_loc [String] name of the storage location which path should be contained by
48
60
  # @raise [StorageLocationUnavailableError] if the path is not in a known/expected storage location
61
+ # @return [StorageLocation] the storage location which contains path, if it was within one.
49
62
  def verify_path_in_location(path, expected_loc = nil)
50
63
  location = get_location_by_path(path)
51
64
  if location.nil?
@@ -53,6 +66,7 @@ module Longleaf
53
66
  elsif !expected_loc.nil? && expected_loc != location.name
54
67
  raise StorageLocationUnavailableError.new("Path #{path} is not contained by storage location #{expected_loc}.")
55
68
  end
69
+ location
56
70
  end
57
71
  end
58
- end
72
+ end
@@ -9,8 +9,8 @@ module Longleaf
9
9
  # Validates application configuration of storage locations
10
10
  class StorageLocationValidator < ConfigurationValidator
11
11
  AF ||= Longleaf::AppFields
12
-
13
- # Validates configuration to ensure that it is syntactically correct and does not violate
12
+
13
+ # Validates configuration to ensure that it is syntactically correct and does not violate
14
14
  # schema and uniqueness requirements.
15
15
  # @param config [Hash] hash containing the application configuration
16
16
  def self.validate_config(config)
@@ -18,18 +18,17 @@ module Longleaf
18
18
  assert("Configuration must contain a root '#{AF::LOCATIONS}' key", config.key?(AF::LOCATIONS))
19
19
  locations = config[AF::LOCATIONS]
20
20
  assert("'#{AF::LOCATIONS}' must be a hash of locations", locations.class == Hash)
21
-
21
+
22
22
  existing_paths = Array.new
23
23
  locations.each do |name, properties|
24
24
  assert("Name of storage location must be a string, but was of type #{name.class}", name.instance_of?(String))
25
25
  assert("Storage location '#{name}' must be a hash, but a #{properties.class} was provided", properties.is_a?(Hash))
26
-
26
+
27
27
  assert_path_property_valid(name, AF::LOCATION_PATH, properties, existing_paths)
28
28
  assert_path_property_valid(name, AF::METADATA_PATH, properties, existing_paths)
29
29
  end
30
30
  end
31
-
32
- private
31
+
33
32
  def self.assert_path_property_valid(name, path_prop, properties, existing_paths)
34
33
  path = properties[path_prop]
35
34
  begin
@@ -41,6 +40,8 @@ module Longleaf
41
40
  assert("Storage location '#{name}' must specify a '#{path_prop}' property", !path.nil? && !path.empty?)
42
41
  assert("Storage location '#{name}' must specify an absolute path for property '#{path_prop}'",
43
42
  Pathname.new(path).absolute? && !path.include?('/..'))
43
+ assert("Storage location '#{name}' specifies a '#{path_prop}' directory which does not exist", Dir.exist?(path))
44
+
44
45
  # Ensure paths have trailing slash to avoid matching on partial directory names
45
46
  path += '/' unless path.end_with?('/')
46
47
  # Verify that the (metadata_)path property's value is not inside of another storage location or vice versa
@@ -53,8 +54,10 @@ module Longleaf
53
54
  raise ConfigurationError.new(msg)
54
55
  end
55
56
  end
56
-
57
+
57
58
  existing_paths << path
58
59
  end
60
+
61
+ private_class_method :assert_path_property_valid
59
62
  end
60
- end
63
+ end
@@ -13,4 +13,4 @@ module Longleaf
13
13
  if path.include?('/..')
14
14
  end
15
15
  end
16
- end
16
+ end
@@ -7,13 +7,13 @@ module Longleaf
7
7
  class ConfigBuilder
8
8
  AF ||= Longleaf::AppFields
9
9
  SF ||= Longleaf::ServiceFields
10
-
10
+
11
11
  attr_accessor :config
12
-
12
+
13
13
  def initialize
14
14
  @config = Hash.new
15
15
  end
16
-
16
+
17
17
  # Add a root 'locations' field to the config
18
18
  # @param locations [Hash] value for the locations fields. Default is {}
19
19
  # @return this builder
@@ -21,7 +21,7 @@ module Longleaf
21
21
  @config[AF::LOCATIONS] = locations
22
22
  self
23
23
  end
24
-
24
+
25
25
  # Add a 'location' to the config
26
26
  # @param name [String] name of the location
27
27
  # @param path [String] value for the 'path' field
@@ -29,7 +29,7 @@ module Longleaf
29
29
  # @return this builder
30
30
  def with_location(name:, path: '/file/path/', md_path: '/metadata/path/', md_digests: nil)
31
31
  @config[AF::LOCATIONS] = Hash.new unless @config.key?(AF::LOCATIONS)
32
-
32
+
33
33
  location = {}
34
34
  @config[AF::LOCATIONS][name] = location
35
35
  location[AF::LOCATION_PATH] = path unless path.nil?
@@ -37,7 +37,7 @@ module Longleaf
37
37
  location[AF::METADATA_DIGESTS] = md_digests unless md_digests.nil?
38
38
  self
39
39
  end
40
-
40
+
41
41
  # Add a root 'services' field to the config
42
42
  # @param services [Hash] value for the services field. Default is {}
43
43
  # @return this builder
@@ -45,7 +45,7 @@ module Longleaf
45
45
  @config[AF::SERVICES] = services
46
46
  self
47
47
  end
48
-
48
+
49
49
  # Add a 'service' to the config
50
50
  # @param name [String] name of the service
51
51
  # @param work_script [String] value for the 'work_script' field
@@ -57,7 +57,7 @@ module Longleaf
57
57
  def with_service(name:, work_script: 'some_pres_service.rb', work_class: nil,
58
58
  frequency: nil, delay: nil, properties: nil)
59
59
  @config[AF::SERVICES] = Hash.new unless @config.key?(AF::SERVICES)
60
-
60
+
61
61
  service = {}
62
62
  service[SF::WORK_SCRIPT] = work_script
63
63
  service[SF::WORK_CLASS] = work_class
@@ -67,7 +67,7 @@ module Longleaf
67
67
  @config[AF::SERVICES][name] = service
68
68
  self
69
69
  end
70
-
70
+
71
71
  # Adds a 'service_mappings' section to the config
72
72
  # @param mappings [Object] the mappings config
73
73
  # @return this builder
@@ -75,32 +75,45 @@ module Longleaf
75
75
  @config[AF::SERVICE_MAPPINGS] = mappings
76
76
  self
77
77
  end
78
-
78
+
79
79
  # Add a mapping from one or more services to one or more location
80
80
  # @param loc_names [Object] one or more location names. Can be a string or array.
81
81
  # @param service_names [Object] one or more service names. Can be a string or array.
82
82
  def map_services(loc_names, service_names)
83
83
  @config[AF::SERVICE_MAPPINGS] = Array.new unless @config.key?(AF::SERVICE_MAPPINGS)
84
-
84
+
85
85
  mapping = Hash.new
86
86
  mapping[AF::LOCATIONS] = loc_names unless loc_names.nil?
87
87
  mapping[AF::SERVICES] = service_names unless service_names.nil?
88
88
  @config[AF::SERVICE_MAPPINGS].push(mapping)
89
89
  self
90
90
  end
91
-
91
+
92
+ # Add a system config section to the config
93
+ def with_system(sys_config)
94
+ @config[AF::SYSTEM] = sys_config
95
+ self
96
+ end
97
+
92
98
  # @return the constructed configuration
93
99
  def get
94
100
  @config
95
101
  end
96
-
102
+
97
103
  # Writes the configuration from this builder into a temporary file
98
104
  # @return the file path of the config file
99
- def write_to_yaml_file
100
- Tempfile.open('config') do |f|
105
+ def write_to_yaml_file(dest_path = nil)
106
+ if dest_path.nil?
107
+ file = Tempfile.new('config')
108
+ file.close
109
+ dest_path = file.path
110
+ # deleting temp file but reusing file name. This is to avoid premature garbage collection.
111
+ file.unlink
112
+ end
113
+ File.open(dest_path, 'w') do |f|
101
114
  f.write(@config.to_yaml)
102
- return f.path
103
115
  end
116
+ dest_path
104
117
  end
105
118
  end
106
- end
119
+ end
@@ -6,4 +6,4 @@ module Longleaf
6
6
  actual.path == expected
7
7
  end
8
8
  end
9
- end
9
+ end
@@ -7,7 +7,7 @@ module Longleaf
7
7
  def make_test_dir(parent: nil, name: 'dir')
8
8
  FileHelpers.make_test_dir(parent: parent, name: name)
9
9
  end
10
-
10
+
11
11
  def self.make_test_dir(parent: nil, name: 'dir')
12
12
  if parent.nil?
13
13
  Dir.mktmpdir(name)
@@ -17,14 +17,15 @@ module Longleaf
17
17
  path
18
18
  end
19
19
  end
20
-
21
- def create_test_file(dir: nil, name: 'test_file', content: 'content')
20
+
21
+ def create_test_file(dir: nil, name: nil, content: 'content')
22
22
  FileHelpers.create_test_file(dir: dir, name: name, content: content)
23
23
  end
24
-
25
- def self.create_test_file(dir: nil, name: 'test_file', content: 'content')
26
- if dir.nil?
27
- file = Tempfile.create(name)
24
+
25
+ def self.create_test_file(dir: nil, name: nil, content: 'content')
26
+ if dir.nil? || name.nil?
27
+ name = 'test_file' if name.nil?
28
+ file = Tempfile.create(name, dir)
28
29
  file << content
29
30
  file.close
30
31
  return file.path
@@ -34,16 +35,16 @@ module Longleaf
34
35
  path
35
36
  end
36
37
  end
37
-
38
- def create_work_class(lib_dir, class_name, file_name, module_name = nil, is_applicable: true, perform: "")
39
- FileHelpers.create_work_class(lib_dir, class_name, file_name, module_name, is_applicable: is_applicable,
40
- perform: perform)
38
+
39
+ def create_work_class(lib_dir, class_name, file_name, module_name = nil, is_applicable: true, init_body: "", perform: "")
40
+ FileHelpers.create_work_class(lib_dir, class_name, file_name, module_name, is_applicable, init_body, perform)
41
41
  end
42
-
43
- def self.create_work_class(lib_dir, class_name, file_name, module_name = nil, is_applicable: true, perform: "")
42
+
43
+ def self.create_work_class(lib_dir, class_name, file_name, module_name, is_applicable, init_body, perform)
44
44
  class_contents = %Q(
45
45
  class #{class_name}
46
46
  def initialize(service_def, app_manager)
47
+ #{init_body}
47
48
  end
48
49
  def perform(file_rec, event)
49
50
  #{perform}
@@ -57,4 +58,4 @@ module Longleaf
57
58
  create_test_file(dir: lib_dir, name: file_name, content: class_contents)
58
59
  end
59
60
  end
60
- end
61
+ end
@@ -0,0 +1,91 @@
1
+ require 'longleaf/models/md_fields'
2
+ require 'longleaf/helpers/service_date_helper'
3
+ require 'yaml'
4
+
5
+ module Longleaf
6
+ # Test helper for constructing file metadata records
7
+ class MetadataBuilder
8
+ MF ||= Longleaf::MDFields
9
+
10
+ def initialize(file_path: nil, registered: ServiceDateHelper::formatted_timestamp)
11
+ @data = Hash.new
12
+ @services = Hash.new
13
+
14
+ unless file_path.nil?
15
+ @last_modified = File.mtime(file_path).utc.iso8601(3)
16
+ @file_size = File.size(file_path)
17
+ end
18
+
19
+ @registered = registered
20
+ end
21
+
22
+ def deregistered(timestamp = ServiceDateHelper::formatted_timestamp)
23
+ @deregistered = timestamp
24
+ self
25
+ end
26
+
27
+ def with_checksum(alg, value)
28
+ @checksums = Hash.new unless @data.key?(MF::CHECKSUMS)
29
+ @checksums[alg] = value
30
+ self
31
+ end
32
+
33
+ def with_service(name, timestamp: ServiceDateHelper::formatted_timestamp, run_needed: false, properties: nil,
34
+ failure_timestamp: nil)
35
+ timestamp = format_timestamp(timestamp)
36
+ failure_timestamp = format_timestamp(failure_timestamp) unless failure_timestamp.nil?
37
+
38
+ @services[name] = ServiceRecord.new(
39
+ properties: properties.nil? ? Hash.new : nil,
40
+ timestamp: timestamp,
41
+ run_needed: run_needed)
42
+ @services[name].failure_timestamp = failure_timestamp
43
+ self
44
+ end
45
+
46
+ def with_properties(properties)
47
+ @properties = properties
48
+ end
49
+
50
+ # @return the constructed metadata record
51
+ def get_metadata_record
52
+ MetadataRecord.new(properties: @properties,
53
+ services: @services,
54
+ deregistered: @deregistered,
55
+ registered: @registered,
56
+ checksums: @checksums,
57
+ file_size: @file_size,
58
+ last_modified: @last_modified)
59
+ end
60
+
61
+ # Add the generated metadata record to the given file record
62
+ def register_to(file_rec)
63
+ file_rec.metadata_record = get_metadata_record
64
+ self
65
+ end
66
+
67
+ # Writes the metadata record from this builder into a temporary file, or if a file
68
+ # record is provided, then to the expected metadata path for the record, and assigns
69
+ # the result as the metadata record for the file record.
70
+ # @return the file path of the config file
71
+ def write_to_yaml_file(file_rec: nil)
72
+ md_path = nil
73
+ if file_rec.nil?
74
+ md_path = TempFile.new(['metadata', 'yml']).path
75
+ else
76
+ md_path = file_rec.metadata_path
77
+ end
78
+
79
+ md_rec = get_metadata_record
80
+ MetadataSerializer::write(metadata: md_rec, file_path: md_path)
81
+ file_rec.metadata_record = md_rec
82
+
83
+ md_path
84
+ end
85
+
86
+ private
87
+ def format_timestamp(timestamp)
88
+ timestamp.kind_of?(Time) ? ServiceDateHelper::formatted_timestamp(timestamp) : timestamp
89
+ end
90
+ end
91
+ end