forest_liana 9.12.2 → 9.14.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 758e323d04ac55ca40f00c3d77ccff83f2b12609ee13d82e9dba8bc785b8ed98
4
- data.tar.gz: 5cd38330c38e58fa252d160d52cdf0635116f75bb99112886991380bf35ea72c
3
+ metadata.gz: 133660170841c0069ffdc0da3e2a4918e4147f417eae55c87b79bb300d9924b0
4
+ data.tar.gz: eba503c46d489b6f6a4e81f26764f284574db290aed180755c6ca33841fc8b19
5
5
  SHA512:
6
- metadata.gz: 7289eb530fb56e1c0f1a70c25b61c3ed92d69fbc34fc437e730e7ece1a63e973f84b257fbb26543e9dd18405890e00dc5c6365efcccbc742e644c560fee07ef1
7
- data.tar.gz: fb211dc081537bb5d8a8900867cc5264c74829b0a83e31f0a378d270f3b2dd0c73c5f05e3088da1919922517951ee274b9c67a6f422fe312d6e2dcdc6799e788
6
+ metadata.gz: a1b5122e4700ae49500fbfc7b7c1060561e3a73cdfe3c980f946453c54eac0fa0c0982914b2ffcb3a18710d9e5c4fddb0cc7b6a12f18feca424d64067987691d
7
+ data.tar.gz: 4b4183d21184e1986ca78158f7d6e99aae06ea6767292448ddb1bc29b00f3fe2862000d9c642eb8b8c84e4ce19166778a5fa5e3439cdac1c33e925d2f0ce988f
@@ -118,12 +118,6 @@ module ForestLiana
118
118
 
119
119
  if ret[:href].blank?
120
120
  begin
121
- if @options[:include].try(:include?, attribute_name.to_s) &&
122
- !SchemaHelper.is_smart_field?(object.class, attribute_name.to_s)
123
-
124
- object.send(attribute_name)
125
- end
126
-
127
121
  SchemaUtils.many_associations(object.class).each do |a|
128
122
  if a.name == attribute_name
129
123
  ret[:href] = "/forest/#{ForestLiana.name_for(object.class)}/#{object.id}/relationships/#{attribute_name}"
@@ -137,6 +131,30 @@ module ForestLiana
137
131
  ret
138
132
  end
139
133
 
134
+ def has_one_relationships
135
+ return {} if self.class.to_one_associations.nil?
136
+ data = {}
137
+ self.class.to_one_associations.each do |attribute_name, attr_data|
138
+ relation = object.class.reflect_on_all_associations.find { |a| a.name == attribute_name }
139
+
140
+ next if !should_include_attr?(attribute_name, attr_data)
141
+
142
+ unless relation.polymorphic?
143
+ relation_class_name = ForestLiana.name_for(relation.klass).demodulize
144
+
145
+ if object.send(relation.foreign_key.to_sym) &&
146
+ @options[:fields][relation_class_name]&.size == 1 &&
147
+ @options[:fields][relation_class_name]&.include?(relation.klass.primary_key.to_sym)
148
+
149
+ attr_data[:attr_or_block] = proc { relation.klass.new(relation.klass.primary_key => object.send(relation.foreign_key.to_sym)) }
150
+ end
151
+ end
152
+
153
+ data[attribute_name] = attr_data
154
+ end
155
+ data
156
+ end
157
+
140
158
  private
141
159
 
142
160
  def intercom_integration?
@@ -19,13 +19,14 @@ module ForestLiana
19
19
 
20
20
  def compute_includes
21
21
  @includes = ForestLiana::QueryHelper.get_one_association_names_symbol(@resource)
22
+ @optional_includes = []
22
23
  end
23
24
 
24
- def optimize_record_loading(resource, records)
25
+ def optimize_record_loading(resource, records, force_preload = true)
25
26
  polymorphic, preload_loads = analyze_associations(resource)
26
- result = records.eager_load(@includes.uniq - preload_loads - polymorphic)
27
+ result = records.eager_load(@includes.uniq - preload_loads - polymorphic - @optional_includes)
27
28
 
28
- result = result.preload(preload_loads) if Rails::VERSION::MAJOR >= 7
29
+ result = result.preload(preload_loads) if Rails::VERSION::MAJOR >= 7 && force_preload
29
30
 
30
31
  result
31
32
  end
@@ -37,6 +37,8 @@ module ForestLiana
37
37
  private
38
38
 
39
39
  def compute_includes
40
+ @optional_includes = []
41
+
40
42
  @includes = @association.klass
41
43
  .reflect_on_all_associations
42
44
  .select do |association|
@@ -31,16 +31,16 @@ module ForestLiana
31
31
 
32
32
  def perform
33
33
  polymorphic_association, preload_loads = analyze_associations(@resource)
34
- includes = @includes.uniq - polymorphic_association - preload_loads
34
+ includes = @includes.uniq - polymorphic_association - preload_loads - @optional_includes
35
35
  has_smart_fields = @params[:fields][@collection_name].split(',').any? do |field|
36
36
  ForestLiana::SchemaHelper.is_smart_field?(@resource, field)
37
37
  end
38
38
 
39
39
  if includes.empty? || has_smart_fields
40
- @records = optimize_record_loading(@resource, @records)
40
+ @records = optimize_record_loading(@resource, @records, false)
41
41
  else
42
42
  select = compute_select_fields
43
- @records = optimize_record_loading(@resource, @records).references(includes).select(*select)
43
+ @records = optimize_record_loading(@resource, @records, false).references(includes).select(*select)
44
44
  end
45
45
 
46
46
  @records
@@ -55,31 +55,115 @@ module ForestLiana
55
55
  end
56
56
 
57
57
  def records
58
- @records.offset(offset).limit(limit).to_a
58
+ records = @records.offset(offset).limit(limit).to_a
59
+
60
+ polymorphic_association, preload_loads = analyze_associations(@resource)
61
+ if polymorphic_association && Rails::VERSION::MAJOR >= 7
62
+ # TODO
63
+ end
64
+
65
+ preload_cross_database_associations(records, preload_loads)
66
+
67
+ records
59
68
  end
60
69
 
61
- def compute_includes
62
- associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)
70
+ def preload_cross_database_associations(records, preload_loads)
71
+ preload_loads.each do |association_name|
72
+ association = @resource.reflect_on_association(association_name)
73
+ next unless separate_database?(@resource, association)
74
+
75
+ columns = columns_for_cross_database_association(association_name)
76
+
77
+ if association.macro == :belongs_to
78
+ foreign_key = association.foreign_key
79
+ primary_key = association.klass.primary_key
63
80
 
64
- includes = associations_has_one.map(&:name)
65
- includes_for_smart_search = []
81
+ ids = records.map { |r| r.public_send(foreign_key) }.compact.uniq
82
+ next if ids.empty?
66
83
 
67
- if @collection && @collection.search_fields
68
- includes_for_smart_search = @collection.search_fields
69
- .select { |field| field.include? '.' }
70
- .map { |field| field.split('.').first.to_sym }
84
+ associated = association.klass.where(primary_key => ids)
85
+ .select(columns)
86
+ .index_by { |record| record.public_send(primary_key) }
71
87
 
72
- includes_has_many = SchemaUtils.many_associations(@resource)
73
- .select { |association| SchemaUtils.model_included?(association.klass) }
74
- .map(&:name)
88
+ records.each do |record|
89
+ record.define_singleton_method(association_name) do
90
+ associated[record.send(foreign_key.to_sym)] || nil
91
+ end
92
+ end
93
+ end
94
+
95
+ if association.macro == :has_one
96
+ foreign_key = association.foreign_key
97
+ primary_key = association.active_record_primary_key
98
+
99
+ ids = records.map { |r| r.public_send(primary_key) }.compact.uniq
100
+ next if ids.empty?
75
101
 
76
- includes_for_smart_search = includes_for_smart_search & includes_has_many
102
+ associated = association.klass.where(foreign_key => ids)
103
+ .select(columns)
104
+ .index_by { |record| record.public_send(foreign_key.to_sym) }
105
+
106
+ records.each do |record|
107
+ record.define_singleton_method(association_name) do
108
+ associated[record.send(primary_key.to_sym)] || nil
109
+ end
110
+ end
111
+ end
77
112
  end
113
+ end
78
114
 
115
+ def columns_for_cross_database_association(association_name)
116
+ return [:id] unless @params[:fields].present?
117
+
118
+ fields = @params[:fields][association_name.to_s]
119
+ return [:id] unless fields
120
+
121
+ base_fields = fields.split(',').map(&:strip).map(&:to_sym) | [:id]
122
+
123
+ association = @resource.reflect_on_association(association_name)
124
+ extra_key = association.foreign_key
125
+
126
+ # Add the foreign key used for the association to ensure it's available in the preloaded records
127
+ # This is necessary for has_one associations, without it calling record.public_send(foreign_key) would raise a "missing attribute" error
128
+ base_fields << extra_key if association.macro == :has_one
129
+
130
+ base_fields.uniq
131
+ end
132
+
133
+ def compute_includes
134
+ associations_has_one = ForestLiana::QueryHelper.get_one_associations(@resource)
135
+ @optional_includes = []
79
136
  if @field_names_requested
137
+ includes = associations_has_one.map do |association|
138
+ association_name = association.name.to_s
139
+
140
+ if @params[:fields].key?(association_name) &&
141
+ @params[:fields][association_name].split(',').size == 1 &&
142
+ @params[:fields][association_name].split(',').include?(association.klass.primary_key)
143
+
144
+ @field_names_requested << association.foreign_key
145
+ @optional_includes << association.name
146
+ end
147
+
148
+ association.name
149
+ end
150
+
151
+ includes_for_smart_search = []
152
+ if @collection && @collection.search_fields
153
+ includes_for_smart_search = @collection.search_fields
154
+ .select { |field| field.include? '.' }
155
+ .map { |field| field.split('.').first.to_sym }
156
+
157
+ includes_has_many = SchemaUtils.many_associations(@resource)
158
+ .select { |association| SchemaUtils.model_included?(association.klass) }
159
+ .map(&:name)
160
+
161
+ includes_for_smart_search = includes_for_smart_search & includes_has_many
162
+ end
163
+
80
164
  @includes = (includes & @field_names_requested).concat(includes_for_smart_search)
81
165
  else
82
- @includes = includes
166
+ @includes = associations_has_one.map(&:name)
83
167
  end
84
168
  end
85
169
 
@@ -226,7 +310,7 @@ module ForestLiana
226
310
 
227
311
  def compute_select_fields
228
312
  select = ['_forest_admin_eager_load']
229
- @params[:fields][@collection_name].split(',').each do |path|
313
+ @field_names_requested.each do |path|
230
314
  association = get_one_association(path)
231
315
  if association
232
316
  while association.options[:through]
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.12.2"
2
+ VERSION = "9.14.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_liana
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.12.2
4
+ version: 9.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-15 00:00:00.000000000 Z
11
+ date: 2025-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails