scrivito_sdk 1.15.0 → 1.17.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/cms/scrivito/attribute_serializer.rb +2 -0
  4. data/app/cms/scrivito/basic_obj.rb +59 -25
  5. data/app/cms/scrivito/basic_widget.rb +36 -14
  6. data/app/cms/scrivito/binary.rb +3 -3
  7. data/app/cms/scrivito/cms_backend.rb +1 -1
  8. data/app/cms/scrivito/cms_field_tag.rb +2 -2
  9. data/app/cms/scrivito/cms_rest_api.rb +7 -4
  10. data/app/cms/scrivito/cms_rest_api/rate_limit.rb +1 -1
  11. data/app/cms/scrivito/cms_rest_api/widget_extractor.rb +42 -27
  12. data/app/cms/scrivito/controller_actions.rb +1 -5
  13. data/app/cms/scrivito/editing_context_middleware.rb +3 -5
  14. data/app/cms/scrivito/errored_widget_tag.rb +1 -1
  15. data/app/cms/scrivito/layout_tags.rb +3 -3
  16. data/app/cms/scrivito/meta_data_collection.rb +1 -1
  17. data/app/cms/scrivito/migrations/migration_store.rb +2 -2
  18. data/app/cms/scrivito/obj_collection.rb +5 -5
  19. data/app/cms/scrivito/obj_search_enumerator.rb +47 -0
  20. data/app/cms/scrivito/page_config.rb +2 -2
  21. data/app/cms/scrivito/sdk_engine.rb +2 -7
  22. data/app/cms/scrivito/test_request.rb +12 -14
  23. data/app/cms/scrivito/ui_config.rb +3 -2
  24. data/app/cms/scrivito/user.rb +1 -1
  25. data/app/cms/scrivito/workspace.rb +1 -1
  26. data/app/controllers/scrivito/objs_controller.rb +2 -2
  27. data/app/controllers/scrivito/ui_controller.rb +2 -2
  28. data/app/controllers/scrivito/webservice_controller.rb +0 -13
  29. data/config/ca-bundle.crt +326 -100
  30. data/lib/assets/javascripts/scrivito.js +1 -1
  31. data/lib/assets/javascripts/scrivito_ui_redirect.js +1 -1
  32. data/lib/assets/javascripts/scrivito_with_js_sdk.js +68 -108
  33. data/lib/assets/stylesheets/scrivito_editing.css +3 -3
  34. metadata +22 -58
  35. data/app/cms/scrivito/legacy_switch.rb +0 -7
  36. data/app/views/google_maps_widget/show.html.erb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89b1e59c229915c692327f0d8a4e4ab3cf3ca52c7c82b1798f6c206d609f4646
4
- data.tar.gz: 1faa4464b7871e33525cfda460262afc0a9aa55080a712dcadaf0425f4a4678d
3
+ metadata.gz: 146486584acb9660d1f7ceb8d7ecbe072bdb0b3e2ea9fab5437db0d66ded97a7
4
+ data.tar.gz: d8d757425944ffd948c56e5513c1e01a91092a205aaec407143ae302b31350e6
5
5
  SHA512:
6
- metadata.gz: d4462657b54b3e78c7262ab7d1ee59162639278c5505dfbed35472d20c2e0d463af546a10ce175376fb91ecffc8cb1643c318de87b700b4c3914dd72ae16e688
7
- data.tar.gz: 5fc475b928f74c0acab2abc6a45b9f42ee06ad0f41d8630a9afad0b2b67639ec9bff6c9322766523e5117e7690803da915de781f2dfff1d9eba461f5fee9ea85
6
+ metadata.gz: ded66fee7754e12b25d6ebbaed8063190990c3e0c218f44d3e8386cd81a7a98ed9885002eaaf21ba880237d06efc9e2ddd8d655164c6cb73dd61b3df4a5ca60e
7
+ data.tar.gz: 9ee6a548c78305663fac85d0b407a77bbac3eb2879bca4ef0afebd7d6604b9c3e2eed34fd400055f92a56a199004c96f891ac6af001d7f7c0af599e11141e2fc
data/README.md CHANGED
@@ -12,7 +12,7 @@ of concepts and APIs, tutorials, and release notes.
12
12
 
13
13
  ## License
14
14
 
15
- Copyright (c) 2009 - 2018 Infopark AG (http://www.infopark.com)
15
+ Copyright (c) 2009 - 2020 Infopark Group GmbH (http://www.infopark.com)
16
16
 
17
17
  This software can be used and modified under the LGPL-3.0. Please refer to
18
18
  http://www.gnu.org/licenses/lgpl-3.0.html for the license text.
@@ -29,6 +29,8 @@ class AttributeSerializer < Struct.new(:obj_class, :obj_id)
29
29
  _permalink
30
30
  ].include?(attribute_name)
31
31
  [attribute_name, attribute_value.to_s]
32
+ elsif attribute_name == '_restriction'
33
+ [attribute_name, attribute_value]
32
34
  else
33
35
  raise_unknown_attribute(attribute_name)
34
36
  end
@@ -1,4 +1,3 @@
1
- require 'json'
2
1
  require 'ostruct'
3
2
  require 'active_model/naming'
4
3
 
@@ -25,15 +24,18 @@ module Scrivito
25
24
  PublicSystemAttributeDefinition = Class.new(AttributeDefinition)
26
25
 
27
26
  SYSTEM_ATTRIBUTES = AttributeDefinitionCollection.new(
28
- '_id' => PublicSystemAttributeDefinition.new(:_id, :string),
29
- '_last_changed' => PublicSystemAttributeDefinition.new(:_last_changed, :date),
30
- '_obj_class' => PublicSystemAttributeDefinition.new(:_obj_class, :string),
31
- '_path' => PublicSystemAttributeDefinition.new(:_path, :string),
32
- '_permalink' => PublicSystemAttributeDefinition.new(:_permalink, :string),
33
-
34
- '_conflicts' => AttributeDefinition.new(:_conflicts, nil),
35
- '_modification' => AttributeDefinition.new(:_modification, nil),
36
- '_widget_pool' => AttributeDefinition.new(:_widget_pool, nil),
27
+ '_id' => PublicSystemAttributeDefinition.new(:_id, :string),
28
+ '_created_at' => PublicSystemAttributeDefinition.new(:_created_at, :date),
29
+ '_last_changed' => PublicSystemAttributeDefinition.new(:_last_changed, :date),
30
+ '_first_published_at' => PublicSystemAttributeDefinition.new(:_first_published_at, :date),
31
+ '_published_at' => PublicSystemAttributeDefinition.new(:_published_at, :date),
32
+ '_obj_class' => PublicSystemAttributeDefinition.new(:_obj_class, :string),
33
+ '_path' => PublicSystemAttributeDefinition.new(:_path, :string),
34
+ '_permalink' => PublicSystemAttributeDefinition.new(:_permalink, :string),
35
+
36
+ '_conflicts' => AttributeDefinition.new(:_conflicts, nil),
37
+ '_modification' => AttributeDefinition.new(:_modification, nil),
38
+ '_widget_pool' => AttributeDefinition.new(:_widget_pool, nil),
37
39
  )
38
40
 
39
41
  UNIQ_ATTRIBUTES = %w[
@@ -44,7 +46,10 @@ module Scrivito
44
46
 
45
47
  GENERATED_ATTRIBUTES = %w[
46
48
  _conflicts
49
+ _created_at
47
50
  _last_changed
51
+ _first_published_at
52
+ _published_at
48
53
  _modification
49
54
  _obj_type
50
55
  _text_links
@@ -138,14 +143,14 @@ module Scrivito
138
143
  else
139
144
  attributes = build_attributes_with_defaults(attributes, context)
140
145
  attributes = prepare_attributes_for_instantiation(attributes)
141
- api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
146
+ extraction, api_attributes = prepare_attributes_for_rest_api(attributes)
142
147
 
143
148
  workspace = Workspace.current
144
149
  obj_data = workspace.create_obj(obj: api_attributes)
145
150
  obj = BasicObj.instantiate(obj_data)
146
151
  obj.revision = workspace.revision
147
152
 
148
- CmsRestApi::WidgetExtractor.notify_persisted_widgets(obj, widget_properties)
153
+ extraction.notify_persisted(obj)
149
154
  obj
150
155
  end
151
156
  end
@@ -301,7 +306,7 @@ module Scrivito
301
306
  # @api public
302
307
  def self.find_by_path(path)
303
308
  Workspace.current.objs.find_one_by(:path, path,
304
- obj_class_name: type_computer.obj_class_name_for_type(self))
309
+ type_computer.obj_class_name_for_type(self))
305
310
  end
306
311
 
307
312
  # Find an {Scrivito::BasicObj Obj} with the given name.
@@ -329,7 +334,7 @@ module Scrivito
329
334
  # @api public
330
335
  def self.find_by_permalink(permalink)
331
336
  Workspace.current.objs.find_one_by(:permalink, permalink,
332
- obj_class_name: type_computer.obj_class_name_for_type(self))
337
+ type_computer.obj_class_name_for_type(self))
333
338
  end
334
339
 
335
340
  # Returns the {Scrivito::BasicObj Obj} with the given permalink, or raises
@@ -414,9 +419,9 @@ module Scrivito
414
419
  # }
415
420
  # )
416
421
  def update(attributes)
417
- api_attributes, widget_properties = prepare_attributes_for_rest_api(attributes)
422
+ extraction, api_attributes = prepare_attributes_for_rest_api(attributes)
418
423
  update_data(workspace.update_obj(id, obj: api_attributes))
419
- CmsRestApi::WidgetExtractor.notify_persisted_widgets(self, widget_properties)
424
+ extraction.notify_persisted(self)
420
425
  self
421
426
  end
422
427
 
@@ -633,7 +638,7 @@ module Scrivito
633
638
  return [] if binary?
634
639
  toclist = children
635
640
  toclist = toclist.reject { |toc| toc.binary? } unless args.include?(:all)
636
- toclist
641
+ toclist.sort_by(&:id)
637
642
  end
638
643
 
639
644
  # @api public
@@ -659,13 +664,23 @@ module Scrivito
659
664
  SYSTEM_KEYS = Set.new(%w[
660
665
  body
661
666
  _id
667
+ _created_at
662
668
  _last_changed
669
+ _first_published_at
670
+ _published_at
663
671
  _path
664
672
  _permalink
665
673
  _obj_class
666
674
  title
667
675
  ])
668
676
 
677
+ DATE_KEYS = Set.new(%w[
678
+ _created_at
679
+ _last_changed
680
+ _first_published_at
681
+ _published_at
682
+ ])
683
+
669
684
  # Returns the value of a system or custom attribute specified by its name.
670
685
  # Passing an invalid key will not raise an error but return +nil+.
671
686
  # @api public
@@ -704,11 +719,30 @@ module Scrivito
704
719
  }
705
720
  end
706
721
 
722
+ # @return [nil, ActiveSupport::TimeWithZone]
723
+ # @api public
724
+ def created_at
725
+ read_attribute('_created_at')
726
+ end
727
+
728
+ # @return [ActiveSupport::TimeWithZone]
707
729
  # @api public
708
730
  def last_changed
709
731
  read_attribute('_last_changed')
710
732
  end
711
733
 
734
+ # @return [nil, ActiveSupport::TimeWithZone]
735
+ # @api public
736
+ def first_published_at
737
+ read_attribute('_first_published_at')
738
+ end
739
+
740
+ # @return [nil, ActiveSupport::TimeWithZone]
741
+ # @api public
742
+ def published_at
743
+ read_attribute('_published_at')
744
+ end
745
+
712
746
  def new?(revision=workspace.base_revision)
713
747
  quick_modification(revision) == "new"
714
748
  end
@@ -820,7 +854,7 @@ module Scrivito
820
854
 
821
855
  # @api public
822
856
  # This method returns the byte length of the binary of the Obj.
823
- # @return [Fixnum] If no binary is set, 0 is returned.
857
+ # @return [Integer] If no binary is set, 0 is returned.
824
858
  def binary_length
825
859
  binary.try(:content_length) || 0
826
860
  end
@@ -991,15 +1025,16 @@ module Scrivito
991
1025
  end
992
1026
 
993
1027
  def self.prepare_attributes_for_rest_api(attributes, obj = nil)
994
- widget_pool_attributes, obj_attributes = CmsRestApi::WidgetExtractor.call(attributes, obj)
1028
+ extraction = CmsRestApi::WidgetExtractor.call(attributes, obj)
1029
+ obj_attributes = extraction.attributes
995
1030
  obj_id = obj ? obj.id : obj_attributes.fetch(:_id)
996
1031
  workspace = obj ? obj.revision.workspace : Workspace.current
997
1032
 
998
1033
  api_attributes = serialize_attributes(
999
- obj_attributes, widget_pool_attributes, workspace, obj_id
1034
+ obj_attributes, extraction.widget_pool_attributes, workspace, obj_id
1000
1035
  )
1001
1036
 
1002
- [api_attributes, widget_pool_attributes]
1037
+ [extraction, api_attributes]
1003
1038
  end
1004
1039
 
1005
1040
  def self.find_attribute_definitions(obj_class, basic_class = self)
@@ -1025,7 +1060,7 @@ module Scrivito
1025
1060
 
1026
1061
  private_class_method def self.serialize_widget_pool_attributes(serializer, widget_pool_attributes)
1027
1062
  {}.tap do |serialized_attributes|
1028
- widget_pool_attributes.each_pair do |widget, widget_attributes|
1063
+ widget_pool_attributes.each_value do |(widget, widget_attributes)|
1029
1064
  obj_class = widget_attributes['_obj_class']
1030
1065
  widget_serializer = AttributeSerializer.new(obj_class, serializer.obj_id)
1031
1066
 
@@ -1066,8 +1101,7 @@ module Scrivito
1066
1101
 
1067
1102
  def instantiate_widget(widget_id, widget_data)
1068
1103
  BasicWidget.instantiate(widget_data).tap do |widget|
1069
- widget.id = widget_id
1070
- widget.obj = self
1104
+ widget.attach_to_obj(self, widget_id)
1071
1105
  end
1072
1106
  end
1073
1107
 
@@ -1151,7 +1185,7 @@ module Scrivito
1151
1185
 
1152
1186
  def value_of_system_attribute(attribute_name)
1153
1187
  attribute_value = data_from_cms.value_of(attribute_name)
1154
- if attribute_name == '_last_changed'
1188
+ if DATE_KEYS.include?(attribute_name)
1155
1189
  DateConversion.deserialize_from_backend(attribute_value)
1156
1190
  else
1157
1191
  attribute_value
@@ -95,8 +95,6 @@ class BasicWidget
95
95
 
96
96
  attr_accessor :container, :container_attribute_name
97
97
 
98
- attr_writer :obj, :id
99
-
100
98
  attr_reader :attributes_to_be_saved
101
99
 
102
100
  delegate :widget_from_pool, to: :obj
@@ -221,12 +219,29 @@ class BasicWidget
221
219
  @attributes_to_be_saved.nil?
222
220
  end
223
221
 
222
+ def attach_to_obj(an_obj, widget_id = compute_widget_id(an_obj))
223
+ @obj = an_obj
224
+ @id = widget_id
225
+
226
+ attributes_to_be_saved
227
+ end
228
+
224
229
  def ==(other)
225
- other.respond_to?(:obj) && obj == other.obj && other.respond_to?(:id) && id == other.id
230
+ eql?(other)
226
231
  end
227
232
 
228
233
  def eql?(other)
229
- self == other
234
+ other.respond_to?(:equality_identifier, true) &&
235
+ equality_identifier.eql?(other.equality_identifier)
236
+ end
237
+
238
+ def hash
239
+ unless persisted?
240
+ # An unbound widget will likely be attached soon, which mutates its #hash value. Evil.
241
+ raise ScrivitoError, 'a new widget is not allowed to be used as a hash key'
242
+ end
243
+
244
+ equality_identifier.hash
230
245
  end
231
246
 
232
247
  #
@@ -262,7 +277,7 @@ class BasicWidget
262
277
  { obj: {_widget_pool: {id => previous_widget_content}} })
263
278
  reload
264
279
  else
265
- raise ScrivitoError, "cannot revert changes, since widget is #{modification}."
280
+ raise ScrivitoError, "cannot revert changes, since widget is #{modification}"
266
281
  end
267
282
  end
268
283
 
@@ -297,14 +312,6 @@ class BasicWidget
297
312
  end
298
313
  end
299
314
 
300
- def hash
301
- if @obj && @id
302
- (id + obj.id).hash
303
- else
304
- super
305
- end
306
- end
307
-
308
315
  # Returns the entity ({Scrivito::BasicObj} or {Scrivito::BasicWidget})
309
316
  # that references this widget.
310
317
  # @api public
@@ -360,7 +367,8 @@ class BasicWidget
360
367
  end
361
368
  end
362
369
 
363
- def forget_unsaved_attributes
370
+ def notify_persisted(obj)
371
+ @obj = obj
364
372
  @attributes_to_be_saved = nil
365
373
  reload_data
366
374
  end
@@ -398,6 +406,12 @@ class BasicWidget
398
406
  referenced + referenced.map { |widget| widget.contained_widgets }.flatten
399
407
  end
400
408
 
409
+ protected
410
+
411
+ def equality_identifier
412
+ [id, obj.id]
413
+ end
414
+
401
415
  private
402
416
 
403
417
  def workspace
@@ -433,6 +447,14 @@ class BasicWidget
433
447
  def value_of_system_attribute(attribute_name)
434
448
  data_from_cms.value_of(attribute_name)
435
449
  end
450
+
451
+ def compute_widget_id(obj)
452
+ @attributes_to_be_saved.delete('_id') || generate_widget_id(obj)
453
+ end
454
+
455
+ def generate_widget_id(obj)
456
+ obj.present? ? obj.generate_widget_pool_id : BasicObj.generate_widget_pool_id
457
+ end
436
458
  end
437
459
 
438
460
  end
@@ -195,7 +195,7 @@ class Binary
195
195
  #
196
196
  # @param [Hash] definition transformation definition
197
197
  #
198
- # @option definition [Fixnum,String] :width The width in pixels of the output image. Must be a
198
+ # @option definition [Integer,String] :width The width in pixels of the output image. Must be a
199
199
  # positive integer.
200
200
  #
201
201
  # If only this dimension has been specified, the other dimension is calculated automatically to
@@ -214,7 +214,7 @@ class Binary
214
214
  #
215
215
  # If the given width and height are adjusted, the aspect ratio is preserved.
216
216
  #
217
- # @option definition [Fixnum,String] :height The height in pixels of the output image. Must be a
217
+ # @option definition [Integer,String] :height The height in pixels of the output image. Must be a
218
218
  # positive integer.
219
219
  #
220
220
  # If only this dimension has been specified, the other dimension is calculated automatically to
@@ -248,7 +248,7 @@ class Binary
248
248
  # i.e. the center of the image is preserved. The area to preserve can be configured using
249
249
  # the +crop+ parameter.
250
250
  #
251
- # @option definition [Fixnum,String] :quality Controls the output quality of lossy file formats.
251
+ # @option definition [Integer,String] :quality Controls the output quality of lossy file formats.
252
252
  # Applies if the format is +"jpg"+. Valid values are in the range from +0+ to +100+.
253
253
  # The default value is +75+.
254
254
  #
@@ -206,7 +206,7 @@ module Scrivito
206
206
  private_class_method :blob_data_cache_key
207
207
 
208
208
  def self.search_params_cache_key(params)
209
- MultiJson.encode(normalize_search_param(params))
209
+ JSON.generate(normalize_search_param(params))
210
210
  end
211
211
  private_class_method :search_params_cache_key
212
212
 
@@ -67,7 +67,7 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
67
67
  if FIELD_TYPES_WITH_ORIGINAL_CONTENT.include?(field_type)
68
68
  original_value = view.scrivito_value(current_value)
69
69
  original_content = original_content(field_type, original_value, current_value)
70
- encoded_content = Base64.strict_encode64(MultiJson.encode(original_content))
70
+ encoded_content = Base64.strict_encode64(JSON.generate(original_content))
71
71
  options['private-field-original-content'] = encoded_content
72
72
  end
73
73
 
@@ -161,7 +161,7 @@ class CmsFieldTag < Struct.new(:view, :tag_name, :obj_or_widget, :editing_option
161
161
  end
162
162
 
163
163
  def valid_values
164
- MultiJson.encode(obj_or_widget.attribute_definitions[field_name].try(:values) || [])
164
+ JSON.generate(obj_or_widget.attribute_definitions[field_name].try(:values) || [])
165
165
  end
166
166
 
167
167
  def assert_valid_attribute
@@ -123,7 +123,7 @@ module Scrivito
123
123
  timer)
124
124
  request = method_to_net_http_class(method).new(path(resource_path))
125
125
  set_headers(request)
126
- request.body = MultiJson.encode(payload) if payload.present?
126
+ request.body = JSON.generate(payload) if payload.present?
127
127
 
128
128
  response = retry_once_on_network_error(method, timer) do
129
129
  CmsRestApi::RateLimit.retry_on_rate_limit(timer) do
@@ -138,12 +138,12 @@ module Scrivito
138
138
  private_class_method def self.handle_response(resource_path, response)
139
139
  http_code = response.code.to_i
140
140
  if response.code.start_with?('2')
141
- MultiJson.load(response.body)
141
+ JSON.parse(response.body)
142
142
  elsif response.code == '403'
143
143
  raise AccessDenied.new(response.body)
144
144
  else
145
145
  begin
146
- error_body = MultiJson.decode(response.body)
146
+ error_body = JSON.parse(response.body)
147
147
  specific_output = error_body['error']
148
148
 
149
149
  if response.code.start_with?('4')
@@ -155,7 +155,7 @@ module Scrivito
155
155
  else # 3xx and >500 are treated as NetworkErrors
156
156
  raise NetworkError.new(response.body, http_code)
157
157
  end
158
- rescue MultiJson::DecodeError
158
+ rescue JSON::ParserError, TypeError
159
159
  raise NetworkError.new(response.body, http_code)
160
160
  end
161
161
  end
@@ -170,6 +170,9 @@ module Scrivito
170
170
  message = task_data["message"] ||
171
171
  "Missing error message in task response #{task_data}"
172
172
 
173
+ if task_data['status'] == 'exception'
174
+ raise BackendError.new(message, 500)
175
+ end
173
176
  backend_code = task_data['code']
174
177
  raise ClientError.new(message, backend_code: backend_code)
175
178
  end
@@ -12,7 +12,7 @@ module RateLimit
12
12
  time_to_sleep = calculate_time_to_sleep(response['Retry-After'].to_f, retry_count)
13
13
 
14
14
  if request_timer.cover?(Time.now + time_to_sleep.seconds)
15
- Warning.warn("Rate limit exceeded. Will retry after #{time_to_sleep} seconds.")
15
+ Scrivito::Warning.warn("Rate limit exceeded. Will retry after #{time_to_sleep} seconds.")
16
16
  sleep time_to_sleep
17
17
  internal_retry(request_proc, request_timer, retry_count + 1)
18
18
  else
@@ -1,53 +1,68 @@
1
1
  module Scrivito
2
2
  class CmsRestApi
3
3
  class WidgetExtractor
4
-
5
4
  # Extract widgets from a given attribute tree
6
5
  # first return param: a flat hash of widget_like => updated widget attributes
7
6
  # second return param: the updated attributes
8
7
  def self.call(attributes, obj=nil)
8
+ new(obj).extract_widgets(attributes)
9
+ end
10
+
11
+ def initialize(obj)
12
+ @obj = obj
13
+ end
14
+
15
+ def extract_widgets(attributes)
16
+ @extracted_widgets = {}
9
17
  updated_attributes = attributes.dup
10
- extracted_widgets = {}
18
+ widget_pool = updated_attributes.delete(:_widget_pool)
19
+ extract_widgets_from_pool(widget_pool) if widget_pool
20
+ extract_widgets_from_attributes(updated_attributes)
11
21
 
12
- (updated_attributes.delete(:_widget_pool) || {}).each do |widget, widget_properties|
13
- unless widget.persisted?
14
- raise ScrivitoError, 'new top-level widgets are not allowed in the _widget_pool'
15
- end
16
- extracted_widgets[widget] = widget_properties
17
- extracted_widgets.merge!(call(widget_properties, obj).first)
22
+ Extraction.new(extracted_widgets, updated_attributes)
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :obj
28
+ attr_reader :extracted_widgets
29
+
30
+ def extract_widgets_from_pool(widget_pool)
31
+ widget_pool.each do |widget, widget_properties|
32
+ extracted_widgets[widget.id] = [widget, widget_properties]
33
+ extract_widgets_from_attributes(widget_properties)
18
34
  end
35
+ end
19
36
 
20
- updated_attributes.each do |attribute_name, value|
37
+ def extract_widgets_from_attributes(attributes)
38
+ attributes.each do |attribute_name, value|
21
39
  if value.is_a?(Array) && value.all? { |w| w.is_a?(BasicWidget) }
22
40
  value.each do |widget|
23
41
  unless widget.persisted?
24
- attach_widget_to_obj(widget, obj)
25
- extracted_widgets[widget] = widget.attributes_to_be_saved
26
- extracted_widgets.merge!(call(widget.attributes_to_be_saved, obj).first)
42
+ widget_attributes = widget.attach_to_obj(obj)
43
+ extracted_widgets[widget.id] = [widget, widget_attributes]
44
+ extract_widgets_from_attributes(widget_attributes)
27
45
  end
28
46
  end
29
47
  end
30
48
  end
31
-
32
- return extracted_widgets, updated_attributes
33
49
  end
34
50
 
35
- def self.attach_widget_to_obj(widget, obj)
36
- widget.id = widget.attributes_to_be_saved.delete('_id') || generate_widget_pool_id(obj)
37
- widget.obj = obj
38
- end
51
+ class Extraction
52
+ attr_reader :widget_pool_attributes, :attributes
39
53
 
40
- def self.notify_persisted_widgets(obj, widget_hash)
41
- widget_hash.each do |widget, attributes|
42
- if attributes.present?
43
- widget.obj = obj
44
- widget.forget_unsaved_attributes
45
- end
54
+ def initialize(widget_pool_attributes, attributes)
55
+ @widget_pool_attributes = widget_pool_attributes
56
+ @attributes = attributes
46
57
  end
47
- end
48
58
 
49
- def self.generate_widget_pool_id(obj)
50
- obj.presence ? obj.generate_widget_pool_id : BasicObj.generate_widget_pool_id
59
+ def notify_persisted(obj)
60
+ widget_pool_attributes.each_value do |(widget, attributes)|
61
+ if attributes.present?
62
+ widget.notify_persisted(obj)
63
+ end
64
+ end
65
+ end
51
66
  end
52
67
  end
53
68
  end