inat-get 0.8.0.11
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 +7 -0
- data/LICENSE +674 -0
- data/README.md +16 -0
- data/Rakefile +4 -0
- data/bin/inat-get +59 -0
- data/docs/logo.png +0 -0
- data/inat-get.gemspec +33 -0
- data/lib/extra/enum.rb +184 -0
- data/lib/extra/period.rb +252 -0
- data/lib/extra/uuid.rb +90 -0
- data/lib/inat/app/application.rb +50 -0
- data/lib/inat/app/config/messagelevel.rb +22 -0
- data/lib/inat/app/config/shiftage.rb +24 -0
- data/lib/inat/app/config/updatemode.rb +20 -0
- data/lib/inat/app/config.rb +296 -0
- data/lib/inat/app/globals.rb +80 -0
- data/lib/inat/app/info.rb +21 -0
- data/lib/inat/app/logging.rb +35 -0
- data/lib/inat/app/preamble.rb +27 -0
- data/lib/inat/app/status.rb +74 -0
- data/lib/inat/app/task/context.rb +47 -0
- data/lib/inat/app/task/dsl.rb +24 -0
- data/lib/inat/app/task.rb +75 -0
- data/lib/inat/data/api.rb +218 -0
- data/lib/inat/data/cache.rb +9 -0
- data/lib/inat/data/db.rb +87 -0
- data/lib/inat/data/ddl.rb +18 -0
- data/lib/inat/data/entity/comment.rb +29 -0
- data/lib/inat/data/entity/flag.rb +22 -0
- data/lib/inat/data/entity/identification.rb +45 -0
- data/lib/inat/data/entity/observation.rb +172 -0
- data/lib/inat/data/entity/observationphoto.rb +25 -0
- data/lib/inat/data/entity/observationsound.rb +26 -0
- data/lib/inat/data/entity/photo.rb +31 -0
- data/lib/inat/data/entity/place.rb +57 -0
- data/lib/inat/data/entity/project.rb +94 -0
- data/lib/inat/data/entity/projectadmin.rb +21 -0
- data/lib/inat/data/entity/projectobservationrule.rb +50 -0
- data/lib/inat/data/entity/request.rb +58 -0
- data/lib/inat/data/entity/sound.rb +27 -0
- data/lib/inat/data/entity/taxon.rb +94 -0
- data/lib/inat/data/entity/user.rb +67 -0
- data/lib/inat/data/entity/vote.rb +22 -0
- data/lib/inat/data/entity.rb +291 -0
- data/lib/inat/data/enums/conservationstatus.rb +30 -0
- data/lib/inat/data/enums/geoprivacy.rb +14 -0
- data/lib/inat/data/enums/iconictaxa.rb +23 -0
- data/lib/inat/data/enums/identificationcategory.rb +13 -0
- data/lib/inat/data/enums/licensecode.rb +16 -0
- data/lib/inat/data/enums/projectadminrole.rb +11 -0
- data/lib/inat/data/enums/projecttype.rb +37 -0
- data/lib/inat/data/enums/qualitygrade.rb +12 -0
- data/lib/inat/data/enums/rank.rb +60 -0
- data/lib/inat/data/model.rb +551 -0
- data/lib/inat/data/query.rb +1145 -0
- data/lib/inat/data/sets/dataset.rb +104 -0
- data/lib/inat/data/sets/list.rb +190 -0
- data/lib/inat/data/sets/listers.rb +15 -0
- data/lib/inat/data/sets/wrappers.rb +137 -0
- data/lib/inat/data/types/extras.rb +88 -0
- data/lib/inat/data/types/location.rb +89 -0
- data/lib/inat/data/types/std.rb +293 -0
- data/lib/inat/report/table.rb +135 -0
- data/lib/inat/utils/deep.rb +30 -0
- metadata +137 -0
@@ -0,0 +1,551 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../app/globals'
|
4
|
+
require_relative 'types/std'
|
5
|
+
|
6
|
+
autoload :Entity, 'inat/data/entity'
|
7
|
+
|
8
|
+
class Model
|
9
|
+
|
10
|
+
include LogDSL
|
11
|
+
|
12
|
+
class Field
|
13
|
+
|
14
|
+
attr_reader :model, :name, :type, :id_field
|
15
|
+
|
16
|
+
def required?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize model, name, type, id_field
|
21
|
+
@model = model
|
22
|
+
@name = name
|
23
|
+
@type = type
|
24
|
+
@id_field = id_field
|
25
|
+
end
|
26
|
+
|
27
|
+
def DDL
|
28
|
+
[ [], [] ]
|
29
|
+
end
|
30
|
+
|
31
|
+
def from_db row
|
32
|
+
[ nil, nil ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_db value
|
36
|
+
[ nil, nil ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def kind
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class ScalarField < Model::Field
|
46
|
+
|
47
|
+
attr_reader :index, :unique, :primary_key
|
48
|
+
|
49
|
+
def required?
|
50
|
+
@required
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize model, name, type, id_field, required, index, unique, primary_key
|
54
|
+
if Class === type && Entity > type && id_field == nil
|
55
|
+
id_field = "#{ name }_id".intern
|
56
|
+
end
|
57
|
+
super model, name, type, id_field
|
58
|
+
@required = required
|
59
|
+
@index = index
|
60
|
+
@unique = unique
|
61
|
+
@primary_key = primary_key
|
62
|
+
end
|
63
|
+
|
64
|
+
def implement
|
65
|
+
nm = @name
|
66
|
+
md = @model
|
67
|
+
rq = @required
|
68
|
+
ni = @id_field
|
69
|
+
tp = @type
|
70
|
+
if ni
|
71
|
+
md.define_method "#{ ni }" do
|
72
|
+
instance_variable_get("@#{ ni }")
|
73
|
+
end
|
74
|
+
md.define_method "#{ ni }=" do |value|
|
75
|
+
prevalue = instance_variable_get "@#{ ni }"
|
76
|
+
if prevalue != value
|
77
|
+
debug "ASS: #{ self.id }: #{ ni } = #{ prevalue.inspect } <=> #{ value.inspect }" if prevalue != nil && self.class.name == 'Taxon'
|
78
|
+
instance_variable_set "@#{ ni }", value
|
79
|
+
instance_variable_set "@saved", false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
md.define_method "#{ nm }" do
|
83
|
+
v = instance_variable_get("@#{ ni }")
|
84
|
+
return nil if v == nil
|
85
|
+
r = tp.fetch v
|
86
|
+
if r.size == 0
|
87
|
+
nil
|
88
|
+
else
|
89
|
+
r.first
|
90
|
+
end
|
91
|
+
end
|
92
|
+
md.define_method "#{ nm }=" do |value|
|
93
|
+
prevalue = instance_variable_get "@#{ ni }"
|
94
|
+
if prevalue != value&.id
|
95
|
+
debug "ASS: #{ self.id }: #{ nm } / #{ ni } = #{ prevalue.inspect } <=> #{ value.inspect }" if prevalue != nil && self.class.name == 'Taxon'
|
96
|
+
instance_variable_set "@#{ ni }", value&.id
|
97
|
+
instance_variable_set "@saved", false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
else
|
101
|
+
md.define_method "#{ nm }" do
|
102
|
+
instance_variable_get "@#{ nm }"
|
103
|
+
end
|
104
|
+
md.define_method "#{ nm }=" do |value|
|
105
|
+
raise TypeError, "Invalid '#{ nm }' value: #{ value.inspect }!", caller unless tp === value || (value == nil && !rq)
|
106
|
+
prevalue = instance_variable_get "@#{ nm }"
|
107
|
+
if prevalue != value
|
108
|
+
debug "ASS: #{ self.id }: #{ nm } = #{ prevalue.inspect } <=> #{ value.inspect }" if prevalue != nil && self.class.name == 'Taxon'
|
109
|
+
instance_variable_set "@#{ nm }", value
|
110
|
+
instance_variable_set "@saved", false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def read?
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def write?
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
def DDL
|
125
|
+
inner = ''
|
126
|
+
outer = []
|
127
|
+
ddl_name = @id_field || @name
|
128
|
+
type_ddl = @type.ddl
|
129
|
+
case type_ddl
|
130
|
+
when String, Symbol
|
131
|
+
inner = " #{ ddl_name } #{ type_ddl }"
|
132
|
+
if @primary_key
|
133
|
+
inner += ' NOT NULL PRIMARY KEY'
|
134
|
+
elsif @unique
|
135
|
+
outer << "CREATE UNIQUE INDEX IF NOT EXISTS uq_#{ @model.table }_#{ ddl_name } ON #{ @model.table } (#{ ddl_name });"
|
136
|
+
elsif @index
|
137
|
+
outer << "CREATE INDEX IF NOT EXISTS ix_#{ @model.table }_#{ ddl_name } ON #{ @model.table } (#{ ddl_name });"
|
138
|
+
end
|
139
|
+
when Hash
|
140
|
+
inner = []
|
141
|
+
names = []
|
142
|
+
type_ddl.each do |k, v|
|
143
|
+
inner << " #{ ddl_name }_#{ k } #{ v }"
|
144
|
+
names << "#{ ddl_name }_#{ k }"
|
145
|
+
end
|
146
|
+
if @unique
|
147
|
+
outer << "CREATE UNIQUE INDEX IF NOT EXISTS uq_#{ @model.table }_#{ ddl_name } ON #{ @model.table } (#{ names.join(',') });"
|
148
|
+
elsif @index
|
149
|
+
outer << "CREATE INDEX IF NOT EXISTS ix_#{ @model.table }_#{ ddl_name } ON #{ @model.table } (#{ names.join(',') });"
|
150
|
+
end
|
151
|
+
else
|
152
|
+
raise TypeError, "Invalid type DDL: #{ type_ddl.inspect }", caller
|
153
|
+
end
|
154
|
+
[ inner, outer ]
|
155
|
+
end
|
156
|
+
|
157
|
+
def from_db row
|
158
|
+
ddl_name = @id_field || @name
|
159
|
+
type_ddl = @type.ddl
|
160
|
+
value = nil
|
161
|
+
case type_ddl
|
162
|
+
when String, Symbol
|
163
|
+
value = row[ddl_name.to_s]
|
164
|
+
value = @type.from_db value unless @id_field || @type === value
|
165
|
+
when Hash
|
166
|
+
value = {}
|
167
|
+
type_ddl.each do |k, v|
|
168
|
+
value[k] = row["#{ ddl_name }_#{k}"]
|
169
|
+
end
|
170
|
+
value = @type.from_db value
|
171
|
+
else
|
172
|
+
raise TypeError, "Invalid type DDL: #{ type_ddl.inspect }!", caller
|
173
|
+
end
|
174
|
+
[ ddl_name, value ]
|
175
|
+
end
|
176
|
+
|
177
|
+
def to_db value
|
178
|
+
ddl_name = @id_field || @name
|
179
|
+
type_ddl = @type.ddl
|
180
|
+
case type_ddl
|
181
|
+
when String, Symbol
|
182
|
+
[ ddl_name, value.to_db ]
|
183
|
+
when Hash
|
184
|
+
keys = []
|
185
|
+
values = []
|
186
|
+
if value != nil
|
187
|
+
hash = value.to_db
|
188
|
+
hash.each do |k, v|
|
189
|
+
keys << "#{ ddl_name }_#{ k }"
|
190
|
+
values << v
|
191
|
+
end
|
192
|
+
else
|
193
|
+
type_ddl.each do |k, _|
|
194
|
+
keys << "#{ ddl_name }_#{ k }"
|
195
|
+
values << nil
|
196
|
+
end
|
197
|
+
end
|
198
|
+
[ keys, values ]
|
199
|
+
else
|
200
|
+
raise TypeError, "Invalid type DDL: #{ type_ddl.inspect }!", caller
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def kind
|
205
|
+
:value
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
class ArrayField < Model::Field
|
211
|
+
|
212
|
+
attr_reader :back_field
|
213
|
+
|
214
|
+
def owned?
|
215
|
+
@owned
|
216
|
+
end
|
217
|
+
|
218
|
+
def initialize model, name, type, id_field, owned, back_field
|
219
|
+
if id_field == nil
|
220
|
+
if name.end_with?('s')
|
221
|
+
id_field = "#{ name[..-2] }_ids".intern
|
222
|
+
else
|
223
|
+
raise ArgumentError, "Argument 'id_field' is required for name '#{ name }'!", caller[1..]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
back_field = "#{ model.name.downcase }_id".intern if back_field == nil
|
227
|
+
super model, name, type, id_field
|
228
|
+
@owned = owned
|
229
|
+
@back_field = back_field
|
230
|
+
end
|
231
|
+
|
232
|
+
def implement
|
233
|
+
nm = @name
|
234
|
+
md = @model
|
235
|
+
# rq = @required
|
236
|
+
ni = @id_field
|
237
|
+
tp = @type
|
238
|
+
if ni
|
239
|
+
md.define_method "#{ ni }" do
|
240
|
+
instance_variable_get("@#{ ni }") || []
|
241
|
+
end
|
242
|
+
md.define_method "#{ ni }=" do |value|
|
243
|
+
prevalue = instance_variable_get "@#{ ni }"
|
244
|
+
if ni.intern == :ancestor_ids
|
245
|
+
prevalue&.delete(self.id)
|
246
|
+
value&.delete(self.id)
|
247
|
+
value&.prepend 48460
|
248
|
+
value = value&.sort.uniq
|
249
|
+
end
|
250
|
+
if prevalue != value
|
251
|
+
debug "ASS: #{ self.id }: #{ ni } = #{ prevalue.inspect } <=> #{ value.inspect } :: #{ caller[..2] }" if prevalue != nil && self.class.name == 'Taxon'
|
252
|
+
instance_variable_set "@#{ ni }", value
|
253
|
+
instance_variable_set "@saved", false
|
254
|
+
end
|
255
|
+
end
|
256
|
+
md.define_method "#{ nm }" do
|
257
|
+
tp.fetch(*(instance_variable_get("@#{ ni }") || []))
|
258
|
+
end
|
259
|
+
md.define_method "#{ nm }=" do |value|
|
260
|
+
value ||= []
|
261
|
+
# value.each do |v|
|
262
|
+
# raise TypeError, "Invalid #{ nm } value: #{ v.inspect }!", caller unless tp === v
|
263
|
+
# end
|
264
|
+
self.send "#{ ni }=", value.map(&:id)
|
265
|
+
end
|
266
|
+
else
|
267
|
+
md.define_method "#{ nm }" do
|
268
|
+
instance_variable_get("@#{ nm }") || []
|
269
|
+
end
|
270
|
+
md.define_method "#{ nm }=" do |value|
|
271
|
+
value ||= []
|
272
|
+
value.each do |v|
|
273
|
+
raise TypeError, "Invalid #{ nm } value: #{ v.inspect }!", caller unless tp === v
|
274
|
+
end
|
275
|
+
prevalue = instance_variable_get("@#{ nm }")
|
276
|
+
if prevalue&.sort != value&.sort
|
277
|
+
debug "ASS: #{ self.id }: #{ nm } = #{ prevalue.inspect } <=> #{ value.inspect } :: #{ caller[..2] }" if prevalue != nil && self.class.name == 'Taxon'
|
278
|
+
instance_variable_set "@#{ nm }", value
|
279
|
+
instance_variable_set "@saved", false
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def read?
|
286
|
+
true
|
287
|
+
end
|
288
|
+
|
289
|
+
def write?
|
290
|
+
@owned
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
class ManyToManyField < Model::ArrayField
|
296
|
+
|
297
|
+
attr_reader :table_name, :link_field, :index
|
298
|
+
|
299
|
+
def initialize model, name, type, id_field, owned, table_name, back_field, link_field, index
|
300
|
+
table_name = "#{ model.name.downcase }_#{ name }".intern if table_name == nil
|
301
|
+
link_field = "#{ type.name.downcase }_id".intern if link_field == nil
|
302
|
+
super model, name, type, id_field, owned, back_field
|
303
|
+
@table_name = table_name
|
304
|
+
@link_field = link_field
|
305
|
+
@index = index
|
306
|
+
end
|
307
|
+
|
308
|
+
def DDL
|
309
|
+
outer = []
|
310
|
+
if @owned
|
311
|
+
outer << "\nCREATE TABLE IF NOT EXISTS #{ @table_name } (\n" +
|
312
|
+
" #{ back_field } INTEGER NOT NULL REFERENCES #{ @model.table } (id),\n" +
|
313
|
+
" #{ link_field } INTEGER NOT NULL REFERENCES #{ @type.table } (id),\n" +
|
314
|
+
" PRIMARY KEY (#{ back_field }, #{ link_field })\n" +
|
315
|
+
");"
|
316
|
+
outer << "CREATE INDEX IF NOT EXISTS ix_#{ @table_name }_#{ back_field } ON #{ @table_name } (#{ back_field });"
|
317
|
+
if @index
|
318
|
+
outer << "CREATE INDEX IF NOT EXISTS ix_#{ @table_name }_#{ link_field } ON #{ @table_name } (#{ link_field });"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
[ [], outer ]
|
322
|
+
end
|
323
|
+
|
324
|
+
def kind
|
325
|
+
:links
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
class OneToManyField < Model::ArrayField
|
331
|
+
|
332
|
+
def kind
|
333
|
+
:backs
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
class SpecialField < Model::Field
|
339
|
+
|
340
|
+
def initialize model, name, type, &block
|
341
|
+
raise ArgumentError, "Block is required!", caller[1..] unless block_given?
|
342
|
+
super model, name, type, nil
|
343
|
+
@block = block
|
344
|
+
end
|
345
|
+
|
346
|
+
def implement
|
347
|
+
nm = @name
|
348
|
+
md = @model
|
349
|
+
md.define_method "#{ nm }=", &@block
|
350
|
+
end
|
351
|
+
|
352
|
+
def read?
|
353
|
+
false
|
354
|
+
end
|
355
|
+
|
356
|
+
def write?
|
357
|
+
true
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
class IgnoreField < Model::SpecialField
|
363
|
+
|
364
|
+
def initialize model, name
|
365
|
+
super model, name, Object do
|
366
|
+
nil
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def implement
|
371
|
+
end
|
372
|
+
|
373
|
+
def read?
|
374
|
+
false
|
375
|
+
end
|
376
|
+
|
377
|
+
def write?
|
378
|
+
false
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
private_constant :Field, :ScalarField, :ArrayField, :ManyToManyField, :OneToManyField, :SpecialField, :IgnoreField
|
384
|
+
|
385
|
+
class << self
|
386
|
+
|
387
|
+
def api_path name = nil
|
388
|
+
raise TypeError, "Path name must be a Symbol!", caller unless name == nil || Symbol === name
|
389
|
+
@api_path = name if name != nil
|
390
|
+
@api_path
|
391
|
+
end
|
392
|
+
|
393
|
+
def has_path?
|
394
|
+
!!@api_path
|
395
|
+
end
|
396
|
+
|
397
|
+
def api_part part = nil
|
398
|
+
raise TypeError, "Part name must be a Symbol!", caller unless part == nil || Symbol === part
|
399
|
+
@api_part = part if part != nil
|
400
|
+
@api_part
|
401
|
+
end
|
402
|
+
|
403
|
+
def api_limit limit = nil
|
404
|
+
raise TypeError, "Part name must be an Integer!", caller unless limit == nil || Integer === limit
|
405
|
+
@api_limit = limit if limit != nil
|
406
|
+
@api_limit
|
407
|
+
end
|
408
|
+
|
409
|
+
def table name = nil
|
410
|
+
raise TypeError, "Table name must be a Symbol!", caller unless name == nil || Symbol === name
|
411
|
+
@table = name if name != nil
|
412
|
+
@table
|
413
|
+
end
|
414
|
+
|
415
|
+
def has_table?
|
416
|
+
!!@table
|
417
|
+
end
|
418
|
+
|
419
|
+
def fields include_super = true
|
420
|
+
@fields ||= {}
|
421
|
+
result = {}
|
422
|
+
if include_super
|
423
|
+
ancestors.reverse.each do |ancestor|
|
424
|
+
if ancestor != self && ancestor.respond_to?(:fields)
|
425
|
+
ancestor_fields = ancestor.fields
|
426
|
+
if Hash === ancestor_fields
|
427
|
+
result.merge! ancestor_fields
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
result.merge! @fields
|
433
|
+
result.freeze
|
434
|
+
end
|
435
|
+
|
436
|
+
private def field name, type: nil, id_field: nil, required: false, index: false, unique: false, primary_key: false
|
437
|
+
raise TypeError, "Field name must be a Symbol!", caller unless Symbol === name
|
438
|
+
raise TypeError, "Field type must be a Module!", caller unless Module === type
|
439
|
+
raise TypeError, "Argument 'id_field' must be a Symbol!", caller unless Symbol === id_field || id_field == nil
|
440
|
+
raise TypeError, "Argument 'required' must be a Boolean!", caller unless Boolean === required
|
441
|
+
raise TypeError, "Argument 'index' must be a Boolean!", caller unless Boolean === index
|
442
|
+
raise TypeError, "Argument 'unique' must be a Boolean!", caller unless Boolean === unique
|
443
|
+
raise TypeError, "Argument 'primary_key' must be a Boolean!", caller unless Boolean === primary_key
|
444
|
+
@fields ||= {}
|
445
|
+
@fields[name] = ScalarField::new self, name, type, id_field, required, index, unique, primary_key
|
446
|
+
@fields[name].implement
|
447
|
+
end
|
448
|
+
|
449
|
+
private def links name, item_type: nil, ids_field: nil, owned: true, table_name: nil, back_field: nil, link_field: nil, index: false
|
450
|
+
raise TypeError, "Field name must be a Symbol!", caller unless Symbol === name
|
451
|
+
raise TypeError, "Item type must be an Entity subclass!", caller unless Class === item_type && Entity > item_type
|
452
|
+
raise TypeError, "Argument 'ids_field' must be a Symbol!", caller unless Symbol === ids_field || ids_field == nil
|
453
|
+
raise TypeError, "Argument 'table_name' must be a Symbol!", caller unless Symbol === table_name || table_name == nil
|
454
|
+
raise TypeError, "Argument 'back_field' must be a Symbol!", caller unless Symbol === back_field || back_field == nil
|
455
|
+
raise TypeError, "Argument 'link_field' must be a Symbol!", caller unless Symbol === link_field || link_field == nil
|
456
|
+
raise TypeError, "Argument 'owned' must be a Boolean!", caller unless Boolean === owned
|
457
|
+
raise TypeError, "Argument 'index' must be a Boolean!", caller unless Boolean === index
|
458
|
+
@fields ||= {}
|
459
|
+
@fields[name] = ManyToManyField::new self, name, item_type, ids_field, owned, table_name, back_field, link_field, index
|
460
|
+
@fields[name].implement
|
461
|
+
end
|
462
|
+
|
463
|
+
private def backs name, item_type: nil, ids_field: nil, owned: true, back_field: nil
|
464
|
+
raise TypeError, "Field name must be a Symbol!", caller unless Symbol === name
|
465
|
+
raise TypeError, "Item type must be an Entity subclass!", caller unless Class === item_type && Entity > item_type
|
466
|
+
raise TypeError, "Argument 'ids_field' must be a Symbol!", caller unless Symbol === ids_field || ids_field == nil
|
467
|
+
raise TypeError, "Argument 'back_field' must be a Symbol!", caller unless Symbol === back_field || back_field == nil
|
468
|
+
raise TypeError, "Argument 'owned' must be a Boolean!", caller unless Boolean === owned
|
469
|
+
@fields ||= {}
|
470
|
+
@fields[name] = OneToManyField::new self, name, item_type, ids_field, owned, back_field
|
471
|
+
@fields[name].implement
|
472
|
+
end
|
473
|
+
|
474
|
+
private def block name, type: nil, &block
|
475
|
+
raise TypeError, "Field name must be a Symbol!", caller unless Symbol === name
|
476
|
+
raise TypeError, "Field type must be a Module!", caller unless Module === type
|
477
|
+
raise ArgumentError, "Block is required!", caller unless block_given?
|
478
|
+
@fields ||= {}
|
479
|
+
@fields[name] = SpecialField::new self, name, type, &block
|
480
|
+
@fields[name].implement
|
481
|
+
end
|
482
|
+
|
483
|
+
private def ignore *names
|
484
|
+
@fields ||= {}
|
485
|
+
names.each do |name|
|
486
|
+
raise TypeError, "Field name must be a Symbol!", caller unless Symbol === name
|
487
|
+
@fields[name] = IgnoreField::new self, name
|
488
|
+
@fields[name].implement
|
489
|
+
end
|
490
|
+
end
|
491
|
+
|
492
|
+
def DDL
|
493
|
+
inner = []
|
494
|
+
outer = []
|
495
|
+
fields.each do |_, field|
|
496
|
+
i, o = field.DDL
|
497
|
+
inner << i
|
498
|
+
outer << o
|
499
|
+
end
|
500
|
+
"CREATE TABLE IF NOT EXISTS #{ @table } (\n#{ inner.flatten.join(",\n") }\n);\n" + "#{ outer.flatten.join("\n") }\n\n"
|
501
|
+
end
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
def initialize
|
506
|
+
@mutex = Mutex::new
|
507
|
+
end
|
508
|
+
|
509
|
+
def process?
|
510
|
+
@process
|
511
|
+
end
|
512
|
+
|
513
|
+
def saved?
|
514
|
+
@saved
|
515
|
+
end
|
516
|
+
|
517
|
+
def post_update
|
518
|
+
# do nothing
|
519
|
+
end
|
520
|
+
|
521
|
+
def update(from_db: false)
|
522
|
+
raise ArgumentError, "Block is required!", caller unless block_given?
|
523
|
+
@process = true
|
524
|
+
@saved = true if from_db
|
525
|
+
result = nil
|
526
|
+
exception = nil
|
527
|
+
@mutex.synchronize do
|
528
|
+
begin
|
529
|
+
result = yield
|
530
|
+
post_update unless from_db
|
531
|
+
rescue Exception => e
|
532
|
+
exception = e
|
533
|
+
end
|
534
|
+
end
|
535
|
+
@saved = true if from_db
|
536
|
+
@process = false
|
537
|
+
raise exception.class, exception.message, caller, cause: exception if exception
|
538
|
+
result
|
539
|
+
end
|
540
|
+
|
541
|
+
def to_h
|
542
|
+
result = {}
|
543
|
+
self.class.fields.each do |key, field|
|
544
|
+
if field.read?
|
545
|
+
result[key] = send "#{ key }"
|
546
|
+
end
|
547
|
+
end
|
548
|
+
result.freeze
|
549
|
+
end
|
550
|
+
|
551
|
+
end
|