capsula 0.0.2 → 0.0.3
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/Gemfile.lock +2 -2
- data/README.md +45 -20
- data/capsula.gemspec +1 -1
- data/lib/capsula/encapsulator.rb +36 -17
- data/spec/capsula_spec.rb +0 -2
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c534b9fef1cf66293f27706a7399215013ca7d41f58b3fd7bbf6e1f4990bc0a8
|
4
|
+
data.tar.gz: 16af229a6c5155c34e8fc4e91eac9911fae50c6cfd29e5b102aa14ebae28a07d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3480eb2df25870f7e9fd507847897cdc8cee3037bbef3c25312ef822218bf9ac54e34ecb4e7acfbf5609bc18334300fb8b71f799b5ca107989b14e17368aef12
|
7
|
+
data.tar.gz: 2240b2987901d94a39d5407f0b4caa9ad943987d0e0bfe1ae4433cb1dbed18673977a13f92eaa65a4ae14c4dd52c53a70e68b43c0501e3a98ad3f636986d5643
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,16 +3,26 @@ The tool for encapsulating (preloading, including) related objects into **any**
|
|
3
3
|
|
4
4
|
## How to use:
|
5
5
|
|
6
|
+
Add to Gemfile:
|
7
|
+
|
8
|
+
```
|
9
|
+
gem 'capsula'
|
10
|
+
```
|
11
|
+
|
12
|
+
Then:
|
13
|
+
|
6
14
|
```ruby
|
7
15
|
starships = <get_your_space_fleet>
|
8
16
|
|
9
|
-
StarshipsEncapsulator.new(starships).plans(
|
17
|
+
starships = StarshipsEncapsulator.new(starships).plans(
|
10
18
|
:fuel, :oxygen, :food, { spaceman: [:space_suit] }
|
11
19
|
).encapsulate
|
12
20
|
|
13
21
|
# Now let's get a suit for our spaceman:
|
14
22
|
starships.first&.spaceman&.space_suit
|
15
|
-
=> <awesome space suit>
|
23
|
+
=> <awesome space suit>
|
24
|
+
# no actual action was triggered,
|
25
|
+
# immediate result was received from Capsula's wrapper
|
16
26
|
```
|
17
27
|
|
18
28
|
Let's see how `StarshipsEncapsulator` looks:
|
@@ -29,23 +39,38 @@ class StarshipsEncapsulator < Capsula::Base
|
|
29
39
|
# Plans for other relations...
|
30
40
|
end
|
31
41
|
```
|
42
|
+
|
43
|
+
### src_key, dst_key
|
44
|
+
|
45
|
+
**default values:**
|
46
|
+
|
32
47
|
Definition for `src_key` and `dst_key` can be skipped if they values are `:fuel_id` (`<key_name>_id`) and `:id`
|
33
48
|
|
49
|
+
**lambdas:**
|
34
50
|
|
35
|
-
|
51
|
+
For key-definition can be used lambdas:
|
36
52
|
|
37
53
|
```ruby
|
38
|
-
src_key: ->(o){ o.some_hash_data.fetch(:fuel_id,
|
39
|
-
dst_key: ->(o){ o.
|
54
|
+
src_key: ->(o){ o.some_hash_data.fetch(:fuel_id, 'A-95') }
|
55
|
+
dst_key: ->(o){ o.extract_fuel_id_from_octane_number }
|
40
56
|
```
|
41
57
|
|
58
|
+
### dst_loader
|
59
|
+
|
60
|
+
**nested plans:**
|
61
|
+
|
62
|
+
`dst_loader` receive plans in `opt[:plans]` only for related class.
|
63
|
+
|
64
|
+
So, if user request plans for `:fuel, :oxygen, :food, { spaceman: [:space_suit] }`,
|
65
|
+
then `SpacemanEncapsulator` receive `[:space_suit]` plans in `opt[:plans]` and so on.
|
66
|
+
|
42
67
|
## How it works:
|
43
68
|
|
44
69
|
All objects is wrapping into special transparent wrapper which
|
45
70
|
translate all methods to wrapped object, except methods-names which was used for encapsulating before:
|
46
71
|
|
47
72
|
```ruby
|
48
|
-
StarshipsEncapsulator.new(starships).
|
73
|
+
starships = StarshipsEncapsulator.new(starships).plans(:fuel, :oxygen).encapsulate
|
49
74
|
starships.first.oxygen
|
50
75
|
=> <oxygen> # instant response, because already present in Capsula
|
51
76
|
```
|
@@ -62,20 +87,20 @@ For difficult preloading logic can be used custom loader:
|
|
62
87
|
|
63
88
|
```ruby
|
64
89
|
class CustomLoader
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
90
|
+
def initialize items:, opt: {}
|
91
|
+
@items = items; @opt = opt; @store = [];
|
92
|
+
end
|
93
|
+
|
94
|
+
# This method is triggered by Capsula
|
95
|
+
def collect_ids_and_load_relations
|
96
|
+
ids = @items.map{ |i| i.fuel_id }
|
97
|
+
@store = Fuel.where(id: ids).to_a
|
98
|
+
end
|
99
|
+
|
100
|
+
# This method is calling by Capsula during encapsulation
|
101
|
+
def get_preloads_for_object starship
|
102
|
+
@store.find { |fuel| starship.fuel_id == fuel.id }
|
103
|
+
end
|
79
104
|
end
|
80
105
|
|
81
106
|
|
data/capsula.gemspec
CHANGED
data/lib/capsula/encapsulator.rb
CHANGED
@@ -98,14 +98,24 @@ module Capsula
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def native_preload key, dec, opt
|
101
|
-
# collect
|
102
|
-
ids =
|
103
|
-
|
104
|
-
|
101
|
+
# collect ids for preloading
|
102
|
+
ids =
|
103
|
+
@items.map do |o|
|
104
|
+
get_value(o, dec[:src_key])
|
105
|
+
end.flatten.uniq.compact
|
106
|
+
|
107
|
+
# preload and index objects
|
108
|
+
col =
|
109
|
+
if ids.any?
|
110
|
+
preloads = dec[:dst_loader].call(ids,opt)
|
111
|
+
# build hash: {id1 => Obj1, ..., idN => ObjN }
|
112
|
+
index_array_by(preloads) { |el| get_value(el, dec[:dst_key]) }
|
113
|
+
else
|
114
|
+
{}
|
115
|
+
end
|
105
116
|
|
106
|
-
# preload relations
|
107
117
|
@collections[key] = {
|
108
|
-
collection:
|
118
|
+
collection: col,
|
109
119
|
declaration: dec
|
110
120
|
}
|
111
121
|
end
|
@@ -122,24 +132,26 @@ module Capsula
|
|
122
132
|
dec = declaration
|
123
133
|
|
124
134
|
@items.each do |i|
|
125
|
-
src_id =
|
135
|
+
src_id = get_value(i, dec[:src_key])
|
136
|
+
|
137
|
+
val =
|
138
|
+
if src_id.is_a?(Array)
|
139
|
+
# if object has many links to related objects (Array)
|
140
|
+
col.values_at(*src_id)
|
141
|
+
else
|
142
|
+
# only one link
|
143
|
+
col[src_id]
|
144
|
+
end
|
126
145
|
|
127
|
-
val = if src_id.is_a?(Array)
|
128
|
-
# if object has many links to related objects (Array)
|
129
|
-
col.select{|c| src_id.include?(c.id) }
|
130
|
-
else
|
131
|
-
# only one link
|
132
|
-
col.find{|c| get_key(c, dec[:dst_key]) == src_id }
|
133
|
-
end
|
134
146
|
i[plan_key] = val
|
135
147
|
end
|
136
148
|
end
|
137
149
|
|
138
|
-
def
|
150
|
+
def get_value _object_, key_or_lambda
|
139
151
|
case key_or_lambda
|
140
|
-
when Symbol, String
|
152
|
+
when ::Symbol, ::String
|
141
153
|
_object_.send(key_or_lambda)
|
142
|
-
when Proc
|
154
|
+
when ::Proc
|
143
155
|
key_or_lambda.call(_object_)
|
144
156
|
else
|
145
157
|
nil
|
@@ -162,5 +174,12 @@ module Capsula
|
|
162
174
|
end
|
163
175
|
end
|
164
176
|
|
177
|
+
# copypasted from activesupport
|
178
|
+
# https://www.rubydoc.info/github/rails/rails/Enumerable:index_by
|
179
|
+
def index_array_by arr
|
180
|
+
result = {}
|
181
|
+
arr.each { |elem| result[yield(elem)] = elem }
|
182
|
+
result
|
183
|
+
end
|
165
184
|
end
|
166
185
|
end
|
data/spec/capsula_spec.rb
CHANGED
@@ -22,8 +22,6 @@ RSpec.describe Capsula do
|
|
22
22
|
objects = [1,2].map{ |i| a.new(i) }
|
23
23
|
objects = klass.new(objects).plans(:b).encapsulate
|
24
24
|
|
25
|
-
puts objects.first.b
|
26
|
-
|
27
25
|
expect(objects.first).to respond_to(:b)
|
28
26
|
expect(objects.first).to be_a(Capsula::Wrapper)
|
29
27
|
expect(objects.first.b.id).to be == objects.first.b_id
|