tdc 0.2.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c04de93f4bf48d68994ce4b8adab06f21ebd2f44e3d47ce6f221400cdc8495d
4
- data.tar.gz: 93a7c7a9d9dc4ea4480cc464cf6dc7c6526b6e941a68d74de956572e3fe71864
3
+ metadata.gz: 6d7775cd9909b7d1aef1f59def823777cc24d6bbfc50ddfd41eda202f5f5f387
4
+ data.tar.gz: 11b7ddadec9d97e1953f34efdee8b66d6f0cb08213dd8d9a9e9d4ae8372687fa
5
5
  SHA512:
6
- metadata.gz: 4986b573ecf8e32558cbfafddba2218a21896f7b01c6197b42bdab1b117cdee729dd3f0cc58fb8907e577223dad3fcd42890a8fedc937e1a9f4280a3be2be224
7
- data.tar.gz: 25aad449a81724ff044343015835148683c540ba3080d298841152bca2b5ac79d29557f1b2de17d7e986d1556629c20a542e4360a7ece7c3c6998c3fc5971402
6
+ metadata.gz: 1f396da321ee096251062cd35ff6add1d19292591dc8fa019509fe432f2fd7faea74844ee4583fbb30bc03e9f8106fbeabdb403732ec5098d70c40b69e2c216c
7
+ data.tar.gz: 69cfdaa1a302ae2d458f6775db22134953db4f6f584ba9af36970f4865d308de9a44d9c5c6ec10e7db0a4ec5c05d340254a4458dfc843d6676225459e2542660
@@ -0,0 +1,16 @@
1
+ root = true
2
+
3
+ [*]
4
+
5
+ # Change these settings to your own preference
6
+ indent_style = space
7
+ indent_size = 2
8
+
9
+ # We recommend you to keep these unchanged
10
+ end_of_line = lf
11
+ charset = utf-8
12
+ trim_trailing_whitespace = true
13
+ insert_final_newline = true
14
+
15
+ [*.md]
16
+ trim_trailing_whitespace = false
data/.rspec CHANGED
@@ -1,3 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
- --require spec_helper
3
+ --require tdc_spec_helper
@@ -64,10 +64,14 @@ Style/BlockDelimiters:
64
64
  EnforcedStyle: line_count_based
65
65
  BracesRequiredMethods:
66
66
  - 'let'
67
+ - 'subject'
67
68
 
68
69
  Style/Documentation:
69
70
  Enabled: false
70
71
 
72
+ Style/EmptyMethod:
73
+ EnforcedStyle: expanded
74
+
71
75
  Style/ExponentialNotation:
72
76
  Enabled: true
73
77
 
@@ -6,6 +6,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.0] - 2020-07-19
10
+
11
+ - Introduce the DefinitionResolver abstraction
12
+
13
+ ## [0.2.4] - 2020-07-19
14
+
15
+ - Improve Tdc::MissingOverrideError diagnostics
16
+
17
+ ## [0.2.3] - 2020-05-21
18
+
19
+ - Update the diagram in the README
20
+
21
+ ## [0.2.2] - 2020-05-20
22
+
23
+ - Adding specs
24
+
25
+ ## [0.2.1] - 2020-05-19
26
+
27
+ - Adding specs
28
+
9
29
  ## [0.2.0] - 2020-05-19
10
30
 
11
31
  #### Breaking Changes
data/README.md CHANGED
@@ -4,7 +4,9 @@
4
4
 
5
5
  **Extension Points**
6
6
 
7
- Define your own test data generators by inheriting from ```StandardGenerator``` or ```Singular Generator```.
7
+ Define your own test data generators by inheriting from ```StandardGenerator``` or ```SingularGenerator```. Best practice is to define an ```ApplicationStandardGenerator``` and an ```ApplicationSingularGenerator``` and have all other generators inherit from them.
8
+
9
+ Define your own definition resolvers by inheriting from ```DefinitionResolver```. Best practice is to define an ```ApplicationDefinitionResolver``` and all other definition resolvers inherit from it.
8
10
 
9
11
  During generation the test data catalog will be represented by ```CatalogEntries``` that are populated by reading from ```YAML``` files with a ```DataDefinitionReader``` or provided directly by an ```InMemoryDataDefinition```.
10
12
 
Binary file
data/lib/tdc.rb CHANGED
@@ -1,24 +1,43 @@
1
1
  require "ostruct"
2
+ require "yaml"
2
3
 
4
+ require "active_support"
3
5
  require "active_support/concern"
4
- require "active_support/core_ext/hash/indifferent_access"
6
+ require "active_support/core_ext"
5
7
 
8
+ require "tdc/version"
9
+
10
+ # Errors
11
+ require "tdc/fatal_error"
12
+ require "tdc/missing_override_error"
13
+
14
+ # Data Definition Hierarchy
6
15
  require "tdc/data_definition"
7
16
  require "tdc/data_definition_file_reader"
8
- require "tdc/fatal_error"
9
17
  require "tdc/in_memory_data_definition"
10
- require "tdc/version"
11
18
  require "tdc/with_indifferent_access_decorator"
12
19
 
20
+ # Generators
13
21
  require "tdc/generators"
22
+
23
+ # Current Catalog
14
24
  require "tdc/generators/catalog_entries"
25
+
26
+ # Concerns
15
27
  require "tdc/generators/definition_resolvable"
16
28
  require "tdc/generators/definition_sourcable"
29
+
30
+ # Generator Hierarchy
17
31
  require "tdc/generators/generator_base"
18
- require "tdc/generators/instance_definition_configurable"
32
+ require "tdc/generators/configurable_generator"
19
33
  require "tdc/generators/singular_generator"
20
34
  require "tdc/generators/standard_generator"
21
35
 
36
+ # Definition Resolvers
37
+ require "tdc/definition_resolvers"
38
+ require "tdc/definition_resolvers/definition_resolver"
39
+ require "tdc/definition_resolvers/tag_resolver"
40
+
22
41
  #
23
42
  # A framework for building a Test Data Catalog
24
43
  #
@@ -4,7 +4,7 @@ module Tdc
4
4
  #
5
5
  class DataDefinition
6
6
  def read(*_path_elements)
7
- raise MissingOverrideError
7
+ raise MissingOverrideError, "Implement the 'read' method"
8
8
  end
9
9
 
10
10
  def with_indifferent_access
@@ -0,0 +1,7 @@
1
+ module Tdc
2
+ #
3
+ # Namespace to host definition resolvers.
4
+ #
5
+ module DefinitionResolvers
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ module Tdc
2
+ module DefinitionResolvers
3
+ #
4
+ # Base class for any definition resolver.
5
+ #
6
+ class DefinitionResolver
7
+ attr_reader :current_catalog
8
+
9
+ def configure_current_catalog(current_catalog)
10
+ @current_catalog = current_catalog
11
+ end
12
+
13
+ def resolve(_instance_definition)
14
+ raise Tdc::MissingOverrideError, "Implement the 'resolve' method"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ module Tdc
2
+ module DefinitionResolvers
3
+ #
4
+ # Knows how to resolve the tag value for the specified instance_definition key by replacing it
5
+ # with an object sourced from the current catalog.
6
+ #
7
+ class TagResolver < Tdc::DefinitionResolvers::DefinitionResolver
8
+ attr_reader :key, :source
9
+
10
+ def initialize(key:, source:)
11
+ @key = key
12
+ @source = source
13
+ end
14
+
15
+ def resolve(instance_definition)
16
+ return unless instance_definition.key?(key)
17
+
18
+ # Lookup the source catalog entry in the current_catalog.
19
+ catalog_entry = instance_eval("current_catalog.#{source}", __FILE__, __LINE__)
20
+
21
+ # Before resolution the instance definition value is a tag.
22
+ tag = instance_definition[key]
23
+
24
+ # Use the tag to source an object from the current catalog.
25
+ sourced_object = catalog_entry.send(tag)
26
+
27
+ unless sourced_object
28
+ message = "Could not find a tag reference for '#{key}' in the catalog entries provided."
29
+
30
+ raise Tdc::FatalError, message
31
+ end
32
+
33
+ # Replace the tag value with the sourced object.
34
+ instance_definition[key] = sourced_object
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,6 +1,6 @@
1
1
  module Tdc
2
2
  #
3
- # Namespace to host generators
3
+ # Namespace to host generators.
4
4
  #
5
5
  module Generators
6
6
  end
@@ -5,27 +5,16 @@ module Tdc
5
5
  #
6
6
  # Shared implementation between the StandardGenerator and the SingularGenerator abstract classes.
7
7
  #
8
- module InstanceDefinitionConfigurable
9
- extend ActiveSupport::Concern
8
+ class ConfigurableGenerator < Tdc::Generators::GeneratorBase
9
+ include Tdc::Generators::DefinitionSourcable
10
10
 
11
- included do
12
- include Tdc::Generators::DefinitionSourcable
11
+ attr_reader :instance_definition
13
12
 
14
- attr_reader :instance_definition
15
-
16
- source_definition_from :instance_definition
17
- end
18
-
19
- private
20
-
21
- def configure_instance_definition(instance_definition)
22
- @instance_definition = instance_definition
23
- configure_definition_source(instance_definition)
24
- end
13
+ source_definition_from :instance_definition
25
14
 
26
15
  def run_resolvers_and_generate_instance
27
16
  run_atx_resolvers(instance_definition)
28
- run_tag_resolvers(instance_definition)
17
+ run_definition_resolvers(instance_definition)
29
18
 
30
19
  generate_instance
31
20
  end
@@ -34,16 +23,24 @@ module Tdc
34
23
  # Hook method: subclasses are expected to define how to generate a model instance.
35
24
  #
36
25
  def generate_instance
37
- raise Tdc::MissingOverrideError
26
+ raise Tdc::MissingOverrideError, "Implement the 'generate_instance' method"
38
27
  end
39
28
 
40
29
  #
41
30
  # Hook method: subclasses may include the DefinitionResolvable concern to override.
42
31
  #
43
- def run_tag_resolvers(_instance_definition)
32
+ def run_definition_resolvers(_instance_definition)
44
33
  # Do nothing
45
34
  end
46
35
 
36
+ private
37
+
38
+ def configure_instance_definition(instance_definition)
39
+ @instance_definition = instance_definition
40
+
41
+ configure_definition_source(instance_definition)
42
+ end
43
+
47
44
  def run_atx_resolvers(instance_definition)
48
45
  atx_definitions = instance_definition.select { |k, _v| /_atx$/ =~ k }
49
46
 
@@ -1,61 +1,59 @@
1
1
  module Tdc
2
2
  module Generators
3
3
  #
4
- # Knows how to resolve tag values in an instance definition. The tag value will be replaced
5
- # with a model instance from the current catalog.
4
+ # Knows how to resolve attribute values in an instance definition. The attribute value will be replaced
5
+ # by the result of invoking the specified resolver.
6
6
  #
7
- # The resolve_tag class macro is provided for generators to define tag resolution.
7
+ # The resolve_tag class macro is provided for generators to define tag resolution. The test data definition
8
+ # YAML file has attribute values than contain a catalog entry tag.
8
9
  #
9
10
  # Example:
10
11
  #
11
- # Suppose a particular instance definition contained { subcomponent: sc_snackers_minis }
12
- # then a generator could resolve the subcomponent by defining:
12
+ # Suppose a particular instance definition contained { subcomponent: sc_snackers_minis } then a generator
13
+ # could resolve the subcomponent tag to the current catalog by defining:
13
14
  #
14
15
  # resolve_tag key: :subcomponent, source: "item_master.items"
15
16
  #
17
+ # The resolve_definition class macro is provided for generators to define definition resolution. The test data
18
+ # definition YAML file has attribute values than contain an arbitrary value that will typically be replaced by
19
+ # an instance of an object returned by the specified resolver.
20
+ #
21
+ # Example:
22
+ #
23
+ # Suppose a particular instance definition contained { pallet_number: P0001 } then a generator could resolve
24
+ # the pallet_number to a pallet instance by defining:
25
+ #
26
+ # resolve_definition :pallet_number, to: :pallet_id, resolver: "TestDataCatalog::Resolvers::PalletNumberResolver"
27
+ #
28
+ # The PalletNumberResolver class inherits from Tdc::DefinitionResolvers::DefinitionResolver.
29
+ #
16
30
  module DefinitionResolvable
17
31
  extend ActiveSupport::Concern
18
32
 
19
33
  included do
20
- class_attribute :_tag_resolvers, instance_writer: false
21
- self._tag_resolvers = []
34
+ class_attribute :_definition_resolvers, instance_writer: false
35
+
36
+ self._definition_resolvers = []
22
37
  end
23
38
 
24
39
  class_methods do
25
- def resolve_tag(key:, source:)
26
- _tag_resolvers << [key, source]
40
+ def resolve_definition(key:, to: nil, resolver:)
41
+ resolver_instance = resolver.constantize.new(key: key, to: to || key)
42
+
43
+ _definition_resolvers << resolver_instance
27
44
  end
28
- end
29
45
 
30
- def run_tag_resolvers(instance_definition)
31
- _tag_resolvers.each { |key, source| _resolve_tag_reference(instance_definition, key, source) }
46
+ def resolve_tag(key:, source:)
47
+ _definition_resolvers << Tdc::DefinitionResolvers::TagResolver.new(key: key, source: source)
48
+ end
32
49
  end
33
50
 
34
- private
35
-
36
- #
37
- # Replace the tag value for the specified instance_definition key with an object sourced from the current catalog.
38
- #
39
- def _resolve_tag_reference(instance_definition, key, source)
40
- return unless instance_definition.key?(key)
41
-
42
- # Lookup the source catalog entry in the current_catalog.
43
- catalog_entry = instance_eval("current_catalog.#{source}", __FILE__, __LINE__)
51
+ def run_definition_resolvers(instance_definition)
52
+ _definition_resolvers.each do |definition_resolver|
53
+ definition_resolver.configure_current_catalog(current_catalog)
44
54
 
45
- # Before resolution the instance definition value is a tag.
46
- tag = instance_definition[key]
47
-
48
- # Use the tag to source an object from the current catalog.
49
- sourced_object = catalog_entry.send(tag)
50
-
51
- unless sourced_object
52
- message = "Could not find a tag reference for '#{key}' in the catalog entries provided."
53
-
54
- raise Tdc::FatalError, message
55
+ definition_resolver.resolve(instance_definition)
55
56
  end
56
-
57
- # Replace the tag value with the sourced object.
58
- instance_definition[key] = sourced_object
59
57
  end
60
58
  end
61
59
  end
@@ -35,13 +35,13 @@ module Tdc
35
35
  def method_missing(method, *args)
36
36
  key = transform_method_to_definition_source_key(method)
37
37
 
38
- definition_source.key?(key) ? definition_source.fetch(key) : super
38
+ definition_source&.key?(key) ? definition_source.fetch(key) : super
39
39
  end
40
40
 
41
41
  def respond_to_missing?(method, include_all = false)
42
42
  key = transform_method_to_definition_source_key(method)
43
43
 
44
- definition_source.key?(key) ? true : super
44
+ definition_source&.key?(key) ? true : super
45
45
  end
46
46
 
47
47
  def transform_method_to_definition_source_key(method)
@@ -12,11 +12,11 @@ module Tdc
12
12
  end
13
13
 
14
14
  def generate
15
- raise Tdc::MissingOverrideError
15
+ raise Tdc::MissingOverrideError, "Implement the 'generate' method"
16
16
  end
17
17
 
18
18
  def instance_definitions
19
- raise Tdc::MissingOverrideError
19
+ raise Tdc::MissingOverrideError, "Implement the 'instance_definitions' method"
20
20
  end
21
21
  end
22
22
  end
@@ -5,15 +5,7 @@ module Tdc
5
5
  #
6
6
  # See also StandardGenerator.
7
7
  #
8
- class SingularGenerator < Tdc::Generators::GeneratorBase
9
- include Tdc::Generators::InstanceDefinitionConfigurable
10
-
11
- def initialize(data_definition, current_catalog)
12
- super
13
-
14
- @additional_definitions = {}
15
- end
16
-
8
+ class SingularGenerator < Tdc::Generators::ConfigurableGenerator
17
9
  def with_definition(additional_definitions)
18
10
  @additional_definitions = additional_definitions.stringify_keys.reject { |_, v| v == :missing_definition }
19
11
 
@@ -21,18 +13,22 @@ module Tdc
21
13
  end
22
14
 
23
15
  def generate
24
- configure_instance_definition(singular_instance_definition.merge(@additional_definitions))
16
+ configure_instance_definition(singular_instance_definition.merge(additional_definitions))
25
17
 
26
18
  run_resolvers_and_generate_instance
27
19
  end
28
20
 
29
21
  private
30
22
 
23
+ def additional_definitions
24
+ @additional_definitions || {}
25
+ end
26
+
31
27
  def singular_instance_definition
32
28
  all_instance_definitions = instance_definitions
33
29
 
34
30
  if all_instance_definitions.many?
35
- raise Tdc::FatalError, "For the moment we only generate a single model instance"
31
+ raise Tdc::FatalError, "A singular generator only generates a single model instance"
36
32
  end
37
33
 
38
34
  # Delete the tag so that the models do not need to filter it out.
@@ -5,9 +5,7 @@ module Tdc
5
5
  #
6
6
  # See also SingularGenerator.
7
7
  #
8
- class StandardGenerator < Tdc::Generators::GeneratorBase
9
- include Tdc::Generators::InstanceDefinitionConfigurable
10
-
8
+ class StandardGenerator < Tdc::Generators::ConfigurableGenerator
11
9
  def generate
12
10
  CatalogEntries.new.tap do |catalog_entries|
13
11
  instance_definitions.each do |instance_definition|
@@ -1,7 +1,3 @@
1
1
  module Tdc
2
- class MissingOverrideError < Tdc::FatalError
3
- def initialize
4
- super("Must be implemented")
5
- end
6
- end
2
+ MissingOverrideError = Class.new(Tdc::FatalError)
7
3
  end
@@ -1,3 +1,3 @@
1
1
  module Tdc
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tdc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alistair McKinnell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-19 00:00:00.000000000 Z
11
+ date: 2020-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -113,6 +113,7 @@ executables: []
113
113
  extensions: []
114
114
  extra_rdoc_files: []
115
115
  files:
116
+ - ".editorconfig"
116
117
  - ".gitignore"
117
118
  - ".rspec"
118
119
  - ".rubocop.yml"
@@ -127,13 +128,16 @@ files:
127
128
  - lib/tdc.rb
128
129
  - lib/tdc/data_definition.rb
129
130
  - lib/tdc/data_definition_file_reader.rb
131
+ - lib/tdc/definition_resolvers.rb
132
+ - lib/tdc/definition_resolvers/definition_resolver.rb
133
+ - lib/tdc/definition_resolvers/tag_resolver.rb
130
134
  - lib/tdc/fatal_error.rb
131
135
  - lib/tdc/generators.rb
132
136
  - lib/tdc/generators/catalog_entries.rb
137
+ - lib/tdc/generators/configurable_generator.rb
133
138
  - lib/tdc/generators/definition_resolvable.rb
134
139
  - lib/tdc/generators/definition_sourcable.rb
135
140
  - lib/tdc/generators/generator_base.rb
136
- - lib/tdc/generators/instance_definition_configurable.rb
137
141
  - lib/tdc/generators/singular_generator.rb
138
142
  - lib/tdc/generators/standard_generator.rb
139
143
  - lib/tdc/in_memory_data_definition.rb
@@ -163,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
167
  - !ruby/object:Gem::Version
164
168
  version: '0'
165
169
  requirements: []
166
- rubygems_version: 3.1.3
170
+ rubygems_version: 3.1.4
167
171
  signing_key:
168
172
  specification_version: 4
169
173
  summary: A simple framework for creating a Test Data Catalog