standard_procedure_fabrik 0.1.2 → 0.2.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/.devcontainer/devcontainer.json +31 -0
- data/Guardfile +22 -0
- data/README.md +52 -43
- data/checksums/standard_procedure_fabrik-0.2.0.gem.sha512 +1 -0
- data/lib/fabrik/database.rb +26 -15
- data/lib/fabrik/version.rb +1 -1
- metadata +6 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af004e81e6ae299bb2cbf3d99f2b44a8f133b439292915ac016d1c4d4ca52233
|
4
|
+
data.tar.gz: 958c26fa0b8ab2b15e6bc0c4098cee7cb206f749f6e7ccda97074feca8515b4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b104ef644aed04238e021d67a0e06c91961d4b68323bba5adc9a5814a429b3304558c0381fff018a3a6c302d2f766dc1e9513a62cd0246a5052a43376cec4073
|
7
|
+
data.tar.gz: 751f1a5ea6ba84141cd82f55ac70c3332fa3e34eddee32a25388e0880b76611334603e6604bbd06d557741998a31cbaa0eb6ce49ec60233d0ddbdbeaf123954c
|
@@ -0,0 +1,31 @@
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/ruby
|
3
|
+
{
|
4
|
+
"name": "Ruby",
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
6
|
+
"image": "mcr.microsoft.com/devcontainers/ruby:1-3.3-bullseye",
|
7
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
8
|
+
// "features": {},
|
9
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
10
|
+
// "forwardPorts": [],
|
11
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
12
|
+
// "postCreateCommand": "ruby --version",
|
13
|
+
"customizations": {
|
14
|
+
"vscode": {
|
15
|
+
"extensions": [
|
16
|
+
"Shopify.ruby-extensions-pack",
|
17
|
+
"testdouble.vscode-standard-ruby",
|
18
|
+
"manuelpuyol.erb-linter",
|
19
|
+
"Shopify.ruby-lsp",
|
20
|
+
"aki77.rails-db-schema",
|
21
|
+
"miguel-savignano.ruby-symbols",
|
22
|
+
"sibiraj-s.vscode-scss-formatter",
|
23
|
+
"Thadeu.vscode-run-rspec-file",
|
24
|
+
"Cronos87.yaml-symbols",
|
25
|
+
"aliariff.vscode-erb-beautify"
|
26
|
+
]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
30
|
+
// "remoteUser": "root"
|
31
|
+
}
|
data/Guardfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
group :development do
|
2
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
3
|
+
watch(%r{^spec/.+_spec.rb$})
|
4
|
+
watch(%r{^lib/(.+).rb$}) { "spec" }
|
5
|
+
end
|
6
|
+
|
7
|
+
guard :bundler do
|
8
|
+
require "guard/bundler"
|
9
|
+
require "guard/bundler/verify"
|
10
|
+
helper = Guard::Bundler::Verify.new
|
11
|
+
|
12
|
+
files = ["Gemfile"]
|
13
|
+
files += Dir["*.gemspec"] if files.any? { |f| helper.uses_gemspec?(f) }
|
14
|
+
|
15
|
+
# Assume files are symlinked from somewhere
|
16
|
+
files.each { |file| watch(helper.real_path(file)) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
guard :standardrb, fix: true, all_on_start: true, progress: true do
|
21
|
+
watch(/.+.rb$/)
|
22
|
+
end
|
data/README.md
CHANGED
@@ -43,16 +43,21 @@ When you're writing a spec, you probably only care about one aspect of the model
|
|
43
43
|
So Fabrik allows you to set default attributes; then when you're creating a model, you can specify the ones you're interested in and ignore all the rest.
|
44
44
|
|
45
45
|
```ruby
|
46
|
-
db.
|
46
|
+
@db = Fabrik.db
|
47
|
+
@db.configure do
|
47
48
|
with Person do
|
48
|
-
|
49
|
+
first_name { Faker::Name.first_name }
|
50
|
+
last_name { Faker::Name.last_name }
|
51
|
+
email { |person| Faker::Internet.email(name: person.first_name.downcase) }
|
52
|
+
age 33
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
52
|
-
@alice = db.people.create
|
56
|
+
@alice = @db.people.create first_name: "Alice"
|
53
57
|
|
54
58
|
puts @alice.first_name # => Alice
|
55
|
-
puts @alice.last_name # =>
|
59
|
+
puts @alice.last_name # => Hermann
|
60
|
+
puts @alice.email # => alice@some-domain.com
|
56
61
|
puts @alice.age # => 33
|
57
62
|
```
|
58
63
|
|
@@ -63,16 +68,17 @@ When you've got a database packed with existing data, you don't want your seeds
|
|
63
68
|
So Fabrik lets you define what makes a model unique. Then when you create it, it checks for an existing record first and only creates a new one if the original is not found.
|
64
69
|
|
65
70
|
```ruby
|
66
|
-
db.configure do
|
71
|
+
@db.configure do
|
67
72
|
with Person do
|
68
|
-
|
73
|
+
unique :email
|
69
74
|
end
|
70
75
|
end
|
71
|
-
@alice = db.people.create first_name: "Alice", last_name: "Aardvark", email: "alice@example.com"
|
72
|
-
@zac = db.people.create first_name: "Zac", last_name: "Zebra", email: "alice@example.com"
|
76
|
+
@alice = @db.people.create first_name: "Alice", last_name: "Aardvark", email: "alice@example.com"
|
77
|
+
@zac = @db.people.create first_name: "Zac", last_name: "Zebra", email: "alice@example.com"
|
73
78
|
|
74
79
|
@alice == @zac # => true
|
75
80
|
puts @zac.first_name # => Alice
|
81
|
+
puts @zac.last_name # => Aardvark
|
76
82
|
```
|
77
83
|
|
78
84
|
### Special processing
|
@@ -82,18 +88,18 @@ Some models are special. They can't live on their own. Their invariants won't
|
|
82
88
|
So Fabrik lets you define specific processing that happens after a new record is created.
|
83
89
|
|
84
90
|
```ruby
|
85
|
-
db.configure do
|
91
|
+
@db.configure do
|
86
92
|
with Company do
|
87
|
-
|
88
|
-
after_create
|
89
|
-
db.employees.create company: company, role: "CEO"
|
90
|
-
end
|
93
|
+
name { Faker::Company.name }
|
94
|
+
after_create { |company| employees.create company: company, role: "CEO" }
|
91
95
|
end
|
92
96
|
with Employee do
|
93
|
-
|
97
|
+
first_name { Faker::Name.first_name }
|
98
|
+
last_name { Faker::Name.last_name }
|
99
|
+
role "Cleaner"
|
94
100
|
end
|
95
101
|
end
|
96
|
-
db.companies.create name: "MegaCorp"
|
102
|
+
@db.companies.create name: "MegaCorp"
|
97
103
|
puts Company.find_by(name: "MegaCorp").employees.size # => 1
|
98
104
|
```
|
99
105
|
|
@@ -104,9 +110,9 @@ You've created a load of models. And you need to reference them to create more
|
|
104
110
|
So Fabrik let's you give your models a label. And then refer back to that model using the label.
|
105
111
|
|
106
112
|
```ruby
|
107
|
-
db.people.create :alice, first_name: "Alice"
|
113
|
+
@db.people.create :alice, first_name: "Alice"
|
108
114
|
|
109
|
-
puts db.people
|
115
|
+
puts @db.people.alice # => Alice
|
110
116
|
```
|
111
117
|
|
112
118
|
## Classes and naming
|
@@ -120,7 +126,7 @@ class Intergalactic::Spaceship < ApplicationRecord
|
|
120
126
|
# whatever
|
121
127
|
end
|
122
128
|
|
123
|
-
db.intergalactic_spaceships.create :ufo
|
129
|
+
@db.intergalactic_spaceships.create :ufo
|
124
130
|
|
125
131
|
puts Intergalactic::Spaceship.count # => 1
|
126
132
|
```
|
@@ -130,13 +136,13 @@ Or maybe you're writing a Rails engine and all your classes are namespaced. Typ
|
|
130
136
|
So Fabrik lets you register an alternative name for your classes.
|
131
137
|
|
132
138
|
```ruby
|
133
|
-
db.configure do
|
139
|
+
@db.configure do
|
134
140
|
with MyAccountsPackageEngine::Invoice, as: :invoice do
|
135
|
-
|
141
|
+
due_date { 7.days.from_now }
|
136
142
|
end
|
137
143
|
end
|
138
144
|
|
139
|
-
db.invoices.create
|
145
|
+
@invoice = @db.invoices.create
|
140
146
|
```
|
141
147
|
|
142
148
|
## Installation
|
@@ -151,15 +157,34 @@ If you're only using it for tests, add it to your `test` group. If you're using
|
|
151
157
|
|
152
158
|
## Usage
|
153
159
|
|
154
|
-
###
|
160
|
+
### Global
|
161
|
+
|
162
|
+
Most of the time, you can use the global `Fabrik.db` instance.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
Fabrik.configure do
|
166
|
+
with Person do
|
167
|
+
first_name { Faker::Name.first_name }
|
168
|
+
last_name { Faker::Name.last_name }
|
169
|
+
email { Faker::Internet.email }
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
Fabrik.db.people.create :alice, first_name: "Alice"
|
174
|
+
```
|
175
|
+
|
176
|
+
Watch out - because this uses a global instance, it's not thread-safe. That's not an issue if you're just using it for database seeds or in most test runners (single-threaded or parallelised with multiple processes). But it *might* cause problems if you're using threads to parallelise your tests, or you're reconfiguring while your application is running.
|
177
|
+
|
178
|
+
### Localised
|
155
179
|
|
156
180
|
Create an instance of a [Fabrik::Database](/lib/fabrik/database.rb), configure it and use it.
|
157
181
|
|
158
182
|
```ruby
|
159
|
-
db = Fabrik::Database.new
|
160
|
-
db.configure do
|
183
|
+
db = Fabrik::Database.new do
|
161
184
|
with Person do
|
162
|
-
|
185
|
+
first_name { Faker::Name.first_name }
|
186
|
+
last_name { Faker::Name.last_name }
|
187
|
+
email { Faker::Internet.email }
|
163
188
|
end
|
164
189
|
end
|
165
190
|
|
@@ -170,9 +195,8 @@ In an RSpec:
|
|
170
195
|
|
171
196
|
```ruby
|
172
197
|
RSpec.describe "Whatever" do
|
173
|
-
let(:db)
|
174
|
-
|
175
|
-
db.configure do
|
198
|
+
let(:db) do
|
199
|
+
Fabrik::Database.new do
|
176
200
|
# ... whatever
|
177
201
|
end
|
178
202
|
end
|
@@ -181,21 +205,6 @@ end
|
|
181
205
|
|
182
206
|
In a minitest ... I don't know, I've not used minitest in years but I'm sure it's easy enough.
|
183
207
|
|
184
|
-
### Global (for seeds and generally creating stuff)
|
185
|
-
|
186
|
-
Configure the global `Fabrik::Database`, then use it.
|
187
|
-
|
188
|
-
```ruby
|
189
|
-
Fabrik.configure do
|
190
|
-
with Person do
|
191
|
-
defaults first_name: ->(db) { Faker::Name.first_name }, last_name: ->(db) { Faker::Name.last_name }, email: ->(db) { Faker::Internet.email }
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
Fabrik.db.people.create :alice, first_name: "Alice"
|
196
|
-
```
|
197
|
-
|
198
|
-
Watch out - because this uses a global instance, it's not thread-safe. That's not an issue if you're just using it for database seeds or in most test runners(single-threaded or parallelised with multiple processes). But it *might* cause problems if you're using threads to parallelise your tests, or you're reconfiguring while your application is running.
|
199
208
|
|
200
209
|
## Development
|
201
210
|
|
@@ -0,0 +1 @@
|
|
1
|
+
8d4ca660ab0691aa90bf0a3c61e8ef85765b746d5d575eeec1225bc6dc3c4fbb8fe8945e7020ec037f46f0d3a5fe6dc0f34b55bc1c0a364d949684220a7a15e9
|
data/lib/fabrik/database.rb
CHANGED
@@ -9,7 +9,9 @@ module Fabrik
|
|
9
9
|
|
10
10
|
def register(klass, as: nil, &block)
|
11
11
|
blueprint_name = (as.nil? ? blueprint_name_for(klass) : as.to_s.pluralize).to_sym
|
12
|
-
|
12
|
+
blueprint = Blueprint.new(klass, &block)
|
13
|
+
@blueprints[blueprint_name] = blueprint
|
14
|
+
@blueprints[klass] = blueprint
|
13
15
|
|
14
16
|
define_singleton_method blueprint_name do
|
15
17
|
proxy_for(blueprint_name)
|
@@ -19,25 +21,24 @@ module Fabrik
|
|
19
21
|
end
|
20
22
|
alias_method :with, :register
|
21
23
|
|
22
|
-
def defaults_for(klass) =
|
24
|
+
def defaults_for(klass) = @blueprints[klass].default_attributes
|
23
25
|
|
24
|
-
def
|
26
|
+
def unique_keys_for(klass) = @blueprints[klass].unique_keys
|
25
27
|
|
26
|
-
def after_create_for(klass) =
|
28
|
+
def after_create_for(klass) = @blueprints[klass].callback
|
27
29
|
|
28
30
|
def method_missing(method_name, *, &block) = (klass = class_from(method_name)).nil? ? super : register(klass)
|
29
31
|
|
30
32
|
def respond_to_missing?(method_name, include_private = false) = !class_from(method_name).nil? || super
|
31
33
|
|
32
|
-
def initialize
|
34
|
+
def initialize &config
|
33
35
|
@blueprints = {}
|
34
36
|
@records = {}
|
37
|
+
instance_eval(&config) unless config.nil?
|
35
38
|
end
|
36
39
|
|
37
40
|
private def blueprint_name_for(klass) = klass.name.split("::").map(&:underscore).join("_").pluralize
|
38
41
|
|
39
|
-
private def blueprint_for(klass) = @blueprints.values.find { |bp| bp.klass == klass }
|
40
|
-
|
41
42
|
private def proxy_for(blueprint_name)
|
42
43
|
@records[blueprint_name] ||= Proxy.new(self, @blueprints[blueprint_name])
|
43
44
|
end
|
@@ -51,32 +52,42 @@ module Fabrik
|
|
51
52
|
end
|
52
53
|
|
53
54
|
class Blueprint
|
54
|
-
attr_reader :klass, :default_attributes, :
|
55
|
+
attr_reader :klass, :default_attributes, :unique_keys, :callback
|
55
56
|
|
56
57
|
def defaults(**default_attributes) = @default_attributes = default_attributes
|
57
58
|
|
58
|
-
def
|
59
|
+
def unique(*keys) = @unique_keys = keys
|
59
60
|
|
60
61
|
def after_create(&block) = @callback = block
|
61
62
|
|
62
|
-
def call_after_create(record, db) = @callback
|
63
|
+
def call_after_create(record, db) = @callback.nil? ? nil : db.instance_exec(record, &@callback)
|
64
|
+
|
65
|
+
def method_missing(method_name, *args, &block)
|
66
|
+
@default_attributes[method_name.to_sym] = args.first.nil? ? block : ->(_) { args.first }
|
67
|
+
end
|
68
|
+
|
69
|
+
def respond_to_missing?(method_name, include_private = false) = @default_attributes.key?(method_name.to_sym) || super
|
63
70
|
|
64
71
|
def initialize(klass, &block)
|
65
72
|
@klass = klass
|
66
73
|
@default_attributes = {}
|
67
|
-
@
|
74
|
+
@unique_keys = []
|
68
75
|
instance_eval(&block) if block_given?
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
72
79
|
class Proxy < SimpleDelegator
|
73
80
|
def create(label = nil, **attributes)
|
74
|
-
(@blueprint.
|
81
|
+
(@blueprint.unique_keys.any? ? find_or_create_record(attributes) : create_record(attributes)).tap do |record|
|
75
82
|
@records[label.to_sym] = record if label
|
76
83
|
end
|
77
84
|
end
|
78
85
|
|
79
|
-
def
|
86
|
+
def method_missing(method_name, *args, &block)
|
87
|
+
@records[method_name.to_sym]
|
88
|
+
end
|
89
|
+
|
90
|
+
def respond_to_missing?(method_name, include_private = false) = @@records.key?(label.to_sym) || super
|
80
91
|
|
81
92
|
def initialize(db, blueprint)
|
82
93
|
@db = db
|
@@ -85,7 +96,7 @@ module Fabrik
|
|
85
96
|
super(klass)
|
86
97
|
end
|
87
98
|
|
88
|
-
private def
|
99
|
+
private def unique_keys = @blueprint.unique_keys
|
89
100
|
|
90
101
|
private def klass = @blueprint.klass
|
91
102
|
|
@@ -96,7 +107,7 @@ module Fabrik
|
|
96
107
|
find_record(attributes) || create_record(attributes)
|
97
108
|
end
|
98
109
|
|
99
|
-
private def find_record(attributes) = attributes.slice(*
|
110
|
+
private def find_record(attributes) = attributes.slice(*unique_keys).empty? ? nil : klass.find_by(**attributes.slice(*unique_keys))
|
100
111
|
|
101
112
|
private def create_record(attributes)
|
102
113
|
klass.create(**attributes_with_defaults(attributes)).tap do |record|
|
data/lib/fabrik/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: standard_procedure_fabrik
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rahoul Baruah
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-06 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: activesupport
|
@@ -24,20 +23,6 @@ dependencies:
|
|
24
23
|
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: 6.0.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: standard-procedure-plumbing
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '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'
|
41
26
|
description: Fixtures, fittings and seeds
|
42
27
|
email:
|
43
28
|
- rahoulb@echodek.co
|
@@ -45,15 +30,18 @@ executables: []
|
|
45
30
|
extensions: []
|
46
31
|
extra_rdoc_files: []
|
47
32
|
files:
|
33
|
+
- ".devcontainer/devcontainer.json"
|
48
34
|
- ".rspec"
|
49
35
|
- ".standard.yml"
|
50
36
|
- CHANGELOG.md
|
37
|
+
- Guardfile
|
51
38
|
- LICENSE
|
52
39
|
- README.md
|
53
40
|
- Rakefile
|
54
41
|
- checksums/standard_procedure_fabrik-0.1.0.gem.sha512
|
55
42
|
- checksums/standard_procedure_fabrik-0.1.1.gem.sha512
|
56
43
|
- checksums/standard_procedure_fabrik-0.1.2.gem.sha512
|
44
|
+
- checksums/standard_procedure_fabrik-0.2.0.gem.sha512
|
57
45
|
- lib/fabrik.rb
|
58
46
|
- lib/fabrik/database.rb
|
59
47
|
- lib/fabrik/version.rb
|
@@ -66,7 +54,6 @@ metadata:
|
|
66
54
|
homepage_uri: https://www.theartandscienceofruby.com
|
67
55
|
source_code_uri: https://github.com/standard-procedure/fabrik
|
68
56
|
changelog_uri: https://github.com/standard-procedure/fabrik/blob/main/CHANGELOG.md
|
69
|
-
post_install_message:
|
70
57
|
rdoc_options: []
|
71
58
|
require_paths:
|
72
59
|
- lib
|
@@ -81,8 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
68
|
- !ruby/object:Gem::Version
|
82
69
|
version: '0'
|
83
70
|
requirements: []
|
84
|
-
rubygems_version: 3.
|
85
|
-
signing_key:
|
71
|
+
rubygems_version: 3.6.2
|
86
72
|
specification_version: 4
|
87
73
|
summary: Fabrik
|
88
74
|
test_files: []
|