luna_park 0.12.1 → 0.13.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/.rubocop.yml +1 -1
- data/CHANGELOG.md +26 -3
- data/Gemfile.lock +1 -1
- data/lib/luna_park/extensions/data_mapper.rb +270 -38
- data/lib/luna_park/extensions/repositories/postgres/create.rb +8 -2
- data/lib/luna_park/extensions/repositories/postgres/delete.rb +1 -1
- data/lib/luna_park/extensions/repositories/postgres/read.rb +22 -31
- data/lib/luna_park/extensions/repositories/postgres/update.rb +6 -3
- data/lib/luna_park/mapper.rb +7 -0
- data/lib/luna_park/mappers/codirectional/copyists/nested.rb +25 -4
- data/lib/luna_park/mappers/codirectional.rb +10 -15
- data/lib/luna_park/mappers/errors.rb +1 -1
- data/lib/luna_park/mappers/simple.rb +11 -9
- data/lib/luna_park/notifiers/tagged_log/tagged_formatter.rb +5 -5
- data/lib/luna_park/notifiers/tagged_log.rb +1 -1
- data/lib/luna_park/version.rb +1 -1
- data/lib/luna_park.rb +1 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3491b1852cab029d5f1b60cdc9ff638e2eac71a1d366e1463e31b7c6a4afa127
|
4
|
+
data.tar.gz: f0b5abc0af74a11ac5d6e392def3daa5e565ac7ba5033315981013dae3f0734a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25f821e41e63fcb3bfcc8744ea203b861360126cc7edc12f5032e7153831a514e527ef80e8453fc5b8f24e5bf5f458c88226f1103ab8bcf85249222d4c7355b5
|
7
|
+
data.tar.gz: 903dd8923a134ea14d8f3611e3c803989f976f923e5b251cc65e47bef6cecc923173ae6654f2361ac2cc49190231d7d24c427d4144720abdbd9d0368b5ae386b
|
data/.rubocop.yml
CHANGED
@@ -50,7 +50,7 @@ Style/Documentation:
|
|
50
50
|
Enabled: false
|
51
51
|
|
52
52
|
Naming/MethodParameterName:
|
53
|
-
AllowedNames: io, id, to, by, on, in, at, ip, db, pk, fk
|
53
|
+
AllowedNames: io, id, to, by, on, in, at, ip, db, pk, fk, ds
|
54
54
|
|
55
55
|
# TODO: поговорить с Филиппом про attr
|
56
56
|
Layout/EmptyLinesAroundAttributeAccessor:
|
data/CHANGELOG.md
CHANGED
@@ -4,13 +4,36 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
-
## [0.
|
7
|
+
## [0.13.0] - 2023-06-06
|
8
8
|
Changed
|
9
|
+
- `Extensions::Repositories::Postgres::Delete` returns boolean
|
10
|
+
- DataMapper became more safe: it will raise `MoreThanOneRecord` if `#read_one` received array with multiple items,
|
11
|
+
so it will show you your critical logic mistake
|
12
|
+
|
13
|
+
Added
|
14
|
+
- `DataMapper.mapper` now can receive block instead of class, to describe anonymous mapper;
|
15
|
+
default parent can be customized by defining `base_anonymous_mapper` method
|
16
|
+
- `DataMapper.entity` now have second argument to customize `coercion` - you can use your own Entity class (even Dry Struct)
|
17
|
+
- DataMapper became more safe: it will not try to transform hash of attributes to array of pairs
|
18
|
+
- Postgres extension `Create` now will set `updated_at` and `created_at` if exists
|
19
|
+
- Postgres extension `Update` now will set `updated_at` if exists
|
20
|
+
- Postgres extension `Read` now have default `#scope` abstract method to handle scoping options
|
21
|
+
- Postgres extension `Read` now have `#transaction` method
|
22
|
+
- Postgres extension `Read` now have `#lock(pk, &block)` method and `#lock!(pk, &block)`
|
23
|
+
- each Repository NotFound exception now have its own exception class, inherited from common NotFound exception
|
24
|
+
- `LunaPark::Mappers::Codirectional` now available as default mapper `LunaPark::Mapper`
|
25
|
+
- `LunaPark::Mappers::Codirectional` now can have nested mappers, and can be configured for arrays
|
26
|
+
|
27
|
+
Fixed
|
28
|
+
- DataMapper configuration (entity, mapper) now can be inherited. NotFound error also will be inherited.
|
29
|
+
|
30
|
+
## [0.12.0] - 2023-02-23
|
31
|
+
Added
|
9
32
|
- Added `TaggedLog`
|
10
33
|
|
11
34
|
## [0.11.7] - 2022-10-07
|
12
35
|
Changed
|
13
|
-
- Added `formatter` to `Notifiers::Log`.
|
36
|
+
- Added `formatter` to `Notifiers::Log`. Usage of `format` in initializer is now deprecated.
|
14
37
|
|
15
38
|
## [0.11.6] - 2021-10-06
|
16
39
|
Changed
|
@@ -21,7 +44,7 @@ Added
|
|
21
44
|
- add short alias for exceptions (`i18n:` instead of `i18n_key:`)
|
22
45
|
|
23
46
|
## [0.11.5] - 2022-09-27
|
24
|
-
|
47
|
+
Added
|
25
48
|
- Added `.custom_error` method to `Extensions::HasError` to define errors with a custom superclass
|
26
49
|
- Added `#inject` method to `Extensions::Injector` - dependencies setter that allows you to create method chains
|
27
50
|
|
data/Gemfile.lock
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'luna_park/mappers/simple'
|
4
|
+
|
3
5
|
module LunaPark
|
4
6
|
module Extensions
|
5
7
|
# @example
|
@@ -20,7 +22,7 @@ module LunaPark
|
|
20
22
|
# def save(input)
|
21
23
|
# entity = wrap(input)
|
22
24
|
# row = to_row(entity)
|
23
|
-
# new_row = products.where(id: entity.id).update(row)
|
25
|
+
# new_row = products.where(id: entity.id).returning.update(row)
|
24
26
|
# new_attrs = from_row(new_row)
|
25
27
|
# entity.set_attributes(new_attrs)
|
26
28
|
# entity
|
@@ -36,33 +38,174 @@ module LunaPark
|
|
36
38
|
# alias products dataset
|
37
39
|
# end
|
38
40
|
module DataMapper
|
39
|
-
|
40
|
-
base
|
41
|
-
|
41
|
+
class << self
|
42
|
+
def extended(base)
|
43
|
+
base.include self
|
44
|
+
end
|
45
|
+
|
46
|
+
def included(base)
|
47
|
+
base.extend ClassMethods
|
48
|
+
base.include InstanceMethods
|
49
|
+
|
50
|
+
base.__define_constants__
|
51
|
+
|
52
|
+
defaults(base)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def defaults(base)
|
58
|
+
base.entity OpenStruct, :new
|
59
|
+
base.mapper LunaPark::Mappers::Simple
|
60
|
+
end
|
42
61
|
end
|
43
62
|
|
44
63
|
module ClassMethods
|
45
|
-
attr_reader :entity_class, :mapper_class
|
64
|
+
attr_reader :entity_class, :mapper_class, :__entity_coercion__
|
46
65
|
|
47
66
|
# Configure repository
|
48
67
|
|
49
|
-
|
50
|
-
|
68
|
+
# Configure tagret entity class and coercion for it
|
69
|
+
#
|
70
|
+
# @example default coercion for entity type than responds to .call
|
71
|
+
# class MyRepository
|
72
|
+
# entity MyEntity
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# input = { foo: 'FOO', bar: 'BAR' }
|
76
|
+
# MyRepository.new.send(:wrap, input) == MyEntity.call(input)
|
77
|
+
#
|
78
|
+
# @example default coercion for entity type than responds to .wrap
|
79
|
+
# class MyRepository
|
80
|
+
# entity MyEntity
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# input = { foo: 'FOO', bar: 'BAR' }
|
84
|
+
# MyRepository.new.send(:wrap, input) == MyEntity.wrap(input)
|
85
|
+
#
|
86
|
+
# @example custom coercion by symbol, for entity type than responds to described_method
|
87
|
+
# class MyRepository
|
88
|
+
# entity MyEntity, :build
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# input = { foo: 'FOO', bar: 'BAR' }
|
92
|
+
# MyRepository.new.send(:wrap, input) == MyEntity.build(input)
|
93
|
+
#
|
94
|
+
# @example custom coercion by callable object
|
95
|
+
# class MyRepository
|
96
|
+
# entity MyEntity, BUILD_ENTITY
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# input = { foo: 'FOO', bar: 'BAR' }
|
100
|
+
# MyRepository.new.send(:wrap, input) == BUILD_ENTITY(input)
|
101
|
+
def entity(entity, coercion = nil)
|
102
|
+
@entity_class = entity
|
103
|
+
@__entity_coercion__ = __build_entity_coercion__(coercion)
|
104
|
+
@entity_class
|
105
|
+
end
|
106
|
+
|
107
|
+
# Configure Mapper
|
108
|
+
#
|
109
|
+
# @example With anonymous mapper
|
110
|
+
# class Repository < LunaPark::Repository
|
111
|
+
# mapper do
|
112
|
+
# attr :foo, row: :fuu
|
113
|
+
# end
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# Repository.mapper_class.to_row(foo: 'Foo') # => { fuu: 'Foo' }
|
117
|
+
#
|
118
|
+
# @example With mapper class
|
119
|
+
# class Repository::Mapper < LunaPark::Mapper
|
120
|
+
# attr :foo, row: :fuu
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# class Repository < LunaPark::Repository
|
124
|
+
# mapper Mapper
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# Repository.new.mapper_class.to_row(foo: 'Foo') # => { fuu: 'Foo' }
|
128
|
+
#
|
129
|
+
# @example Without mapper
|
130
|
+
# class Repository < LunaPark::Repository
|
131
|
+
# def example_to_row(attrs)
|
132
|
+
# to_row attrs
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# Repository.new.example_to_row(foo: 'Foo') # => { foo: 'Foo' }
|
137
|
+
#
|
138
|
+
def mapper(mapper = Undefined, &block)
|
139
|
+
raise ArgumentError, 'Expected mapper xOR block' unless (mapper == Undefined) ^ block.nil?
|
140
|
+
|
141
|
+
return @mapper_class = mapper if block.nil?
|
142
|
+
|
143
|
+
@mapper_class = Class.new(base_anonymous_mapper)
|
144
|
+
@mapper_class.class_eval(&block)
|
145
|
+
@mapper_class
|
146
|
+
end
|
147
|
+
|
148
|
+
def __build_entity_coercion__(coercion) # rubocop:disable Metrics/AbcSize
|
149
|
+
return entity_class.method(coercion) if coercion.is_a? Symbol
|
150
|
+
return coercion if coercion.respond_to?(:call)
|
151
|
+
|
152
|
+
raise ArgumentError, 'coercion MUST be call\'able, Symbol or nil' unless coercion.nil?
|
153
|
+
|
154
|
+
infer_entity_coercion
|
155
|
+
end
|
156
|
+
|
157
|
+
def infer_entity_coercion # rubocop:disable Metrics/AbcSize
|
158
|
+
return entity_class.method(:call) if entity_class.respond_to?(:call)
|
159
|
+
return entity_class.method(:wrap) if entity_class.respond_to?(:wrap)
|
160
|
+
|
161
|
+
->(input) { entity_class.new(input.to_h) }
|
51
162
|
end
|
52
163
|
|
53
|
-
|
54
|
-
|
164
|
+
# @abstract
|
165
|
+
#
|
166
|
+
# @example
|
167
|
+
# class Transaction::Repository < LunaPark::Repository
|
168
|
+
# # Parent of this mapper will be changed
|
169
|
+
# mapper do
|
170
|
+
# attr :foo
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# def self.base_anonymous_mapper
|
174
|
+
# MyBaseMapper
|
175
|
+
# end
|
176
|
+
# end
|
177
|
+
def base_anonymous_mapper
|
178
|
+
LunaPark::Mappers::Codirectional
|
179
|
+
end
|
180
|
+
|
181
|
+
def primary_key(attr)
|
182
|
+
@primary_key_attr = attr
|
55
183
|
end
|
56
184
|
|
57
185
|
DEFAULT_PRIMARY_KEY = :id
|
58
186
|
|
59
|
-
def
|
60
|
-
@
|
187
|
+
def primary_key_attr
|
188
|
+
@primary_key_attr || DEFAULT_PRIMARY_KEY
|
189
|
+
end
|
190
|
+
|
191
|
+
def __define_constants__(not_found: LunaPark::Extensions::DataMapper::NotFound)
|
192
|
+
__define_class__ 'NotFound', not_found
|
61
193
|
end
|
62
194
|
|
63
|
-
def
|
64
|
-
|
195
|
+
def __define_class__(name, parent)
|
196
|
+
klass = Class.new(parent)
|
197
|
+
const_set name, klass
|
65
198
|
end
|
199
|
+
|
200
|
+
def inherited(klass)
|
201
|
+
klass.__define_constants__(not_found: NotFound)
|
202
|
+
klass.entity entity_class, __entity_coercion__
|
203
|
+
klass.mapper mapper_class
|
204
|
+
super
|
205
|
+
end
|
206
|
+
|
207
|
+
class Undefined; end
|
208
|
+
private_constant :Undefined
|
66
209
|
end
|
67
210
|
|
68
211
|
module InstanceMethods
|
@@ -74,22 +217,56 @@ module LunaPark
|
|
74
217
|
|
75
218
|
# Helpers
|
76
219
|
|
220
|
+
# Repository Helpers
|
221
|
+
|
77
222
|
# Get collection of entities from row
|
78
223
|
# @example
|
79
224
|
# def where_type(type)
|
80
|
-
# read_all
|
225
|
+
# read_all scoped dataset.where(type: type)
|
81
226
|
# end
|
82
227
|
def read_all(rows)
|
83
|
-
to_entities from_rows rows
|
228
|
+
to_entities from_rows __to_array__(rows)
|
84
229
|
end
|
85
230
|
|
86
231
|
# Get one entity from row
|
87
232
|
# @example
|
88
233
|
# def find(id)
|
89
|
-
#
|
234
|
+
# # limit 2 allows to check if there are more than 1 record
|
235
|
+
# read_one dataset.where(id: id).limit(2)
|
236
|
+
# end
|
237
|
+
def read_one(rows)
|
238
|
+
to_entity from_row __one_from__(rows)
|
239
|
+
end
|
240
|
+
|
241
|
+
# Get one entity from row
|
242
|
+
# @example
|
243
|
+
# def find!(id)
|
244
|
+
# read_one! dataset.where(id: id).limit(1)
|
245
|
+
# end
|
246
|
+
def read_one!(row, not_found_by: nil, not_found_meta: nil)
|
247
|
+
warn 'Deprecated option #not_found_meta used' unless not_found_meta.nil?
|
248
|
+
|
249
|
+
found! read_one(row), not_found_by: not_found_by || not_found_meta
|
250
|
+
end
|
251
|
+
|
252
|
+
# Check if record was found
|
253
|
+
# @example
|
254
|
+
# class MyRepository < LunaPark::Repository
|
255
|
+
# def find_by_x!(x)
|
256
|
+
# found! nil, not_found_by: "x: #{x}"
|
257
|
+
# end
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
# begin
|
261
|
+
# MyRepository.new.find_by_x 'X'
|
262
|
+
# rescue MyRepository::NotFound => e
|
263
|
+
# raise HTTP404, "Record #{e.details[:name]} not found by #{e.details[:by]}"
|
90
264
|
# end
|
91
|
-
|
92
|
-
|
265
|
+
#
|
266
|
+
def found!(value, not_found_by: nil)
|
267
|
+
return value unless value.nil?
|
268
|
+
|
269
|
+
raise self.class::NotFound.new name: self.class.entity_class.name, by: not_found_by
|
93
270
|
end
|
94
271
|
|
95
272
|
# Mapper helpers
|
@@ -100,7 +277,7 @@ module LunaPark
|
|
100
277
|
# database.insert_many(rows)
|
101
278
|
# end
|
102
279
|
def to_rows(input_array)
|
103
|
-
mapper_class
|
280
|
+
self.class.mapper_class.to_rows(input_array)
|
104
281
|
end
|
105
282
|
|
106
283
|
# @example
|
@@ -109,7 +286,7 @@ module LunaPark
|
|
109
286
|
# database.insert(row)
|
110
287
|
# end
|
111
288
|
def to_row(input)
|
112
|
-
mapper_class
|
289
|
+
self.class.mapper_class.to_row(input)
|
113
290
|
end
|
114
291
|
|
115
292
|
# @example
|
@@ -118,7 +295,7 @@ module LunaPark
|
|
118
295
|
# entities_attrs.map { |entity_attrs| Entity.new(entity_attrs) }
|
119
296
|
# end
|
120
297
|
def from_rows(rows_array)
|
121
|
-
mapper_class
|
298
|
+
self.class.mapper_class.from_rows(rows_array)
|
122
299
|
end
|
123
300
|
|
124
301
|
# @example
|
@@ -130,7 +307,7 @@ module LunaPark
|
|
130
307
|
return if input.nil?
|
131
308
|
raise ArgumentError, 'Can not be an Array' if input.is_a?(Array)
|
132
309
|
|
133
|
-
mapper_class
|
310
|
+
self.class.mapper_class.from_row(input.to_h)
|
134
311
|
end
|
135
312
|
|
136
313
|
# Entity construction helpers
|
@@ -139,7 +316,7 @@ module LunaPark
|
|
139
316
|
# to_entities(attributes_hashes) # => Array of Entity
|
140
317
|
# to_entities(attributes_hash) # => Array of Entity
|
141
318
|
def to_entities(attrs_array)
|
142
|
-
|
319
|
+
__to_array__(attrs_array).map { |attrs| to_entity(attrs) }
|
143
320
|
end
|
144
321
|
|
145
322
|
# @example
|
@@ -147,48 +324,103 @@ module LunaPark
|
|
147
324
|
def to_entity(attrs)
|
148
325
|
return if attrs.nil?
|
149
326
|
|
150
|
-
entity_class
|
327
|
+
self.class.entity_class.new(attrs)
|
151
328
|
end
|
152
329
|
|
153
330
|
# Entity wrapping helpers
|
154
331
|
|
155
332
|
# @example
|
156
|
-
#
|
157
|
-
#
|
158
|
-
#
|
333
|
+
# wrap_all(attributes_hashes) # => Array of Entity
|
334
|
+
# wrap_all(entities) # => Array of Entity
|
335
|
+
# wrap_all(entity) # => Array of Entity
|
159
336
|
def wrap_all(input_array)
|
160
|
-
|
337
|
+
__to_array__(input_array).map { |input| wrap(input) }
|
161
338
|
end
|
162
339
|
|
163
340
|
# @example
|
164
|
-
#
|
165
|
-
#
|
341
|
+
# wrap(id: 42) # => <#MyEntity @id=42>
|
342
|
+
# wrap(entity) # => <#MyEntity @id=42>
|
166
343
|
def wrap(input)
|
167
344
|
return if input.nil?
|
168
345
|
|
169
|
-
|
346
|
+
self.class.__entity_coercion__.call(input)
|
170
347
|
end
|
171
348
|
|
172
|
-
#
|
173
|
-
|
174
|
-
|
175
|
-
|
349
|
+
# @example scope after query build
|
350
|
+
# def all(**opts)
|
351
|
+
# read_all scoped(**opts).order(:created_at)
|
352
|
+
# end
|
353
|
+
#
|
354
|
+
# @example scope before query build
|
355
|
+
# def all(**opts)
|
356
|
+
# read_all scoped(dataset.order(:created_at), **opts)
|
357
|
+
# end
|
358
|
+
#
|
359
|
+
def scoped(ds = dataset, **opts)
|
360
|
+
scope(ds, **opts)
|
176
361
|
end
|
177
362
|
|
178
|
-
|
179
|
-
|
363
|
+
# @abstract
|
364
|
+
#
|
365
|
+
# @example
|
366
|
+
# def scope(dataset, deleted: nil, for_update: false, **scope)
|
367
|
+
# ds = super(dataset, **scope)
|
368
|
+
# ds = ds.for_update if for_update == true
|
369
|
+
# ds = ds.where(deleted_at: nil) if deleted == false
|
370
|
+
# ds = ds.where.not(deleted_at: nil) if deleted == true
|
371
|
+
# ds
|
372
|
+
# end
|
373
|
+
#
|
374
|
+
# def all(**scope)
|
375
|
+
# read_all scoped(**scope) # same as `scope(dataset, **scope)`
|
376
|
+
# end
|
377
|
+
#
|
378
|
+
# all # get all
|
379
|
+
# all(deleted: false) # get not deleted
|
380
|
+
# all(deleted: true) # get deleted
|
381
|
+
def scope(dataset, **_scope)
|
382
|
+
dataset
|
180
383
|
end
|
181
384
|
|
385
|
+
# Read config
|
386
|
+
|
182
387
|
def primary_key
|
183
|
-
self.class.
|
388
|
+
self.class.primary_key_attr
|
184
389
|
end
|
185
390
|
|
186
391
|
# Factory Methods
|
187
392
|
|
393
|
+
# @abstract
|
394
|
+
#
|
188
395
|
# Usefull for extensions
|
189
396
|
def dataset
|
190
397
|
raise NotImplementedError
|
191
398
|
end
|
399
|
+
|
400
|
+
# fixes problem: `Array({ a: 1 }) # => [[:a, 1]]`
|
401
|
+
def __to_array__(input)
|
402
|
+
input.is_a?(Hash) ? [input] : Array(input)
|
403
|
+
end
|
404
|
+
|
405
|
+
# checks if there are only one item in the given array
|
406
|
+
def __one_from__(input)
|
407
|
+
case input
|
408
|
+
when Hash then input
|
409
|
+
else
|
410
|
+
array = input.is_a?(Array) ? input : Array(input)
|
411
|
+
raise MoreThanOneRecord.new count: array.size if array.size > 1
|
412
|
+
|
413
|
+
array.first
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
class NotFound < LunaPark::Errors::NotFound
|
419
|
+
message { |d| "#{d[:name]} (#{d[:by]})" }
|
420
|
+
end
|
421
|
+
|
422
|
+
class MoreThanOneRecord < LunaPark::Errors::System
|
423
|
+
message { |d| "Expected only one record, but there are #{d[:count]} records" }
|
192
424
|
end
|
193
425
|
end
|
194
426
|
end
|
@@ -7,9 +7,15 @@ module LunaPark
|
|
7
7
|
module Create
|
8
8
|
def create(input)
|
9
9
|
entity = wrap(input)
|
10
|
-
|
11
|
-
|
10
|
+
|
11
|
+
time = Time.now
|
12
|
+
entity.created_at = time if entity.respond_to?(:created_at)
|
13
|
+
entity.updated_at = time if entity.respond_to?(:updated_at)
|
14
|
+
|
15
|
+
row = to_row(entity)
|
16
|
+
new_row = dataset.returning.insert(row).first
|
12
17
|
new_attrs = from_row(new_row)
|
18
|
+
|
13
19
|
entity.set_attributes(new_attrs)
|
14
20
|
entity
|
15
21
|
end
|
@@ -5,56 +5,47 @@ module LunaPark
|
|
5
5
|
module Repositories
|
6
6
|
module Postgres
|
7
7
|
module Read
|
8
|
-
def find!(pk_value, for_update: false)
|
9
|
-
ds = dataset.where(primary_key => pk_value)
|
10
|
-
read_one!(ds, for_update: for_update, not_found_meta: pk_value)
|
11
|
-
end
|
12
|
-
|
13
|
-
def find(pk_value, for_update: false)
|
14
|
-
ds = dataset.where(primary_key => pk_value)
|
15
|
-
read_one(ds, for_update: for_update)
|
16
|
-
end
|
17
|
-
|
18
8
|
def lock!(pk_value)
|
19
|
-
|
9
|
+
transaction { yield find! pk_value, for_update: true }
|
20
10
|
end
|
21
11
|
|
22
12
|
def lock(pk_value)
|
23
|
-
|
13
|
+
transaction { yield find pk_value, for_update: true }
|
24
14
|
end
|
25
15
|
|
26
|
-
def
|
27
|
-
dataset.
|
16
|
+
def transaction(&block)
|
17
|
+
dataset.transaction(&block)
|
28
18
|
end
|
29
19
|
|
30
|
-
def
|
31
|
-
|
20
|
+
def find!(pk_value, **scope)
|
21
|
+
found! find(pk_value, **scope), not_found_by: pk_value
|
32
22
|
end
|
33
23
|
|
34
|
-
def
|
35
|
-
|
24
|
+
def find(pk_value, **scope)
|
25
|
+
read_one scoped(**scope).where(primary_key => pk_value)
|
36
26
|
end
|
37
27
|
|
38
|
-
|
28
|
+
def count(**scope)
|
29
|
+
scoped(**scope).count
|
30
|
+
end
|
39
31
|
|
40
|
-
def
|
41
|
-
|
42
|
-
raise Errors::NotFound, "#{short_class_name} (#{not_found_meta})" if entity.nil?
|
43
|
-
end
|
32
|
+
def all(**scope)
|
33
|
+
read_all(scoped(**scope).order { created_at.desc })
|
44
34
|
end
|
45
35
|
|
46
|
-
def
|
47
|
-
|
48
|
-
row = dataset.first
|
49
|
-
to_entity from_row(row)
|
36
|
+
def first(**scope)
|
37
|
+
read_one scoped(**scope).order(:created_at).first
|
50
38
|
end
|
51
39
|
|
52
|
-
def
|
53
|
-
|
40
|
+
def last(**scope)
|
41
|
+
read_one scoped(**scope).order(:created_at).last
|
54
42
|
end
|
55
43
|
|
56
|
-
|
57
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
def scope(dataset, for_update: false, **_)
|
47
|
+
dataset = dataset.for_update if for_update
|
48
|
+
dataset
|
58
49
|
end
|
59
50
|
end
|
60
51
|
end
|
@@ -7,10 +7,13 @@ module LunaPark
|
|
7
7
|
module Update
|
8
8
|
def save(input)
|
9
9
|
entity = wrap(input)
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
|
11
|
+
entity.updated_at = Time.now if entity.respond_to?(:updated_at)
|
12
|
+
|
13
|
+
row = to_row(entity)
|
14
|
+
new_row = dataset.where(primary_key => row[primary_key]).returning.update(row).first
|
13
15
|
new_attrs = from_row(new_row)
|
16
|
+
|
14
17
|
entity.set_attributes(new_attrs)
|
15
18
|
entity
|
16
19
|
end
|
@@ -7,32 +7,46 @@ module LunaPark
|
|
7
7
|
# Copyist for copiyng value between two schemas with DIFFERENT or NESTED paths
|
8
8
|
# (Works with only one described attribute)
|
9
9
|
class Nested
|
10
|
-
def initialize(attrs_path:, row_path:)
|
10
|
+
def initialize(attrs_path:, row_path:, mapper:, map_array:)
|
11
11
|
@attrs_path = attrs_path
|
12
12
|
@row_path = row_path
|
13
13
|
|
14
|
+
@mapper_config = mapper
|
15
|
+
@map_array = map_array
|
16
|
+
|
14
17
|
raise ArgumentError, 'attr path can not be nil' if attrs_path.nil?
|
15
18
|
raise ArgumentError, 'store path can not be nil' if row_path.nil?
|
19
|
+
raise ArgumentError, 'array option MUST be nil when no Mapper given' if map_array && mapper.nil?
|
16
20
|
end
|
17
21
|
|
18
22
|
def from_row(row:, attrs:)
|
19
|
-
copy_nested(from: row, to: attrs, from_path: @row_path, to_path: @attrs_path)
|
23
|
+
copy_nested(from: row, to: attrs, from_path: @row_path, to_path: @attrs_path, direction: :from_row)
|
20
24
|
end
|
21
25
|
|
22
26
|
def to_row(row:, attrs:)
|
23
|
-
copy_nested(from: attrs, to: row, from_path: @attrs_path, to_path: @row_path)
|
27
|
+
copy_nested(from: attrs, to: row, from_path: @attrs_path, to_path: @row_path, direction: :to_row)
|
24
28
|
end
|
25
29
|
|
26
30
|
private
|
27
31
|
|
28
|
-
def copy_nested(from:, to:, from_path:, to_path:)
|
32
|
+
def copy_nested(from:, to:, from_path:, to_path:, direction:)
|
29
33
|
value = read(from, from_path)
|
30
34
|
|
31
35
|
return if value == Undefined # omit undefined keys
|
32
36
|
|
37
|
+
value = apply_mapper(value, direction) unless mapper.nil?
|
38
|
+
|
33
39
|
write(to, to_path, value)
|
34
40
|
end
|
35
41
|
|
42
|
+
def apply_mapper(value, direction)
|
43
|
+
if @map_array
|
44
|
+
value.map { |v| mapper.public_send direction, v }
|
45
|
+
else
|
46
|
+
mapper.public_send direction, value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
36
50
|
def read(from, from_path)
|
37
51
|
if from_path.is_a?(Array) # when given `%i[key path]` - not just `:key`
|
38
52
|
read_nested(from, path: from_path)
|
@@ -82,6 +96,13 @@ module LunaPark
|
|
82
96
|
path.inject(nested_hash) { |output, key| output[key] ||= {} }
|
83
97
|
end
|
84
98
|
|
99
|
+
def mapper
|
100
|
+
return @mapper if instance_variable_defined?(:@mapper)
|
101
|
+
return @mapper = Object.const_get(@mapper_config) if @mapper_config.is_a? String
|
102
|
+
|
103
|
+
@mapper = @mapper_config
|
104
|
+
end
|
105
|
+
|
85
106
|
class Undefined; end
|
86
107
|
|
87
108
|
private_constant :Undefined
|
@@ -53,33 +53,28 @@ module LunaPark
|
|
53
53
|
# class Mappers::Transaction < LunaPark::Mappers::Codirectional
|
54
54
|
# attr :uid, row: :id
|
55
55
|
# attr %i[charge amount], row: :charge_amount
|
56
|
+
# attr :comment
|
56
57
|
# end
|
57
|
-
def attr(attr, row: nil)
|
58
|
-
return attrs(attr) if row.nil?
|
59
|
-
|
58
|
+
def attr(attr, row: attr, mapper: nil, array: nil)
|
60
59
|
attr_path = to_path(attr)
|
61
60
|
row_path = to_path(row)
|
62
61
|
|
63
|
-
if attr_path == row_path
|
64
|
-
|
62
|
+
if attr_path == row_path && !attr_path.is_a?(Array)
|
63
|
+
slice_copyist.add_key(attr_path)
|
65
64
|
else
|
66
|
-
nested_copyists << Copyists::Nested.new(
|
65
|
+
nested_copyists << Copyists::Nested.new(
|
66
|
+
attrs_path: attr_path, row_path: row_path,
|
67
|
+
mapper: mapper, map_array: array
|
68
|
+
)
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
70
72
|
# @example
|
71
73
|
# class Mappers::Transaction < LunaPark::Mappers::Codirectional
|
72
|
-
# attrs :
|
74
|
+
# attrs :created_at, :updated_at, :deleted_at
|
73
75
|
# end
|
74
76
|
def attrs(*common_keys)
|
75
|
-
common_keys.each
|
76
|
-
path = to_path(common_key)
|
77
|
-
if path.is_a?(Array)
|
78
|
-
nested_copyists << Copyists::Nested.new(attrs_path: path, row_path: path)
|
79
|
-
else
|
80
|
-
slice_copyist.add_key(path)
|
81
|
-
end
|
82
|
-
end
|
77
|
+
common_keys.each { |common_key| attr common_key }
|
83
78
|
end
|
84
79
|
|
85
80
|
def from_row(input)
|
@@ -6,7 +6,7 @@ module LunaPark
|
|
6
6
|
module Mappers
|
7
7
|
module Errors
|
8
8
|
class NotArray < LunaPark::Errors::System
|
9
|
-
message { |d| "input MUST
|
9
|
+
message { |d| "input MUST respond to #to_a, but given #{d[:input].class} `#{d[:input].inspect}`" }
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
@@ -67,28 +67,30 @@ module LunaPark
|
|
67
67
|
# Transforms array of rows to array of attribute hashes
|
68
68
|
def from_rows(rows)
|
69
69
|
return [] if rows.nil?
|
70
|
-
raise Errors::NotArray.new(input: rows)
|
70
|
+
raise Errors::NotArray.new(input: rows) if rows.is_a?(Hash)
|
71
|
+
raise Errors::NotArray.new(input: rows) unless rows.respond_to?(:to_a)
|
71
72
|
|
72
73
|
rows.to_a.map { |hash| from_row(hash) }
|
73
74
|
end
|
74
75
|
|
75
76
|
##
|
76
77
|
# Transforms array of attribute hashes to array of rows
|
77
|
-
def to_rows(
|
78
|
-
return [] if
|
79
|
-
raise Errors::NotArray.new(input:
|
78
|
+
def to_rows(attrs_array)
|
79
|
+
return [] if attrs_array.nil?
|
80
|
+
raise Errors::NotArray.new(input: rows) if attrs_array.is_a?(Hash)
|
81
|
+
raise Errors::NotArray.new(input: attrs_array) unless attrs_array.respond_to?(:to_a)
|
80
82
|
|
81
|
-
|
83
|
+
attrs_array.to_a.map { |entity| to_row(entity) }
|
82
84
|
end
|
83
85
|
|
84
86
|
# @abstract
|
85
|
-
def from_row(
|
86
|
-
|
87
|
+
def from_row(row)
|
88
|
+
row.to_h
|
87
89
|
end
|
88
90
|
|
89
91
|
# @abstract
|
90
|
-
def to_row(
|
91
|
-
|
92
|
+
def to_row(attrs)
|
93
|
+
attrs.to_h
|
92
94
|
end
|
93
95
|
end
|
94
96
|
end
|
@@ -48,17 +48,17 @@ module LunaPark
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
def error_payload(
|
51
|
+
def error_payload(err) # rubocop:disable Metrics/MethodLength
|
52
52
|
error_hash = {
|
53
|
-
class:
|
54
|
-
message:
|
53
|
+
class: err.class,
|
54
|
+
message: err.message
|
55
55
|
}
|
56
|
-
error_hash.merge!(backtrace: "\n" +
|
56
|
+
error_hash.merge!(backtrace: "\n" + err.backtrace.join("\n") + "\n") if err.backtrace
|
57
57
|
payload = {
|
58
58
|
error: error_hash,
|
59
59
|
ok: false
|
60
60
|
}
|
61
|
-
payload.merge!(details:
|
61
|
+
payload.merge!(details: err.details) if err.respond_to?(:details)
|
62
62
|
payload
|
63
63
|
end
|
64
64
|
|
data/lib/luna_park/version.rb
CHANGED
data/lib/luna_park.rb
CHANGED
@@ -66,8 +66,7 @@ LunaPark::Tools.if_gem_installed('dry-validation', '~> 1.1') { require 'luna_par
|
|
66
66
|
require 'luna_park/values/compound'
|
67
67
|
require 'luna_park/values/single'
|
68
68
|
require 'luna_park/values/attributable'
|
69
|
-
require 'luna_park/
|
70
|
-
require 'luna_park/mappers/codirectional'
|
69
|
+
require 'luna_park/mapper'
|
71
70
|
require 'luna_park/repository'
|
72
71
|
require 'luna_park/repositories/sequel'
|
73
72
|
require 'luna_park/repositories/postgres'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: luna_park
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Kudrin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-12-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bugsnag
|
@@ -424,6 +424,7 @@ files:
|
|
424
424
|
- lib/luna_park/http/request.rb
|
425
425
|
- lib/luna_park/http/response.rb
|
426
426
|
- lib/luna_park/http/send.rb
|
427
|
+
- lib/luna_park/mapper.rb
|
427
428
|
- lib/luna_park/mappers/codirectional.rb
|
428
429
|
- lib/luna_park/mappers/codirectional/copyists/nested.rb
|
429
430
|
- lib/luna_park/mappers/codirectional/copyists/slice.rb
|