fume-aloader 0.1.0 → 0.1.1

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: e76d23af085408f974ed12200e21710eb7e41ed838f1e1f4906b28cda813967e
4
- data.tar.gz: f8ca566f229ef016c0f25a47d4018ec2b82fbb2e9c3c04c1a760d297fcf22edc
3
+ metadata.gz: 12208e51420d3bf1332bd4b482170903d823458bdd62064bbc6a90c7b6e8af8a
4
+ data.tar.gz: 86ef2d545231f0164d7d0c05e8279d924c00819ffb1395707157020274234493
5
5
  SHA512:
6
- metadata.gz: 3cced0b48ce3022d0467db55bbc4f6b474546bf3990991d531a479d99914afaa568de687d608dc0f8deccc3cbea338eb0218d65656207b85e935e18f8dcc6f7a
7
- data.tar.gz: d2897f943c2cbceb053a2580190faa83e42fab3700d07999bd1aa18f675238fd0c864ee97168a8686b7b48a3962ad9ba80865048be730fc5bf98af91eb12ba05
6
+ metadata.gz: 2c974e163a5d91eddd8c933e54abfa56f3e0ae665d3a887113d343070e689560ec5b54d7d044eca8586341e364746890335e07aeeff501d21bf011c27952da45
7
+ data.tar.gz: 215584ac4d4589fa16ffacfadebaa29d82ab91018234a8418daf6842c9c70ba528f5db6322c50fee3f0bed7326dda986e77f2600dd083555ab21a1b51433c7ca
@@ -1,3 +1,5 @@
1
+ require_relative "relationship"
2
+
1
3
  module Fume::Aloader
2
4
  class AssociationLoader
3
5
  attr_accessor :klass
@@ -20,29 +22,21 @@ module Fume::Aloader
20
22
  end
21
23
 
22
24
  def find_cached_value(record, name)
23
- association = record.association(name)
24
- reflection = association.reflection
25
-
26
- key = reflection.collection? ? record.send(:id) : record.send(reflection.join_foreign_key)
27
-
28
- unless self.cached_values.key?(name)
29
- init_records_value(name)
30
- end
31
-
32
- self.cached_values[name][key]
25
+ relationship = Relationship.build(klass, name)
26
+ cache_key = relationship.get_cache_key(record)
27
+ self.cached_values[name][cache_key]
33
28
  end
34
29
 
35
30
  def load(record, name)
36
31
  association = record.association(name)
37
- if association.loaded? && !self.cached_values.key?(name)
38
- init_records_value(name, ->(scope) {
39
- values = self.records.flat_map(&name.to_sym).compact
40
- scope.send(:load_records, values)
41
- scope.al_init_records
42
- })
32
+ if self.cached_values.key?(name)
33
+ # prefer use cached value
34
+ association.target = find_cached_value(record, name)
35
+ elsif association.loaded?
36
+ # ignore
43
37
  else
44
- value = find_cached_value(record, name)
45
- association.target = value
38
+ init_records_value(name)
39
+ association.target = find_cached_value(record, name)
46
40
  end
47
41
  end
48
42
 
@@ -51,59 +45,43 @@ module Fume::Aloader
51
45
  name = path.shift
52
46
 
53
47
  if path.size.zero?
54
- fill_records_value(name, values)
48
+ cache_association_values(name, values)
55
49
  else
56
50
  self.preload_values[name] ||= []
57
51
  self.preload_values[name] << [ path, values ]
58
52
  end
59
53
  end
60
54
 
61
- def init_records_value(name, callback = nil)
62
- association = klass.new.association(name)
63
- values = build_association_values_scope(name, association)
64
- callback&.(values)
65
-
66
- if self.preload_values.key?(name)
67
- preload_values[name].each do |args|
68
- values.al_preload_all(*args)
55
+ def init_records_value(name)
56
+ values_list = build_association_values_scopes(name)
57
+ values_list.each do |values|
58
+ if self.preload_values.key?(name)
59
+ preload_values[name].each do |args|
60
+ values.al_preload_all(*args)
61
+ end
69
62
  end
70
- end
71
63
 
72
- fill_records_value(name, values)
64
+ cache_association_values(name, values)
65
+ end
73
66
  end
74
67
 
75
- def fill_records_value(name, values)
76
- association = klass.new.association(name)
77
- reflection = association.reflection
68
+ def cache_association_values(name, values)
69
+ relationship = Relationship.build(klass, name)
78
70
 
79
- if reflection.collection?
80
- self.cached_values[name] = values.each_with_object(Hash.new { [] }) do |it, result|
81
- key = it.send(reflection.join_primary_key)
82
- result[key] += [ it ]
83
- end
84
- elsif reflection.belongs_to?
85
- self.cached_values[name] = values.index_by(&:id)
71
+ if self.cached_values.key?(name)
72
+ self.cached_values[name].update(relationship.build_cached_value(values))
86
73
  else
87
- self.cached_values[name] = values.index_by { |it| it.send(reflection.join_primary_key) }
74
+ self.cached_values[name] = relationship.build_cached_value(values)
88
75
  end
89
76
  end
90
77
 
91
- def build_association_values_scope(name, association)
92
- reflection = association.reflection
93
-
94
- # HACK: 重写第一次取值,升级后可能会报错
95
- # 不能使用子查询 select, 可能内存占用过多
96
- hack_values = [ records.map { |item| item.read_attribute(reflection.join_foreign_key) }.uniq ]
97
-
98
- value_transformation = ->(val) {
99
- hack_values.shift || val
100
- }
78
+ def build_association_values_scopes(name)
79
+ relationship = Relationship.build(klass, name)
80
+ values_scopes = relationship.build_values_scopes(records)
101
81
 
102
- association_scope = ActiveRecord::Associations::AssociationScope.new(value_transformation)
103
- values_scope = association.send(:target_scope).merge(association_scope.scope(association))
104
- values_scope = apply_profile_attribute_includes(values_scope, name)
105
- values_scope = values_scope.limit(nil).offset(0)
106
- values_scope
82
+ values_scopes.map do |values_scope|
83
+ apply_profile_attribute_includes(values_scope, name)
84
+ end
107
85
  end
108
86
 
109
87
  def apply_profile_attribute_includes(base, name)
@@ -114,10 +92,9 @@ module Fume::Aloader
114
92
  return base.al_to_scope(attr_preset_name)
115
93
  end
116
94
 
117
- includes = find_attribute_includes(preset, name) || []
95
+ includes = find_attribute_includes(preset, name, base.klass) || []
118
96
  return base if includes.empty?
119
97
 
120
-
121
98
  except = (self.preload_values[name] || []).map(&:first)
122
99
  columns = simplify_includes_hash(convert_to_includes_hash(includes, [], except))
123
100
 
@@ -145,7 +122,7 @@ module Fume::Aloader
145
122
  result.update convert_to_includes_hash({ name => value }, [], except)
146
123
  end
147
124
  else
148
- includes = find_attribute_includes(preset, root) || {}
125
+ includes = find_attribute_includes(preset, root, nil) || {}
149
126
  result.update convert_to_includes_hash({ root => includes }, [], except)
150
127
  end
151
128
  end
@@ -201,24 +178,18 @@ module Fume::Aloader
201
178
  end
202
179
  end
203
180
 
204
- def find_attribute_includes(preset, name)
181
+ def find_attribute_includes(preset, name, association_klass)
205
182
  attribute = preset.dig(:attributes, name) || {}
206
183
 
207
184
  attr_preset_name = attribute[:preset]
208
185
  return attribute[:scope_includes] || [] if attr_preset_name.nil?
209
186
 
210
- loader = build_attribute_aloader(name, attr_preset_name)
187
+ association_klass ||= klass.new.association(name).reflection.klass
188
+ loader = association_klass.al_build([])
189
+ loader.active(attr_preset_name)
211
190
  loader.build_profile_scope_includes
212
191
  end
213
192
 
214
- def build_attribute_aloader(name, profile)
215
- association = klass.new.association(name)
216
- reflection = association.reflection
217
- loader = reflection.klass.al_build([])
218
- loader.active(profile)
219
- loader
220
- end
221
-
222
193
  def active(name)
223
194
  self.profile = name
224
195
  end
@@ -0,0 +1,30 @@
1
+ module Fume::Aloader
2
+ module Relationship
3
+ class Base
4
+ attr_accessor :klass
5
+ attr_accessor :association
6
+ attr_accessor :reflection
7
+
8
+ def initialize(klass, association, reflection)
9
+ self.klass = klass
10
+ self.association = association
11
+ self.reflection = reflection
12
+ end
13
+
14
+ def build_values_scopes(records)
15
+ # HACK: 重写第一次取值,升级后可能会报错
16
+ # 不能使用子查询 select, 可能内存占用过多
17
+ hack_values = [ records.map { |item| item.read_attribute(reflection.join_foreign_key) }.uniq ]
18
+
19
+ value_transformation = ->(val) {
20
+ hack_values.shift || val
21
+ }
22
+
23
+ association_scope = ActiveRecord::Associations::AssociationScope.new(value_transformation)
24
+ values_scope = association.send(:target_scope).merge(association_scope.scope(association))
25
+ values_scope = values_scope.limit(nil).offset(0)
26
+ [ values_scope ]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "base"
2
+
3
+ module Fume::Aloader
4
+ module Relationship
5
+ class BelongsTo < Base
6
+ def get_cache_key(record)
7
+ record.send(reflection.join_foreign_key)
8
+ end
9
+
10
+ def build_cached_value(values)
11
+ values.index_by(&:id)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "base"
2
+
3
+ module Fume::Aloader
4
+ module Relationship
5
+ class BelongsToPolymorphic < Base
6
+ def get_cache_key(record)
7
+ [ record.send(reflection.join_foreign_type), record.send(reflection.join_foreign_key) ]
8
+ end
9
+
10
+ def build_cached_value(values)
11
+ values.index_by { |it| [ it.class.to_s, it.id ] }
12
+ end
13
+
14
+ def build_values_scopes(records)
15
+ values_mapping = records.each_with_object({}).each do |record, hash|
16
+ type = record.read_attribute(reflection.join_foreign_type)
17
+ next if type.nil?
18
+ hash[type] ||= Set.new
19
+
20
+ value = record.read_attribute(reflection.join_foreign_key)
21
+ hash[type] << value if !value.nil?
22
+ end
23
+
24
+
25
+ values_mapping.map do |type, values|
26
+ values = [ values.to_a ]
27
+
28
+ # HACK: 重写第一次取值,升级后可能会报错
29
+ # 不能使用子查询 select, 可能内存占用过多
30
+ value_transformation = ->(val) {
31
+ values.shift || val
32
+ }
33
+
34
+ association_scope = ActiveRecord::Associations::AssociationScope.new(value_transformation)
35
+
36
+ association = klass.new(reflection.join_foreign_type => type, reflection.join_foreign_key => -1).association(reflection.name)
37
+ values_scope = association.send(:target_scope).merge(association_scope.scope(association))
38
+ values_scope = values_scope.limit(nil).offset(0)
39
+ values_scope
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,8 @@
1
+ # require_relative "base"
2
+
3
+ # module Fume::Aloader
4
+ # module Relationship
5
+ # class HasAndBelongsToMany < Base
6
+ # end
7
+ # end
8
+ # end
@@ -0,0 +1,18 @@
1
+ require_relative "base"
2
+
3
+ module Fume::Aloader
4
+ module Relationship
5
+ class HasMany < Base
6
+ def get_cache_key(record)
7
+ record.send(reflection.join_foreign_key)
8
+ end
9
+
10
+ def build_cached_value(values)
11
+ values.each_with_object(Hash.new { [] }) do |it, result|
12
+ key = it.send(reflection.join_primary_key)
13
+ result[key] += [ it ]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "base"
2
+
3
+ module Fume::Aloader
4
+ module Relationship
5
+ class HasOne < Base
6
+ def get_cache_key(record)
7
+ record.send(reflection.join_foreign_key)
8
+ end
9
+
10
+ def build_cached_value(values)
11
+ values.index_by { |it| it.send(reflection.join_primary_key) }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require_relative "relationship/has_many"
2
+ require_relative "relationship/belongs_to"
3
+ require_relative "relationship/belongs_to_polymorphic"
4
+ # require_relative "relationship/has_and_belongs_to_many"
5
+ require_relative "relationship/has_one"
6
+
7
+ module Fume::Aloader
8
+ module Relationship
9
+ def self.build(klass, name)
10
+ association = klass.new.association(name)
11
+ reflection = association.reflection
12
+
13
+ reflection_class = nil
14
+ if reflection.through_reflection?
15
+ raise "Not supported through reflection yet"
16
+ elsif reflection.collection?
17
+ reflection_class = HasMany
18
+ elsif reflection.has_one?
19
+ reflection_class = HasOne
20
+ elsif reflection.polymorphic?
21
+ reflection_class = BelongsToPolymorphic
22
+ else
23
+ reflection_class = BelongsTo
24
+ end
25
+
26
+ reflection_class.new(klass, association, reflection)
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,5 @@
1
1
  module Fume
2
2
  module Aloader
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fume-aloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - sunteya
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-04-29 00:00:00.000000000 Z
11
+ date: 2022-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -130,10 +130,7 @@ extensions: []
130
130
  extra_rdoc_files: []
131
131
  files:
132
132
  - ".rspec"
133
- - ".rubocop.yml"
134
- - CHANGELOG.md
135
133
  - Gemfile
136
- - LICENSE
137
134
  - LICENSE.txt
138
135
  - README.md
139
136
  - config.ru
@@ -143,6 +140,13 @@ files:
143
140
  - lib/fume/aloader/railtie.rb
144
141
  - lib/fume/aloader/record_addons.rb
145
142
  - lib/fume/aloader/relation_addons.rb
143
+ - lib/fume/aloader/relationship.rb
144
+ - lib/fume/aloader/relationship/base.rb
145
+ - lib/fume/aloader/relationship/belongs_to.rb
146
+ - lib/fume/aloader/relationship/belongs_to_polymorphic.rb
147
+ - lib/fume/aloader/relationship/has_and_belongs_to_many.rb
148
+ - lib/fume/aloader/relationship/has_many.rb
149
+ - lib/fume/aloader/relationship/has_one.rb
146
150
  - lib/fume/aloader/version.rb
147
151
  homepage: https://github.com/sunteya/fume-aloader
148
152
  licenses:
data/.rubocop.yml DELETED
@@ -1,13 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 2.6
3
-
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
- Style/StringLiteralsInInterpolation:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
11
-
12
- Layout/LineLength:
13
- Max: 120
data/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- ## [Unreleased]
2
-
3
- ## [0.1.0] - 2022-04-27
4
-
5
- - Initial release
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 sunteya
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.