zyra 0.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Code Climate](https://codeclimate.com/github/darthjee/zyra/badges/gpa.svg)](https://codeclimate.com/github/darthjee/zyra)
|
4
4
|
[![Test Coverage](https://codeclimate.com/github/darthjee/zyra/badges/coverage.svg)](https://codeclimate.com/github/darthjee/zyra/coverage)
|
@@ -9,9 +9,15 @@ Jace
|
|
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/
|
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
|