trophonius 2.1.7 → 2.2.1

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: b7e8354023ac2793edf55b987cb32151705e4e36d173e4ce543e85f18cb54cea
4
- data.tar.gz: b8445ef34040269df2bcb9d6183359fa36107410126d2065707a76b768188125
3
+ metadata.gz: 4a84b2bfd47dee89330a357ee38cee77f490e3eba810f9743bac995b61b3601f
4
+ data.tar.gz: 19209c6e6b419c624be3b25e3d6bbda7cf68340e719b7ab5851c6a98f635cafc
5
5
  SHA512:
6
- metadata.gz: ab653916b39228ed1de20c164d7aa9a1c1fd151ab1b9dac251efd827561e974b832c7ebbb080e2dc18719752ad2a6f2a9684abe32a088499fb4c934ac1797a6b
7
- data.tar.gz: fcbb644e94eb52245bc2300f4b13307f2f707a451c2d1826fc94c4d1daaa9f02a800a1d40d5ff1d0a935603ee022d59bfbe6b0a1355145bec6175c00b2b9ff67
6
+ metadata.gz: 1d1d01693932f60cce0f79011efecbe5610bf594725850c468ed1ecb4363d8c16c87b97eb556d7499de63e830b41a8247fb6e97644e0a899ab4cc42f52024606
7
+ data.tar.gz: a851707b8c5cd2dd9e6680a66065eebebbcdc3c131280c69d91c7366e863e9e7f4feb84ba72decc23b6f08b45cf2f3aebe7eca2c3979f6c1581998bfa0fd0da2
@@ -30,6 +30,8 @@ module Trophonius
30
30
  # Disconnects from the FileMaker server
31
31
  #
32
32
  def disconnect
33
+ return unless test_connection
34
+
33
35
  uri = URI::RFC2396_Parser.new
34
36
  url =
35
37
  URI(
@@ -174,8 +176,10 @@ module Trophonius
174
176
  return !last_connection.nil? && last_connection > Time.now - (15 * 60) if Trophonius.config.layout_name == ''
175
177
 
176
178
  path = "/layouts/#{Trophonius.config.layout_name}/records?_limit=1"
179
+
180
+ token_to_check = Trophonius.config.redis_connection ? Trophonius::RedisManager.get_key(key: 'token') : @token
177
181
  response =
178
- Trophonius::DatabaseRequest.make_request(path, :get, {}, bypass_queue_with: "Bearer #{@token}")
182
+ Trophonius::DatabaseRequest.make_request(path, :get, {}, bypass_queue_with: "Bearer #{token_to_check}")
179
183
  response['messages'][0]['code'] == '0'
180
184
  rescue StandardError => e
181
185
  puts e
@@ -27,7 +27,8 @@ module Trophonius
27
27
  end
28
28
 
29
29
  def disconnect_all
30
- @connections.each { |_connection_id, connection| connection[:connection].disconnect }
30
+ @connections.reject { |_connection_id, connection| connection[:token].blank? }
31
+ .each { |_connection_id, connection| connection[:connection].disconnect }
31
32
  end
32
33
 
33
34
  private
data/lib/record.rb CHANGED
@@ -22,10 +22,11 @@ module Trophonius
22
22
  @modifiable_fields = {}
23
23
  @modified_fields = {}
24
24
  @model_name = model
25
- @model = model_name.instance_of?(String) ? ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.classify(ActiveSupport::Inflector.singularize(model_name))) : model_name
25
+ @model = model_name.instance_of?(String) ? constantize_model(model_name) : model_name
26
26
  @layout_name = @model.layout_name
27
- define_field_methods(fm_record)
28
- define_portal_methods(fm_record)
27
+ @portals = []
28
+ define_field_methods(fm_record) if fm_record.present?
29
+ define_portal_methods(fm_record) if fm_record.present?
29
30
  super()
30
31
  end
31
32
 
@@ -41,113 +42,14 @@ module Trophonius
41
42
 
42
43
  def method_missing(method, *args, &block)
43
44
  if ActiveSupport::Inflector.pluralize(method).to_s == method.to_s
44
- model = ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.classify(ActiveSupport::Inflector.singularize(method)))
45
- pk_model = ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.classify(ActiveSupport::Inflector.singularize(model_name)))
46
-
47
- if model.belongs_to_relations[ActiveSupport::Inflector.parameterize(model_name).to_sym]
48
- relation = model.belongs_to_relations[ActiveSupport::Inflector.parameterize(model_name).to_sym]
49
- layout = model.layout_name
50
- model.create_translations if model.translations.keys.empty?
51
-
52
- url = "/layouts/#{layout}/_find"
53
- foreign_key_field = if model.translations.key?(relation[:foreign_key])
54
- model.translations[relation[:foreign_key]].to_s
55
- else
56
- relation[:foreign_key].to_s
57
- end
58
-
59
- primary_key_field = if pk_model.translations.key?(relation[:primary_key])
60
- pk_model.translations[relation[:primary_key]].to_s
61
- else
62
- relation[:primary_key].to_s
63
- end
64
-
65
- body = { query: [{ foreign_key_field => self[primary_key_field].to_s }], limit: 100_000 }.to_json
66
- response = DatabaseRequest.make_request(url, 'post', body)
67
-
68
- if response['messages'][0]['code'] == '0'
69
- r_results = response['response']['data']
70
- ret_val = RecordSet.new(layout, model.non_modifiable_fields)
71
- r_results.each do |r|
72
- hash = model.build_result(r)
73
- ret_val << hash
74
- end
75
- @response = ret_val
76
- @response
77
- elsif response['messages'][0]['code'] == '101' || response['messages'][0]['code'] == '401'
78
- RecordSet.new(layout, model.non_modifiable_fields)
79
-
80
- else
81
- if response['messages'][0]['code'] == '102'
82
- results = DatabaseRequest.retrieve_first(layout)
83
- if results['messages'][0]['code'] == '0'
84
- r_results = results['response']['data']
85
- ret_val = r_results.empty? ? Error.throw_error('102') : r_results[0]['fieldData']
86
- query_keys = [foreign_key_field]
87
- Error.throw_error('102', (query_keys - ret_val.keys.map(&:downcase)).flatten.join(', '), layout)
88
- else
89
- Error.throw_error('102')
90
- end
91
- end
92
- Error.throw_error(response['messages'][0]['code'])
93
- end
94
- end
95
- elsif ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.classify(ActiveSupport::Inflector.singularize(method))).respond_to?('first')
96
- fk_model = ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.classify(ActiveSupport::Inflector.singularize(model_name)))
97
- pk_model = ActiveSupport::Inflector.constantize(ActiveSupport::Inflector.classify(ActiveSupport::Inflector.singularize(method)))
98
-
99
- if pk_model.has_many_relations[ActiveSupport::Inflector.parameterize(ActiveSupport::Inflector.pluralize(model_name)).to_sym]
100
- relation = pk_model.has_many_relations[ActiveSupport::Inflector.parameterize(ActiveSupport::Inflector.pluralize(model_name)).to_sym]
101
- layout = pk_model.layout_name
102
- pk_model.create_translations if pk_model.translations.keys.empty?
103
-
104
- url = "/layouts/#{layout}/_find"
105
-
106
- foreign_key_field = if fk_model.translations.key?(relation[:foreign_key])
107
- fk_model.translations[relation[:foreign_key]].to_s
108
- else
109
- relation[:foreign_key].to_s
110
- end
111
-
112
- primary_key_field = if pk_model.translations.key?(relation[:primary_key])
113
- pk_model.translations[relation[:primary_key]].to_s
114
- else
115
- relation[:primary_key].to_s
116
- end
117
-
118
- body = { query: [{ primary_key_field => self[foreign_key_field].to_s }], limit: 1 }.to_json
119
-
120
- response = DatabaseRequest.make_request(url, 'post', body)
121
- if response['messages'][0]['code'] == '0'
122
- r_results = response['response']['data']
123
- ret_val = RecordSet.new(layout, pk_model.non_modifiable_fields)
124
- r_results.each do |r|
125
- hash = pk_model.build_result(r)
126
- ret_val << hash
127
- end
128
- @response = ret_val
129
- @response.first
130
- elsif response['messages'][0]['code'] == '101' || response['messages'][0]['code'] == '401'
131
- RecordSet.new(layout, pk_model.non_modifiable_fields)
132
-
133
- else
134
- if response['messages'][0]['code'] == '102'
135
- results = DatabaseRequest.retrieve_first(layout)
136
- if results['messages'][0]['code'] == '0'
137
- r_results = results['response']['data']
138
- ret_val = r_results.empty? ? Error.throw_error('102') : r_results[0]['fieldData']
139
- query_keys = [primary_key_field]
140
- Error.throw_error('102', (query_keys - ret_val.keys.map(&:downcase)).flatten.join(', '), layout)
141
- else
142
- Error.throw_error('102')
143
- end
144
- end
145
- Error.throw_error(response['messages'][0]['code'])
146
- end
147
- end
148
- else
149
- super
45
+ result = find_has_many_relation(method)
46
+ return result if result
47
+ elsif constantize_model(method).respond_to?('first')
48
+ result = find_belongs_to_relation(method)
49
+ return result if result
150
50
  end
51
+
52
+ super
151
53
  rescue NameError => e
152
54
  if e.message.include?('constant')
153
55
  Error.throw_error('102', e.message.split(' ')[-1], layout_name)
@@ -156,6 +58,20 @@ module Trophonius
156
58
  end
157
59
  end
158
60
 
61
+ def respond_to_missing?(method, include_private = false)
62
+ if ActiveSupport::Inflector.pluralize(method).to_s == method.to_s
63
+ target_model = constantize_model(method)
64
+ return true if target_model.belongs_to_relations[parameterize_name(model_name)]
65
+ else
66
+ target_model = constantize_model(method)
67
+ relation_key = parameterize_name(ActiveSupport::Inflector.pluralize(model_name))
68
+ return true if target_model.has_many_relations[relation_key]
69
+ end
70
+ super
71
+ rescue NameError
72
+ super
73
+ end
74
+
159
75
  ##
160
76
  # Runs a FileMaker script from the context of the Model.
161
77
  #
@@ -216,6 +132,11 @@ module Trophonius
216
132
  # @return [True] if successful
217
133
  def update(field_data, portal_data: {})
218
134
  url = "layouts/#{layout_name}/records/#{record_id}"
135
+ differences = calculate_differences_before_update(field_data, portal_data)
136
+ puts differences
137
+ puts differences.all? { |diff| diff.length.zero? }
138
+ return if differences.all? { |diff| diff.length.zero? }
139
+
219
140
  field_data.each_key { |field| modifiable_fields[field] = field_data[field] }
220
141
  field_data.transform_keys! { |k| (@model.translations[k.to_s] || k).to_s }
221
142
  @model.run_before_update
@@ -289,6 +210,10 @@ module Trophonius
289
210
  def define_portal_methods(fm_record)
290
211
  fm_record['portalData'].each_key do |key|
291
212
  method_name = methodize_field(key)
213
+ relation_name = portal_relation_name(fm_record.dig('portalData', key, 0))
214
+ @portals.push(key)
215
+ @portals.push(relation_name) unless relation_name == key
216
+
292
217
  define_singleton_method(method_name) { self[key] }
293
218
  fm_record['portalData'][key].each do |portal_record|
294
219
  portal_record.each_key do |inner_key|
@@ -311,5 +236,111 @@ module Trophonius
311
236
  Error.throw_error('102')
312
237
  end
313
238
  end
239
+
240
+ def constantize_model(name)
241
+ ActiveSupport::Inflector.constantize(
242
+ ActiveSupport::Inflector.classify(
243
+ ActiveSupport::Inflector.singularize(name.to_s)
244
+ )
245
+ )
246
+ end
247
+
248
+ def parameterize_name(name)
249
+ ActiveSupport::Inflector.parameterize(name.to_s).to_sym
250
+ end
251
+
252
+ def resolve_field(model, field_key)
253
+ model.create_translations if model.translations.keys.empty?
254
+ if model.translations.key?(field_key)
255
+ model.translations[field_key].to_s
256
+ else
257
+ field_key.to_s
258
+ end
259
+ end
260
+
261
+ def build_relation_record_set(model, layout, data)
262
+ ret_val = RecordSet.new(layout, model.non_modifiable_fields)
263
+ data.each { |r| ret_val << model.build_result(r) }
264
+ ret_val
265
+ end
266
+
267
+ def handle_relation_field_error(code, query_field, layout)
268
+ return unless code == '102'
269
+
270
+ results = DatabaseRequest.retrieve_first(layout)
271
+ if results['messages'][0]['code'] == '0'
272
+ r_results = results['response']['data']
273
+ if r_results.empty?
274
+ Error.throw_error('102')
275
+ else
276
+ ret_val = r_results[0]['fieldData']
277
+ Error.throw_error('102', ([query_field] - ret_val.keys.map(&:downcase)).flatten.join(', '), layout)
278
+ end
279
+ else
280
+ Error.throw_error('102')
281
+ end
282
+ end
283
+
284
+ def execute_relation_query(target_model, query_field, query_value, limit:)
285
+ layout = target_model.layout_name
286
+ url = "/layouts/#{layout}/_find"
287
+ body = { query: [{ query_field => query_value.to_s }], limit: limit }.to_json
288
+ response = DatabaseRequest.make_request(url, 'post', body)
289
+ code = response['messages'][0]['code']
290
+
291
+ if code == '0'
292
+ build_relation_record_set(target_model, layout, response['response']['data'])
293
+ elsif code == '101' || code == '401'
294
+ RecordSet.new(layout, target_model.non_modifiable_fields)
295
+ else
296
+ handle_relation_field_error(code, query_field, layout)
297
+ Error.throw_error(code)
298
+ end
299
+ end
300
+
301
+ def find_has_many_relation(method)
302
+ target_model = constantize_model(method)
303
+ current_model = constantize_model(model_name)
304
+ relation = target_model.belongs_to_relations[parameterize_name(model_name)]
305
+ return nil unless relation
306
+
307
+ foreign_key_field = resolve_field(target_model, relation[:foreign_key])
308
+ primary_key_field = resolve_field(current_model, relation[:primary_key])
309
+
310
+ @response = execute_relation_query(
311
+ target_model,
312
+ foreign_key_field,
313
+ self[primary_key_field],
314
+ limit: 100_000
315
+ )
316
+ end
317
+
318
+ def find_belongs_to_relation(method)
319
+ current_model = constantize_model(model_name)
320
+ target_model = constantize_model(method)
321
+ relation = target_model.has_many_relations[parameterize_name(ActiveSupport::Inflector.pluralize(model_name))]
322
+ return nil unless relation
323
+
324
+ foreign_key_field = resolve_field(current_model, relation[:foreign_key])
325
+ primary_key_field = resolve_field(target_model, relation[:primary_key])
326
+
327
+ @response = execute_relation_query(
328
+ target_model,
329
+ primary_key_field,
330
+ self[foreign_key_field],
331
+ limit: 1
332
+ )
333
+ @response.first
334
+ end
335
+
336
+ def calculate_differences_before_update(field_data, portal_data)
337
+ fields = self.reject { |k,v| @portals.include?(k) || !field_data.keys.include?(k) }
338
+ portals = self.select { |k,v| @portals.include?(k) && portal_data.keys.include?(k) }
339
+
340
+ updated_fields = field_data.present? ? field_data.to_set - fields.to_set : []
341
+ updated_portals = portal_data.present? ? portal_data.to_set - portals.to_set : []
342
+
343
+ [updated_fields.to_h, updated_portals.to_h]
344
+ end
314
345
  end
315
346
  end
data/lib/translator.rb CHANGED
@@ -11,5 +11,11 @@ module Trophonius
11
11
  def methodize_portal_field(field_name)
12
12
  ActiveSupport::Inflector.parameterize(ActiveSupport::Inflector.underscore(field_name.gsub(/\w+::/, '').to_s), separator: '_')
13
13
  end
14
+
15
+ def portal_relation_name(first_related_record)
16
+ return '' if first_related_record.nil?
17
+
18
+ first_related_record.keys.map{ |f| f[/.*(?=::)/] }.tally.max_by(&:last).first
19
+ end
14
20
  end
15
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trophonius
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.7
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kempen Automatisering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-15 00:00:00.000000000 Z
11
+ date: 2026-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  requirements: []
101
- rubygems_version: 3.4.7
101
+ rubygems_version: 3.5.5
102
102
  signing_key:
103
103
  specification_version: 4
104
104
  summary: Link between Ruby (on Rails) and FileMaker.