rom-factory 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 +4 -4
- data/.travis.yml +11 -10
- data/.yardopts +7 -0
- data/CHANGELOG.md +15 -0
- data/Gemfile +30 -4
- data/README.md +20 -190
- data/benchmarks/basic.rb +70 -0
- data/lib/rom/factory.rb +15 -0
- data/lib/rom/factory/attribute_registry.rb +64 -0
- data/lib/rom/factory/attributes.rb +12 -0
- data/lib/rom/factory/attributes/association.rb +98 -0
- data/lib/rom/factory/attributes/callable.rb +20 -8
- data/lib/rom/factory/attributes/sequence.rb +15 -3
- data/lib/rom/factory/attributes/value.rb +31 -0
- data/lib/rom/factory/builder.rb +26 -37
- data/lib/rom/factory/builder/persistable.rb +51 -0
- data/lib/rom/factory/dsl.rb +81 -18
- data/lib/rom/factory/factories.rb +121 -5
- data/lib/rom/factory/tuple_evaluator.rb +104 -0
- data/lib/rom/factory/version.rb +1 -1
- data/rom-factory.gemspec +4 -3
- metadata +37 -11
- data/lib/rom/factory/attributes/regular.rb +0 -13
- data/lib/rom/factory/struct.rb +0 -21
@@ -4,28 +4,56 @@ require 'dry/core/inflector'
|
|
4
4
|
require 'rom/factory/dsl'
|
5
5
|
|
6
6
|
module ROM::Factory
|
7
|
+
# In-memory builder API
|
8
|
+
#
|
9
|
+
# @api public
|
7
10
|
class Structs
|
11
|
+
# @!attribute [r] registry
|
12
|
+
# @return [Hash<Symbol=>Builder>]
|
8
13
|
attr_reader :registry
|
9
14
|
|
15
|
+
# @api private
|
10
16
|
def initialize(registry)
|
11
17
|
@registry = registry
|
12
18
|
end
|
13
19
|
|
20
|
+
# Build an in-memory struct
|
21
|
+
#
|
22
|
+
# @example create a struct with default attributes
|
23
|
+
# MyFactory[:user]
|
24
|
+
#
|
25
|
+
# @example create a struct with some attributes overridden
|
26
|
+
# MyFactory.structs[:user, name: "Jane"]
|
27
|
+
#
|
28
|
+
# @param [Symbol] name The name of the registered factory
|
29
|
+
# @param [Hash] attrs An optional hash with attributes
|
30
|
+
#
|
31
|
+
# @return [ROM::Struct]
|
32
|
+
#
|
33
|
+
# @api public
|
14
34
|
def [](name, attrs = {})
|
15
35
|
registry[name].create(attrs)
|
16
36
|
end
|
17
37
|
end
|
18
38
|
|
39
|
+
# A registry with all configured factories
|
40
|
+
#
|
41
|
+
# @api public
|
19
42
|
class Factories
|
20
43
|
extend Dry::Configurable
|
21
44
|
|
22
45
|
setting :rom
|
23
46
|
|
24
47
|
class << self
|
48
|
+
# @!attribute [r] registry
|
49
|
+
# @return [Hash<Symbol=>Builder>]
|
25
50
|
attr_reader :registry
|
26
51
|
|
52
|
+
# @!attribute [r] structs
|
53
|
+
# @return [Structs] In-memory struct builder instance
|
27
54
|
attr_reader :structs
|
28
55
|
|
56
|
+
# @api private
|
29
57
|
def inherited(klass)
|
30
58
|
registry = {}
|
31
59
|
klass.instance_variable_set(:'@registry', registry)
|
@@ -33,6 +61,68 @@ module ROM::Factory
|
|
33
61
|
super
|
34
62
|
end
|
35
63
|
|
64
|
+
# Define a new builder
|
65
|
+
#
|
66
|
+
# @example a simple builder
|
67
|
+
# MyFactory.define(:user) do |f|
|
68
|
+
# f.name "Jane"
|
69
|
+
# f.email "jane@doe.org"
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# @example a builder using auto-generated fake values
|
73
|
+
# MyFactory.define(:user) do |f|
|
74
|
+
# f.name { fake(:name) }
|
75
|
+
# f.email { fake(:internet, :email) }
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# @example a builder using sequenced values
|
79
|
+
# MyFactory.define(:user) do |f|
|
80
|
+
# f.sequence(:name) { |n| "user-#{n}" }
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# @example a builder using values from other attribute(s)
|
84
|
+
# MyFactory.define(:user) do |f|
|
85
|
+
# f.name "Jane"
|
86
|
+
# f.email { |name| "#{name.downcase}@rom-rb.org" }
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# @example a builder with "belongs-to" association
|
90
|
+
# MyFactory.define(:group) do |f|
|
91
|
+
# f.name "Admins"
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# MyFactory.define(:user) do |f|
|
95
|
+
# f.name "Jane"
|
96
|
+
# f.association(:group)
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# @example a builder with "has-many" association
|
100
|
+
# MyFactory.define(:group) do |f|
|
101
|
+
# f.name "Admins"
|
102
|
+
# f.association(:users, count: 2)
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# MyFactory.define(:user) do |f|
|
106
|
+
# f.sequence(:name) { |n| "user-#{n}" }
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# @example a builder which extends another builder
|
110
|
+
# MyFactory.define(:user) do |f|
|
111
|
+
# f.name "Jane"
|
112
|
+
# f.admin false
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# MyFactory.define(admin: :user) do |f|
|
116
|
+
# f.admin true
|
117
|
+
# end
|
118
|
+
#
|
119
|
+
# @param [Symbol, Hash<Symbol=>Symbol>] spec Builder identifier, can point to a parent builder too
|
120
|
+
# @param [Hash] opts Additional options
|
121
|
+
# @option opts [Symbol] relation An optional relation name (defaults to pluralized builder name)
|
122
|
+
#
|
123
|
+
# @return [ROM::Factory::Builder]
|
124
|
+
#
|
125
|
+
# @api public
|
36
126
|
def define(spec, **opts, &block)
|
37
127
|
name, parent = spec.is_a?(Hash) ? spec.flatten(1) : spec
|
38
128
|
|
@@ -52,16 +142,42 @@ module ROM::Factory
|
|
52
142
|
registry[name] = builder
|
53
143
|
end
|
54
144
|
|
145
|
+
# Create and persist a new struct
|
146
|
+
#
|
147
|
+
# @example create a struct with default attributes
|
148
|
+
# MyFactory[:user]
|
149
|
+
#
|
150
|
+
# @example create a struct with some attributes overridden
|
151
|
+
# MyFactory[:user, name: "Jane"]
|
152
|
+
#
|
153
|
+
# @param [Symbol] name The name of the registered factory
|
154
|
+
# @param [Hash] attrs An optional hash with attributes
|
155
|
+
#
|
156
|
+
# @return [ROM::Struct]
|
157
|
+
#
|
158
|
+
# @api public
|
159
|
+
def [](name, attrs = {})
|
160
|
+
registry[name].persistable.create(attrs)
|
161
|
+
end
|
162
|
+
|
163
|
+
# @api private
|
164
|
+
def for_relation(relation)
|
165
|
+
registry.fetch(infer_factory_name(relation.name.to_sym))
|
166
|
+
end
|
167
|
+
|
168
|
+
# @api private
|
169
|
+
def infer_factory_name(name)
|
170
|
+
::Dry::Core::Inflector.singularize(name).to_sym
|
171
|
+
end
|
172
|
+
|
173
|
+
# @api private
|
55
174
|
def infer_relation(name)
|
56
175
|
::Dry::Core::Inflector.pluralize(name).to_sym
|
57
176
|
end
|
58
177
|
|
178
|
+
# @api private
|
59
179
|
def extend_builder(name, parent, &block)
|
60
|
-
DSL.new(name,
|
61
|
-
end
|
62
|
-
|
63
|
-
def [](name, attrs = {})
|
64
|
-
registry[name].persistable.create(attrs)
|
180
|
+
DSL.new(name, attributes: parent.attributes, relation: parent.relation, factories: self, &block).call
|
65
181
|
end
|
66
182
|
end
|
67
183
|
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module ROM
|
2
|
+
module Factory
|
3
|
+
# @api private
|
4
|
+
class TupleEvaluator
|
5
|
+
# @api private
|
6
|
+
attr_reader :attributes
|
7
|
+
|
8
|
+
# @api private
|
9
|
+
attr_reader :relation
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
attr_reader :model
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
attr_reader :sequence
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
def initialize(attributes, relation)
|
19
|
+
@attributes = attributes
|
20
|
+
@relation = relation.with(auto_struct: true)
|
21
|
+
@model = @relation.combine(*assoc_names).mapper.model
|
22
|
+
@sequence = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def defaults(attrs)
|
27
|
+
evaluate(attrs).merge(attrs)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
def struct(attrs)
|
32
|
+
model.new(struct_attrs.merge(defaults(attrs)))
|
33
|
+
end
|
34
|
+
|
35
|
+
# @api private
|
36
|
+
def persist_associations(tuple, parent)
|
37
|
+
assoc_names.each do |name|
|
38
|
+
assoc = tuple[name]
|
39
|
+
assoc.(parent) if assoc.is_a?(Proc)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @api private
|
44
|
+
def assoc_names
|
45
|
+
attributes.associations.map(&:name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
def has_associations?
|
50
|
+
assoc_names.size > 0
|
51
|
+
end
|
52
|
+
|
53
|
+
# @api private
|
54
|
+
def primary_key
|
55
|
+
relation.primary_key
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# @api private
|
61
|
+
def evaluate(attrs)
|
62
|
+
evaluate_values(attrs).merge(evaluate_associations(attrs))
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api private
|
66
|
+
def evaluate_values(attrs)
|
67
|
+
attributes.values.tsort.each_with_object({}) do |attr, h|
|
68
|
+
deps = attr.dependency_names.map { |k| h[k] }.compact
|
69
|
+
result = attr.(attrs, *deps)
|
70
|
+
|
71
|
+
if result
|
72
|
+
h.update(result)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @api private
|
78
|
+
def evaluate_associations(attrs)
|
79
|
+
attributes.associations.each_with_object({}) do |assoc, h|
|
80
|
+
if assoc.dependency?(relation)
|
81
|
+
h[assoc.name] = -> parent { assoc.call(parent) }
|
82
|
+
else
|
83
|
+
result = assoc.(attrs)
|
84
|
+
h.update(result) if result
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# @api private
|
90
|
+
def struct_attrs
|
91
|
+
relation.schema.
|
92
|
+
reject(&:primary_key?).
|
93
|
+
map { |attr| [attr.name, nil] }.
|
94
|
+
to_h.
|
95
|
+
merge(primary_key => next_id)
|
96
|
+
end
|
97
|
+
|
98
|
+
# @api private
|
99
|
+
def next_id
|
100
|
+
@sequence += 1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/rom/factory/version.rb
CHANGED
data/rom-factory.gemspec
CHANGED
@@ -27,8 +27,9 @@ 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_dependency "dry-configurable", "~> 0.
|
31
|
-
spec.add_dependency "dry-core", "~> 0.
|
32
|
-
spec.add_dependency "dry-struct", "~> 0.
|
30
|
+
spec.add_dependency "dry-configurable", "~> 0.7"
|
31
|
+
spec.add_dependency "dry-core", "~> 0.3", ">= 0.3.1"
|
32
|
+
spec.add_dependency "dry-struct", "~> 0.3"
|
33
|
+
spec.add_dependency "rom-core", "~> 4.0"
|
33
34
|
spec.add_dependency "faker", "~> 1.7"
|
34
35
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rom-factory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janis Miezitis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-10-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dry-configurable
|
@@ -17,42 +17,62 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '0.
|
20
|
+
version: '0.7'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '0.
|
27
|
+
version: '0.7'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: dry-core
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '0.
|
34
|
+
version: '0.3'
|
35
|
+
- - ">="
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.3.1
|
35
38
|
type: :runtime
|
36
39
|
prerelease: false
|
37
40
|
version_requirements: !ruby/object:Gem::Requirement
|
38
41
|
requirements:
|
39
42
|
- - "~>"
|
40
43
|
- !ruby/object:Gem::Version
|
41
|
-
version: '0.
|
44
|
+
version: '0.3'
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.3.1
|
42
48
|
- !ruby/object:Gem::Dependency
|
43
49
|
name: dry-struct
|
44
50
|
requirement: !ruby/object:Gem::Requirement
|
45
51
|
requirements:
|
46
52
|
- - "~>"
|
47
53
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0.
|
54
|
+
version: '0.3'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.3'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rom-core
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.0'
|
49
69
|
type: :runtime
|
50
70
|
prerelease: false
|
51
71
|
version_requirements: !ruby/object:Gem::Requirement
|
52
72
|
requirements:
|
53
73
|
- - "~>"
|
54
74
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0
|
75
|
+
version: '4.0'
|
56
76
|
- !ruby/object:Gem::Dependency
|
57
77
|
name: faker
|
58
78
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,21 +98,27 @@ files:
|
|
78
98
|
- ".gitignore"
|
79
99
|
- ".rspec"
|
80
100
|
- ".travis.yml"
|
101
|
+
- ".yardopts"
|
81
102
|
- CHANGELOG.md
|
82
103
|
- CODE_OF_CONDUCT.md
|
83
104
|
- Gemfile
|
84
105
|
- LICENSE.txt
|
85
106
|
- README.md
|
86
107
|
- Rakefile
|
108
|
+
- benchmarks/basic.rb
|
87
109
|
- lib/rom-factory.rb
|
88
110
|
- lib/rom/factory.rb
|
111
|
+
- lib/rom/factory/attribute_registry.rb
|
112
|
+
- lib/rom/factory/attributes.rb
|
113
|
+
- lib/rom/factory/attributes/association.rb
|
89
114
|
- lib/rom/factory/attributes/callable.rb
|
90
|
-
- lib/rom/factory/attributes/regular.rb
|
91
115
|
- lib/rom/factory/attributes/sequence.rb
|
116
|
+
- lib/rom/factory/attributes/value.rb
|
92
117
|
- lib/rom/factory/builder.rb
|
118
|
+
- lib/rom/factory/builder/persistable.rb
|
93
119
|
- lib/rom/factory/dsl.rb
|
94
120
|
- lib/rom/factory/factories.rb
|
95
|
-
- lib/rom/factory/
|
121
|
+
- lib/rom/factory/tuple_evaluator.rb
|
96
122
|
- lib/rom/factory/version.rb
|
97
123
|
- rom-factory.gemspec
|
98
124
|
homepage: https://github.com/rom-rb/rom-factory
|
@@ -116,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
142
|
version: '0'
|
117
143
|
requirements: []
|
118
144
|
rubyforge_project:
|
119
|
-
rubygems_version: 2.6.
|
145
|
+
rubygems_version: 2.6.11
|
120
146
|
signing_key:
|
121
147
|
specification_version: 4
|
122
148
|
summary: ROM based builder library to make your specs awesome. DSL partially inspired
|
data/lib/rom/factory/struct.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
require 'dry/struct'
|
2
|
-
require 'dry/core/cache'
|
3
|
-
require 'dry/core/class_builder'
|
4
|
-
|
5
|
-
module ROM::Factory
|
6
|
-
class Struct < Dry::Struct
|
7
|
-
extend Dry::Core::Cache
|
8
|
-
|
9
|
-
def self.define(name, schema)
|
10
|
-
fetch_or_store(schema) do
|
11
|
-
id = Dry::Core::Inflector.classify(Dry::Core::Inflector.singularize(name))
|
12
|
-
|
13
|
-
Dry::Core::ClassBuilder.new(name: "ROM::Factory::Struct[#{id}]", parent: self).call do |klass|
|
14
|
-
schema.each do |attr|
|
15
|
-
klass.attribute attr.name, attr.type
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|