fixtury 0.1.0.rc1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57860e63181271b5c3f036cf9f10fa8a7cbab90fa5c2f3fb0e01cacc2a026c06
4
- data.tar.gz: 1304df2a1825633dd3c31ac34a070839aca8d4aaf0b4f09738e2d24115f57602
3
+ metadata.gz: c8026933c72e6773550464325733bf9c31c7fc601c00aabcc6fa18dd384439d6
4
+ data.tar.gz: a96c566602765f01a949814362022d687e5b06e794d1c09ecbef8dd5fd254a34
5
5
  SHA512:
6
- metadata.gz: 93aa1dd2df814ec78ee09fbfa630d3c886e848913dad7acd31a060bfa7415fdf0ac620572d16fcc9a22944bee54bda38efbe2e21654a9309dd42f05f7bbb477b
7
- data.tar.gz: 53d99202d931458e3bdacaf494bedd70f0ef57591cf9ff67fcc81862a9b625505bf5f7786085f926b4aca4939fe4a9e0d0767bf14d89f5e029de5978dc347357
6
+ metadata.gz: 7b95b7af4242c7e90f29f913be405a4a50163c66b8d363b2187ca04e4f71ff59b9cfdf88acbf6c4754c226321f8663c6d54b02472fe9c49342a49b9f98a1e2b2
7
+ data.tar.gz: 7b78291b5980f5dc3a854e31f15668ee6765a7173869d68b09a19d8e58cc06e60240c293fa7984afd7271c3d31e8977d5d1e0d96cf71bfd018ada973fe3cf75f
@@ -1,26 +1,26 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fixtury (0.1.0.beta2)
4
+ fixtury (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- activesupport (6.0.1)
9
+ activesupport (6.0.3.1)
10
10
  concurrent-ruby (~> 1.0, >= 1.0.2)
11
11
  i18n (>= 0.7, < 2)
12
12
  minitest (~> 5.1)
13
13
  tzinfo (~> 1.1)
14
- zeitwerk (~> 2.2)
14
+ zeitwerk (~> 2.2, >= 2.2.2)
15
15
  ansi (1.5.0)
16
16
  autotest (5.0.0)
17
17
  minitest-autotest (~> 1.0)
18
18
  builder (3.2.3)
19
19
  byebug (11.0.1)
20
- concurrent-ruby (1.1.5)
20
+ concurrent-ruby (1.1.6)
21
21
  globalid (0.4.2)
22
22
  activesupport (>= 4.2.0)
23
- i18n (1.7.0)
23
+ i18n (1.8.2)
24
24
  concurrent-ruby (~> 1.0)
25
25
  metaclass (0.0.4)
26
26
  minitest (5.13.0)
@@ -37,13 +37,13 @@ GEM
37
37
  mocha (1.8.0)
38
38
  metaclass (~> 0.0.1)
39
39
  path_expander (1.1.0)
40
- rake (10.5.0)
40
+ rake (13.0.1)
41
41
  ruby-progressbar (1.10.1)
42
42
  sqlite (1.0.2)
43
43
  thread_safe (0.3.6)
44
- tzinfo (1.2.5)
44
+ tzinfo (1.2.7)
45
45
  thread_safe (~> 0.1)
46
- zeitwerk (2.2.2)
46
+ zeitwerk (2.3.0)
47
47
 
48
48
  PLATFORMS
49
49
  ruby
@@ -57,7 +57,7 @@ DEPENDENCIES
57
57
  minitest (~> 5.0)
58
58
  minitest-reporters
59
59
  mocha
60
- rake (~> 10.0)
60
+ rake (~> 13.0)
61
61
  sqlite
62
62
 
63
63
  BUNDLED WITH
@@ -34,6 +34,6 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "minitest", "~> 5.0"
35
35
  spec.add_development_dependency "minitest-reporters"
36
36
  spec.add_development_dependency "mocha"
37
- spec.add_development_dependency "rake", "~> 10.0"
37
+ spec.add_development_dependency "rake", "~> 13.0"
38
38
  spec.add_development_dependency "sqlite"
39
39
  end
@@ -7,7 +7,6 @@ require "fixtury/version"
7
7
  require "fixtury/schema"
8
8
  require "fixtury/locator"
9
9
  require "fixtury/store"
10
- require "fixtury/execution_context"
11
10
 
12
11
  module Fixtury
13
12
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "fixtury/definition_executor"
4
+
3
5
  module Fixtury
4
6
  class Definition
5
7
 
@@ -24,42 +26,23 @@ module Fixtury
24
26
  @enhancements.any?
25
27
  end
26
28
 
27
- def call(store: nil, execution_context: nil)
28
- maybe_set_store_context(store: store) do
29
- value = run_callable(store: store, callable: callable, execution_context: execution_context, value: nil)
30
- enhancements.each do |e|
31
- value = run_callable(store: store, callable: e, execution_context: execution_context, value: value)
32
- end
33
- value
34
- end
29
+ def info
30
+ {
31
+ name: name,
32
+ loc: location_from_callable(callable),
33
+ enhancements: enhancements.map { |e| location_from_callable(e) },
34
+ }
35
35
  end
36
36
 
37
- protected
38
-
39
- def maybe_set_store_context(store:)
40
- return yield unless store
41
-
42
- store.with_relative_schema(schema) do
43
- yield
44
- end
37
+ def call(store: nil, execution_context: nil)
38
+ executor = ::Fixtury::DefinitionExecutor.new(store: store, definition: self, execution_context: execution_context)
39
+ executor.__call
45
40
  end
46
41
 
47
- def run_callable(store:, callable:, execution_context:, value:)
48
- execution_context ||= self
49
-
50
- args = []
51
- args << value unless value.nil?
52
- if callable.arity > args.length
53
- raise ArgumentError, "A store store must be provided if the definition expects it." unless store
54
-
55
- args << store
56
- end
42
+ def location_from_callable(callable)
43
+ return nil unless callable.respond_to?(:source_location)
57
44
 
58
- if args.length.positive?
59
- execution_context.instance_exec(*args, &callable)
60
- else
61
- execution_context.instance_eval(&callable)
62
- end
45
+ callable.source_location.join(":")
63
46
  end
64
47
 
65
48
  end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fixtury
4
+ class DefinitionExecutor
5
+
6
+ attr_reader :value, :execution_type, :definition, :store, :execution_context
7
+
8
+ def initialize(store: nil, execution_context: nil, definition:)
9
+ @store = store
10
+ @definition = definition
11
+ @execution_context = execution_context
12
+ @execution_type = nil
13
+ @value = nil
14
+ end
15
+
16
+ def __call
17
+ maybe_set_store_context do
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
+
26
+ value
27
+ end
28
+
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
+ private
49
+
50
+ def run_callable(callable:, type:)
51
+ @execution_type = type
52
+
53
+ @value = if callable.arity.positive?
54
+ instance_exec(self, &callable)
55
+ else
56
+ instance_eval(&callable)
57
+ end
58
+ end
59
+
60
+ def maybe_set_store_context
61
+ return yield unless store
62
+
63
+ store.with_relative_schema(definition.schema) do
64
+ yield
65
+ end
66
+ end
67
+
68
+ def provide_schema_hooks
69
+ return yield unless definition.schema
70
+
71
+ @value = definition.schema.around_fixture_hook(self) do
72
+ yield
73
+ value
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -9,16 +9,37 @@ require "fixtury/errors/schema_frozen_error"
9
9
  module Fixtury
10
10
  class Schema
11
11
 
12
- attr_reader :definitions, :children, :name, :parent, :relative_name
12
+ attr_reader :definitions, :children, :name, :parent, :relative_name, :around_fixture_definition
13
13
 
14
14
  def initialize(parent:, name:)
15
15
  @name = name
16
16
  @parent = parent
17
17
  @relative_name = @name.split("/").last
18
+ @around_fixture_definition = nil
18
19
  @frozen = false
19
20
  reset!
20
21
  end
21
22
 
23
+ def around_fixture(&block)
24
+ @around_fixture_definition = block
25
+ end
26
+
27
+ def around_fixture_hook(executor, &definition)
28
+ maybe_invoke_parent_around_fixture_hook(executor) do
29
+ if around_fixture_definition.nil?
30
+ yield
31
+ else
32
+ around_fixture_definition.call(executor, definition)
33
+ end
34
+ end
35
+ end
36
+
37
+ def maybe_invoke_parent_around_fixture_hook(executor, &block)
38
+ return yield unless parent
39
+
40
+ parent.around_fixture_hook(executor, &block)
41
+ end
42
+
22
43
  def reset!
23
44
  @children = {}
24
45
  @definitions = {}
@@ -99,6 +120,8 @@ module Fixtury
99
120
  end
100
121
  end
101
122
 
123
+ around_fixture(&other_ns.around_fixture_definition) if other_ns.around_fixture_definition
124
+
102
125
  self
103
126
  end
104
127
 
@@ -172,6 +195,7 @@ module Fixtury
172
195
  self.class.new(name: child_name, parent: self)
173
196
  end
174
197
  end
198
+ child
175
199
  end
176
200
 
177
201
  def find_child_definition(name:)
@@ -187,7 +211,7 @@ module Fixtury
187
211
  def build_child_name(name:)
188
212
  name = name&.to_s
189
213
  raise ArgumentError, "`name` must be provided" if name.nil?
190
- raise ArgumentError, "#{name} is invalid. `name` must contain only a-z, A-Z, 0-9, and _." unless name.match(/^[a-zA-Z_0-9]+$/)
214
+ raise ArgumentError, "#{name} is invalid. `name` must contain only a-z, A-Z, 0-9, and _." unless /^[a-zA-Z_0-9]+$/.match?(name)
191
215
 
192
216
  arr = ["", self.name, name]
193
217
  arr.join("/").gsub(%r{/{2,}}, "/")
@@ -5,33 +5,38 @@ require "singleton"
5
5
  require "yaml"
6
6
  require "fixtury/locator"
7
7
  require "fixtury/errors/circular_dependency_error"
8
- require "fixtury/execution_context"
9
8
  require "fixtury/reference"
10
9
 
11
10
  module Fixtury
12
11
  class Store
13
12
 
13
+ LOG_LEVELS = {
14
+ (LOG_LEVEL_NONE = :none) => 0,
15
+ (LOG_LEVEL_INFO = :info) => 1,
16
+ (LOG_LEVEL_DEBUG = :debug) => 2,
17
+ }.freeze
18
+
14
19
  cattr_accessor :instance
15
20
 
16
21
  attr_reader :filepath, :references, :ttl, :auto_refresh_expired
17
22
  attr_reader :schema, :locator
18
- attr_reader :verbose
19
- attr_reader :execution_context
23
+ attr_reader :log_level
20
24
 
21
25
  def initialize(
22
26
  filepath: nil,
23
27
  locator: ::Fixtury::Locator.instance,
24
- verbose: false,
28
+ log_level: nil,
25
29
  ttl: nil,
26
30
  schema: nil,
27
31
  auto_refresh_expired: false
28
32
  )
29
33
  @schema = schema || ::Fixtury.schema
30
- @verbose = verbose
34
+ @log_level = log_level.nil? ? ENV["FIXTURY_LOG_LEVEL"] : log_level
35
+ @log_level ||= LOG_LEVEL_NONE
36
+ @log_level = @log_level.to_s.to_sym
31
37
  @locator = locator
32
38
  @filepath = filepath
33
39
  @references = @filepath && ::File.file?(@filepath) ? ::YAML.load_file(@filepath) : {}
34
- @execution_context = ::Fixtury::ExecutionContext.new
35
40
  @ttl = ttl ? ttl.to_i : ttl
36
41
  @auto_refresh_expired = !!auto_refresh_expired
37
42
  self.class.instance ||= self
@@ -54,7 +59,7 @@ module Fixtury
54
59
 
55
60
  references.delete_if do |name, ref|
56
61
  is_expired = ref_invalid?(ref)
57
- log { "expiring #{name}" } if is_expired
62
+ log(level: LOG_LEVEL_INFO) { "expiring #{name}" } if is_expired
58
63
  is_expired
59
64
  end
60
65
  end
@@ -72,11 +77,11 @@ module Fixtury
72
77
  def clear_cache!(pattern: nil)
73
78
  pattern ||= "*"
74
79
  pattern = "/" + pattern unless pattern.start_with?("/")
75
- glob = pattern.ends_with?("*")
80
+ glob = pattern.end_with?("*")
76
81
  pattern = pattern[0...-1] if glob
77
82
  references.delete_if do |key, _value|
78
83
  hit = glob ? key.start_with?(pattern) : key == pattern
79
- log(true) { "clearing #{key}" } if hit
84
+ log(level: LOG_LEVEL_INFO) { "clearing #{key}" } if hit
80
85
  hit
81
86
  end
82
87
  dump_to_file
@@ -95,11 +100,11 @@ module Fixtury
95
100
  full_name = dfn.name
96
101
  ref = references[full_name]
97
102
  result = ref&.real?
98
- log { result ? "hit #{full_name}" : "miss #{full_name}" }
103
+ log(level: LOG_LEVEL_DEBUG) { result ? "hit #{full_name}" : "miss #{full_name}" }
99
104
  result
100
105
  end
101
106
 
102
- def get(name)
107
+ def get(name, execution_context: nil)
103
108
  dfn = schema.get_definition!(name)
104
109
  full_name = dfn.name
105
110
  ref = references[full_name]
@@ -109,7 +114,7 @@ module Fixtury
109
114
  end
110
115
 
111
116
  if ref && auto_refresh_expired && ref_invalid?(ref)
112
- log { "refreshing #{full_name}" }
117
+ log(level: LOG_LEVEL_INFO) { "refreshing #{full_name}" }
113
118
  clear_ref(full_name)
114
119
  ref = nil
115
120
  end
@@ -117,11 +122,11 @@ module Fixtury
117
122
  value = nil
118
123
 
119
124
  if ref
120
- log { "hit #{full_name}" }
125
+ log(level: LOG_LEVEL_DEBUG) { "hit #{full_name}" }
121
126
  value = load_ref(ref.value)
122
127
  if value.nil?
123
128
  clear_ref(full_name)
124
- log { "missing #{full_name}" }
129
+ log(level: LOG_LEVEL_DEBUG) { "missing #{full_name}" }
125
130
  end
126
131
  end
127
132
 
@@ -131,7 +136,7 @@ module Fixtury
131
136
 
132
137
  value = dfn.call(store: self, execution_context: execution_context)
133
138
 
134
- log { "store #{full_name}" }
139
+ log(level: LOG_LEVEL_INFO) { "store #{full_name}" }
135
140
 
136
141
  ref = dump_ref(full_name, value)
137
142
  ref = ::Fixtury::Reference.new(full_name, ref)
@@ -160,10 +165,14 @@ module Fixtury
160
165
  !locator.recognize?(ref.value)
161
166
  end
162
167
 
163
- def log(local_verbose = false, &block)
164
- return unless verbose || local_verbose
168
+ def log(level: LOG_LEVEL_DEBUG, name: "store")
169
+ desired_level = LOG_LEVELS.fetch(log_level) { LOG_LEVEL_NONE }
170
+ return if desired_level == LOG_LEVEL_NONE
171
+
172
+ message_level = LOG_LEVELS.fetch(level) { LOG_LEVEL_DEBUG }
173
+ return unless desired_level >= message_level
165
174
 
166
- puts "[fixtury|store] #{block.call}"
175
+ puts "[fixtury|#{name}] #{yield}"
167
176
  end
168
177
 
169
178
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fixtury/store"
4
+ require "active_support/core_ext/class/attribute"
4
5
 
5
6
  module Fixtury
6
7
  module TestHooks
@@ -14,36 +15,66 @@ module Fixtury
14
15
 
15
16
  module ClassMethods
16
17
 
17
- def fixtury(*names)
18
- self.fixtury_dependencies += names.flatten.compact.map(&:to_s)
19
- end
18
+ def fixtury(*names, &definition)
19
+ opts = names.extract_options!
20
+
21
+ # define fixtures if blocks are given
22
+ if block_given?
23
+ raise ArgumentError, "A fixture cannot be defined in an anonymous class" if name.nil?
24
+
25
+ namespace = name.underscore
20
26
 
21
- def define_fixture(name, &block)
22
- fixture_name = name
23
- namespace_names = self.name.underscore.split("/")
27
+ ns = ::Fixtury.schema
24
28
 
25
- ns = ::Fixtury.schema
29
+ namespace.split("/").each do |ns_name|
30
+ ns = ns.namespace(ns_name){}
31
+ end
26
32
 
27
- namespace_names.each do |ns_name|
28
- ns = ns.namespace(ns_name){}
33
+ names.each do |fixture_name|
34
+ ns.fixture(fixture_name, &definition)
35
+ self.fixtury_dependencies += ["#{namespace}/#{fixture_name}"]
36
+ end
37
+
38
+ # otherwise, just record the dependency
39
+ else
40
+ self.fixtury_dependencies += names.flatten.compact.map(&:to_s)
29
41
  end
30
42
 
31
- ns.fixture(fixture_name, &block)
43
+ accessor_option = opts.key?(:accessor) ? opts[:accessor] : true
44
+
45
+ if accessor_option
46
+
47
+ if accessor_option != true && names.length > 1
48
+ raise ArgumentError, "A named :accessor option is only available when providing one fixture"
49
+ end
32
50
 
33
- fixtury("#{namespace_names.join("/")}/#{fixture_name}")
51
+ names.each do |fixture_name|
52
+ method_name = accessor_option == true ? fixture_name.split("/").last : accessor_option
53
+ ivar = :"@#{method_name}"
54
+
55
+ class_eval <<-EV, __FILE__, __LINE__ + 1
56
+ def #{method_name}
57
+ return #{ivar} if defined?(#{ivar})
58
+
59
+ value = fixtury("#{fixture_name}")
60
+ #{ivar} = value
61
+ end
62
+ EV
63
+ end
64
+ end
34
65
  end
35
66
 
36
67
  end
37
68
 
38
69
  def fixtury(name)
39
- return nil unless ::Fixtury::Store.instance
70
+ return nil unless fixtury_store
40
71
 
41
72
  name = name.to_s
42
73
 
43
74
  unless name.include?("/")
44
75
  local_name = "#{self.class.name.underscore}/#{name}"
45
76
  if self.fixtury_dependencies.include?(local_name)
46
- return ::Fixtury::Store.instance.get(local_name)
77
+ return fixtury_store.get(local_name, execution_context: self)
47
78
  end
48
79
  end
49
80
 
@@ -51,13 +82,17 @@ module Fixtury
51
82
  raise ArgumentError, "Unrecognized fixtury dependency `#{name}` for #{self.class}"
52
83
  end
53
84
 
54
- ::Fixtury::Store.instance.get(name)
85
+ fixtury_store.get(name, execution_context: self)
86
+ end
87
+
88
+ def fixtury_store
89
+ ::Fixtury::Store.instance
55
90
  end
56
91
 
57
92
  def fixtury_loaded?(name)
58
- return false unless ::Fixtury::Store.instance
93
+ return false unless fixtury_store
59
94
 
60
- ::Fixtury::Store.instance.loaded?(name)
95
+ fixtury_store.loaded?(name)
61
96
  end
62
97
 
63
98
  def fixtury_database_connections
@@ -100,9 +135,9 @@ module Fixtury
100
135
  end
101
136
 
102
137
  def clear_expired_fixtury_fixtures!
103
- return unless ::Fixtury::Store.instance
138
+ return unless fixtury_store
104
139
 
105
- ::Fixtury::Store.instance.clear_expired_references!
140
+ fixtury_store.clear_expired_references!
106
141
  end
107
142
 
108
143
  def load_all_fixtury_fixtures!
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fixtury
4
4
 
5
- VERSION = "0.1.0.rc1"
5
+ VERSION = "0.3.0"
6
6
 
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fixtury
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.rc1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Nelson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-22 00:00:00.000000000 Z
11
+ date: 2020-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: autotest
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '10.0'
117
+ version: '13.0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '10.0'
124
+ version: '13.0'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: sqlite
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -155,12 +155,12 @@ files:
155
155
  - fixtury.gemspec
156
156
  - lib/fixtury.rb
157
157
  - lib/fixtury/definition.rb
158
+ - lib/fixtury/definition_executor.rb
158
159
  - lib/fixtury/errors/already_defined_error.rb
159
160
  - lib/fixtury/errors/circular_dependency_error.rb
160
161
  - lib/fixtury/errors/fixture_not_defined_error.rb
161
162
  - lib/fixtury/errors/schema_frozen_error.rb
162
163
  - lib/fixtury/errors/unrecognizable_locator_error.rb
163
- - lib/fixtury/execution_context.rb
164
164
  - lib/fixtury/locator.rb
165
165
  - lib/fixtury/locator_backend/common.rb
166
166
  - lib/fixtury/locator_backend/globalid.rb
@@ -191,9 +191,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
191
191
  version: '0'
192
192
  required_rubygems_version: !ruby/object:Gem::Requirement
193
193
  requirements:
194
- - - ">"
194
+ - - ">="
195
195
  - !ruby/object:Gem::Version
196
- version: 1.3.1
196
+ version: '0'
197
197
  requirements: []
198
198
  rubygems_version: 3.0.6
199
199
  signing_key:
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # a class made available so helper methods can be provided within the fixture dsl
4
- module Fixtury
5
- class ExecutionContext
6
-
7
- end
8
- end