longleaf 0.2.0.pre.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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