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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7452d0058119526d1ece08203106f8ae07da51f41e22f764c4d5262d2340e7f4
4
- data.tar.gz: 88fc11727ba9a6d016afb22ec03beafc30412e37502daf35e33dce6532ef02b1
3
+ metadata.gz: 187dd89025805fae04c8f188a900f6373cfd84584a720787dec852c0281a2060
4
+ data.tar.gz: 778e0e23ede44cc3fbb304e7216d4846745c25cb0e9fc1d87c8871375b21efcb
5
5
  SHA512:
6
- metadata.gz: 69eff49f3efbfd1261477cd3b895786a0dc97ca09e2933aab4471eea85c61f4e54651d37a4d3138e4c9d7c64f4802c3fda0d45152d84598e206072a14b828089
7
- data.tar.gz: e6a26b39284b4b80891fed68af41d930b1d12e83aaadad45c8d6fee4c9f1bfadbaebd3ff56407a09b5714f2248d3cf39cdb58a37cc4a8835241933294f99a2d9
6
+ metadata.gz: e1071cf066822a8da8b1bf522c51c80e012b7e8c8c137aa3c7666a2965952d254bd863d4fbb9df4d77192458ff0b744a96e01e7485f2be6a94fc1faa37b1fe7f
7
+ data.tar.gz: eebfbac3a6a3c0202255f15bad0ced512d8c60c79f601aaada4706790a5af518c06b7413dce5ffb38e6f5e9e8318de7221ae12049ec4129f1ed23a7efb55b1d4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dynamic-records-meritfront (3.0.24)
4
+ dynamic-records-meritfront (3.1.0)
5
5
  hashid-rails
6
6
 
7
7
  GEM
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,5 +1,5 @@
1
1
 
2
2
  module DynamicRecordsMeritfront
3
- VERSION = '3.0.24'
3
+ VERSION = '3.1.0'
4
4
  end
5
5
  #this file gets overwritten automatically on minor updates, major ones need to be manually changed
@@ -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
- # DevScript.ping("atr #{atr} def #{value}")
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 = base_arr.map{|o|
839
- [base_on.call(o), o]
840
- }.to_h
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
- attach_arr.each{|attach|
886
- out = attach_on.call(attach) #you can use null to escape the vals
887
- if out.nil?
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
- if out
891
- base = h[out] #it is also escaped if no base element is found
892
- if base
893
- add_to_base.call(base, attach)
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 attach_arr
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
- self.class.headache_preload(records, associations)
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.24
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-10 00:00:00.000000000 Z
11
+ date: 2023-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hashid-rails