zyra 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![zyra](https://raw.githubusercontent.com/darthjee/zyra/master/zyra.jpg)
|
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
|