factory_bot-blueprint 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 89384db0522f5262709df19f70d39b08af7c7eadd26e252560590e536a83d817
4
+ data.tar.gz: 2be455b31165be74a8a2fd2f6008d6424287c9aeefbef4b4499d2b061d70d374
5
+ SHA512:
6
+ metadata.gz: c1b9a4ccf44e9dcef033bcbe83180f3f34b5650f53f033e73d5bb2c157ada4b8fdf10a98e0ec963c585f689c913252cdf5c270be018af4eb72c101f5c1af696a
7
+ data.tar.gz: 7b16fa3b35e416ae46f7e9db4bebec026a387cb2556ad670e96e6c6c5d2de3168403a39340d50436d19b05d22cc943a1dc78cecef8c4dcec3f7a1f93c5e831be
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 yubrot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # `factory_bot-blueprint` gem
2
+
3
+ This is a part of [FactoryBot::Blueprint](https://github.com/yubrot/factory_bot-blueprint) library.
4
+
5
+ FactoryBot::Blueprint is a FactoryBot extension for building structured objects using a declarative DSL.
6
+ On the DSL, the factories defined in FactoryBot can be used without any additional configuration.
7
+
8
+ ## License
9
+
10
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FactoryBot
4
+ module Blueprint
5
+ # A declarative DSL for building {Factrey::Blueprint}.
6
+ # This DSL automatically recognizes factories defined in FactoryBot as types.
7
+ class DSL < Factrey::DSL
8
+ # Internals:
9
+ # Here we rely on some of FactoryBot's internal APIs.
10
+ # We would like to minimize these dependencies as much as possible.
11
+
12
+ # @!visibility private
13
+ def respond_to_missing?(type_name, _)
14
+ FactoryBot.factories.registered? type_name
15
+ end
16
+
17
+ def method_missing(type_name, ...)
18
+ raise NoMethodError, "Unknown type #{type_name}" unless FactoryBot.factories.registered? type_name
19
+
20
+ factory = FactoryBot.factories.find(type_name)
21
+ self.class.add_type(self.class.type_from_factory_bot_factory(type_name, factory))
22
+ __send__(type_name, ...)
23
+ end
24
+
25
+ class << self
26
+ # @!visibility private
27
+ def type_from_factory_bot_factory(type_name, factory)
28
+ # NOTE: We consider aliases to be incompatible with each other
29
+ compatible_types = []
30
+ auto_references = {}
31
+
32
+ while factory.is_a?(FactoryBot::Factory)
33
+ compatible_types << factory.name
34
+ # Here, we use reverse_each to prioritize the upper association
35
+ factory.with_traits(factory.defined_traits.map(&:name)).associations.reverse_each do |assoc|
36
+ auto_references[Array(assoc.factory)[0].to_sym] = assoc.name
37
+ end
38
+
39
+ factory = factory.__send__(:parent)
40
+ end
41
+
42
+ Factrey::Blueprint::Type.new(type_name, compatible_types:, auto_references:, &FACTORY)
43
+ end
44
+
45
+ FACTORY = lambda { |type, context, *args, **kwargs|
46
+ FactoryBot.__send__(context[:strategy], type.name, *args, **kwargs)
47
+ }
48
+
49
+ private_constant :FACTORY
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FactoryBot
4
+ module Blueprint
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "factrey"
4
+ require "factory_bot"
5
+ require_relative "blueprint/version"
6
+ require_relative "blueprint/dsl"
7
+
8
+ module FactoryBot
9
+ # A FactoryBot extension for building structured objects using a declarative DSL.
10
+ # First we can build (or extend) a creation plan for a set of objects as <code>Blueprint</code>,
11
+ # and then we can create actual objects from it.
12
+ #
13
+ # <code>Blueprint</code>s can be built using a declarative DSL provided by a core library called {Factrey}.
14
+ # Each node declaration in the DSL code is automatically correspond to the FactoryBot's factory. For example,
15
+ # a declaration <code>user(name: 'John')</code> corresponds to <code>FactoryBot.create(:user, name: 'John')</code>.
16
+ module Blueprint
17
+ class << self
18
+ # Entry point to build or extend a {Factrey::Blueprint}.
19
+ # @param blueprint [Factrey::Blueprint, nil] to extend an existing blueprint
20
+ # @param ext [Object] an external object that can be accessed using {DSL#ext} in the DSL
21
+ # @yield Write Blueprint DSL code here. See {Factrey::DSL} methods for DSL details
22
+ # @return [Factrey::Blueprint] the built or extended blueprint
23
+ # @example
24
+ # # In this example, we have three factories in FactoryBot:
25
+ # FactoryBot.define do
26
+ # factory(:blog)
27
+ # factory(:article) { association :blog }
28
+ # factory(:comment) { association :article }
29
+ # end
30
+ #
31
+ # bp =
32
+ # FactoryBot::Blueprint.plan do
33
+ # let.blog do
34
+ # article(title: "Article 1")
35
+ # article(title: "Article 2")
36
+ # article(title: "Article 3") do
37
+ # comment(name: "John")
38
+ # comment(name: "Doe")
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ # # Create a set of objects in FactoryBot (with `build` strategy) from the blueprint:
44
+ # instance = FactoryBot::Blueprint.build(bp)
45
+ #
46
+ # # This behaves as:
47
+ # instance = {}
48
+ # instance[:blog] = blog = FactoryBot.build(:blog)
49
+ # instance[gen_random_sym] = FactoryBot.build(:article, title: "Article 1", blog:)
50
+ # instance[gen_random_sym] = FactoryBot.build(:article, title: "Article 2", blog:)
51
+ # instance[gen_random_sym] = article3 = FactoryBot.build(:article, title: "Article 3", blog:)
52
+ # instance[gen_random_sym] = FactoryBot.build(:comment, name: "John", article: article3)
53
+ # instance[gen_random_sym] = FactoryBot.build(:comment, name: "Doe", article: article3)
54
+ def plan(blueprint = nil, ext: nil, &) = Factrey.blueprint(blueprint, ext:, dsl: DSL, &)
55
+
56
+ # Create a set of objects by <code>build</code> strategy in FactoryBot.
57
+ # See {.plan} for more details.
58
+ # @param blueprint [Factrey::Blueprint, nil]
59
+ # @param ext [Object] an external object that can be accessed using {DSL#ext} in the DSL
60
+ # @yield Write Blueprint DSL code here
61
+ # @return [Hash{Symbol => Object}]
62
+ def build(blueprint = nil, ext: nil, &) = instantiate(:build, blueprint, ext:, &)
63
+
64
+ # Create a set of objects by <code>create</code> strategy in FactoryBot.
65
+ # See {.plan} for more details.
66
+ # @param blueprint [Factrey::Blueprint, nil]
67
+ # @param ext [Object] an external object that can be accessed using {DSL#ext} in the DSL
68
+ # @yield Write Blueprint DSL code here
69
+ # @return [Hash{Symbol => Object}]
70
+ def create(blueprint = nil, ext: nil, &) = instantiate(:create, blueprint, ext:, &)
71
+
72
+ # @!visibility private
73
+ def instantiate(strategy, blueprint = nil, ext: nil, &)
74
+ raise ArgumentError, "Unsupported strategy: #{strategy}" unless %i[create build].include?(strategy)
75
+
76
+ plan(blueprint, ext:, &).instantiate(strategy:)
77
+ end
78
+ end
79
+ end
80
+ end
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: factory_bot-blueprint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - yubrot
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-07-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: factory_bot
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: factrey
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
41
+ description: |
42
+ FactoryBot::Blueprint is a FactoryBot extension for building structured objects using a declarative DSL.
43
+ On the DSL, the factories defined in FactoryBot can be used without any additional configuration.
44
+ email:
45
+ - yubrot@gmail.com
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - LICENSE.txt
51
+ - README.md
52
+ - lib/factory_bot/blueprint.rb
53
+ - lib/factory_bot/blueprint/dsl.rb
54
+ - lib/factory_bot/blueprint/version.rb
55
+ homepage: https://github.com/yubrot/factory_bot-blueprint
56
+ licenses:
57
+ - MIT
58
+ metadata:
59
+ homepage_uri: https://github.com/yubrot/factory_bot-blueprint
60
+ source_code_uri: https://github.com/yubrot/factory_bot-blueprint
61
+ changelog_uri: https://github.com/yubrot/factory_bot-blueprint/blob/main/CHANGELOG.md
62
+ rubygems_mfa_required: 'true'
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 3.1.0
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubygems_version: 3.5.11
79
+ signing_key:
80
+ specification_version: 4
81
+ summary: FactoryBot extension for building structured objects using a declarative
82
+ DSL
83
+ test_files: []