zyra 0.0.1 → 1.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 +4 -4
- data/.circleci/config.yml +20 -6
- data/.rubocop.yml +1 -0
- data/Dockerfile +1 -1
- data/Dockerfile.circleci +7 -0
- data/README.md +68 -2
- data/config/check_specs.yml +1 -0
- data/config/yardstick.yml +6 -2
- data/docker-compose.yml +11 -0
- data/lib/zyra/creator.rb +68 -0
- data/lib/zyra/exceptions.rb +11 -0
- data/lib/zyra/finder.rb +103 -0
- data/lib/zyra/finder_creator.rb +162 -0
- data/lib/zyra/registry.rb +201 -0
- data/lib/zyra/version.rb +2 -2
- data/lib/zyra.rb +187 -1
- 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 +19 -0
- data/spec/lib/zyra_spec.rb +14 -1
- data/spec/spec_helper.rb +18 -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 +5 -0
- data/spec/support/schema.rb +18 -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 +7 -0
- metadata +107 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62a10b511a268b79401186b32fb0c25fe60cfbe062770f8d6de2d97a6dfee4ea
|
4
|
+
data.tar.gz: 9f5a45fc0216b1e65a7d2f5482a05087ef78787accf8c5e940bf26794e5ca7bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0c63b3d56aec07eacdc683fb8c8084ddb1a5f8c45011bcd8f85ce43a08df85c7d412482256075539038b266d7678dfe36a019dd36267402b8bc625ec4c821a71
|
7
|
+
data.tar.gz: bc6e5d3e3a3d3695716b048c40ea29bfeb43e62f3ffd192fb94f5887526acba84b28dcabaed422d1dd9c9e8caaa9a4b5d51528ac0504a5146b1cb79b8816c548
|
data/.circleci/config.yml
CHANGED
@@ -7,8 +7,12 @@ workflows:
|
|
7
7
|
filters:
|
8
8
|
tags:
|
9
9
|
only: /.*/
|
10
|
+
- checks:
|
11
|
+
filters:
|
12
|
+
tags:
|
13
|
+
only: /.*/
|
10
14
|
- build-and-release:
|
11
|
-
requires: [test]
|
15
|
+
requires: [test, checks]
|
12
16
|
filters:
|
13
17
|
tags:
|
14
18
|
only: /\d+\.\d+\.\d+/
|
@@ -18,7 +22,7 @@ workflows:
|
|
18
22
|
jobs:
|
19
23
|
test:
|
20
24
|
docker:
|
21
|
-
- image: darthjee/
|
25
|
+
- image: darthjee/circleci_rails_gems:1.1.0
|
22
26
|
environment:
|
23
27
|
PROJECT: zyra
|
24
28
|
steps:
|
@@ -32,12 +36,22 @@ jobs:
|
|
32
36
|
- run:
|
33
37
|
name: RSpec
|
34
38
|
command: bundle exec rspec
|
35
|
-
- run:
|
36
|
-
name: Rubocop
|
37
|
-
command: rubocop
|
38
39
|
- run:
|
39
40
|
name: Coverage Test Report
|
40
41
|
command: cc-test-reporter after-build --exit-code $?
|
42
|
+
checks:
|
43
|
+
docker:
|
44
|
+
- image: darthjee/circleci_rails_gems:1.1.0
|
45
|
+
environment:
|
46
|
+
PROJECT: zyra
|
47
|
+
steps:
|
48
|
+
- checkout
|
49
|
+
- run:
|
50
|
+
name: Bundle Install
|
51
|
+
command: bundle install
|
52
|
+
- run:
|
53
|
+
name: Rubocop
|
54
|
+
command: rubocop
|
41
55
|
- run:
|
42
56
|
name: Yardstick coverage check
|
43
57
|
command: bundle exec rake verify_measurements
|
@@ -52,7 +66,7 @@ jobs:
|
|
52
66
|
command: check_specs
|
53
67
|
build-and-release:
|
54
68
|
docker:
|
55
|
-
- image: darthjee/
|
69
|
+
- image: darthjee/circleci_rails_gems:1.1.0
|
56
70
|
environment:
|
57
71
|
PROJECT: zyra
|
58
72
|
steps:
|
data/.rubocop.yml
CHANGED
data/Dockerfile
CHANGED
data/Dockerfile.circleci
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
Zyra
|
2
2
|
====
|
3
3
|
[](https://codeclimate.com/github/darthjee/zyra)
|
4
4
|
[](https://codeclimate.com/github/darthjee/zyra/coverage)
|
@@ -9,9 +9,15 @@ Jace
|
|
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/
|
20
|
+
[https://www.rubydoc.info/gems/zyra/1.1.0](https://www.rubydoc.info/gems/zyra/1.1.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/check_specs.yml
CHANGED
data/config/yardstick.yml
CHANGED
@@ -15,13 +15,17 @@ rules:
|
|
15
15
|
exclude: []
|
16
16
|
ExampleTag:
|
17
17
|
enabled: true
|
18
|
-
|
18
|
+
excludei: []
|
19
19
|
ReturnTag:
|
20
20
|
enabled: true
|
21
21
|
exclude: []
|
22
22
|
Summary::Presence:
|
23
23
|
enabled: true
|
24
|
-
exclude:
|
24
|
+
exclude:
|
25
|
+
- Zyra::Finder#initialize
|
26
|
+
- Zyra::Creator#initialize
|
27
|
+
- Zyra::FinderCreator#initialize
|
28
|
+
- Zyra::Registry#initialize
|
25
29
|
Summary::Length:
|
26
30
|
enabled: true
|
27
31
|
exclude: []
|
data/docker-compose.yml
CHANGED
@@ -21,3 +21,14 @@ services:
|
|
21
21
|
<<: *base
|
22
22
|
depends_on: [base_build]
|
23
23
|
command: /bin/bash -c 'rspec && yard && rake yardstick_measure && rake verify_measurements'
|
24
|
+
|
25
|
+
circleci:
|
26
|
+
<<: *base
|
27
|
+
build:
|
28
|
+
context: ./
|
29
|
+
dockerfile: Dockerfile.circleci
|
30
|
+
command: echo done
|
31
|
+
image: zyra_circleci
|
32
|
+
working_dir: /home/circleci/project
|
33
|
+
volumes:
|
34
|
+
- .:/home/circleci/project
|
data/lib/zyra/creator.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Zyra
|
4
|
+
# @api private
|
5
|
+
# @author Darthjee
|
6
|
+
#
|
7
|
+
# Class responsible for building a model
|
8
|
+
class Creator
|
9
|
+
# @param model_class [Class] Model class to be initialized
|
10
|
+
# into a model
|
11
|
+
# @param event_registry [Jace::Registry] event registry to handle events
|
12
|
+
def initialize(model_class, event_registry:)
|
13
|
+
@model_class = model_class
|
14
|
+
@event_registry = event_registry
|
15
|
+
end
|
16
|
+
|
17
|
+
# Creates an instance of the registered model class
|
18
|
+
#
|
19
|
+
# @param attributes [Hash] attributes to be set in the model
|
20
|
+
# @param block [Proc] block to be ran after where more attributes
|
21
|
+
# will be set
|
22
|
+
#
|
23
|
+
# @yield [Object] Instance of the model class
|
24
|
+
#
|
25
|
+
# @return [Object] an instance of model class
|
26
|
+
def create(**attributes, &block)
|
27
|
+
block ||= proc {}
|
28
|
+
|
29
|
+
model = build(**attributes)
|
30
|
+
|
31
|
+
event_registry.trigger(:create, model) do
|
32
|
+
model.tap(&:save).tap(&block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
# @private
|
39
|
+
# Builds an instance of the registered model class
|
40
|
+
#
|
41
|
+
# @param (see #create)
|
42
|
+
# @yield (see #create)
|
43
|
+
# @return (see #create)
|
44
|
+
def build(**attributes)
|
45
|
+
model_class.new(attributes).tap do |model|
|
46
|
+
event_registry.trigger(:build, model)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @method model_class
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
# Model class to be initialized into a model
|
54
|
+
#
|
55
|
+
# @return [Class]
|
56
|
+
|
57
|
+
# @method event_registry
|
58
|
+
# @private
|
59
|
+
#
|
60
|
+
# Event registry
|
61
|
+
#
|
62
|
+
# The event registry will contain all handlers for
|
63
|
+
# post build or creating events
|
64
|
+
#
|
65
|
+
# @return [Jace::Registry]
|
66
|
+
attr_reader :model_class, :event_registry
|
67
|
+
end
|
68
|
+
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
|