factory_bot-blueprint-rspec 0.4.0 → 0.5.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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2559690c18cf146c86024386fff1630989c0e201499564fda52e09003a58b6bf
|
4
|
+
data.tar.gz: a6ecd312a009b3b3caf698fec6b8b1f2eb2f2973aaf3f8d8d9c92d5ab91e3b40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4d38916bb4b3d7b7d68cd60fb73fc4b15acd5513e08e447d9a5c3907972c409a686367e43dc6af562c8343388aa52e7a0d2d5694382b92e57be91e6c9aed1a40
|
7
|
+
data.tar.gz: 29886e7b5454c8b346e0d6c6f0a961c1913079a9ed85acdbbe03b751e7ae6f8530f3a7bcf725775662bd8f9e7e5ac20bbd6bbcc18d0587106bd9e37f15d50796
|
@@ -4,129 +4,18 @@ module FactoryBot
|
|
4
4
|
module Blueprint
|
5
5
|
module RSpec
|
6
6
|
# Helper methods to integrate <code>factory_bot-blueprint</code> with RSpec.
|
7
|
-
# This module is automatically extended to
|
8
|
-
#
|
9
|
-
# To use FactoryBot::Blueprint from RSpec with minimal effort, usually {#letbp} is the best choice.
|
7
|
+
# This module is automatically extended to <code>RSpec::Core::ExampleGroup</code>.
|
10
8
|
module Driver
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# @param inherit [Boolean] whether to extend the blueprint by <code>super()</code>
|
15
|
-
# @yield Write Blueprint DSL code here
|
16
|
-
# @example
|
17
|
-
# RSpec.describe "something" do
|
18
|
-
# let(:blog_id) { SecureRandom.uuid }
|
19
|
-
#
|
20
|
-
# let_blueprint(:blog_bp) do
|
21
|
-
# let.blog(id: ext.blog_id, title: "Daily log") do
|
22
|
-
# let.article(title: "Article 1")
|
23
|
-
# article(title: "Article 2")
|
24
|
-
# article(title: "Article 3")
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
# end
|
28
|
-
def let_blueprint(name, inherit: false, &)
|
29
|
-
let(name) { ::FactoryBot::Blueprint.plan(inherit ? super() : nil, ext: self, &) }
|
30
|
-
name
|
31
|
-
end
|
32
|
-
|
33
|
-
# Build objects by <code>build</code> strategy in FactoryBot from a blueprint and declare them using RSpec's
|
34
|
-
# <code>let</code>.
|
35
|
-
# @param map [Hash{Symbol => Object}]
|
36
|
-
# map data structure from source blueprints to instance definitions.
|
37
|
-
# Each instance will be built with <code>FactoryBot::Blueprint.build(__send__(source))</code>
|
38
|
-
# @example
|
39
|
-
# RSpec.describe "something" do
|
40
|
-
# let_blueprint(:blog_bp) do
|
41
|
-
# # ... Write some DSL ...
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# # Simplest example:
|
45
|
-
# # This is equivalent to `let_blueprint_build blog_bp: { result: :blog }`
|
46
|
-
# let_blueprint_build blog_bp: :blog
|
47
|
-
#
|
48
|
-
# # Another shorthand example:
|
49
|
-
# # This is equivalent to `let_blueprint_build blog_bp: { items: %i[blog article] }`
|
50
|
-
# let_blueprint_build blog_bp: %i[blog article]
|
51
|
-
#
|
52
|
-
# # Most flexible example:
|
53
|
-
# # :result specifies the name of the result object to be declared. Defaults to nil
|
54
|
-
# # :items specifies the names of the objects to be declared. Defaults to []
|
55
|
-
# # :instance specifies the name of the instance object to be declared. Defaults to :"#{source}_instance"
|
56
|
-
# let_blueprint_build blog_bp: { result: :blog, items: %i[article], instance: :blog_instance }
|
57
|
-
#
|
58
|
-
# # Above example will be expanded to:
|
59
|
-
# let(:blog_instance) { ::FactoryBot::Blueprint.build(blog_bp) } # the instance object
|
60
|
-
# let(:blog) { blog_instance[Factrey::Blueprint::Node::RESULT_NAME] } # the result object
|
61
|
-
# let(:article) { blog_instance[:article] } # the item objects
|
62
|
-
# end
|
63
|
-
def let_blueprint_build(**map) = let_blueprint_instantiate(:build, **map)
|
64
|
-
|
65
|
-
# Build objects by <code>create</code> strategy in FactoryBot from a blueprint and declare them using RSpec's
|
66
|
-
# <code>let</code>.
|
67
|
-
# See {#let_blueprint_build} for more details.
|
68
|
-
# @param map [Hash{Symbol => Object}]
|
69
|
-
# map data structure from source blueprints to instance definitions.
|
70
|
-
# Each instance will be built with <code>FactoryBot::Blueprint.build(__send__(source))</code>
|
71
|
-
def let_blueprint_create(**map) = let_blueprint_instantiate(:create, **map)
|
72
|
-
|
73
|
-
# @!visibility private
|
74
|
-
def let_blueprint_instantiate(strategy, **map)
|
75
|
-
raise ArgumentError, "Unsupported strategy: #{strategy}" if strategy && !%i[create build].include?(strategy)
|
76
|
-
|
77
|
-
map.map do |source, definition|
|
78
|
-
raise TypeError, "source must be a Symbol" unless source.is_a?(Symbol)
|
79
|
-
|
80
|
-
definition =
|
81
|
-
case definition
|
82
|
-
when Symbol
|
83
|
-
{ result: definition }
|
84
|
-
when Array
|
85
|
-
{ items: definition }
|
86
|
-
when Hash
|
87
|
-
definition
|
88
|
-
else
|
89
|
-
raise TypeError, "definition must be one of Symbol, Array, Hash"
|
90
|
-
end
|
91
|
-
|
92
|
-
result_name = definition[:result]
|
93
|
-
item_names = definition[:items] || []
|
94
|
-
instance = definition[:instance] || :"#{source}_instance"
|
95
|
-
|
96
|
-
raise TypeError, "result must be a Symbol" if result_name && !result_name.is_a?(Symbol)
|
97
|
-
if !item_names.is_a?(Array) || !item_names.all? { _1.is_a?(Symbol) }
|
98
|
-
raise TypeError, "items must be an Array of Symbols"
|
99
|
-
end
|
100
|
-
raise TypeError, "instance must be a Symbol" unless instance.is_a?(Symbol)
|
101
|
-
|
102
|
-
if strategy # If no strategy is specified, the instance is assumed to exist
|
103
|
-
let(instance) { ::FactoryBot::Blueprint.instantiate(strategy, __send__(source)) }
|
104
|
-
end
|
105
|
-
|
106
|
-
let(result_name) { __send__(instance)[::Factrey::Blueprint::Node::RESULT_NAME] } if result_name
|
107
|
-
|
108
|
-
item_names.each { |name| let(name) { __send__(instance)[name] } }
|
109
|
-
|
110
|
-
instance
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# Write the blueprint in DSL, create an instance of it, and declare each object of the instance using RSpec's
|
115
|
-
# <code>let</code>.
|
9
|
+
# This method expresses that the names given as arguments will be used in the let declaration for the set of
|
10
|
+
# objects to be created from the blueprint. Subsequent method calls specify specifically how to use FactoryBot
|
11
|
+
# to create the set of objects from the blueprint.
|
116
12
|
#
|
117
|
-
#
|
118
|
-
# @param name [Symbol]
|
119
|
-
# name of the result object to be declared using RSpec's <code>let</code>.
|
120
|
-
# It is also used as a name prefix of the blueprint
|
13
|
+
# @param name [Symbol] name of the result object to be declared using RSpec's <code>let</code>
|
121
14
|
# @param items [Array<Symbol>] names of the objects to be declared using RSpec's <code>let</code>
|
122
|
-
# @
|
123
|
-
# @param strategy [:create, :build]
|
124
|
-
# FactoryBot strategy to use when building objects.
|
125
|
-
# This option is ignored if <code>inherit: true</code>
|
126
|
-
# @yield Write Blueprint DSL code here
|
15
|
+
# @return [Letbp]
|
127
16
|
# @example
|
128
17
|
# RSpec.describe "something" do
|
129
|
-
# letbp(:blog, %i[article]) do
|
18
|
+
# letbp(:blog, %i[article]).build do
|
130
19
|
# blog(title: "Daily log") do
|
131
20
|
# let.article(title: "Article 1")
|
132
21
|
# article(title: "Article 2")
|
@@ -134,48 +23,43 @@ module FactoryBot
|
|
134
23
|
# end
|
135
24
|
# end
|
136
25
|
#
|
137
|
-
# # Above example will be expanded to
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
26
|
+
# # Above example will be expanded to ...
|
27
|
+
#
|
28
|
+
# # Create a blueprint:
|
29
|
+
# let(:_letbp_blog_blueprint) do
|
30
|
+
# FactoryBot::Blueprint.plan(ext: self) do
|
31
|
+
# blog(title: "Daily log") do
|
32
|
+
# let.article(title: "Article 1")
|
33
|
+
# article(title: "Article 2")
|
34
|
+
# article(title: "Article 3")
|
35
|
+
# end
|
143
36
|
# end
|
144
37
|
# end
|
145
|
-
#
|
38
|
+
#
|
39
|
+
# # Create a set of objects (with `build` build strategy) from it:
|
40
|
+
# let(:_letbp_blog_instance) { FactoryBot::Blueprint.build(_letbp_blog_blueprint) }
|
41
|
+
#
|
42
|
+
# # Declare the result object:
|
43
|
+
# let(:blog) { _letbp_blog_instance[Factrey::Blueprint::Node::RESULT_NAME] }
|
44
|
+
#
|
45
|
+
# # Declare the named objects:
|
46
|
+
# let(:article) { _letbp_blog_instance[:article] }
|
146
47
|
# end
|
147
|
-
def letbp(name, items = []
|
148
|
-
raise TypeError, "name must be a Symbol" unless name.is_a?(Symbol)
|
149
|
-
|
150
|
-
source = :"#{name}_blueprint"
|
151
|
-
strategy = nil if inherit
|
152
|
-
|
153
|
-
let_blueprint(source, inherit:, &)
|
154
|
-
let_blueprint_instantiate strategy, source => { result: name, items: }
|
155
|
-
end
|
156
|
-
|
157
|
-
# <code>let!</code> version of {#let_blueprint}.
|
158
|
-
def let_blueprint!(...)
|
159
|
-
name = let_blueprint(...)
|
160
|
-
before { __send__(name) }
|
161
|
-
end
|
162
|
-
|
163
|
-
# <code>let!</code> version of {#let_blueprint_build}.
|
164
|
-
def let_blueprint_build!(...)
|
165
|
-
names = let_blueprint_build(...)
|
166
|
-
before { names.each { __send__(_1) } }
|
167
|
-
end
|
168
|
-
|
169
|
-
# <code>let!</code> version of {#let_blueprint_create}.
|
170
|
-
def let_blueprint_create!(...)
|
171
|
-
names = let_blueprint_create(...)
|
172
|
-
before { names.each { __send__(_1) } }
|
173
|
-
end
|
48
|
+
def letbp(name, items = []) = Letbp.new(self, :lazy, name, items)
|
174
49
|
|
175
50
|
# <code>let!</code> version of {#letbp}.
|
176
|
-
|
177
|
-
|
178
|
-
|
51
|
+
# @param name [Symbol]
|
52
|
+
# @param items [Array<Symbol>]
|
53
|
+
# @return [Letbp]
|
54
|
+
def letbp!(name, items = []) = Letbp.new(self, :eager, name, items)
|
55
|
+
|
56
|
+
# <code>let_it_be</code> version of {#letbp}. This requires {https://github.com/test-prof/test-prof test-prof}.
|
57
|
+
# @param name [Symbol]
|
58
|
+
# @param items [Array<Symbol>]
|
59
|
+
# @param options [Hash] options for <code>let_it_be</code>
|
60
|
+
# @return [Letbp]
|
61
|
+
def letbp_it_be(name, items = [], **options)
|
62
|
+
Letbp.new(self, :let_it_be, name, items, let_it_be_options: options)
|
179
63
|
end
|
180
64
|
end
|
181
65
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FactoryBot
|
4
|
+
module Blueprint
|
5
|
+
module RSpec
|
6
|
+
# An intermediate object for <code>letbp</code> syntax. See {Driver#letbp} for more details.
|
7
|
+
class Letbp
|
8
|
+
# @!visibility private
|
9
|
+
def initialize(context, kind, name, items, let_it_be_options: nil)
|
10
|
+
raise TypeError, "context must include FactoryBot::Blueprint::RSpec::Driver" unless context.is_a?(Driver)
|
11
|
+
raise ArgumentError, "unknown kind: #{kind}" unless %i[lazy eager let_it_be].include?(kind)
|
12
|
+
raise TypeError, "name must be a Symbol" unless name.is_a?(Symbol)
|
13
|
+
unless items.is_a?(Array) && items.all? { _1.is_a?(Symbol) }
|
14
|
+
raise TypeError, "items must be an Array of Symbols"
|
15
|
+
end
|
16
|
+
if let_it_be_options && kind != :let_it_be
|
17
|
+
raise ArgumentError, "`let_it_be_options` must be used with `let_it_be` kind"
|
18
|
+
end
|
19
|
+
|
20
|
+
@context = context
|
21
|
+
@kind = kind
|
22
|
+
@name = name
|
23
|
+
@items = items
|
24
|
+
@let_it_be_options = let_it_be_options
|
25
|
+
end
|
26
|
+
|
27
|
+
# Create a new blueprint, and create a set of objects (with <code>build</code> build strategy) from it.
|
28
|
+
# @yield Write Blueprint DSL code here
|
29
|
+
# @example
|
30
|
+
# letbp(:blog, %i[article]).build do
|
31
|
+
# blog(title: "Daily log") do
|
32
|
+
# let.article(title: "Article 1")
|
33
|
+
# article(title: "Article 2")
|
34
|
+
# article(title: "Article 3")
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
def build(&)
|
38
|
+
let_blueprint(:new, &)
|
39
|
+
let_instance(:build)
|
40
|
+
let_objects
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create a new blueprint, and create a set of objects (with <code>build_stubbed</code> build strategy) from it.
|
44
|
+
# @yield Write Blueprint DSL code here
|
45
|
+
def build_stubbed(&)
|
46
|
+
let_blueprint(:new, &)
|
47
|
+
let_instance(:build_stubbed)
|
48
|
+
let_objects
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new blueprint, and create a set of objects (with <code>create</code> build strategy) from it.
|
52
|
+
# @yield Write Blueprint DSL code here
|
53
|
+
def create(&)
|
54
|
+
let_blueprint(:new, &)
|
55
|
+
let_instance(:create)
|
56
|
+
let_objects
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create a set of objects (with <code>build</code> build strategy) from an existing blueprint.
|
60
|
+
# @yield Usual let context for retrieving an existing blueprint
|
61
|
+
# @example
|
62
|
+
# let(:blog_blueprint) do
|
63
|
+
# bp.plan do
|
64
|
+
# blog(title: "Daily log") do
|
65
|
+
# let.article(title: "Article 1")
|
66
|
+
# article(title: "Article 2")
|
67
|
+
# article(title: "Article 3")
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
# letbp(:blog, %i[article]).build_from { blog_blueprint }
|
72
|
+
def build_from(&)
|
73
|
+
let_blueprint(:from, &)
|
74
|
+
let_instance(:build)
|
75
|
+
let_objects
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create a set of objects (with <code>build_stubbed</code> build strategy) from an existing blueprint.
|
79
|
+
# @yield Usual let context for retrieving an existing blueprint
|
80
|
+
def build_stubbed_from(&)
|
81
|
+
let_blueprint(:from, &)
|
82
|
+
let_instance(:build_stubbed)
|
83
|
+
let_objects
|
84
|
+
end
|
85
|
+
|
86
|
+
# Create a set of objects (with <code>create</code> build strategy) from an existing blueprint.
|
87
|
+
# @yield Usual let context for retrieving an existing blueprint
|
88
|
+
def create_from(&)
|
89
|
+
let_blueprint(:from, &)
|
90
|
+
let_instance(:create)
|
91
|
+
let_objects
|
92
|
+
end
|
93
|
+
|
94
|
+
# Extend <code>super()</code> blueprint.
|
95
|
+
# @yield Write Blueprint DSL code here
|
96
|
+
# @example
|
97
|
+
# RSpec.describe "something" do
|
98
|
+
# letbp(:blog, %i[article]).build do
|
99
|
+
# blog(title: "Daily log") do
|
100
|
+
# let.article(title: "Article")
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# context "with a comment on the article" do
|
105
|
+
# letbp(:blog, %i[comment]).inherit do
|
106
|
+
# on.article do
|
107
|
+
# let.comment(text: "Comment")
|
108
|
+
# end
|
109
|
+
# end
|
110
|
+
# end
|
111
|
+
# end
|
112
|
+
def inherit(&)
|
113
|
+
let_blueprint(:inherit, &)
|
114
|
+
# We don't need `let_instance` here because it's already defined in the parent context
|
115
|
+
let_objects
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def blueprint_name = :"_letbp_#{@name}_blueprint"
|
121
|
+
def instance_name = :"_letbp_#{@name}_instance"
|
122
|
+
|
123
|
+
def let(var, &)
|
124
|
+
case @kind
|
125
|
+
when :lazy
|
126
|
+
@context.let(var, &)
|
127
|
+
when :eager
|
128
|
+
@context.let!(var, &)
|
129
|
+
when :let_it_be
|
130
|
+
@context.let_it_be(var, **@let_it_be_options, &)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param source [:new, :from, :inherit]
|
135
|
+
def let_blueprint(source, &)
|
136
|
+
case source
|
137
|
+
when :new
|
138
|
+
let(blueprint_name) { ::FactoryBot::Blueprint.plan(ext: self, &) }
|
139
|
+
when :from
|
140
|
+
let(blueprint_name, &)
|
141
|
+
when :inherit
|
142
|
+
raise ArgumentError, "#inherit does not work with `letbp_it_be`" if @kind == :let_it_be
|
143
|
+
|
144
|
+
let(blueprint_name) { ::FactoryBot::Blueprint.plan(super(), ext: self, &) }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @param build_strategy [:build, :build_stubbed, :create]
|
149
|
+
def let_instance(build_strategy)
|
150
|
+
blueprint_name = self.blueprint_name
|
151
|
+
let(instance_name) { ::FactoryBot::Blueprint.instantiate(build_strategy, __send__(blueprint_name)) }
|
152
|
+
end
|
153
|
+
|
154
|
+
def let_objects
|
155
|
+
instance_name = self.instance_name
|
156
|
+
|
157
|
+
let(@name) { __send__(instance_name)[::Factrey::Blueprint::Node::RESULT_NAME] }
|
158
|
+
@items.each { |item| let(item) { __send__(instance_name)[item] } }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: factory_bot-blueprint-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yubrot
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-11-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: factory_bot-blueprint
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.5.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 0.5.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec-core
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -49,6 +49,7 @@ files:
|
|
49
49
|
- README.md
|
50
50
|
- lib/factory_bot/blueprint/rspec.rb
|
51
51
|
- lib/factory_bot/blueprint/rspec/driver.rb
|
52
|
+
- lib/factory_bot/blueprint/rspec/letbp.rb
|
52
53
|
- lib/factory_bot/blueprint/rspec/version.rb
|
53
54
|
homepage: https://github.com/yubrot/factory_bot-blueprint
|
54
55
|
licenses:
|
@@ -66,14 +67,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
67
|
requirements:
|
67
68
|
- - ">="
|
68
69
|
- !ruby/object:Gem::Version
|
69
|
-
version: 3.
|
70
|
+
version: 3.2.0
|
70
71
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
72
|
requirements:
|
72
73
|
- - ">="
|
73
74
|
- !ruby/object:Gem::Version
|
74
75
|
version: '0'
|
75
76
|
requirements: []
|
76
|
-
rubygems_version: 3.5.
|
77
|
+
rubygems_version: 3.5.22
|
77
78
|
signing_key:
|
78
79
|
specification_version: 4
|
79
80
|
summary: FactoryBot::Blueprint integration for RSpec
|