fixtury 0.4.1 → 1.0.0.beta2
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 +4 -4
- data/.ruby-version +1 -1
- data/Gemfile.lock +48 -44
- data/README.md +4 -4
- data/fixtury.gemspec +6 -6
- data/lib/fixtury/configuration.rb +117 -0
- data/lib/fixtury/definition.rb +34 -38
- data/lib/fixtury/definition_executor.rb +23 -52
- data/lib/fixtury/dependency.rb +52 -0
- data/lib/fixtury/dependency_store.rb +45 -0
- data/lib/fixtury/errors.rb +81 -0
- data/lib/fixtury/hooks.rb +90 -0
- data/lib/fixtury/locator.rb +52 -23
- data/lib/fixtury/locator_backend/common.rb +17 -19
- data/lib/fixtury/locator_backend/global_id.rb +32 -0
- data/lib/fixtury/locator_backend/memory.rb +9 -8
- data/lib/fixtury/mutation_observer.rb +140 -0
- data/lib/fixtury/path_resolver.rb +45 -0
- data/lib/fixtury/railtie.rb +4 -0
- data/lib/fixtury/reference.rb +12 -10
- data/lib/fixtury/schema.rb +47 -225
- data/lib/fixtury/schema_node.rb +175 -0
- data/lib/fixtury/store.rb +168 -84
- data/lib/fixtury/test_hooks.rb +125 -101
- data/lib/fixtury/version.rb +1 -1
- data/lib/fixtury.rb +84 -12
- metadata +35 -35
- data/lib/fixtury/errors/already_defined_error.rb +0 -13
- data/lib/fixtury/errors/circular_dependency_error.rb +0 -13
- data/lib/fixtury/errors/fixture_not_defined_error.rb +0 -13
- data/lib/fixtury/errors/option_collision_error.rb +0 -13
- data/lib/fixtury/errors/schema_frozen_error.rb +0 -13
- data/lib/fixtury/errors/unrecognizable_locator_error.rb +0 -11
- data/lib/fixtury/locator_backend/globalid.rb +0 -32
- data/lib/fixtury/path.rb +0 -36
- data/lib/fixtury/tasks.rake +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd14efe323fc1de1a2ec63805508e7fb816db30f028998e951c78e95cbe14dfe
|
4
|
+
data.tar.gz: e0a1055bf1ebfff930569a5b678b1b433c21b48e2a4305197bdeb7938e22332a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d819ff4d3096eee7a2ac3c4617f89463b28b7826f3046efbf391408b595b8758f52ac556c8e337eae396109839a148a033750b2ae5ac10ebddce27a041344be3
|
7
|
+
data.tar.gz: febce1127eceb8f3ef6e148b7212591e657dab0d62f4b57d0ab6e8b6f8ba8d9c210180f6713fe78d5de6ce35356a5c448cfd9903d60a406efe5bc835c9cbbe11
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
3.2.2
|
data/Gemfile.lock
CHANGED
@@ -1,64 +1,68 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fixtury (0.
|
4
|
+
fixtury (1.0.0.beta2)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
|
9
|
+
activemodel (7.1.3.2)
|
10
|
+
activesupport (= 7.1.3.2)
|
11
|
+
activerecord (7.1.3.2)
|
12
|
+
activemodel (= 7.1.3.2)
|
13
|
+
activesupport (= 7.1.3.2)
|
14
|
+
timeout (>= 0.4.0)
|
15
|
+
activesupport (7.1.3.2)
|
16
|
+
base64
|
17
|
+
bigdecimal
|
10
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
byebug (11.
|
20
|
-
concurrent-ruby (1.
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
connection_pool (>= 2.2.5)
|
20
|
+
drb
|
21
|
+
i18n (>= 1.6, < 2)
|
22
|
+
minitest (>= 5.1)
|
23
|
+
mutex_m
|
24
|
+
tzinfo (~> 2.0)
|
25
|
+
base64 (0.2.0)
|
26
|
+
bigdecimal (3.1.6)
|
27
|
+
byebug (11.1.3)
|
28
|
+
concurrent-ruby (1.2.3)
|
29
|
+
connection_pool (2.4.1)
|
30
|
+
drb (2.2.1)
|
31
|
+
globalid (1.2.1)
|
32
|
+
activesupport (>= 6.1)
|
33
|
+
i18n (1.14.4)
|
34
|
+
concurrent-ruby (~> 1.0)
|
35
|
+
m (1.6.2)
|
36
|
+
method_source (>= 0.6.7)
|
37
|
+
rake (>= 0.9.2.2)
|
38
|
+
method_source (1.0.0)
|
39
|
+
mini_portile2 (2.8.5)
|
40
|
+
minitest (5.22.2)
|
41
|
+
mocha (2.1.0)
|
42
|
+
ruby2_keywords (>= 0.0.5)
|
43
|
+
mutex_m (0.2.0)
|
44
|
+
rake (13.1.0)
|
45
|
+
ruby2_keywords (0.0.5)
|
46
|
+
sqlite3 (1.7.2)
|
47
|
+
mini_portile2 (~> 2.8.0)
|
48
|
+
timeout (0.4.1)
|
49
|
+
tzinfo (2.0.6)
|
24
50
|
concurrent-ruby (~> 1.0)
|
25
|
-
metaclass (0.0.4)
|
26
|
-
minitest (5.13.0)
|
27
|
-
minitest-autotest (1.1.1)
|
28
|
-
minitest-server (~> 1.0)
|
29
|
-
path_expander (~> 1.0)
|
30
|
-
minitest-reporters (1.4.2)
|
31
|
-
ansi
|
32
|
-
builder
|
33
|
-
minitest (>= 5.0)
|
34
|
-
ruby-progressbar
|
35
|
-
minitest-server (1.0.5)
|
36
|
-
minitest (~> 5.0)
|
37
|
-
mocha (1.8.0)
|
38
|
-
metaclass (~> 0.0.1)
|
39
|
-
path_expander (1.1.0)
|
40
|
-
rake (13.0.1)
|
41
|
-
ruby-progressbar (1.10.1)
|
42
|
-
sqlite (1.0.2)
|
43
|
-
thread_safe (0.3.6)
|
44
|
-
tzinfo (1.2.7)
|
45
|
-
thread_safe (~> 0.1)
|
46
|
-
zeitwerk (2.3.0)
|
47
51
|
|
48
52
|
PLATFORMS
|
49
53
|
ruby
|
50
54
|
|
51
55
|
DEPENDENCIES
|
52
|
-
|
53
|
-
bundler
|
56
|
+
activerecord
|
57
|
+
bundler
|
54
58
|
byebug
|
55
59
|
fixtury!
|
56
60
|
globalid
|
57
|
-
|
58
|
-
minitest
|
61
|
+
m
|
62
|
+
minitest
|
59
63
|
mocha
|
60
|
-
rake
|
61
|
-
|
64
|
+
rake
|
65
|
+
sqlite3
|
62
66
|
|
63
67
|
BUNDLED WITH
|
64
|
-
2.
|
68
|
+
2.5.6
|
data/README.md
CHANGED
@@ -8,10 +8,10 @@ For example, if a developer is running a test locally in their development envir
|
|
8
8
|
|
9
9
|
```ruby
|
10
10
|
class MyTest < ::ActiveSupport::TestCase
|
11
|
-
|
11
|
+
prepend ::Fixtury::TestHooks
|
12
12
|
|
13
|
-
fixtury "users
|
14
|
-
let(:user) { fixtury("users
|
13
|
+
fixtury "users/fresh"
|
14
|
+
let(:user) { fixtury("users/fresh") }
|
15
15
|
|
16
16
|
def test_whatever
|
17
17
|
assert_eq "Doug", user.first_name
|
@@ -20,6 +20,6 @@ class MyTest < ::ActiveSupport::TestCase
|
|
20
20
|
end
|
21
21
|
```
|
22
22
|
|
23
|
-
Loading this file would ensure `users
|
23
|
+
Loading this file would ensure `users/fresh` is loaded into the fixture set before the suite is run. In the context of ActiveSupport::TestCase, the Fixtury::Hooks file will ensure the database records are present prior to your suite running. Setting `use_transactional_fixtures` ensures all records are rolled back prior to running another test.
|
24
24
|
|
25
25
|
In a CI environment, we'd likely want to preload all fixtures. This can be done by requiring all the test files, then telling the fixtury store to load all definitions.
|
data/fixtury.gemspec
CHANGED
@@ -27,13 +27,13 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
29
|
|
30
|
-
spec.add_development_dependency "
|
31
|
-
spec.add_development_dependency "bundler", "~> 2.0"
|
30
|
+
spec.add_development_dependency "bundler"
|
32
31
|
spec.add_development_dependency "byebug"
|
33
32
|
spec.add_development_dependency "globalid"
|
34
|
-
spec.add_development_dependency "
|
35
|
-
spec.add_development_dependency "
|
33
|
+
spec.add_development_dependency "activerecord"
|
34
|
+
spec.add_development_dependency "m"
|
35
|
+
spec.add_development_dependency "minitest"
|
36
36
|
spec.add_development_dependency "mocha"
|
37
|
-
spec.add_development_dependency "rake"
|
38
|
-
spec.add_development_dependency "
|
37
|
+
spec.add_development_dependency "rake"
|
38
|
+
spec.add_development_dependency "sqlite3"
|
39
39
|
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
module Fixtury
|
4
|
+
# Provides an interface for managing settings and dependencies related to fixture
|
5
|
+
# generation.
|
6
|
+
class Configuration
|
7
|
+
|
8
|
+
attr_reader :filepath, :fixture_files, :dependency_files
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@filepath = nil
|
12
|
+
@fixture_files = Set.new
|
13
|
+
@dependency_files = Set.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_level
|
17
|
+
return @log_level if @log_level
|
18
|
+
|
19
|
+
@log_level = ENV["FIXTURY_LOG_LEVEL"]
|
20
|
+
@log_level ||= DEFAULT_LOG_LEVEL
|
21
|
+
@log_level = @log_level.to_s.to_sym
|
22
|
+
@log_level
|
23
|
+
end
|
24
|
+
|
25
|
+
# Delete the storage file if it exists.
|
26
|
+
def reset
|
27
|
+
File.delete(filepath) if filepath && File.file?(filepath)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Set the location of the storage file. The storage file will maintain
|
31
|
+
# checksums of all tracked files and serialized references to fixtures.
|
32
|
+
#
|
33
|
+
# @param path [String] The path to the storage file.
|
34
|
+
def filepath=(path)
|
35
|
+
@filepath = path.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add a file or glob pattern to the list of fixture files.
|
39
|
+
#
|
40
|
+
# @param path_or_globs [String, Array<String>] The file or glob pattern(s) to add.
|
41
|
+
def add_fixture_path(*path_or_globs)
|
42
|
+
@fixture_files = fixture_files | Dir[*path_or_globs]
|
43
|
+
end
|
44
|
+
alias add_fixture_paths add_fixture_path
|
45
|
+
|
46
|
+
# Add a file or glob pattern to the list of dependency files.
|
47
|
+
#
|
48
|
+
# @param path_or_globs [String, Array<String>] The file or glob pattern(s) to add.
|
49
|
+
def add_dependency_path(*path_or_globs)
|
50
|
+
@dependency_files = dependency_files | Dir[*path_or_globs]
|
51
|
+
end
|
52
|
+
alias add_dependency_paths add_dependency_path
|
53
|
+
|
54
|
+
# The references stored in the dependency file. When stores are initialized
|
55
|
+
# these will be used to bootstrap the references.
|
56
|
+
#
|
57
|
+
# @return [Hash] The references stored in the dependency file.
|
58
|
+
def stored_references
|
59
|
+
return {} if stored_data.nil?
|
60
|
+
|
61
|
+
stored_data[:references] || {}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Dump the current state of the dependency manager to the storage file.
|
65
|
+
def dump_file
|
66
|
+
return unless filepath
|
67
|
+
|
68
|
+
FileUtils.mkdir_p(File.dirname(filepath))
|
69
|
+
File.binwrite(filepath, file_data.to_yaml)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def file_data
|
75
|
+
checksums = {}
|
76
|
+
calculate_checksums do |filepath, checksum|
|
77
|
+
checksums[filepath] = checksum
|
78
|
+
end
|
79
|
+
|
80
|
+
{
|
81
|
+
dependencies: checksums,
|
82
|
+
references: ::Fixtury.store.references,
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def stored_data
|
87
|
+
return nil unless filepath
|
88
|
+
return nil unless File.file?(filepath)
|
89
|
+
|
90
|
+
YAML.unsafe_load_file(filepath)
|
91
|
+
end
|
92
|
+
|
93
|
+
def files_changed?
|
94
|
+
return true if stored_data.nil?
|
95
|
+
|
96
|
+
stored_checksums = (stored_data[:dependencies] || {})
|
97
|
+
seen_filepaths = []
|
98
|
+
calculate_checksums do |filepath, checksum|
|
99
|
+
# Early return if the checksums don't match
|
100
|
+
return true unless stored_checksums[filepath] == checksum
|
101
|
+
|
102
|
+
seen_filepaths << filepath
|
103
|
+
end
|
104
|
+
|
105
|
+
# If we have a new file or a file has been removed, we need to report a change.
|
106
|
+
seen_filepaths.sort != stored_checksums.keys.sort
|
107
|
+
end
|
108
|
+
|
109
|
+
def calculate_checksums(&block)
|
110
|
+
(fixture_files.to_a | dependency_files.to_a).sort.each do |filepath|
|
111
|
+
yield filepath, Digest::MD5.file(filepath).hexdigest
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
data/lib/fixtury/definition.rb
CHANGED
@@ -1,52 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "fixtury/definition_executor"
|
4
|
-
|
5
3
|
module Fixtury
|
4
|
+
# A class that contains the definition of a fixture. It also maintains a list of it's
|
5
|
+
# dependencies to allow for analysis of the fixture graph.
|
6
6
|
class Definition
|
7
|
+
include ::Fixtury::SchemaNode
|
8
|
+
extend ::Forwardable
|
9
|
+
|
10
|
+
# Initializes a new Definition object.
|
11
|
+
#
|
12
|
+
# @param deps [Array] An array of dependencies.
|
13
|
+
# @param opts [Hash] Additional options for the Definition.
|
14
|
+
# @param block [Proc] A block of code to be executed.
|
15
|
+
def initialize(deps: [], **opts, &block)
|
16
|
+
super(**opts)
|
17
|
+
|
18
|
+
@dependencies = Array(deps).each_with_object({}) do |d, deps|
|
19
|
+
parsed_deps = Dependency.from(parent, d)
|
20
|
+
parsed_deps.each do |dep|
|
21
|
+
existing = deps[dep.accessor]
|
22
|
+
raise ArgumentError, "Accessor #{dep.accessor} is already declared by #{existing.search}" if existing
|
23
|
+
|
24
|
+
deps[dep.accessor] = dep
|
25
|
+
end
|
26
|
+
end
|
7
27
|
|
8
|
-
attr_reader :name
|
9
|
-
attr_reader :schema
|
10
|
-
alias parent schema
|
11
|
-
attr_reader :options
|
12
|
-
|
13
|
-
attr_reader :callable
|
14
|
-
attr_reader :enhancements
|
15
|
-
|
16
|
-
def initialize(schema: nil, name:, options: {}, &block)
|
17
|
-
@name = name
|
18
|
-
@schema = schema
|
19
28
|
@callable = block
|
20
|
-
@options = options
|
21
|
-
@enhancements = []
|
22
|
-
end
|
23
|
-
|
24
|
-
def enhance(&block)
|
25
|
-
@enhancements << block
|
26
29
|
end
|
27
30
|
|
28
|
-
|
29
|
-
|
31
|
+
# Indicates whether the Definition acts like a Fixtury definition.
|
32
|
+
#
|
33
|
+
# @return [Boolean] `true` if it acts like a Fixtury definition, `false` otherwise.
|
34
|
+
def acts_like_fixtury_definition?
|
35
|
+
true
|
30
36
|
end
|
31
37
|
|
32
|
-
|
33
|
-
|
34
|
-
name: name,
|
35
|
-
loc: location_from_callable(callable),
|
36
|
-
enhancements: enhancements.map { |e| location_from_callable(e) },
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
def call(store: nil, execution_context: nil)
|
41
|
-
executor = ::Fixtury::DefinitionExecutor.new(store: store, definition: self, execution_context: execution_context)
|
42
|
-
executor.__call
|
43
|
-
end
|
38
|
+
# Delegates the `call` method to the `callable` object.
|
39
|
+
def_delegator :callable, :call
|
44
40
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
41
|
+
# Returns the parent schema of the Definition.
|
42
|
+
#
|
43
|
+
# @return [Object] The parent schema.
|
44
|
+
alias schema parent
|
50
45
|
|
46
|
+
attr_reader :callable, :dependencies
|
51
47
|
end
|
52
48
|
end
|
@@ -1,77 +1,48 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Fixtury
|
4
|
+
# A container that manages the execution of a definition in the context of a store.
|
4
5
|
class DefinitionExecutor
|
5
6
|
|
6
|
-
attr_reader :value, :
|
7
|
+
attr_reader :value, :definition, :store
|
7
8
|
|
8
|
-
def initialize(store: nil,
|
9
|
+
def initialize(store: nil, definition:)
|
9
10
|
@store = store
|
10
11
|
@definition = definition
|
11
|
-
@execution_context = execution_context
|
12
|
-
@execution_type = nil
|
13
12
|
@value = nil
|
14
13
|
end
|
15
14
|
|
16
|
-
def
|
17
|
-
|
18
|
-
provide_schema_hooks do
|
19
|
-
run_callable(callable: definition.callable, type: :definition)
|
20
|
-
definition.enhancements.each do |e|
|
21
|
-
run_callable(callable: e, type: :enhancement)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
15
|
+
def call
|
16
|
+
run_definition
|
26
17
|
value
|
27
18
|
end
|
28
19
|
|
29
|
-
def get(name)
|
30
|
-
raise ArgumentError, "A store is required for #{definition.name}" unless store
|
31
|
-
|
32
|
-
store.get(name, execution_context: execution_context)
|
33
|
-
end
|
34
|
-
alias [] get
|
35
|
-
|
36
|
-
def method_missing(method_name, *args, &block)
|
37
|
-
return super unless execution_context
|
38
|
-
|
39
|
-
execution_context.send(method_name, *args, &block)
|
40
|
-
end
|
41
|
-
|
42
|
-
def respond_to_missing?(method_name)
|
43
|
-
return super unless execution_context
|
44
|
-
|
45
|
-
execution_context.respond_to?(method_name, true)
|
46
|
-
end
|
47
|
-
|
48
20
|
private
|
49
21
|
|
50
|
-
|
51
|
-
|
22
|
+
# If the callable has a positive arity we generate a DependencyStore
|
23
|
+
# and yield it to the callable. Otherwise we just instance_eval the callable.
|
24
|
+
# We wrap the actual execution of the definition with a hook for observation.
|
25
|
+
def run_definition
|
26
|
+
callable = definition.callable
|
52
27
|
|
53
28
|
@value = if callable.arity.positive?
|
54
|
-
|
29
|
+
deps = build_dependency_store
|
30
|
+
::Fixtury.hooks.call(:execution, self) do
|
31
|
+
instance_exec(deps, &callable)
|
32
|
+
end
|
55
33
|
else
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
def maybe_set_store_context
|
61
|
-
return yield unless store
|
62
|
-
|
63
|
-
store.with_relative_schema(definition.schema) do
|
64
|
-
yield
|
34
|
+
::Fixtury.hooks.call(:execution, self) do
|
35
|
+
instance_eval(&callable)
|
36
|
+
end
|
65
37
|
end
|
38
|
+
rescue Errors::Base
|
39
|
+
raise
|
40
|
+
rescue => e
|
41
|
+
raise Errors::DefinitionExecutionError.new(definition.pathname, e)
|
66
42
|
end
|
67
43
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
@value = definition.schema.around_fixture_hook(self) do
|
72
|
-
yield
|
73
|
-
value
|
74
|
-
end
|
44
|
+
def build_dependency_store
|
45
|
+
DependencyStore.new(definition: definition, store: store)
|
75
46
|
end
|
76
47
|
|
77
48
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Fixtury
|
2
|
+
class Dependency
|
3
|
+
|
4
|
+
# Resolve a Dependency from a multitude of input types
|
5
|
+
# @param parent [Fixtury::Definition] the parent definition
|
6
|
+
# @param thing [Fixtury::Dependency, Hash, Array, String, Symbol] the thing to resolve
|
7
|
+
# @option thing [Fixtury::Dependency] a dependency will be cloned.
|
8
|
+
# @option thing [Hash] a hash with exactly one key will be resolved as { accessor => search }.
|
9
|
+
# @option thing [Array] an array with two elements will be resolved as [ accessor, search ].
|
10
|
+
# @option thing [String, Symbol] a string or symbol will be resolved as both the accessor and the search.
|
11
|
+
# @return [Array<Fixtury::Dependency>] the resolved dependency
|
12
|
+
def self.from(parent, thing)
|
13
|
+
out = case thing
|
14
|
+
when self
|
15
|
+
Dependency.new(parent: parent, search: thing.search, accessor: thing.accessor)
|
16
|
+
when Hash
|
17
|
+
thing.each_with_object([]) do |(k, v), arr|
|
18
|
+
arr << Dependency.new(parent: parent, search: v, accessor: k)
|
19
|
+
end
|
20
|
+
when Array
|
21
|
+
raise ArgumentError, "Array must have an even number of elements" unless thing.size % 2 == 0
|
22
|
+
|
23
|
+
thing.each_slice(2).map do |pair|
|
24
|
+
Dependency.new(parent: parent, search: pair[1], accessor: pair[0])
|
25
|
+
end
|
26
|
+
when String, Symbol
|
27
|
+
Dependency.new(parent: parent, search: thing, accessor: thing)
|
28
|
+
else
|
29
|
+
raise ArgumentError, "Unknown dependency type: #{thing.inspect}"
|
30
|
+
end
|
31
|
+
|
32
|
+
Array(out)
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :parent, :search, :accessor
|
36
|
+
|
37
|
+
def initialize(parent:, search:, accessor:)
|
38
|
+
@parent = parent
|
39
|
+
@search = search.to_s
|
40
|
+
@accessor = accessor.to_s.split("/").last
|
41
|
+
end
|
42
|
+
|
43
|
+
def definition
|
44
|
+
@definition ||= parent&.get!(search)
|
45
|
+
end
|
46
|
+
|
47
|
+
def inspect
|
48
|
+
"#{self.class}(accessor: #{accessor.inspect}, search: #{search.inspect}, parent: #{parent.name.inspect})"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Fixtury
|
2
|
+
# An object which allows access to a specific subset of fixtures
|
3
|
+
# in the context of a definition's dependencies.
|
4
|
+
class DependencyStore
|
5
|
+
|
6
|
+
attr_reader :definition, :store
|
7
|
+
|
8
|
+
def initialize(definition:, store:)
|
9
|
+
@definition = definition
|
10
|
+
@store = store
|
11
|
+
end
|
12
|
+
|
13
|
+
def inspect
|
14
|
+
"#{self.class}(definition: #{definition.pathname.inspect}, dependencies: #{definition.dependencies.keys.inspect})"
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the value of the dependency with the given key
|
18
|
+
#
|
19
|
+
# @param key [String, Symbol] the accessor of the dependency
|
20
|
+
# @return [Object] the value of the dependency
|
21
|
+
# @raise [Fixtury::Errors::UnknownDependencyError] if the definition does not contain the provided dependency
|
22
|
+
def get(key)
|
23
|
+
dep = definition.dependencies.fetch(key.to_s) do
|
24
|
+
raise Errors::UnknownDependencyError.new(definition, key)
|
25
|
+
end
|
26
|
+
store.get(dep.definition.pathname)
|
27
|
+
end
|
28
|
+
alias [] get
|
29
|
+
|
30
|
+
# If an accessor is used and we recognize the accessor as a dependency
|
31
|
+
# of our definition, we return the value of the dependency.
|
32
|
+
def method_missing(method, *args, &block)
|
33
|
+
if definition.dependencies.key?(method.to_s)
|
34
|
+
get(method)
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def respond_to_missing?(method, include_private = false)
|
41
|
+
definition.dependencies.key?(method.to_s) || super
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fixtury
|
4
|
+
module Errors
|
5
|
+
|
6
|
+
class Base < StandardError
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class AlreadyDefinedError < Base
|
11
|
+
|
12
|
+
def initialize(name)
|
13
|
+
super("An element identified by #{name.inspect} already exists.")
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class CircularDependencyError < Base
|
19
|
+
|
20
|
+
def initialize(name)
|
21
|
+
super("One of the dependencies of #{name.inspect} is dependent on #{name.inspect}.")
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
class DefinitionExecutionError < Base
|
27
|
+
|
28
|
+
def initialize(pathname, error)
|
29
|
+
super("Error while building #{pathname.inspect}: #{error}")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class SchemaNodeNotDefinedError < Base
|
35
|
+
|
36
|
+
def initialize(pathname, search)
|
37
|
+
super("A schema node identified by #{search.inspect} could not be found from #{pathname.inspect}.")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class SchemaNodeNameInvalidError < Base
|
43
|
+
def initialize(parent_name, child_name)
|
44
|
+
super("The schema node name #{child_name.inspect} must start with #{parent_name.inspect} to be added to it.")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class OptionCollisionError < Base
|
49
|
+
|
50
|
+
def initialize(schema_name, option_key, old_value, new_value)
|
51
|
+
super("The #{schema_name.inspect} schema #{option_key.inspect} option value of #{old_value.inspect} conflicts with the new value #{new_value.inspect}.")
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class UnrecognizableLocatorError < Base
|
57
|
+
|
58
|
+
def initialize(action, thing)
|
59
|
+
super("Locator did not recognize #{thing} during #{action}")
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
class IsolatedMutationError < Base
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
class UnknownTestDependencyError < Base
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class UnknownDependencyError < Base
|
73
|
+
|
74
|
+
def initialize(defn, key)
|
75
|
+
super("#{defn.pathname} does not contain the provided dependency: #{key}")
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|