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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cb11a8449da1cdc0b87c0600b96c3b7e9c76b024b95bc4a722b764570296f50
4
- data.tar.gz: 5848a55463e899d78ff4c28dd6ae81993916cd449b3dfd35692bb47f46777452
3
+ metadata.gz: 7c8f905bbdb0fec56487d7be0a0999983394975cfe3878397ffa7b12f8b43229
4
+ data.tar.gz: ef03ada86291579a39b0f1dbb9b4bd6dc868090752240522eb22a5c89d6ad559
5
5
  SHA512:
6
- metadata.gz: 5b13ca88db8dfa338c68f4af3177d39202d2274a88ec0738a7146b59619e6ccb3369277280e0954fe68150e43ed3f8aac1566d95802c79a552b74db340235f96
7
- data.tar.gz: ff5cfc24cbc93e90107ec311b1c6baf25c9fc81d9e8aba0bb89ba1f2d876cf93198f6769a5fcf21d3db5db670e4ad399a1762081a41cf21cdd848d46bbf0dc8f
6
+ metadata.gz: 6132bf64c4fb482e408c24d6e84964bc7862ed31890ee3257608476ee659e63252db0f01602da4afe367ce7a8f6661c65d7099634a9982aa97b20c6d10817d29
7
+ data.tar.gz: 8b79a9c2f74a31b83e50e98571920f6b3d2e8ac441c58a64e88dd9224a2b5bfe7a5bd043bc5b131c63d2857d6840ff1b9e8f0f1fa1e1009bc213a7fcc808d29b
data/.rubocop.yml CHANGED
@@ -20,6 +20,7 @@ RSpec/AlignLeftLetBrace:
20
20
  RSpec/DescribedClass:
21
21
  Exclude:
22
22
  - 'spec/integration/yard/**/*_spec.rb'
23
+ - 'spec/integration/readme/**/*_spec.rb'
23
24
 
24
25
  RSpec/ExampleLength:
25
26
  Exclude:
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.2](https://www.rubydoc.info/gems/zyra/0.0.2)
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: 96
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
- exclude:
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::Builder#initialize
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 Builder
8
+ class Creator
9
9
  # @param model_class [Class] Model class to be initialized
10
10
  # into a model
11
- def initialize(model_class)
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
- # @api public
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
- # @api public
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
- # Checks if another builder is equal to the current builder
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
- # @return [TrueClass,FalseClass]
71
- def ==(other)
72
- return unless other.class == self.class
39
+ # @param (see #create)
40
+ # @yield (see #create)
41
+ # @return (see #create)
42
+ def build(**attributes, &block)
43
+ block ||= proc {}
73
44
 
74
- other.model_class == model_class
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
- def event_registry
96
- @event_registry ||= Jace::Registry.new
97
- end
66
+ attr_reader :model_class, :event_registry
98
67
  end
99
68
  end
@@ -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
- class BuilderNotRegistered < StandardError; end
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
@@ -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