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
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fixtury
|
4
|
+
# Provides a mechanism for observing Fixtury lifecycle events.
|
5
|
+
class Hooks
|
6
|
+
|
7
|
+
attr_reader :hooks
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@hooks = Hash.new { |h, k| h[k] = { before: [], after: [], around: [], on: [] } }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Register a hook to be called around the execution of a trigger.
|
14
|
+
# The around hook should ensure the return value is preserved.
|
15
|
+
# This also means that the hook itself could modify the return value.
|
16
|
+
#
|
17
|
+
# @param trigger_type [Symbol] the type of trigger to hook into
|
18
|
+
# @param hook [Proc] the hook to be called
|
19
|
+
def around(trigger_type, &hook)
|
20
|
+
register_hook(trigger_type, :around, hook)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Register a hook to be called before the execution of a trigger.
|
24
|
+
# (see #register_hook)
|
25
|
+
def before(trigger_type, &hook)
|
26
|
+
register_hook(trigger_type, :before, hook)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Register a hook to be called after the execution of a trigger.
|
30
|
+
# The return value will be provided as the first argument to the hook.
|
31
|
+
# (see #register_hook)
|
32
|
+
def after(trigger_type, &hook)
|
33
|
+
register_hook(trigger_type, :after, hook)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Similar to after, but the return value is not injected.
|
37
|
+
# (see #register_hook)
|
38
|
+
def on(trigger_type, &hook)
|
39
|
+
register_hook(trigger_type, :on, hook)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Trigger the hooks registered for a specific trigger type.
|
43
|
+
# :before hooks will be triggered first, followed by :around hooks,
|
44
|
+
# :on hooks, and finally :after hooks.
|
45
|
+
#
|
46
|
+
# @param trigger_type [Symbol] the type of trigger to initiate
|
47
|
+
# @param args [Array] arguments to be passed to the hooks
|
48
|
+
# @param block [Proc] a block of code to be executed
|
49
|
+
# @return [Object] the return value of the block
|
50
|
+
def call(trigger_type, *args, &block)
|
51
|
+
hook_lists = hooks[trigger_type.to_sym]
|
52
|
+
|
53
|
+
call_inline_hooks(hook_lists[:before], *args)
|
54
|
+
return_value = call_around_hooks(hook_lists[:around], 0, block, *args)
|
55
|
+
call_inline_hooks(hook_lists[:on], *args)
|
56
|
+
call_inline_hooks(hook_lists[:after], return_value, *args)
|
57
|
+
|
58
|
+
return_value
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Register a hook to be called for a specific trigger type and hook type.
|
64
|
+
#
|
65
|
+
# @param trigger_type [Symbol] the type of trigger to hook into
|
66
|
+
# @param hook_type [Symbol] the point in the trigger to hook into
|
67
|
+
# @param hook [Proc] the hook to be called
|
68
|
+
# @return [Fixtury::Hooks] the current instance
|
69
|
+
def register_hook(trigger_type, hook_type, hook)
|
70
|
+
hooks[trigger_type.to_sym][hook_type.to_sym] << hook
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def call_around_hooks(hook_list, idx, block, *args)
|
75
|
+
if idx >= hook_list.length
|
76
|
+
block.call
|
77
|
+
else
|
78
|
+
hook_list[idx].call(*args) do
|
79
|
+
call_around_hooks(hook_list, idx + 1, block, *args)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def call_inline_hooks(hook_list, *args)
|
85
|
+
hook_list.each do |hook|
|
86
|
+
hook.call(*args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/fixtury/locator.rb
CHANGED
@@ -1,48 +1,77 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "fixtury/locator_backend/memory"
|
4
|
+
|
3
5
|
module Fixtury
|
6
|
+
# Locator is responsible for recognizing, loading, and dumping references.
|
7
|
+
# It is a simple wrapper around a backend that is responsible for the actual work.
|
8
|
+
# The backend is expected to implement the following methods: recognizable_key?, recognized_value?, load_recognized_reference, dump_recognized_value.
|
4
9
|
class Locator
|
5
10
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
def self.from(thing)
|
12
|
+
case thing
|
13
|
+
when ::Fixtury::Locator
|
14
|
+
thing
|
15
|
+
when nil
|
16
|
+
::Fixtury::Locator.new
|
17
|
+
when Symbol
|
18
|
+
begin
|
19
|
+
require "fixtury/locator_backend/#{thing}"
|
20
|
+
rescue LoadError
|
16
21
|
end
|
22
|
+
backend = ::Fixtury::LocatorBackend.const_get(thing.to_s.camelize, false).new
|
23
|
+
::Fixtury::Locator.new(backend: backend)
|
24
|
+
else
|
25
|
+
raise ArgumentError, "Unable to create a locator from #{thing.inspect}"
|
17
26
|
end
|
18
|
-
|
19
27
|
end
|
20
28
|
|
21
29
|
attr_reader :backend
|
22
30
|
|
23
|
-
def initialize(backend:)
|
31
|
+
def initialize(backend: ::Fixtury::LocatorBackend::Memory.new)
|
24
32
|
@backend = backend
|
25
33
|
end
|
26
34
|
|
27
|
-
def
|
28
|
-
|
35
|
+
def inspect
|
36
|
+
"#{self.class}(backend: #{backend.class})"
|
37
|
+
end
|
38
|
+
|
39
|
+
# Determine if the provided locator_key is a valid form recognized by the backend.
|
40
|
+
#
|
41
|
+
# @param locator_key [Object] the locator key to check
|
42
|
+
# @return [Boolean] true if the locator key is recognizable by the backend
|
43
|
+
# @raise [ArgumentError] if the locator key is nil
|
44
|
+
def recognizable_key?(locator_key)
|
45
|
+
raise ArgumentError, "Unable to recognize a nil locator value" if locator_key.nil?
|
29
46
|
|
30
|
-
backend.
|
47
|
+
backend.recognizable_key?(locator_key)
|
31
48
|
end
|
32
49
|
|
33
|
-
|
34
|
-
|
50
|
+
# Load the value associated with the provided locator key.
|
51
|
+
#
|
52
|
+
# @param locator_key [Object] the locator key to load
|
53
|
+
# @return [Object] the loaded value
|
54
|
+
# @raise [ArgumentError] if the locator key is nil
|
55
|
+
def load(locator_key)
|
56
|
+
raise ArgumentError, "Unable to load a nil locator value" if locator_key.nil?
|
35
57
|
|
36
|
-
backend.load(
|
58
|
+
backend.load(locator_key)
|
37
59
|
end
|
38
60
|
|
39
|
-
|
40
|
-
|
61
|
+
# Provide the value to the backend to generate a locator key.
|
62
|
+
#
|
63
|
+
# @param stored_value [Object] the value to dump
|
64
|
+
# @param context [String] a string to include in the error message if the value is nil
|
65
|
+
# @return [Object] the locator key
|
66
|
+
# @raise [ArgumentError] if the value is nil
|
67
|
+
# @raise [ArgumentError] if the backend is unable to dump the value
|
68
|
+
def dump(stored_value, context: nil)
|
69
|
+
raise ArgumentError, "Unable to dump a nil value. #{context}" if stored_value.nil?
|
41
70
|
|
42
|
-
|
43
|
-
raise ArgumentError, "
|
71
|
+
locator_key = backend.dump(stored_value)
|
72
|
+
raise ArgumentError, "Dump resulted in a nil locator value. #{context}" if locator_key.nil?
|
44
73
|
|
45
|
-
|
74
|
+
locator_key
|
46
75
|
end
|
47
76
|
|
48
77
|
end
|
@@ -1,54 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "fixtury/errors/unrecognizable_locator_error"
|
4
|
-
|
5
3
|
module Fixtury
|
6
4
|
module LocatorBackend
|
7
5
|
module Common
|
8
6
|
|
9
|
-
def
|
7
|
+
def recognizable_key?(_locator_value)
|
10
8
|
raise NotImplementedError
|
11
9
|
end
|
12
10
|
|
13
|
-
def
|
11
|
+
def recognizable_value?(_stored_value)
|
14
12
|
raise NotImplementedError
|
15
13
|
end
|
16
14
|
|
17
|
-
def
|
15
|
+
def load_reference(_locator_value)
|
18
16
|
raise NotImplementedError
|
19
17
|
end
|
20
18
|
|
21
|
-
def
|
19
|
+
def dump_value(_stored_value)
|
22
20
|
raise NotImplementedError
|
23
21
|
end
|
24
22
|
|
25
|
-
def load(
|
26
|
-
return
|
23
|
+
def load(locator_value)
|
24
|
+
return load_reference(locator_value) if recognizable_key?(locator_value)
|
27
25
|
|
28
|
-
case
|
26
|
+
case locator_value
|
29
27
|
when Array
|
30
|
-
|
28
|
+
locator_value.map { |subvalue| self.load(subvalue) }
|
31
29
|
when Hash
|
32
|
-
|
33
|
-
h[k] = self.load(
|
30
|
+
locator_value.each_with_object({}) do |(k, subvalue), h|
|
31
|
+
h[k] = self.load(subvalue)
|
34
32
|
end
|
35
33
|
else
|
36
|
-
raise
|
34
|
+
raise Errors::UnrecognizableLocatorError.new(:load, locator_value)
|
37
35
|
end
|
38
36
|
end
|
39
37
|
|
40
|
-
def dump(
|
41
|
-
return
|
38
|
+
def dump(stored_value)
|
39
|
+
return dump_value(stored_value) if recognizable_value?(stored_value)
|
42
40
|
|
43
|
-
case
|
41
|
+
case stored_value
|
44
42
|
when Array
|
45
|
-
|
43
|
+
stored_value.map { |subvalue| dump(subvalue) }
|
46
44
|
when Hash
|
47
|
-
|
45
|
+
stored_value.each_with_object({}) do |(k, subvalue), h|
|
48
46
|
h[k] = dump(subvalue)
|
49
47
|
end
|
50
48
|
else
|
51
|
-
raise
|
49
|
+
raise Errors::UnrecognizableLocatorError.new(:dump, stored_value)
|
52
50
|
end
|
53
51
|
end
|
54
52
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./common"
|
4
|
+
require "globalid"
|
5
|
+
|
6
|
+
module Fixtury
|
7
|
+
module LocatorBackend
|
8
|
+
class GlobalId
|
9
|
+
|
10
|
+
include ::Fixtury::LocatorBackend::Common
|
11
|
+
|
12
|
+
MATCHER = %r{^gid://}.freeze
|
13
|
+
|
14
|
+
def recognizable_key?(locator_value)
|
15
|
+
locator_value.is_a?(String) && MATCHER.match?(locator_value)
|
16
|
+
end
|
17
|
+
|
18
|
+
def recognizable_value?(stored_value)
|
19
|
+
stored_value.respond_to?(:to_global_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_reference(locator_value)
|
23
|
+
::GlobalID::Locator.locate locator_value
|
24
|
+
end
|
25
|
+
|
26
|
+
def dump_value(stored_value)
|
27
|
+
stored_value.to_global_id.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -8,27 +8,28 @@ module Fixtury
|
|
8
8
|
|
9
9
|
include ::Fixtury::LocatorBackend::Common
|
10
10
|
|
11
|
-
MATCHER = /^fixtury-oid-(?<object_id>[\d]+)$/.freeze
|
11
|
+
MATCHER = /^fixtury-oid-(?<process_id>[\d]+)-(?<object_id>[\d]+)$/.freeze
|
12
12
|
|
13
|
-
def
|
14
|
-
|
13
|
+
def recognizable_key?(locator_value)
|
14
|
+
locator_value.is_a?(String) && MATCHER.match?(locator_value)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def recognizable_value?(_stored_value)
|
18
18
|
true
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
match = MATCHER.match(
|
21
|
+
def load_reference(locator_value)
|
22
|
+
match = MATCHER.match(locator_value)
|
23
23
|
return nil unless match
|
24
|
+
return nil unless match[:process_id].to_i == Process.pid
|
24
25
|
|
25
26
|
::ObjectSpace._id2ref(match[:object_id].to_i)
|
26
27
|
rescue RangeError
|
27
28
|
nil
|
28
29
|
end
|
29
30
|
|
30
|
-
def
|
31
|
-
"fixtury-oid-#{
|
31
|
+
def dump_value(stored_value)
|
32
|
+
"fixtury-oid-#{Process.pid}-#{stored_value.object_id}"
|
32
33
|
end
|
33
34
|
|
34
35
|
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/lazy_load_hooks"
|
4
|
+
|
5
|
+
module Fixtury
|
6
|
+
# The mutation observer class is responsible for tracking the isolation level of resources as they are created and updated.
|
7
|
+
# If a resource is created in one isolation level, but updated in another, the mutation observer will raise an error.
|
8
|
+
# If Rails is present, the Railtie will hook into ActiveRecord to automatically report these changes to the MutationObserver
|
9
|
+
module MutationObserver
|
10
|
+
|
11
|
+
# Hooks into the lifecycle of an ActiveRecord::Base object to report changes to the MutationObserver.
|
12
|
+
# This is automatically prepended to ActiveRecord::Base when Rails is present.
|
13
|
+
module ActiveRecordHooks
|
14
|
+
|
15
|
+
def _create_record(*args)
|
16
|
+
result = super
|
17
|
+
MutationObserver.on_record_create(self)
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def _update_record(**args)
|
22
|
+
MutationObserver.on_record_update(self, changes)
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_columns(changes)
|
27
|
+
MutationObserver.on_record_update(self, changes)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
|
35
|
+
attr_reader :current_execution
|
36
|
+
|
37
|
+
def log(msg, level: ::Fixtury::LOG_LEVEL_DEBUG)
|
38
|
+
::Fixtury.log(msg, name: "mutation_observer", level: level)
|
39
|
+
end
|
40
|
+
|
41
|
+
def owners
|
42
|
+
@owners ||= {}
|
43
|
+
end
|
44
|
+
|
45
|
+
def reported_owner(locator_key)
|
46
|
+
owners[locator_key]
|
47
|
+
end
|
48
|
+
|
49
|
+
# Observe mutation activity while the given block is executed.
|
50
|
+
#
|
51
|
+
# @param execution [Fixtury::Execution] The execution that is currently being observed.
|
52
|
+
# @yield [void] The block to execute while observing the given execution.
|
53
|
+
def observe(execution)
|
54
|
+
prev_execution = current_execution
|
55
|
+
@current_execution = execution
|
56
|
+
yield
|
57
|
+
ensure
|
58
|
+
@current_execution = prev_execution
|
59
|
+
end
|
60
|
+
|
61
|
+
# The isolation key of the current definition associated with the current execution.
|
62
|
+
#
|
63
|
+
# @return [String, nil] The isolation key of the current definition, or nil if there is no current definition.
|
64
|
+
def current_isolation_key
|
65
|
+
current_definition&.isolation_key
|
66
|
+
end
|
67
|
+
|
68
|
+
# The definition associated with the current execution.
|
69
|
+
#
|
70
|
+
# @return [Fixtury::Definition, nil] The definition associated with the current execution, or nil if there is no current execution.
|
71
|
+
def current_definition
|
72
|
+
current_execution&.definition
|
73
|
+
end
|
74
|
+
|
75
|
+
# Since there may be inheritance at play, we use the base class to consolidate
|
76
|
+
# ensure the same db record always produces the same locator key by using the
|
77
|
+
# base class to generate the locator key.
|
78
|
+
#
|
79
|
+
# @param obj [ActiveRecord::Base] The object to generate a locator key for.
|
80
|
+
# @return [String, nil] The locator key for the given object, or nil if there is no current execution.
|
81
|
+
def normalized_locator_key(obj)
|
82
|
+
return nil unless current_execution
|
83
|
+
|
84
|
+
pk = obj.class.primary_key
|
85
|
+
delegate_object = obj.class.base_class.new(pk => obj.read_attribute(pk))
|
86
|
+
current_execution.store.locator.dump(delegate_object, context: "<mutation_observer>")
|
87
|
+
end
|
88
|
+
|
89
|
+
# When a record is created we assign ownership to the current isolation key, if present.
|
90
|
+
#
|
91
|
+
# @param obj [ActiveRecord::Base] The record that was created.
|
92
|
+
# @return [void]
|
93
|
+
def on_record_create(obj)
|
94
|
+
locator_key = normalized_locator_key(obj)
|
95
|
+
return unless locator_key
|
96
|
+
|
97
|
+
log("Setting isolation level of #{locator_key.inspect} to #{current_isolation_key.inspect} via #{current_definition.inspect}")
|
98
|
+
owners[locator_key] = current_isolation_key
|
99
|
+
end
|
100
|
+
|
101
|
+
# When a record is updated we check to see if the reported owner matches the current isolation key.
|
102
|
+
# If it doesn't, we raise an error.
|
103
|
+
#
|
104
|
+
# @param obj [ActiveRecord::Base] The record that was updated.
|
105
|
+
# @param changes [Hash] The changes that were made to the record.
|
106
|
+
# @return [void]
|
107
|
+
# @raise [Fixtury::Errors::IsolatedMutationError] if the record is updated in a different isolation level than it was created in.
|
108
|
+
def on_record_update(obj, changes)
|
109
|
+
return if changes.blank?
|
110
|
+
|
111
|
+
locator_key = normalized_locator_key(obj)
|
112
|
+
log("verifying record update for #{locator_key}")
|
113
|
+
|
114
|
+
actual_owner = reported_owner(locator_key)
|
115
|
+
return unless actual_owner
|
116
|
+
|
117
|
+
if current_isolation_key.nil?
|
118
|
+
log("Allowing update to #{locator_key.inspect} because there is no registered owner.")
|
119
|
+
return
|
120
|
+
end
|
121
|
+
|
122
|
+
if actual_owner == current_isolation_key
|
123
|
+
log("Allowing update to #{locator_key.inspect} in the #{actual_owner.inspect} isolation level via #{current_definition.inspect}.")
|
124
|
+
return
|
125
|
+
end
|
126
|
+
|
127
|
+
raise Errors::IsolatedMutationError, "Cannot modify #{locator_key.inspect}. Owned by: #{actual_owner.inspect}. Modified by: #{current_isolation_key.inspect}. Requested changes: #{changes.inspect}"
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Observe all executions and report changes to the MutationObserver.
|
135
|
+
::Fixtury.hooks.around(:execution) do |execution, &block|
|
136
|
+
::Fixtury::MutationObserver.observe(execution, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
# If/when activerecord loads, prepend the hooks module to ActiveRecord::Base
|
140
|
+
ActiveSupport.on_load(:active_record) { prepend Fixtury::MutationObserver::ActiveRecordHooks }
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fixtury
|
4
|
+
# Takes a namespace as context and a search string and resolves the possible
|
5
|
+
# absolute paths that a user could be referring to.
|
6
|
+
class PathResolver
|
7
|
+
|
8
|
+
attr_reader :namespace, :search
|
9
|
+
|
10
|
+
def initialize(namespace:, search:)
|
11
|
+
@namespace = namespace.to_s
|
12
|
+
@search = search.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def possible_absolute_paths
|
16
|
+
@possible_absolute_paths ||= begin
|
17
|
+
out = []
|
18
|
+
# If the search starts with a slash it's an absolute
|
19
|
+
# path and it should be the only possible path.
|
20
|
+
if search.start_with?("/")
|
21
|
+
out << search
|
22
|
+
|
23
|
+
# Otherwise we need to consider the namespace.
|
24
|
+
else
|
25
|
+
# Try the namespace as a prefix for the search.
|
26
|
+
# This should take priority because it is the most specific.
|
27
|
+
out << ::File.join(namespace, search)
|
28
|
+
|
29
|
+
# In addition, someone may be referencing a path relative
|
30
|
+
# to root but not including the leading slash. We should
|
31
|
+
# consider this case as well.
|
32
|
+
out << ::File.join("/", search) unless search.include?(".")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Get rid of any `.` and `..` in the paths.
|
36
|
+
out.map! { |path| File.expand_path(path, "/").to_s }
|
37
|
+
# Get rid of any duplicates.
|
38
|
+
out.uniq!
|
39
|
+
# voila
|
40
|
+
out
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
data/lib/fixtury/railtie.rb
CHANGED
data/lib/fixtury/reference.rb
CHANGED
@@ -1,29 +1,31 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Fixtury
|
4
|
+
# Acts as an reference between the schema and an object in some remote store.
|
5
|
+
# The Store uses these references to keep track of the fixtures it has created.
|
6
|
+
# The references are used by the locator to retrieve the fixture data from whatever
|
7
|
+
# backend is being used.
|
4
8
|
class Reference
|
5
9
|
|
6
|
-
|
10
|
+
# A special key used to indicate that the a definition is currently building an
|
11
|
+
# object for this locator_key. This is used to prevent circular dependencies.
|
12
|
+
HOLDER_KEY = "__BUILDING_FIXTURE__"
|
7
13
|
|
8
14
|
def self.holder(name)
|
9
|
-
new(name,
|
15
|
+
new(name, HOLDER_KEY)
|
10
16
|
end
|
11
17
|
|
12
|
-
|
13
|
-
new(name, value)
|
14
|
-
end
|
15
|
-
|
16
|
-
attr_reader :name, :value, :created_at, :options
|
18
|
+
attr_reader :name, :locator_key, :created_at, :options
|
17
19
|
|
18
|
-
def initialize(name,
|
20
|
+
def initialize(name, locator_key, options = {})
|
19
21
|
@name = name
|
20
|
-
@
|
22
|
+
@locator_key = locator_key
|
21
23
|
@created_at = Time.now.to_i
|
22
24
|
@options = options
|
23
25
|
end
|
24
26
|
|
25
27
|
def holder?
|
26
|
-
|
28
|
+
locator_key == HOLDER_KEY
|
27
29
|
end
|
28
30
|
|
29
31
|
def real?
|