dynamic-records-meritfront 3.0.24 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +82 -0
- data/lib/dynamic-records-meritfront/version.rb +1 -1
- data/lib/dynamic-records-meritfront.rb +69 -30
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 187dd89025805fae04c8f188a900f6373cfd84584a720787dec852c0281a2060
|
4
|
+
data.tar.gz: 778e0e23ede44cc3fbb304e7216d4846745c25cb0e9fc1d87c8871375b21efcb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1071cf066822a8da8b1bf522c51c80e012b7e8c8c137aa3c7666a2965952d254bd863d4fbb9df4d77192458ff0b744a96e01e7485f2be6a94fc1faa37b1fe7f
|
7
|
+
data.tar.gz: eebfbac3a6a3c0202255f15bad0ced512d8c60c79f601aaada4706790a5af518c06b7413dce5ffb38e6f5e9e8318de7221ae12049ec4129f1ed23a7efb55b1d4
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -315,6 +315,52 @@ the output:
|
|
315
315
|
...]}
|
316
316
|
```
|
317
317
|
</details>
|
318
|
+
|
319
|
+
<details>
|
320
|
+
<summary>above example with relations (no need for dynamic_attach)</summary>
|
321
|
+
#get list of users, those users friends, and who those users follow, all in one request.
|
322
|
+
|
323
|
+
```ruby
|
324
|
+
friend_attach = attach_on: Proc.new {|users_friend|
|
325
|
+
users_friend.friended_to
|
326
|
+
}
|
327
|
+
|
328
|
+
output = ApplicationRecord.instaload_sql([
|
329
|
+
User.instaload(%Q{
|
330
|
+
select id
|
331
|
+
from users
|
332
|
+
where users.id = any (:user_ids) and users.created_at > :time
|
333
|
+
}, table_name: :limited_users, relied_on: true),
|
334
|
+
User.instaload(%Q{
|
335
|
+
select friends.smaller_user_id as id, friends.bigger_user_id as friended_to
|
336
|
+
from friends
|
337
|
+
inner join limited_users on limited_users.id = bigger_user_id
|
338
|
+
union
|
339
|
+
select friends.bigger_user_id as id, friends.smaller_user_id as friended_to
|
340
|
+
from friends
|
341
|
+
inner join limited_users ON limited_users.id = smaller_user_id
|
342
|
+
}, table_name: :users_friends, base_name: :limited_users, attach_on: attach_on),
|
343
|
+
ApplicationRecord.instaload(%Q{
|
344
|
+
SELECT follows.followable_id, follows.follower_id
|
345
|
+
FROM follows
|
346
|
+
INNER JOIN limited_users ON follows.follower_id = limited_users.id
|
347
|
+
}, table_name: :users_follows, base_name: :limited_users)
|
348
|
+
], user_ids: uids, time: t)
|
349
|
+
|
350
|
+
pp out['limited_users']
|
351
|
+
|
352
|
+
```
|
353
|
+
sql output: same as example above this example
|
354
|
+
|
355
|
+
printed output (same as in dynamic attach example):
|
356
|
+
```ruby
|
357
|
+
#<User id: 3, users_friends: [#<User id: 5, friended_to: 3>, #<User id: 6, friended_to: 3>, #<User id: 21, friended_to: 3>], users_follows: [{"followable_id"=>935, "follower_id"=>3}, {"followable_id"=>938, "follower_id"=>3}, ...]>,
|
358
|
+
#<User id: 14, users_friends: [#<User id: 9, friended_to: 14>, #<User id: 21, friended_to: 14>, ...], users_follows: [{"followable_id"=>936, "follower_id"=>14}, {"followable_id"=>937, "follower_id"=>14}, {"followable_id"=>938, "follower_id"=>14}, ...]>,
|
359
|
+
#<User id: 9, users_friends: [#<User id: 14, friended_to: 9>, #<User id: 22, friended_to: 9>, ...], users_follows: [{"followable_id"=>938, "follower_id"=>9}, {"followable_id"=>937, "follower_id"=>9}, ...]>,
|
360
|
+
#<User id: 19, users_friends: [#<User id: 1, friended_to: 19>, #<User id: 18, friended_to: 19>, ...], users_follows: [{"followable_id"=>935, "follower_id"=>19}, {"followable_id"=>936, "follower_id"=>19}, {"followable_id"=>938, "follower_id"=>19}, ...]>,
|
361
|
+
```
|
362
|
+
|
363
|
+
</details>
|
318
364
|
|
319
365
|
#### self.instaload(sql, table_name: nil, relied_on: false, dont_return: false)
|
320
366
|
A method used to prepare data for the instaload_sql method. It returns a hash of options.
|
@@ -470,6 +516,9 @@ v3.0.6
|
|
470
516
|
- changed how variables are set for ActiveRecord objects, I gave up on figuring out what ActiveRecord is doing for *the most part* and i now just do a eval("self.#{parameter}=value") type deal. Works well. Allows you to override relations when doing polymorphic stuff which is a pretty big use case.
|
471
517
|
- I am thinking of changing how arrays are handled as that is really the only postgresql based dependency here and that will allow the library to open up to other databases. Issue is all the code I have already written in my app dependant on such things.
|
472
518
|
|
519
|
+
3.0.24
|
520
|
+
- changed how questionable_attribute_set works again, this time by using attr_accessors on the singleton class. Seems to paper over the default reflections nicely which has been a huge issue. They use this weird delegate thing which has been throwing me off. Anyway, no more evals which is nice. This fixed an issue with dynamic attach one-to-many relations.
|
521
|
+
|
473
522
|
## Questions
|
474
523
|
- Q: does the name of a sql operation have anything to do with prepared statements?
|
475
524
|
- A: no, the prepared statement determines uniqueness in some other way, dont worry about collisions. The only issue with prepared statements that I can determine is when you write a statement where things change every time, thus preparing potentially infinite prepared statements. This can be alleviated by using sql arguments correctly. Using arguments correctly also will stop sql injection attacks so. You know. Do it properly. Dont just hard code them into the query.
|
@@ -477,6 +526,39 @@ v3.0.6
|
|
477
526
|
- A: We set the location of where you called the function as the default name for easy debugging. Its not an error, we just take some info from the stacktrace. It also includes the method name which can provide some insite into what the query is doing. Makes logs alot nicer to look at.
|
478
527
|
- Q: Whats MeritFront?
|
479
528
|
- A: I am making a social media platform
|
529
|
+
- Q: Inspect on user records doesn't seem to work properly
|
530
|
+
- A: inspect is overwritten by many diffrent libraries, in terms of devise for example, they override our override of active record's inspect. The best way to deal with this is to look at the source location of these methods and bring them together (user.method(:inspect).source_location). In my case with devise, i ended up with this in the user record:
|
531
|
+
```ruby
|
532
|
+
def inspect
|
533
|
+
#this is the same as the dynamic records inspect execpt i also melded together some of the
|
534
|
+
#devise stuff to keep passwords etc safe
|
535
|
+
|
536
|
+
inspection = if defined?(@attributes) && @attributes
|
537
|
+
self.attribute_names.filter_map do |name|
|
538
|
+
if _has_attribute?(name)
|
539
|
+
if UNSAFE_ATTRIBUTES_FOR_SERIALIZATION.include? name.to_sym
|
540
|
+
"#{name}: [redacted]"
|
541
|
+
else
|
542
|
+
"#{name}: #{attribute_for_inspect(name)}"
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end.join(", ")
|
546
|
+
else
|
547
|
+
"not initialized"
|
548
|
+
end
|
549
|
+
|
550
|
+
self.dynamic_reflections ||= []
|
551
|
+
dyna = self.dynamic_reflections.map{|dr|
|
552
|
+
[dr, self.method(dr.to_sym).call()]
|
553
|
+
}.to_h
|
554
|
+
|
555
|
+
if dyna.keys.any?
|
556
|
+
"#<#{self.class} #{inspection} | #{dyna.to_s}>"
|
557
|
+
else
|
558
|
+
"#<#{self.class} #{inspection}>"
|
559
|
+
end
|
560
|
+
end
|
561
|
+
```
|
480
562
|
|
481
563
|
## Contributing
|
482
564
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require "dynamic-records-meritfront/version"
|
2
2
|
require 'hashid/rails'
|
3
3
|
|
4
|
-
#this file contains multiple classes which should honestly be split up
|
4
|
+
#this file contains multiple classes which should honestly be split up.
|
5
5
|
|
6
6
|
module DynamicRecordsMeritfront
|
7
7
|
extend ActiveSupport::Concern
|
@@ -87,12 +87,10 @@ module DynamicRecordsMeritfront
|
|
87
87
|
Time => ActiveModel::Type::Time,
|
88
88
|
Float => ActiveModel::Type::Float,
|
89
89
|
NilClass => ActiveModel::Type::Boolean,
|
90
|
-
Array => Proc.new{ |first_el_class| ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(DB_TYPE_MAPS[first_el_class].new) }
|
90
|
+
Array => Proc.new{ |first_el_class| ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array.new(DB_TYPE_MAPS[first_el_class].new) } #this one was a mistake
|
91
91
|
}
|
92
92
|
|
93
93
|
def convert_to_query_attribute(name, v)
|
94
|
-
#yes its dumb I know dont look at me look at rails
|
95
|
-
|
96
94
|
# https://stackoverflow.com/questions/40407700/rails-exec-query-bindings-ignored
|
97
95
|
# binds = [ ActiveRecord::Relation::QueryAttribute.new(
|
98
96
|
# "id", 6, ActiveRecord::Type::Integer.new
|
@@ -201,18 +199,25 @@ module DynamicRecordsMeritfront
|
|
201
199
|
|
202
200
|
#end
|
203
201
|
|
202
|
+
# pi = atr.to_sym == :current_user_follow
|
203
|
+
#DevScript.ping(self.inspect) if pi
|
204
204
|
if as_default
|
205
205
|
if self.method(atr.to_sym).call().nil?
|
206
206
|
self.method("#{atr}=".to_sym).call(value)
|
207
|
-
#
|
207
|
+
# Rails.logger.info "Y #{value.followable_type if value.respond_to? :followable_type}" if pi
|
208
208
|
end
|
209
|
+
# Rails.logger.info "#{atr} ||= #{value}" if pi
|
209
210
|
elsif push
|
210
211
|
self.method(atr.to_sym).call().push value
|
212
|
+
# Rails.logger.info #{atr} << #{value}" if pi
|
211
213
|
# DevScript.ping("atr #{atr} push #{value}")
|
212
214
|
else
|
215
|
+
#out =
|
213
216
|
self.method("#{atr}=".to_sym).call(value)
|
217
|
+
# Rails.logger.info "[#{self.class.to_s} #{self.id}].#{atr} -> #{out.inspect}" if pi
|
214
218
|
# DevScript.ping("atr #{atr} set #{value}")
|
215
219
|
end
|
220
|
+
#Rails.logger.info self.inspect if pi
|
216
221
|
|
217
222
|
# raise StandardError.new('bad options') if as_default and push
|
218
223
|
# if as_default
|
@@ -293,14 +298,14 @@ module DynamicRecordsMeritfront
|
|
293
298
|
end
|
294
299
|
|
295
300
|
self.dynamic_reflections ||= []
|
296
|
-
dyna = dynamic_reflections.map{|dr|
|
297
|
-
self.method(dr.to_sym).call()
|
298
|
-
}
|
301
|
+
dyna = self.dynamic_reflections.map{|dr|
|
302
|
+
[dr, self.method(dr.to_sym).call()]
|
303
|
+
}.to_h
|
299
304
|
|
300
|
-
if dyna.any?
|
305
|
+
if dyna.keys.any?
|
301
306
|
"#<#{self.class} #{inspection} | #{dyna.to_s}>"
|
302
307
|
else
|
303
|
-
"#<#{self.class} #{inspection}
|
308
|
+
"#<#{self.class} #{inspection}>"
|
304
309
|
end
|
305
310
|
end
|
306
311
|
|
@@ -663,14 +668,22 @@ module DynamicRecordsMeritfront
|
|
663
668
|
#formatting options
|
664
669
|
for insta in insta_array
|
665
670
|
if insta[:base_name]
|
666
|
-
if insta[:as]
|
667
|
-
Rails.logger.debug "#{insta[:table_name]} as #{insta[:as]} -> #{insta[:base_name]}"
|
668
|
-
else
|
669
|
-
Rails.logger.debug "#{insta[:table_name]} -> #{insta[:base_name]}"
|
670
|
-
end
|
671
671
|
#in this case, 'as' is meant as to what pseudonym to dynamicly attach it as
|
672
|
-
dynamic_attach(ret_hash, insta[:base_name], insta[:table_name], base_on: insta[:base_on], attach_on: insta[:attach_on],
|
672
|
+
n_attached = dynamic_attach(ret_hash, insta[:base_name], insta[:table_name], base_on: insta[:base_on], attach_on: insta[:attach_on],
|
673
673
|
one_to_one: insta[:one_to_one], as: insta[:as])
|
674
|
+
|
675
|
+
if Rails.logger.level <= 1
|
676
|
+
tn = insta[:table_name]
|
677
|
+
bn = insta[:base_name]
|
678
|
+
tc = ret_hash[tn].count
|
679
|
+
btc = ret_hash[bn].count
|
680
|
+
|
681
|
+
if insta[:as]
|
682
|
+
Rails.logger.debug "#{n_attached} attached from #{tn}(#{tc}) as #{insta[:as]} -> #{bn}(#{btc})"
|
683
|
+
else
|
684
|
+
Rails.logger.debug "#{n_attached} attached from #{tn}(#{tc}) -> #{bn}(#{btc})"
|
685
|
+
end
|
686
|
+
end
|
674
687
|
elsif insta[:as]
|
675
688
|
Rails.logger.debug "#{insta[:table_name]} as #{insta[:as]}"
|
676
689
|
#in this case, the idea is more polymorphic in nature. unless they are confused and just want to rename the table (this can be done with
|
@@ -791,7 +804,7 @@ module DynamicRecordsMeritfront
|
|
791
804
|
base_arr = instaload_sql_output[base_name]
|
792
805
|
|
793
806
|
#return if there is nothing for us to attach to.
|
794
|
-
return if base_arr.nil? or not base_arr.any?
|
807
|
+
return 0 if base_arr.nil? or not base_arr.any?
|
795
808
|
|
796
809
|
#set variables for neatness and so we dont compute each time
|
797
810
|
# base class information
|
@@ -816,7 +829,7 @@ module DynamicRecordsMeritfront
|
|
816
829
|
|
817
830
|
#make sure the attach class has something going on. We do this after the default stage
|
818
831
|
attach_arr = instaload_sql_output[attach_name]
|
819
|
-
return if attach_arr.nil? or not attach_arr.any?
|
832
|
+
return 0 if attach_arr.nil? or not attach_arr.any?
|
820
833
|
|
821
834
|
# attach class information
|
822
835
|
attach_class = attach_arr.first.class
|
@@ -835,9 +848,16 @@ module DynamicRecordsMeritfront
|
|
835
848
|
end
|
836
849
|
|
837
850
|
#return an id->object hash for the base table for better access
|
838
|
-
h =
|
839
|
-
|
840
|
-
|
851
|
+
h = {}
|
852
|
+
duplicates_base = Set[]
|
853
|
+
for base_rec in base_arr
|
854
|
+
bo = base_on.call(base_rec)
|
855
|
+
if h[bo]
|
856
|
+
duplicates_base << bo
|
857
|
+
else
|
858
|
+
h[bo] = base_rec
|
859
|
+
end
|
860
|
+
end
|
841
861
|
|
842
862
|
#decide on the method of getting the matching id for the attach table
|
843
863
|
unless attach_on
|
@@ -882,20 +902,38 @@ module DynamicRecordsMeritfront
|
|
882
902
|
# 1. match base id to the attach id (both configurable)
|
883
903
|
# 2. cancel out if there is no match
|
884
904
|
# 3. otherwise add to the base object.
|
885
|
-
|
886
|
-
|
887
|
-
|
905
|
+
x = 0
|
906
|
+
|
907
|
+
attach_arr.each{|attach_rec|
|
908
|
+
#we have it plural in case it attaches to multiple, for example a user can belong to many post-cards. Yes, this
|
909
|
+
#was a bug. In order to solve it you have to do some sort of 'distinct' or 'group' sql.
|
910
|
+
|
911
|
+
attachment_keys = attach_on.call(attach_rec) #you can use null to escape the vals
|
912
|
+
|
913
|
+
if attachment_keys.nil?
|
888
914
|
Rails.logger.debug "attach_on proc output (which compares to the base_on proc) is outputting nil, this could be a problem depending on your use-case."
|
915
|
+
elsif not attachment_keys.kind_of? Array
|
916
|
+
attachment_keys = [attachment_keys]
|
889
917
|
end
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
918
|
+
|
919
|
+
if attachment_keys and attachment_keys.any?
|
920
|
+
for ak in attachment_keys
|
921
|
+
base_rec = h[ak] #it is also escaped if no base element is found
|
922
|
+
if base_rec
|
923
|
+
dupl = duplicates_base.include? ak
|
924
|
+
if dupl
|
925
|
+
Rails.logger.warn "WARNING in #{attach_name} -> #{base_name}. Duplicate base_on key being utilized (this is usually in error). Only one base record will have an attachment. For the base table, consider using GROUP BY for your id and ARRAY_AGG for the base_on column."
|
926
|
+
Rails.logger.warn "base_on key: #{ak.to_s}"
|
927
|
+
end
|
928
|
+
|
929
|
+
x += 1 unless dupl
|
930
|
+
add_to_base.call(base_rec, attach_rec)
|
931
|
+
end
|
894
932
|
end
|
895
933
|
end
|
896
934
|
}
|
897
935
|
|
898
|
-
return
|
936
|
+
return x
|
899
937
|
end
|
900
938
|
alias swiss_attach dynamic_attach
|
901
939
|
|
@@ -954,8 +992,9 @@ module DynamicRecordsMeritfront
|
|
954
992
|
|
955
993
|
#just for ease of use
|
956
994
|
def headache_preload(records, associations)
|
957
|
-
|
995
|
+
self.class.headache_preload(records, associations)
|
958
996
|
end
|
997
|
+
|
959
998
|
def safe_increment(col, val) #also used in follow, also used in comment#kill
|
960
999
|
self.class.where(id: self.id).update_all("#{col} = #{col} + #{val}")
|
961
1000
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dynamic-records-meritfront
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luke Clancy
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hashid-rails
|