zyra 0.0.2 → 1.0.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/.rubocop.yml +1 -0
- data/README.md +67 -1
- data/config/yardstick.yml +7 -15
- data/lib/zyra/{builder.rb → creator.rb} +18 -49
- data/lib/zyra/exceptions.rb +3 -2
- data/lib/zyra/finder.rb +103 -0
- data/lib/zyra/finder_creator.rb +162 -0
- data/lib/zyra/registry.rb +179 -13
- data/lib/zyra/version.rb +1 -1
- data/lib/zyra.rb +130 -56
- data/spec/integration/readme/zyra_spec.rb +54 -0
- data/spec/integration/yard/zyra/registry_spec.rb +135 -0
- data/spec/integration/yard/zyra_spec.rb +128 -0
- data/spec/lib/zyra/creator_spec.rb +75 -0
- data/spec/lib/zyra/finder_creator_spec.rb +263 -0
- data/spec/lib/zyra/finder_spec.rb +117 -0
- data/spec/lib/zyra/registry_spec.rb +6 -92
- data/spec/lib/zyra_spec.rb +8 -144
- data/spec/spec_helper.rb +13 -0
- data/spec/support/factories/user.rb +9 -0
- data/spec/support/factory_bot.rb +7 -0
- data/spec/support/models/post.rb +5 -0
- data/spec/support/models/user.rb +1 -0
- data/spec/support/schema.rb +8 -0
- data/spec/support/shared_examples/registry/find_or_create.rb +130 -0
- data/spec/support/shared_examples/registry/on.rb +42 -0
- data/spec/support/shared_examples/registry/register.rb +54 -0
- data/zyra.gemspec +3 -1
- metadata +53 -12
- data/spec/lib/zyra/builder_spec.rb +0 -129
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c8f905bbdb0fec56487d7be0a0999983394975cfe3878397ffa7b12f8b43229
|
4
|
+
data.tar.gz: ef03ada86291579a39b0f1dbb9b4bd6dc868090752240522eb22a5c89d6ad559
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6132bf64c4fb482e408c24d6e84964bc7862ed31890ee3257608476ee659e63252db0f01602da4afe367ce7a8f6661c65d7099634a9982aa97b20c6d10817d29
|
7
|
+
data.tar.gz: 8b79a9c2f74a31b83e50e98571920f6b3d2e8ac441c58a64e88dd9224a2b5bfe7a5bd043bc5b131c63d2857d6840ff1b9e8f0f1fa1e1009bc213a7fcc808d29b
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -9,9 +9,15 @@ Zyra
|
|
9
9
|
|
10
10
|

|
11
11
|
|
12
|
+
Zyra is intented to easy the seeding stage of projects by ensuring an
|
13
|
+
entity exists without having to reinsert it every time in the database
|
14
|
+
|
15
|
+
The process is done by registering a model class, then performing
|
16
|
+
a creation in case of missing the entry
|
17
|
+
|
12
18
|
Yard Documentation
|
13
19
|
-------------------
|
14
|
-
[https://www.rubydoc.info/gems/zyra/0.0
|
20
|
+
[https://www.rubydoc.info/gems/zyra/1.0.0](https://www.rubydoc.info/gems/zyra/1.0.0)
|
15
21
|
|
16
22
|
Installation
|
17
23
|
---------------
|
@@ -31,3 +37,63 @@ Installation
|
|
31
37
|
```bash
|
32
38
|
bundle install zyra
|
33
39
|
```
|
40
|
+
|
41
|
+
Usage
|
42
|
+
-----
|
43
|
+
|
44
|
+
The usage is done by registering a model, adding hooks
|
45
|
+
and calling `find_or_create` and passing a block to be executed
|
46
|
+
after
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Zyra
|
50
|
+
.register(User, find_by: :email)
|
51
|
+
.on(:build) do |user|
|
52
|
+
user.reference = SecureRandom.hex(16)
|
53
|
+
end
|
54
|
+
|
55
|
+
attributes = {
|
56
|
+
email: 'usr@srv.com',
|
57
|
+
name: 'Some User',
|
58
|
+
password: 'pass'
|
59
|
+
}
|
60
|
+
|
61
|
+
user = Zyra.find_or_create(:user, attributes) do |usr|
|
62
|
+
usr.update(attributes)
|
63
|
+
end
|
64
|
+
|
65
|
+
# returns an instance of User that is persisted in the database
|
66
|
+
# user.email is the key as 'usr@srv.com'
|
67
|
+
# user.name will always be updated to 'Some User'
|
68
|
+
# user.password will always be updated to 'pass'
|
69
|
+
# user.reference will be generated in the first time, and never again regenerated
|
70
|
+
```
|
71
|
+
|
72
|
+
## Hooks
|
73
|
+
|
74
|
+
hooks can be registered when registering a model or after to be executed in 4
|
75
|
+
points, `found`, `build`, `create` and `return`
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Zyra
|
79
|
+
.register(User, find_by: :email)
|
80
|
+
.on(:build) do |user|
|
81
|
+
user.posts.build(name: 'first', content: 'some content')
|
82
|
+
end
|
83
|
+
|
84
|
+
Zyra.on(:user, :return) do |user|
|
85
|
+
user.update(reference: SecureRandom.hex(16))
|
86
|
+
end
|
87
|
+
|
88
|
+
attributes = {
|
89
|
+
email: 'usr@srv.com',
|
90
|
+
name: 'Some User',
|
91
|
+
password: 'pass'
|
92
|
+
}
|
93
|
+
|
94
|
+
user = Zyra.find_or_create(:user, attributes).reload
|
95
|
+
|
96
|
+
# Returns a User with email 'usr@srv.com'
|
97
|
+
# Creates a post for the user, only in the first time
|
98
|
+
# Regenerates the reference every time the code is ran
|
99
|
+
```
|
data/config/yardstick.yml
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
threshold:
|
1
|
+
threshold: 100
|
2
2
|
require_exact_threshold: false
|
3
3
|
rules:
|
4
4
|
ApiTag::Presence:
|
@@ -15,25 +15,17 @@ rules:
|
|
15
15
|
exclude: []
|
16
16
|
ExampleTag:
|
17
17
|
enabled: true
|
18
|
-
|
19
|
-
- Zyra.register
|
20
|
-
- Zyra.after
|
21
|
-
- Zyra.build
|
22
|
-
- Zyra.create
|
23
|
-
- Zyra::Builder#after
|
24
|
-
- Zyra::Builder#build
|
25
|
-
- Zyra::Builder#create
|
26
|
-
- Zyra::Registry#register
|
18
|
+
excludei: []
|
27
19
|
ReturnTag:
|
28
20
|
enabled: true
|
29
|
-
exclude:
|
30
|
-
- Zyra.after
|
31
|
-
- Zyra.build
|
32
|
-
- Zyra.create
|
21
|
+
exclude: []
|
33
22
|
Summary::Presence:
|
34
23
|
enabled: true
|
35
24
|
exclude:
|
36
|
-
- Zyra::
|
25
|
+
- Zyra::Finder#initialize
|
26
|
+
- Zyra::Creator#initialize
|
27
|
+
- Zyra::FinderCreator#initialize
|
28
|
+
- Zyra::Registry#initialize
|
37
29
|
Summary::Length:
|
38
30
|
enabled: true
|
39
31
|
exclude: []
|
@@ -5,15 +5,16 @@ module Zyra
|
|
5
5
|
# @author Darthjee
|
6
6
|
#
|
7
7
|
# Class responsible for building a model
|
8
|
-
class
|
8
|
+
class Creator
|
9
9
|
# @param model_class [Class] Model class to be initialized
|
10
10
|
# into a model
|
11
|
-
|
11
|
+
# @param event_registry [Jace::Registry] event registry to handle events
|
12
|
+
def initialize(model_class, event_registry:)
|
12
13
|
@model_class = model_class
|
14
|
+
@event_registry = event_registry
|
13
15
|
end
|
14
16
|
|
15
|
-
#
|
16
|
-
# Builds an instance of the registered model class
|
17
|
+
# Creates an instance of the registered model class
|
17
18
|
#
|
18
19
|
# @param attributes [Hash] attributes to be set in the model
|
19
20
|
# @param block [Proc] block to be ran after where more attributes
|
@@ -22,22 +23,6 @@ module Zyra
|
|
22
23
|
# @yield [Object] Instance of the model class
|
23
24
|
#
|
24
25
|
# @return [Object] an instance of model class
|
25
|
-
def build(**attributes, &block)
|
26
|
-
block ||= proc {}
|
27
|
-
|
28
|
-
model_class.new(attributes).tap(&block).tap do |model|
|
29
|
-
event_registry.trigger(:build, model)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# @api public
|
34
|
-
# Creates an instance of the registered model class
|
35
|
-
#
|
36
|
-
# This behaves like {#build}, but persists the entry
|
37
|
-
#
|
38
|
-
# @param (see #build)
|
39
|
-
# @yield (see #build)
|
40
|
-
# @return (see #build)
|
41
26
|
def create(**attributes, &block)
|
42
27
|
model = build(**attributes, &block)
|
43
28
|
|
@@ -46,44 +31,30 @@ module Zyra
|
|
46
31
|
end
|
47
32
|
end
|
48
33
|
|
49
|
-
|
50
|
-
# Register a handler on a certain event
|
51
|
-
#
|
52
|
-
# Possible events are +build+, +create+
|
53
|
-
#
|
54
|
-
# @param event [Symbol,String] event to be watched.
|
55
|
-
# @param block [Proc] block to be executed when the event is called
|
56
|
-
#
|
57
|
-
# @yield [Object] the model built
|
58
|
-
#
|
59
|
-
# @return [Builder] the builder itself
|
60
|
-
def after(event, &block)
|
61
|
-
tap { event_registry.register(event, &block) }
|
62
|
-
end
|
34
|
+
protected
|
63
35
|
|
64
|
-
#
|
65
|
-
#
|
66
|
-
# This is used mostly for rspec expectations
|
67
|
-
#
|
68
|
-
# @param other [Object] other object to be compared
|
36
|
+
# @private
|
37
|
+
# Builds an instance of the registered model class
|
69
38
|
#
|
70
|
-
# @
|
71
|
-
|
72
|
-
|
39
|
+
# @param (see #create)
|
40
|
+
# @yield (see #create)
|
41
|
+
# @return (see #create)
|
42
|
+
def build(**attributes, &block)
|
43
|
+
block ||= proc {}
|
73
44
|
|
74
|
-
|
45
|
+
model_class.new(attributes).tap(&block).tap do |model|
|
46
|
+
event_registry.trigger(:build, model)
|
47
|
+
end
|
75
48
|
end
|
76
49
|
|
77
|
-
protected
|
78
|
-
|
79
50
|
# @method model_class
|
80
51
|
# @api private
|
81
52
|
#
|
82
53
|
# Model class to be initialized into a model
|
83
54
|
#
|
84
55
|
# @return [Class]
|
85
|
-
attr_reader :model_class
|
86
56
|
|
57
|
+
# @method event_registry
|
87
58
|
# @private
|
88
59
|
#
|
89
60
|
# Event registry
|
@@ -92,8 +63,6 @@ module Zyra
|
|
92
63
|
# post build or creating events
|
93
64
|
#
|
94
65
|
# @return [Jace::Registry]
|
95
|
-
|
96
|
-
@event_registry ||= Jace::Registry.new
|
97
|
-
end
|
66
|
+
attr_reader :model_class, :event_registry
|
98
67
|
end
|
99
68
|
end
|
data/lib/zyra/exceptions.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'sinclair'
|
4
3
|
require 'jace'
|
5
4
|
|
6
5
|
module Zyra
|
7
6
|
module Exceptions
|
8
|
-
|
7
|
+
# Exception returned when a model has not been registered
|
8
|
+
# and there is an attempt to use it
|
9
|
+
class NotRegistered < StandardError; end
|
9
10
|
end
|
10
11
|
end
|
data/lib/zyra/finder.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zyra
|
4
|
+
# @api private
|
5
|
+
# @author Darthjee
|
6
|
+
#
|
7
|
+
# Class responsible for finding a model in the DB
|
8
|
+
class Finder
|
9
|
+
# @param model_class [Class] Model class that does the ORM
|
10
|
+
# @param keys [Array<Symbol,String>] keys used when searching
|
11
|
+
# for the entry
|
12
|
+
# @param event_registry [Jace::Registry] event registry to handle events
|
13
|
+
def initialize(model_class, keys, event_registry:)
|
14
|
+
@model_class = model_class
|
15
|
+
@keys = [keys].flatten.map(&:to_sym)
|
16
|
+
@event_registry = event_registry
|
17
|
+
end
|
18
|
+
|
19
|
+
# Search the entry in the database
|
20
|
+
#
|
21
|
+
# The query is done using part of the expected
|
22
|
+
# attributes filtered by the configured keys}
|
23
|
+
#
|
24
|
+
# if the model is found an event is triggered
|
25
|
+
#
|
26
|
+
# @param attributes [Hash] expected model attribiutes
|
27
|
+
# @param block [Proc] block to be ran after where
|
28
|
+
# more attributes will be set
|
29
|
+
#
|
30
|
+
# @yield [Object] Instance of the model class found
|
31
|
+
#
|
32
|
+
# @return [Object] the model from the database
|
33
|
+
def find(attributes, &block)
|
34
|
+
model = find_by(attributes)
|
35
|
+
return unless model
|
36
|
+
|
37
|
+
block ||= proc {}
|
38
|
+
model.tap(&block)
|
39
|
+
|
40
|
+
event_registry.trigger(:found, model) { model }
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# @method model_class
|
46
|
+
# @api private
|
47
|
+
#
|
48
|
+
# Model class to be initialized into a model
|
49
|
+
#
|
50
|
+
# @return [Class]
|
51
|
+
|
52
|
+
# @method keys
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
# Keys used when finding a model
|
56
|
+
#
|
57
|
+
# @return [Array<Symbol>]
|
58
|
+
|
59
|
+
# @method event_registry
|
60
|
+
# @private
|
61
|
+
#
|
62
|
+
# Event registry
|
63
|
+
#
|
64
|
+
# The event registry will contain all handlers for
|
65
|
+
# post found events
|
66
|
+
#
|
67
|
+
# @return [Jace::Registry]
|
68
|
+
attr_reader :model_class, :keys, :event_registry
|
69
|
+
|
70
|
+
# private
|
71
|
+
#
|
72
|
+
# Extracts queriable attributes
|
73
|
+
#
|
74
|
+
# The queriable attributes are taken from the expected
|
75
|
+
# attributes filtered by the given keys in the Finder
|
76
|
+
# initialization
|
77
|
+
#
|
78
|
+
# @param (see #find)
|
79
|
+
#
|
80
|
+
# @return [Hash]
|
81
|
+
def query_from(attributes)
|
82
|
+
attributes.symbolize_keys.select do |attribute, _value|
|
83
|
+
keys.include?(attribute)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# @private
|
88
|
+
#
|
89
|
+
# Search the entry in the database
|
90
|
+
#
|
91
|
+
# The query is done using part of the expected
|
92
|
+
# attributes filtered by the configured keys}
|
93
|
+
#
|
94
|
+
# @param (see #find)
|
95
|
+
#
|
96
|
+
# @return (see #find)
|
97
|
+
def find_by(attributes)
|
98
|
+
query = query_from(attributes)
|
99
|
+
|
100
|
+
model_class.find_by(**query)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zyra
|
4
|
+
# @api private
|
5
|
+
# Class responsible for making sure a model existis
|
6
|
+
#
|
7
|
+
# First, the object searchs in he database by the keys, and
|
8
|
+
# if it fails to finds, it creates a new entry
|
9
|
+
class FinderCreator
|
10
|
+
# @param model_class [Class] Model class that does the ORM
|
11
|
+
# @param keys [Array<Symbol,String>] keys used when searching
|
12
|
+
# for the entry
|
13
|
+
def initialize(model_class, keys)
|
14
|
+
@model_class = model_class
|
15
|
+
@keys = [keys].flatten.map(&:to_sym)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Ensures an entry exists in the database
|
19
|
+
#
|
20
|
+
# The query happens first by looking in the database
|
21
|
+
# for an entry using {Finder finder}
|
22
|
+
#
|
23
|
+
# The query only takes some attributes into consideration,
|
24
|
+
#
|
25
|
+
# In case no entry is found, a new one is created using all
|
26
|
+
# the given attributes
|
27
|
+
#
|
28
|
+
# @param attributes [Hash] expected attributes
|
29
|
+
# @param block [Proc] block to be ran after where more attributes
|
30
|
+
# will be set
|
31
|
+
#
|
32
|
+
# @yield [Object] Instance of the model class
|
33
|
+
#
|
34
|
+
# @return [Object] An instance of model either from
|
35
|
+
# database or recently inserted
|
36
|
+
#
|
37
|
+
# @see Zyra::Finder#find
|
38
|
+
# @see Zyra::Creator#create
|
39
|
+
def find_or_create(attributes, &block)
|
40
|
+
model = find(attributes, &block) || create(attributes, &block)
|
41
|
+
|
42
|
+
event_registry.trigger(:return, model) { model }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Register a handler on a certain event
|
46
|
+
#
|
47
|
+
# Possible events are +found+, +build+, +create+
|
48
|
+
# and +return+
|
49
|
+
#
|
50
|
+
# @param event [Symbol,String] event to be watched.
|
51
|
+
# Current events are +found+, +build+, +create+ and +return+
|
52
|
+
# @param block [Proc] block to be executed when the event is called
|
53
|
+
#
|
54
|
+
# @yield [Object] the model to be returned
|
55
|
+
#
|
56
|
+
# @return [Finder] the finder itself
|
57
|
+
def on(event, &block)
|
58
|
+
tap { event_registry.register(event, &block) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks if another finder creator is equal to the current
|
62
|
+
#
|
63
|
+
# This is used mostly for rspec expectations
|
64
|
+
#
|
65
|
+
# @param other [Object] other object to be compared
|
66
|
+
#
|
67
|
+
# @return [TrueClass,FalseClass]
|
68
|
+
def ==(other)
|
69
|
+
return unless other.class == self.class
|
70
|
+
|
71
|
+
other.model_class == model_class &&
|
72
|
+
other.keys == keys
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
# @method model_class
|
78
|
+
# @api private
|
79
|
+
#
|
80
|
+
# Model class to be initialized into a model
|
81
|
+
#
|
82
|
+
# @return [Class]
|
83
|
+
|
84
|
+
# @method keys
|
85
|
+
# @api private
|
86
|
+
#
|
87
|
+
# Keys used when finding a model
|
88
|
+
#
|
89
|
+
# @return [Array<Symbol>]
|
90
|
+
attr_reader :model_class, :keys
|
91
|
+
|
92
|
+
# @private
|
93
|
+
#
|
94
|
+
# Event registry
|
95
|
+
#
|
96
|
+
# The event registry will contain all handlers for
|
97
|
+
# post build or creating events
|
98
|
+
#
|
99
|
+
# @return [Jace::Registry]
|
100
|
+
def event_registry
|
101
|
+
@event_registry ||= Jace::Registry.new
|
102
|
+
end
|
103
|
+
|
104
|
+
# @private
|
105
|
+
#
|
106
|
+
# Returns an instance of {Finder}
|
107
|
+
#
|
108
|
+
# Finder will use the same event registry so that event
|
109
|
+
# handling and registration is centralized
|
110
|
+
#
|
111
|
+
# @return Finder
|
112
|
+
def finder
|
113
|
+
@finder ||= Finder.new(model_class, keys, event_registry: event_registry)
|
114
|
+
end
|
115
|
+
|
116
|
+
# @method find(attributes)
|
117
|
+
#
|
118
|
+
# @private
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
# Search the entry in the database
|
122
|
+
#
|
123
|
+
# The query is done using part of the expected
|
124
|
+
# attributes filtered by the configured keys}
|
125
|
+
#
|
126
|
+
# if the model is found an event is triggered
|
127
|
+
#
|
128
|
+
# @overload find(attributes)
|
129
|
+
# @param attributes [Hash] expected model attribiutes
|
130
|
+
#
|
131
|
+
# @return [Object] the model from the database
|
132
|
+
delegate :find, to: :finder
|
133
|
+
|
134
|
+
# @method create(attributes, &block)
|
135
|
+
# @private
|
136
|
+
# @api private
|
137
|
+
#
|
138
|
+
# Creates an instance of the registered model class
|
139
|
+
#
|
140
|
+
# @overload create(attributes, &block)
|
141
|
+
# @param attributes [Hash] attributes to be set in the model
|
142
|
+
# @param block [Proc] block to be ran after where more attributes
|
143
|
+
# will be set
|
144
|
+
#
|
145
|
+
# @yield [Object] Instance of the model class
|
146
|
+
#
|
147
|
+
# @return [Object] an instance of model class
|
148
|
+
delegate :create, to: :creator
|
149
|
+
|
150
|
+
# @private
|
151
|
+
#
|
152
|
+
# Returns an instance of {Creator}
|
153
|
+
#
|
154
|
+
# Creator will use the same event registry so that event
|
155
|
+
# handling and registration is centralized
|
156
|
+
#
|
157
|
+
# @return Creator
|
158
|
+
def creator
|
159
|
+
@creator ||= Creator.new(model_class, event_registry: event_registry)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|