model_fact 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/README.md +31 -0
- data/Rakefile +9 -0
- data/lib/model_fact/helpers/ar_helpers.rb +392 -0
- data/lib/model_fact/helpers/filter_builder.rb +80 -0
- data/lib/model_fact/helpers/model_helper.rb +84 -0
- data/lib/model_fact/helpers/poro_helpers.rb +50 -0
- data/lib/model_fact/helpers.rb +11 -0
- data/lib/model_fact/registry.rb +100 -0
- data/lib/model_fact/version.rb +5 -0
- data/lib/model_fact.rb +119 -0
- data/sig/model_fact.rbs +4 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 41b770abcc6205f56ea4913a107e0cae3f1be7a238341a1a017c54fc95619576
|
4
|
+
data.tar.gz: ba13febedc9c395b60b9f0c9045a0df0403cc64a96f5f0bda675575c2ffa8c61
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5fccf83a947103bce939c0af509218ffc6e8e5fc7c370d0905e933084dce1eeccb792dc21f9c123ff30195a85a4b12f067eb02b28d8aaa034f0475074f5eb041
|
7
|
+
data.tar.gz: c7d663479ee1982e3cdc0e1bb41522c72c751f5298f52e79fd35c431812e4a149266c53ffe4c05c09488d8ff356368939fe729b37b9577c5174b5ce16db1d4b5
|
data/.rspec
ADDED
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# ModelFact
|
2
|
+
|
3
|
+
TODO: Delete this and the text below, and describe your gem
|
4
|
+
|
5
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/model_fact`. To experiment with that code, run `bin/console` for an interactive prompt.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
10
|
+
|
11
|
+
Install the gem and add to the application's Gemfile by executing:
|
12
|
+
|
13
|
+
$ bundle add UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
|
14
|
+
|
15
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
16
|
+
|
17
|
+
$ gem install UPDATE_WITH_YOUR_GEM_NAME_PRIOR_TO_RELEASE_TO_RUBYGEMS_ORG
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Development
|
24
|
+
|
25
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
|
+
|
27
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/model_fact.
|
data/Rakefile
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Helper for ActiveRecord
|
4
|
+
#
|
5
|
+
|
6
|
+
module ModelFact
|
7
|
+
module Helpers
|
8
|
+
module ActiveRecordHelper
|
9
|
+
include TR::CondUtils
|
10
|
+
|
11
|
+
class ReqMethodNotGiven < StandardError; end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
include TR::CondUtils
|
15
|
+
|
16
|
+
def on_event(evt, opts = { }, &block)
|
17
|
+
|
18
|
+
_listeners[evt] = [] if _listeners[evt].nil?
|
19
|
+
|
20
|
+
case evt
|
21
|
+
when :before_save
|
22
|
+
_listeners[evt] << block
|
23
|
+
self.class.class_eval <<-END
|
24
|
+
before_save :_mf_trigger_listener(:before_save)
|
25
|
+
END
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def listeners
|
30
|
+
_listeners.freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
### Standardized search API
|
34
|
+
#
|
35
|
+
# Issue : Record need to be part of the
|
36
|
+
# business object as Banis is controller
|
37
|
+
# driven. Whatever return from AR need
|
38
|
+
# to be converted to business model which
|
39
|
+
# difficult to be done generically
|
40
|
+
|
41
|
+
def filter(opts = {}, &block)
|
42
|
+
|
43
|
+
res = self.all
|
44
|
+
|
45
|
+
@goToPage = 0
|
46
|
+
|
47
|
+
if block
|
48
|
+
|
49
|
+
filter = FilterBuilder.new
|
50
|
+
flt = block.call(:cond, filter)
|
51
|
+
|
52
|
+
orArray = []
|
53
|
+
if not_empty?(flt.main_cond)
|
54
|
+
flt.main_cond.each do |val|
|
55
|
+
val.each do |k,v|
|
56
|
+
case k
|
57
|
+
when :and
|
58
|
+
res = res.where(v)
|
59
|
+
when :or
|
60
|
+
val[:or].each do |r,vv|
|
61
|
+
orArray << ["#{r} = #{vv}"]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
res = res.where("(#{orArray.join(" or ")})") if not_empty?(orArray)
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
if not_empty?(flt.modifier)
|
72
|
+
flt.modifier.each do |mod|
|
73
|
+
mod.each do |k,v|
|
74
|
+
case k
|
75
|
+
when :sort
|
76
|
+
res = res.order(v)
|
77
|
+
when :first
|
78
|
+
cnt = v || 1
|
79
|
+
res = res.first(cnt)
|
80
|
+
when :last
|
81
|
+
cnt = v || 1
|
82
|
+
res = res.last(cnt)
|
83
|
+
when :page
|
84
|
+
@goToPage = v
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
ignorePaging = block.call(:ignore_paging?)
|
91
|
+
ignorePaging = false if is_empty?(ignorePaging)
|
92
|
+
|
93
|
+
if is_paginated? and not ignorePaging
|
94
|
+
|
95
|
+
ttl = res.count
|
96
|
+
block.call(:result, :total_record, ttl)
|
97
|
+
|
98
|
+
ttlPage = (ttl/per_page_count).to_i
|
99
|
+
ttlPage += 1 if (ttlPage*per_page_count) != ttl
|
100
|
+
block.call(:result, :total_page, ttlPage)
|
101
|
+
|
102
|
+
res = res.limit(per_page_count)
|
103
|
+
|
104
|
+
if @goToPage <= 0
|
105
|
+
block.call(:result, :first_page?, true)
|
106
|
+
else
|
107
|
+
block.call(:result, :first_page?, false)
|
108
|
+
end
|
109
|
+
|
110
|
+
offset = @goToPage * per_page_count
|
111
|
+
res = res.offset(offset)
|
112
|
+
|
113
|
+
if (offset+per_page_count) >= ttl
|
114
|
+
block.call(:result, :last_page?, true)
|
115
|
+
else
|
116
|
+
block.call(:result, :last_page?, false)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
if flt.is_return_count?
|
122
|
+
res.count
|
123
|
+
else
|
124
|
+
translate(res)
|
125
|
+
end
|
126
|
+
|
127
|
+
else
|
128
|
+
translate(res)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
def remove(rec)
|
134
|
+
self.delete(rec)
|
135
|
+
end
|
136
|
+
|
137
|
+
def remove_all
|
138
|
+
self.delete_all
|
139
|
+
end
|
140
|
+
|
141
|
+
def first(row = 1)
|
142
|
+
res = self.filter do |ops, val|
|
143
|
+
case ops
|
144
|
+
when :cond
|
145
|
+
val.first(row)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
res.first if row == 1
|
149
|
+
end
|
150
|
+
|
151
|
+
def last(row = 1)
|
152
|
+
res = self.filter do |ops, val|
|
153
|
+
case ops
|
154
|
+
when :cond
|
155
|
+
val.last(row)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
res.first if row == 1
|
159
|
+
end
|
160
|
+
|
161
|
+
def find_record(opts = {}, &block)
|
162
|
+
self.filter do |ops, *val|
|
163
|
+
case ops
|
164
|
+
when :cond
|
165
|
+
val.first.filter(opts)
|
166
|
+
else
|
167
|
+
block.call(ops, *val) if block
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
alias_method :find_records, :find_record
|
172
|
+
|
173
|
+
|
174
|
+
#def find_record(opts = {}, &block)
|
175
|
+
# opts = {} if opts.nil?
|
176
|
+
# if not_empty?(opts) and not_empty?(opts[:fcond])
|
177
|
+
# if not_empty?(opts[:fcond][:per_page_count])
|
178
|
+
# paginate(per_page: opts[:fcond][:per_page_count].to_i)
|
179
|
+
# opts[:fcond].delete(:per_page_count)
|
180
|
+
# end
|
181
|
+
# res = self.where(opts[:fcond])
|
182
|
+
# else
|
183
|
+
# res = self
|
184
|
+
# end
|
185
|
+
|
186
|
+
# apply_user_filter(res, opts) do |ops, val|
|
187
|
+
# case ops
|
188
|
+
# when :translate
|
189
|
+
# if block
|
190
|
+
# res = translate(block.call(:callback, val))
|
191
|
+
# res
|
192
|
+
# else
|
193
|
+
# res = translate(val)
|
194
|
+
# res
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# else
|
198
|
+
# if block
|
199
|
+
# block.call(ops,val)
|
200
|
+
# else
|
201
|
+
# val
|
202
|
+
# end
|
203
|
+
#
|
204
|
+
# end
|
205
|
+
# end
|
206
|
+
#end
|
207
|
+
#alias_method :find_records, :find_record
|
208
|
+
|
209
|
+
#def all_record(opts = {}, &block)
|
210
|
+
|
211
|
+
# opts = {} if opts.nil?
|
212
|
+
# noPaging = opts[:no_paging]
|
213
|
+
# noPaging = false if is_empty?(noPaging)
|
214
|
+
|
215
|
+
# res = self.all
|
216
|
+
|
217
|
+
# if not opts[:no_paging]
|
218
|
+
# apply_user_filter(res, opts) do |ops, val|
|
219
|
+
# case ops
|
220
|
+
# when :translate
|
221
|
+
# translate(val)
|
222
|
+
|
223
|
+
# else
|
224
|
+
# if block
|
225
|
+
# block.call(ops, val)
|
226
|
+
# else
|
227
|
+
# val
|
228
|
+
# end
|
229
|
+
# end
|
230
|
+
# end
|
231
|
+
# else
|
232
|
+
# translate(res)
|
233
|
+
# end
|
234
|
+
|
235
|
+
#end
|
236
|
+
#alias_method :all_records, :all_record
|
237
|
+
|
238
|
+
#def first_record(count = 1)
|
239
|
+
# translate(self.first(count).first)
|
240
|
+
#end
|
241
|
+
|
242
|
+
#def last_record(count = 1)
|
243
|
+
# translate(self.last(count).first)
|
244
|
+
#end
|
245
|
+
|
246
|
+
def record_translator(&block)
|
247
|
+
@recTranslator = block
|
248
|
+
end
|
249
|
+
|
250
|
+
def translate(rec)
|
251
|
+
if @recTranslator
|
252
|
+
@recTranslator.call(rec)
|
253
|
+
else
|
254
|
+
rec
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
### Paginate helpers
|
259
|
+
def paginate(opts = {})
|
260
|
+
@_per_page = opts[:per_page] || 10
|
261
|
+
@_paginate = true
|
262
|
+
end
|
263
|
+
|
264
|
+
def per_page_count
|
265
|
+
@_per_page
|
266
|
+
end
|
267
|
+
|
268
|
+
def per_page_count=(val)
|
269
|
+
@_per_page = val
|
270
|
+
end
|
271
|
+
|
272
|
+
def is_paginated?
|
273
|
+
not_empty?(@_paginate) and @_paginate
|
274
|
+
end
|
275
|
+
## End paginate helpers
|
276
|
+
|
277
|
+
### End standardize search API
|
278
|
+
|
279
|
+
private
|
280
|
+
def _listeners
|
281
|
+
if @_listeners.nil?
|
282
|
+
@_listeners = { }
|
283
|
+
end
|
284
|
+
@_listeners
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
#def apply_user_filter(mdl, opts = {}, &block)
|
289
|
+
|
290
|
+
# raise Error, "Block is required" if not block
|
291
|
+
|
292
|
+
# order = opts[:order] || { created_at: :asc }
|
293
|
+
# res = mdl.send(:order, order)
|
294
|
+
|
295
|
+
# if is_paginated?
|
296
|
+
|
297
|
+
# cnt = res.count
|
298
|
+
|
299
|
+
# block.call(:total_record, cnt)
|
300
|
+
|
301
|
+
# limit = per_page_count
|
302
|
+
# res = res.limit(limit) if not_empty?(limit)
|
303
|
+
|
304
|
+
# ttlPage = (cnt / limit).to_i + 1
|
305
|
+
|
306
|
+
# block.call(:total_page, ttlPage)
|
307
|
+
|
308
|
+
# page = (opts[:paginate].nil? ? 0 : opts[:paginate][:page]) || 0
|
309
|
+
#
|
310
|
+
# if page <= 0
|
311
|
+
# block.call(:first_page?, true)
|
312
|
+
# else
|
313
|
+
# block.call(:first_page?, false)
|
314
|
+
# end
|
315
|
+
|
316
|
+
# offset = page * limit
|
317
|
+
# res = res.offset(offset)
|
318
|
+
|
319
|
+
# if (offset+limit) >= cnt
|
320
|
+
# block.call(:last_page?, true)
|
321
|
+
# else
|
322
|
+
# block.call(:last_page?, false)
|
323
|
+
# end
|
324
|
+
|
325
|
+
# end
|
326
|
+
|
327
|
+
# count = opts[:count]
|
328
|
+
# if not_empty?(count)
|
329
|
+
# res.count
|
330
|
+
# else
|
331
|
+
# block.call(:translate, res)
|
332
|
+
# end
|
333
|
+
|
334
|
+
#end
|
335
|
+
|
336
|
+
end
|
337
|
+
def self.included(klass)
|
338
|
+
klass.extend(ClassMethods)
|
339
|
+
end
|
340
|
+
|
341
|
+
def _mf_trigger_listener(evt)
|
342
|
+
list = self.class.listeners[evt]
|
343
|
+
if not_empty?(list)
|
344
|
+
list.each do |l|
|
345
|
+
l.call(self)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
### Standardized search API
|
351
|
+
#
|
352
|
+
# Issue : Record need to be part of the
|
353
|
+
# business object as Banis is controller
|
354
|
+
# driven. Whatever return from AR need
|
355
|
+
# to be converted to business model which
|
356
|
+
# difficult to be done generically
|
357
|
+
#
|
358
|
+
#def find_record(opts = {}, &block)
|
359
|
+
# if not_empty?(opts)
|
360
|
+
# res = self.class.where(opts)
|
361
|
+
# else
|
362
|
+
# res = self.class
|
363
|
+
# end
|
364
|
+
|
365
|
+
# if block
|
366
|
+
# self.class.translate(block.call(res))
|
367
|
+
# else
|
368
|
+
# self.class.translate(res)
|
369
|
+
# end
|
370
|
+
#end
|
371
|
+
#alias_method :find_records, :find_record
|
372
|
+
|
373
|
+
#def all_record
|
374
|
+
# translate(self.all)
|
375
|
+
#end
|
376
|
+
#alias_method :all_records, :all_record
|
377
|
+
|
378
|
+
#def first_record(count = 1)
|
379
|
+
# translate(self.first(count).first)
|
380
|
+
#end
|
381
|
+
|
382
|
+
#def last_record(count = 1)
|
383
|
+
# translate(self.last(count).first)
|
384
|
+
#end
|
385
|
+
|
386
|
+
### End standardize search API
|
387
|
+
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module ModelFact
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
# based on sql?
|
7
|
+
class FilterBuilder
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@mainCond = []
|
11
|
+
@modifier = []
|
12
|
+
@_return_count = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def and(condHash = {}, opts = {})
|
16
|
+
main_cond << { and: condHash }
|
17
|
+
self
|
18
|
+
end
|
19
|
+
alias_method :filter, :and
|
20
|
+
|
21
|
+
def or(condHash = {}, opts = {})
|
22
|
+
main_cond << { or: condHash }
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def top(cnt = 1, opts = {})
|
27
|
+
modifier << { first: cnt }
|
28
|
+
self
|
29
|
+
end
|
30
|
+
alias_method :first, :top
|
31
|
+
|
32
|
+
def bottom(cnt = 1, opts = {})
|
33
|
+
modifier << { last: cnt }
|
34
|
+
self
|
35
|
+
end
|
36
|
+
alias_method :last, :bottom
|
37
|
+
|
38
|
+
#def sub_list_items(fromIndx, len, opts = {})
|
39
|
+
# modifier << { sub_index: { from: fromIndx, length: len } }
|
40
|
+
# self
|
41
|
+
#end
|
42
|
+
|
43
|
+
def count
|
44
|
+
@_return_count = true
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def is_return_count?
|
49
|
+
@_return_count == true
|
50
|
+
end
|
51
|
+
|
52
|
+
def go_to_page(pageNo)
|
53
|
+
modifier << { page: pageNo }
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def sort(field, type = :asc)
|
58
|
+
modifier << { sort: { field => type } }
|
59
|
+
self
|
60
|
+
end
|
61
|
+
alias_method :order, :sort
|
62
|
+
|
63
|
+
def main_cond
|
64
|
+
if @_main_cond.nil?
|
65
|
+
@_main_cond = []
|
66
|
+
end
|
67
|
+
@_main_cond
|
68
|
+
end
|
69
|
+
|
70
|
+
def modifier
|
71
|
+
if @_modifier.nil?
|
72
|
+
@_modifier = []
|
73
|
+
end
|
74
|
+
@_modifier
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module ModelFact
|
4
|
+
module ModelHelper
|
5
|
+
class ReqFieldNotAvail < StandardError; end
|
6
|
+
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
#
|
11
|
+
# A way to document which fields are needed for
|
12
|
+
# the operational of this class
|
13
|
+
#
|
14
|
+
def req_accessor(*fields)
|
15
|
+
@reqFields = [] if @reqFields.nil?
|
16
|
+
@reqFields.concat(fields)
|
17
|
+
#@reqFields = fields
|
18
|
+
end
|
19
|
+
|
20
|
+
def req_fields
|
21
|
+
(@reqFields || []).freeze
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_model_key(key)
|
25
|
+
@modelKey = key
|
26
|
+
end
|
27
|
+
|
28
|
+
def _model_key
|
29
|
+
@modelKey
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(mtd, *args, &block)
|
33
|
+
ModelFact.instance(_model_key).send(mtd, *args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def respond_to_missing?(mtd, priv)
|
37
|
+
ModelFact.instance(_model_key).respond_to_missing?(mtd, priv)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
def self.included(klass)
|
42
|
+
klass.extend(ClassMethods)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ensure_attributes_defined
|
46
|
+
self.class.req_fields.each do |f|
|
47
|
+
raise ReqFieldNotAvail, "Requred field '#{f}' not available on #{self}" if not (self.respond_to?(f) and self.respond_to?("#{f}="))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_model_instance(mdlInst)
|
52
|
+
@_mdlInst = mdlInst
|
53
|
+
end
|
54
|
+
|
55
|
+
def _model_key
|
56
|
+
self.class._model_key
|
57
|
+
end
|
58
|
+
|
59
|
+
def model
|
60
|
+
@_mdlInst
|
61
|
+
end
|
62
|
+
|
63
|
+
# comparing two model
|
64
|
+
def ==(val)
|
65
|
+
if val.is_a?(ModelFact)
|
66
|
+
model == val.model
|
67
|
+
else
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def method_missing(mtd, *args, &block)
|
74
|
+
model.send(mtd, *args, &block)
|
75
|
+
end
|
76
|
+
|
77
|
+
def respond_to_missing?(mtd, priv)
|
78
|
+
res = model.respond_to?(mtd)
|
79
|
+
res
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
module ModelFact
|
5
|
+
module Helpers
|
6
|
+
# Plain Old Ruby Object (PORO) helpers
|
7
|
+
module POROHelpers
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def on_event(evt, opts = { }, &block)
|
12
|
+
_listeners[evt] = [] if not _listeners.keys.include?(evt)
|
13
|
+
_listeners[evt] << block
|
14
|
+
end
|
15
|
+
|
16
|
+
def listeners
|
17
|
+
_listeners.freeze
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def _listeners
|
22
|
+
if @_listeners.nil?
|
23
|
+
@_listeners = { }
|
24
|
+
end
|
25
|
+
|
26
|
+
@_listeners
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.included(klass)
|
32
|
+
klass.extend(ClassMethods)
|
33
|
+
end
|
34
|
+
|
35
|
+
## instance methods
|
36
|
+
|
37
|
+
# triggering of this should be respective model's
|
38
|
+
# method
|
39
|
+
# E.g. before save(), model should manually check this
|
40
|
+
def trigger_listener(evt)
|
41
|
+
if self.class.listeners.keys.include?(evt)
|
42
|
+
self.class.listeners[evt].each do |l|
|
43
|
+
l.call(self)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
require 'singleton'
|
3
|
+
|
4
|
+
module ModelFact
|
5
|
+
class Registry
|
6
|
+
include Singleton
|
7
|
+
include TR::CondUtils
|
8
|
+
|
9
|
+
#
|
10
|
+
# To be called by application developer during
|
11
|
+
# startup to match which model to give to application
|
12
|
+
# This is configuration per se like on which 'database adapter' to go
|
13
|
+
#
|
14
|
+
def register(id, cls, opts = { })
|
15
|
+
|
16
|
+
opts = { } if opts.nil?
|
17
|
+
|
18
|
+
overwrite = opts[:overwrite]
|
19
|
+
overwrite = false if is_empty?(overwrite) or not_bool?(overwrite)
|
20
|
+
|
21
|
+
raise InvalidParamValue, "Given ID is nil or empty" if is_empty?(id)
|
22
|
+
raise InvalidParamValue, "Given class is nil or empty" if is_empty?(cls)
|
23
|
+
|
24
|
+
raise DuplicatedIDException, "Given ID '#{id}' already registered" if is_registered?(id)
|
25
|
+
|
26
|
+
idCls[id] = { target: cls }
|
27
|
+
|
28
|
+
als = opts[:alias]
|
29
|
+
if not_empty?(als)
|
30
|
+
if is_alias_registered?(als) and not overwrite
|
31
|
+
raise DuplicatedIDException, "Given alias '#{als}' already registered"
|
32
|
+
else
|
33
|
+
aliases[als] = id
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
cls
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_registered?(id)
|
41
|
+
idCls.keys.include?(id)
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_alias_registered?(als)
|
45
|
+
aliases.keys.include?(als)
|
46
|
+
end
|
47
|
+
|
48
|
+
def find(id)
|
49
|
+
if is_registered?(id)
|
50
|
+
idCls[id][:target]
|
51
|
+
elsif is_alias_registered?(id)
|
52
|
+
indx = aliases[id]
|
53
|
+
idCls[indx][:target]
|
54
|
+
else
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_event(id, event ,opts = {}, &block)
|
60
|
+
inst = find(id)
|
61
|
+
if not_empty?(inst)
|
62
|
+
hasMethod = inst.respond_to?(:on_event)
|
63
|
+
if hasMethod
|
64
|
+
inst.on_event(event,opts,&block)
|
65
|
+
true
|
66
|
+
else
|
67
|
+
logger.warn "Instance '#{id}' does not have method on_event"
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Private Section
|
78
|
+
#
|
79
|
+
private
|
80
|
+
def idCls
|
81
|
+
if @idCls.nil? and not @idCls.is_a?(Hash)
|
82
|
+
@idCls = { }
|
83
|
+
end
|
84
|
+
|
85
|
+
@idCls
|
86
|
+
end
|
87
|
+
|
88
|
+
def aliases
|
89
|
+
if @aliases.nil?
|
90
|
+
@aliases = { }
|
91
|
+
end
|
92
|
+
@aliases
|
93
|
+
end
|
94
|
+
|
95
|
+
def logger
|
96
|
+
ModelFact.logger(:mf_registry)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
data/lib/model_fact.rb
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'toolrack'
|
4
|
+
require 'teLogger'
|
5
|
+
|
6
|
+
require_relative "model_fact/version"
|
7
|
+
|
8
|
+
require_relative 'model_fact/registry'
|
9
|
+
|
10
|
+
require_relative 'model_fact/helpers'
|
11
|
+
|
12
|
+
require_relative 'model_fact/helpers/model_helper'
|
13
|
+
|
14
|
+
module ModelFact
|
15
|
+
include TR::CondUtils
|
16
|
+
|
17
|
+
class Error < StandardError; end
|
18
|
+
# Your code goes here...
|
19
|
+
|
20
|
+
class InvalidParamValue < StandardError; end
|
21
|
+
class ModelNotFound < StandardError; end
|
22
|
+
class ModelNotAvailable < StandardError; end
|
23
|
+
class DuplicatedIDException < StandardError; end
|
24
|
+
|
25
|
+
def self.register(id, cls, opts = {})
|
26
|
+
Registry.instance.register(id, cls, opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
# try_register will not throw exception if ID already registered
|
30
|
+
def self.try_register(id, cls, opts = { })
|
31
|
+
|
32
|
+
opts = { } if opts.nil?
|
33
|
+
|
34
|
+
if Registry.instance.is_registered?(id) or (not_empty?(opts[:alias]) and Registry.instance.is_alias_registered?(opts[:alias]))
|
35
|
+
false
|
36
|
+
else
|
37
|
+
register(id, cls, opts)
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# To be used by buss app to get model object
|
43
|
+
# by given id
|
44
|
+
def self.instance(id, raise_if_not_found = true)
|
45
|
+
res = Registry.instance.find(id)
|
46
|
+
if res.nil?
|
47
|
+
if raise_if_not_found
|
48
|
+
raise ModelNotFound, "Given ID '#{id}' is not registered"
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
else
|
53
|
+
|
54
|
+
if defined?(ActiveRecord)
|
55
|
+
require_relative 'model_fact/helpers/ar_helpers'
|
56
|
+
logger.debug "ActiveRecord defined for class!"
|
57
|
+
if res.ancestors.include?(ActiveRecord::Base)
|
58
|
+
logger.debug "Including ActiveRecordHelper into #{res}"
|
59
|
+
res.send(:include, ModelFact::Helpers::ActiveRecordHelper)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
res
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.new_instance(id, *args, &block)
|
69
|
+
cls = instance(id, true)
|
70
|
+
logger.debug "new_instance got #{cls}"
|
71
|
+
inst = cls.send(:new, *args, &block)
|
72
|
+
if defined?(ActiveRecord)
|
73
|
+
require_relative 'model_fact/helpers/ar_helpers'
|
74
|
+
logger.debug "ActiveRecord defined!"
|
75
|
+
if inst.is_a?(ActiveRecord::Base) #and inst.class.include?(ModelFact::Helpers::ActiveRecordHelper)
|
76
|
+
logger.debug "Including ActiveRecordHelper into #{cls}"
|
77
|
+
inst.class.send(:include, ModelFact::Helpers::ActiveRecordHelper)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
inst
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.on_event(id = nil, event = nil, opts = {}, &block)
|
84
|
+
Registry.instance.on_event(id, event, opts, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.method_missing(mtd, *args, &block)
|
88
|
+
if Registry.instance.respond_to?(mtd)
|
89
|
+
Registry.instance.send(mtd, *args, &block)
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.logger(tag = nil, &block)
|
96
|
+
if @_logger.nil?
|
97
|
+
@_logger = TeLogger::Tlogger.new(File.join(Dir.home,"test123.log"))
|
98
|
+
end
|
99
|
+
|
100
|
+
if block
|
101
|
+
if not_empty?(tag)
|
102
|
+
@_logger.with_tag(tag, &block)
|
103
|
+
else
|
104
|
+
@_logger.with_tag(@_logger.tag, &block)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
if is_empty?(tag)
|
108
|
+
@_logger.tag = :model_fact
|
109
|
+
@_logger
|
110
|
+
else
|
111
|
+
# no block but tag is given? hmm
|
112
|
+
@_logger.tag = tag
|
113
|
+
@_logger
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
data/sig/model_fact.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: model_fact
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-10-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: toolrack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: teLogger
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: release-gem
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: ''
|
56
|
+
email:
|
57
|
+
- chris@antrapol.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".rspec"
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- lib/model_fact.rb
|
66
|
+
- lib/model_fact/helpers.rb
|
67
|
+
- lib/model_fact/helpers/ar_helpers.rb
|
68
|
+
- lib/model_fact/helpers/filter_builder.rb
|
69
|
+
- lib/model_fact/helpers/model_helper.rb
|
70
|
+
- lib/model_fact/helpers/poro_helpers.rb
|
71
|
+
- lib/model_fact/registry.rb
|
72
|
+
- lib/model_fact/version.rb
|
73
|
+
- sig/model_fact.rbs
|
74
|
+
homepage: ''
|
75
|
+
licenses: []
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.6.0
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubygems_version: 3.4.6
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: ''
|
96
|
+
test_files: []
|