avo 3.0.0.pre13 → 3.0.0.pre14

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of avo might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Gemfile.lock +2 -1
  4. data/app/components/avo/alert_component.html.erb +1 -1
  5. data/app/components/avo/base_component.rb +7 -7
  6. data/app/components/avo/field_wrapper_component.rb +1 -1
  7. data/app/components/avo/fields/area_field/edit_component.html.erb +1 -1
  8. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +5 -5
  9. data/app/components/avo/fields/belongs_to_field/edit_component.rb +4 -4
  10. data/app/components/avo/fields/boolean_field/edit_component.html.erb +1 -0
  11. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
  12. data/app/components/avo/fields/code_field/edit_component.html.erb +1 -0
  13. data/app/components/avo/fields/country_field/edit_component.html.erb +1 -0
  14. data/app/components/avo/fields/file_field/index_component.rb +2 -2
  15. data/app/components/avo/fields/has_one_field/show_component.html.erb +1 -0
  16. data/app/components/avo/fields/index_component.rb +1 -0
  17. data/app/components/avo/fields/location_field/show_component.html.erb +1 -1
  18. data/app/components/avo/fields/markdown_field/edit_component.html.erb +1 -0
  19. data/app/components/avo/fields/number_field/edit_component.html.erb +1 -0
  20. data/app/components/avo/fields/password_field/edit_component.html.erb +1 -0
  21. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +1 -0
  22. data/app/components/avo/fields/status_field/edit_component.html.erb +1 -1
  23. data/app/components/avo/fields/text_field/edit_component.html.erb +1 -1
  24. data/app/components/avo/fields/textarea_field/edit_component.html.erb +1 -0
  25. data/app/components/avo/fields/trix_field/edit_component.html.erb +2 -1
  26. data/app/components/avo/fields/trix_field/show_component.html.erb +1 -1
  27. data/app/components/avo/index/resource_controls_component.rb +6 -6
  28. data/app/components/avo/item_switcher_component.html.erb +9 -4
  29. data/app/components/avo/item_switcher_component.rb +2 -1
  30. data/app/components/avo/resource_component.rb +5 -3
  31. data/app/components/avo/resource_sidebar_component.rb +1 -1
  32. data/app/components/avo/row_component.html.erb +3 -0
  33. data/app/components/avo/row_component.rb +12 -0
  34. data/app/components/avo/sidebar/link_component.html.erb +2 -0
  35. data/app/components/avo/sidebar/link_component.rb +5 -3
  36. data/app/components/avo/sidebar_component.html.erb +3 -3
  37. data/app/components/avo/sidebar_component.rb +4 -4
  38. data/app/components/avo/sidebar_profile_component.html.erb +3 -3
  39. data/app/components/avo/views/resource_edit_component.rb +1 -1
  40. data/app/components/avo/views/resource_index_component.html.erb +1 -1
  41. data/app/components/avo/views/resource_index_component.rb +8 -8
  42. data/app/controllers/avo/actions_controller.rb +11 -7
  43. data/app/controllers/avo/application_controller.rb +71 -66
  44. data/app/controllers/avo/associations_controller.rb +4 -6
  45. data/app/controllers/avo/attachments_controller.rb +1 -1
  46. data/app/controllers/avo/base_controller.rb +22 -15
  47. data/app/controllers/avo/home_controller.rb +1 -1
  48. data/app/controllers/avo/search_controller.rb +14 -12
  49. data/app/controllers/concerns/avo/initializes_avo.rb +2 -5
  50. data/app/javascript/js/controllers/fields/easy_mde_controller.js +1 -0
  51. data/app/views/avo/associations/new.html.erb +1 -1
  52. data/app/views/avo/debug/status.html.erb +1 -1
  53. data/app/views/avo/partials/_custom_tools_alert.html.erb +2 -2
  54. data/app/views/avo/partials/_footer.html.erb +1 -1
  55. data/app/views/avo/partials/_javascript.html.erb +1 -1
  56. data/app/views/avo/partials/_navbar.html.erb +1 -1
  57. data/app/views/layouts/avo/application.html.erb +2 -2
  58. data/avo.gemspec +1 -0
  59. data/config/initializers/pagy.rb +12 -10
  60. data/config/routes.rb +3 -3
  61. data/db/factories.rb +2 -1
  62. data/lib/avo/base_action.rb +4 -1
  63. data/lib/avo/base_resource.rb +118 -89
  64. data/lib/avo/concerns/has_item_type.rb +4 -0
  65. data/lib/avo/concerns/has_items.rb +20 -15
  66. data/lib/avo/concerns/model_class_constantized.rb +0 -2
  67. data/lib/avo/current.rb +22 -1
  68. data/lib/avo/dsl/field_parser.rb +1 -1
  69. data/lib/avo/dynamic_router.rb +12 -1
  70. data/lib/avo/engine.rb +4 -7
  71. data/lib/avo/fields/base_field.rb +25 -3
  72. data/lib/avo/fields/belongs_to_field.rb +8 -7
  73. data/lib/avo/fields/concerns/is_searchable.rb +1 -1
  74. data/lib/avo/fields/concerns/use_resource.rb +1 -1
  75. data/lib/avo/fields/field_manager.rb +13 -3
  76. data/lib/avo/fields/has_base_field.rb +4 -4
  77. data/lib/avo/fields/has_one_field.rb +1 -1
  78. data/lib/avo/fields/location_field.rb +18 -1
  79. data/lib/avo/licensing/h_q.rb +11 -6
  80. data/lib/avo/licensing/license.rb +1 -1
  81. data/lib/avo/licensing/license_manager.rb +1 -1
  82. data/lib/avo/licensing/{null_license.rb → nil_license.rb} +1 -1
  83. data/lib/avo/loaders/fields_loader.rb +7 -1
  84. data/lib/avo/plugin_manager.rb +2 -4
  85. data/lib/avo/reloader.rb +1 -1
  86. data/lib/avo/resources/items/holder.rb +5 -1
  87. data/lib/avo/resources/items/item_group.rb +1 -0
  88. data/lib/avo/resources/items/row.rb +54 -0
  89. data/lib/avo/resources/resource_manager.rb +4 -7
  90. data/lib/avo/services/debug_service.rb +6 -6
  91. data/lib/avo/services/telemetry_service.rb +3 -3
  92. data/lib/avo/version.rb +1 -1
  93. data/lib/avo.rb +107 -25
  94. data/lib/generators/avo/action_generator.rb +8 -8
  95. data/lib/generators/avo/card_generator.rb +27 -0
  96. data/lib/generators/avo/filter_generator.rb +8 -8
  97. data/lib/generators/avo/templates/action.tt +3 -3
  98. data/lib/generators/avo/templates/cards/chartkick_card.tt +1 -1
  99. data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +1 -1
  100. data/lib/generators/avo/templates/cards/metric_card.tt +1 -1
  101. data/lib/generators/avo/templates/cards/metric_card_sample.tt +1 -1
  102. data/lib/generators/avo/templates/cards/partial_card.tt +1 -1
  103. data/lib/generators/avo/templates/cards/partial_card_sample.tt +1 -1
  104. data/lib/generators/avo/templates/dashboards/dashboard.tt +1 -1
  105. data/lib/generators/avo/templates/scope.tt +1 -1
  106. data/lib/tasks/avo_tasks.rake +1 -28
  107. data/public/avo-assets/avo.base.css +26 -31
  108. data/public/avo-assets/avo.base.js +281 -280
  109. data/public/avo-assets/avo.base.js.map +3 -3
  110. metadata +21 -8
  111. data/lib/avo/app.rb +0 -170
  112. data/lib/generators/avo/card/chartkick_generator.rb +0 -18
  113. data/lib/generators/avo/card/metric_generator.rb +0 -18
  114. data/lib/generators/avo/card/partial_generator.rb +0 -19
  115. data/lib/generators/avo/templates/standalone_action.tt +0 -15
@@ -5,13 +5,15 @@ def pagy_locale_path(file_name)
5
5
  Avo::Engine.root.join("lib", "generators", "avo", "templates", "locales", "pagy", file_name)
6
6
  end
7
7
 
8
- Pagy::I18n.load(
9
- { locale: 'en' },
10
- { locale: 'fr' },
11
- { locale: 'nb' },
12
- { locale: 'pt-BR' },
13
- { locale: 'pt' },
14
- { locale: 'tr' },
15
- { locale: 'nn', filepath: pagy_locale_path("nn.yml") },
16
- { locale: 'ro', filepath: pagy_locale_path("ro.yml") },
17
- )
8
+ extra_locales = [
9
+ {locale: "en"},
10
+ {locale: "fr"},
11
+ {locale: "nb"},
12
+ {locale: "pt-BR"},
13
+ {locale: "pt"},
14
+ {locale: "tr"},
15
+ {locale: "nn", filepath: pagy_locale_path("nn.yml")},
16
+ {locale: "ro", filepath: pagy_locale_path("ro.yml")}
17
+ ]
18
+
19
+ Pagy::I18n.send(:build, *extra_locales)
data/config/routes.rb CHANGED
@@ -4,9 +4,9 @@ Avo::Engine.routes.draw do
4
4
  get "resources", to: redirect(Avo.configuration.root_path)
5
5
  get "dashboards", to: redirect(Avo.configuration.root_path)
6
6
 
7
- mount AvoFilters::Engine, at: "/avo_filters" if defined?(AvoFilters)
8
- mount AvoDashboards::Engine, at: "/dashboards" if defined?(AvoDashboards::Engine)
9
- mount AvoPro::Engine, at: "/avo_pro" if defined?(AvoPro)
7
+ mount Avo::DynamicFilters::Engine, at: "/avo-dynamic_filters" if defined?(Avo::DynamicFilters::Engine)
8
+ mount Avo::Dashboards::Engine, at: "/dashboards" if defined?(Avo::Dashboards::Engine)
9
+ mount Avo::Pro::Engine, at: "/avo/avo-pro" if defined?(Avo::Pro::Engine)
10
10
 
11
11
  post "/rails/active_storage/direct_uploads", to: "/active_storage/direct_uploads#create"
12
12
 
data/db/factories.rb CHANGED
@@ -110,7 +110,8 @@ FactoryBot.define do
110
110
  factory :city do
111
111
  name { Faker::Address.city }
112
112
  population { rand(10000..999000) }
113
- coordinates { [Faker::Address.latitude, Faker::Address.longitude] }
113
+ latitude { Faker::Address.latitude }
114
+ longitude { Faker::Address.longitude }
114
115
  is_capital { [true, false].sample }
115
116
  features { Faker::Address.community }
116
117
  metadata { Faker::Address.community }
@@ -101,12 +101,15 @@ module Avo
101
101
  records, fields, current_user, resource = args.values_at(:records, :fields, :current_user, :resource)
102
102
  # Fetching the field definitions and not the actual fields (get_fields) because they will break if the user uses a `visible` block and adds a condition using the `params` variable. The params are different in the show method and the handle method.
103
103
  action_fields = get_field_definitions.map { |field| [field.id, field] }.to_h
104
+ puts ["action_fields->", action_fields].inspect
104
105
 
105
106
  # For some fields, like belongs_to, the id and database_id differ (user vs user_id).
106
107
  # That's why we need to fetch the database_id for when we process the action.
107
108
  action_fields_by_database_id = action_fields.map do |id, value|
108
109
  [value.database_id.to_sym, value]
109
110
  end.to_h
111
+ puts ["action_fields_by_database_id->", action_fields_by_database_id].inspect
112
+ abort 1.inspect
110
113
 
111
114
  if fields.present?
112
115
  processed_fields = fields.to_unsafe_h.map do |name, value|
@@ -128,7 +131,7 @@ module Avo
128
131
  resource: resource
129
132
  }
130
133
 
131
- args[:records] = records unless standalone
134
+ args[:records] = records
132
135
 
133
136
  handle(**args)
134
137
 
@@ -84,6 +84,8 @@ module Avo
84
84
  end
85
85
 
86
86
  # This resolves the scope when doing "where" queries (not find queries)
87
+ #
88
+ # It's used to apply the authorization feature.
87
89
  def query_scope
88
90
  authorization.apply_policy Avo::ExecutionContext.new(
89
91
  target: index_query,
@@ -92,6 +94,8 @@ module Avo
92
94
  end
93
95
 
94
96
  # This resolves the scope when finding records (not "where" queries)
97
+ #
98
+ # It's used to apply the authorization feature.
95
99
  def find_scope
96
100
  authorization.apply_policy model_class
97
101
  end
@@ -127,8 +131,31 @@ module Avo
127
131
  end
128
132
  end
129
133
 
134
+ # Returns the model class being used for this resource.
135
+ #
136
+ # The Resource instance has a model_class method too so it can support the STI use cases
137
+ # where we figure out the model class from the record
138
+ def model_class(record_class: nil)
139
+ # get the model class off of the static property
140
+ return @model_class if @model_class.present?
141
+
142
+ # get the model class off of the record for STI models
143
+ return record_class if record_class.present?
144
+
145
+ # generate a model class
146
+ class_name.safe_constantize
147
+ end
148
+
149
+ # This is used as the model class ID
150
+ # We use this instead of the route_key to maintain compatibility with uncountable models
151
+ # With uncountable models route key appends an _index suffix (Fish->fish_index)
152
+ # Example: User->users, MediaItem->media_items, Fish->fish
153
+ def model_key
154
+ model_class.model_name.plural
155
+ end
156
+
130
157
  def class_name
131
- name.demodulize
158
+ to_s.demodulize
132
159
  end
133
160
 
134
161
  def route_key
@@ -138,11 +165,81 @@ module Avo
138
165
  def singular_route_key
139
166
  route_key.singularize
140
167
  end
168
+
169
+ def translation_key
170
+ @translation_key || "avo.resource_translations.#{class_name.underscore}"
171
+ end
172
+
173
+ def name
174
+ default = class_name.underscore.humanize
175
+
176
+ if translation_key
177
+ t(translation_key, count: 1, default: default).capitalize
178
+ else
179
+ default
180
+ end
181
+ end
182
+ alias_method :singular_name, :name
183
+
184
+ def plural_name
185
+ default = name.pluralize
186
+
187
+ if translation_key
188
+ t(translation_key, count: 2, default: default).capitalize
189
+ else
190
+ default
191
+ end
192
+ end
193
+
194
+ def underscore_name
195
+ return @name if @name.present?
196
+
197
+ name.demodulize.underscore
198
+ end
199
+
200
+ def navigation_label
201
+ plural_name.humanize
202
+ end
203
+
204
+ def find_record(id, query: nil, params: nil)
205
+ Avo::ExecutionContext.new(
206
+ target: find_record_method,
207
+ query: query || find_scope, # If no record is given we'll use the default
208
+ id: id,
209
+ params: params
210
+ ).handle
211
+ end
212
+
213
+ def search_query
214
+ search.dig(:query)
215
+ end
216
+
217
+ def fetch_search(key, record: nil)
218
+ # self.class.fetch_search
219
+ Avo::ExecutionContext.new(target: search[key], resource: self, record: record).handle
220
+ end
141
221
  end
142
222
 
143
223
  delegate :context, to: ::Avo::Current
224
+ delegate :name, to: :class
225
+ delegate :singular_name, to: :class
226
+ delegate :plural_name, to: :class
227
+ delegate :underscore_name, to: :class
228
+ delegate :underscore_name, to: :class
229
+ delegate :find_record, to: :class
230
+ delegate :model_key, to: :class
231
+
232
+ def initialize(record: nil, view: nil, user: nil, params: nil)
233
+ @view = view if view.present?
234
+ @user = user if user.present?
235
+ @params = params if params.present?
236
+
237
+ if record.present?
238
+ @record = record
239
+
240
+ hydrate_model_with_default_values if @view == :new
241
+ end
144
242
 
145
- def initialize
146
243
  detect_fields
147
244
 
148
245
  unless self.class.model_class.present?
@@ -194,7 +291,7 @@ module Avo
194
291
 
195
292
  # def get_action_arguments / def get_filter_arguments / def get_scope_arguments
196
293
  define_method "get_#{entity}_arguments" do |entity_class|
197
- send("get_#{plural_entity}").find { |entity| entity[:class] == entity_class.constantize }[:arguments]
294
+ send("get_#{plural_entity}").find { |entity| entity[:class].to_s == entity_class.to_s }[:arguments]
198
295
  end
199
296
  end
200
297
 
@@ -225,15 +322,14 @@ module Avo
225
322
  end
226
323
  end
227
324
 
325
+ # Returns the model class being used for this resource.
326
+ #
327
+ # We use the class method as a fallback but we pass it the record too so it can support the STI use cases
328
+ # where we figure out the model class from that record.
228
329
  def model_class
229
- # get the model class off of the static property
230
- return self.class.model_class if self.class.model_class.present?
231
-
232
- # get the model class off of the record
233
- return @record.base_class if @record.present?
330
+ record_class = @record&.class
234
331
 
235
- # generate a model class
236
- class_name.safe_constantize
332
+ self.class.model_class record_class: record_class
237
333
  end
238
334
 
239
335
  def record_title
@@ -251,46 +347,6 @@ module Avo
251
347
  end
252
348
  end
253
349
 
254
- def translation_key
255
- self.class.translation_key || "avo.resource_translations.#{class_name.underscore}"
256
- end
257
-
258
- def name
259
- return @name if @name.present?
260
-
261
- default = class_name.underscore.humanize
262
-
263
- if translation_key
264
- t(translation_key, count: 1, default: default).capitalize
265
- else
266
- default
267
- end
268
- end
269
-
270
- def singular_name
271
- name
272
- end
273
-
274
- def plural_name
275
- default = name.pluralize
276
-
277
- if translation_key
278
- t(translation_key, count: 2, default: default).capitalize
279
- else
280
- default
281
- end
282
- end
283
-
284
- def underscore_name
285
- return @name if @name.present?
286
-
287
- self.class.name.demodulize.underscore
288
- end
289
-
290
- def navigation_label
291
- plural_name.humanize
292
- end
293
-
294
350
  def available_view_types
295
351
  if self.class.view_types.present?
296
352
  return Array(
@@ -310,15 +366,15 @@ module Avo
310
366
  view_types
311
367
  end
312
368
 
313
- def attached_file_fields
369
+ def attachment_fields
314
370
  get_field_definitions.select do |field|
315
371
  [Avo::Fields::FileField, Avo::Fields::FilesField].include? field.class
316
372
  end
317
373
  end
318
374
 
319
- def fill_record(record, params, extra_params: [])
320
- # Map the received params to their actual fields
321
- fields_by_database_id = get_field_definitions
375
+ # Map the received params to their actual fields
376
+ def fields_by_database_id
377
+ get_field_definitions
322
378
  .reject do |field|
323
379
  field.computed
324
380
  end
@@ -326,7 +382,9 @@ module Avo
326
382
  [field.database_id.to_s, field]
327
383
  end
328
384
  .to_h
385
+ end
329
386
 
387
+ def fill_record(record, params, extra_params: [])
330
388
  # Write the field values
331
389
  params.each do |key, value|
332
390
  field = fields_by_database_id[key]
@@ -383,20 +441,18 @@ module Avo
383
441
  !field.computed
384
442
  end
385
443
  .map do |field|
386
- id = field.id
387
444
  value = field.value
388
445
 
389
446
  if field.type == "belongs_to"
390
- id = field.foreign_key.to_sym
391
447
 
392
448
  reflection = @record._reflections[@params[:via_relation]]
393
449
 
394
450
  if field.polymorphic_as.present? && field.types.map(&:to_s).include?(@params[:via_relation_class])
395
451
  # set the value to the actual record
396
- via_resource = Avo::App.resources.get_resource_by_model_class(@params[:via_relation_class])
452
+ via_resource = Avo.resource_manager.get_resource_by_model_class(@params[:via_relation_class])
397
453
  value = via_resource.find_record(@params[:via_record_id])
398
454
  elsif reflection.present? && reflection.foreign_key.present? && field.id.to_s == @params[:via_relation].to_s
399
- resource = Avo::App.resources.get_resource_by_model_class params[:via_relation_class]
455
+ resource = Avo.resource_manager.get_resource_by_model_class params[:via_relation_class]
400
456
  record = resource.find_record @params[:via_record_id], params: params
401
457
  id_param = reflection.options[:primary_key] || :id
402
458
 
@@ -404,28 +460,18 @@ module Avo
404
460
  end
405
461
  end
406
462
 
407
- [id, value]
463
+ [field, value]
408
464
  end
409
465
  .to_h
410
- .select do |id, value|
466
+ .select do |_, value|
411
467
  value.present?
412
468
  end
413
469
 
414
- default_values.each do |id, value|
415
- if @record.send(id).nil?
416
- @record.send("#{id}=", value)
417
- end
470
+ default_values.each do |field, value|
471
+ field.assign_value record: @record, value: value
418
472
  end
419
473
  end
420
474
 
421
- # This is used as the model class ID
422
- # We use this instead of the route_key to maintain compatibility with uncountable models
423
- # With uncountable models route key appends an _index suffix (Fish->fish_index)
424
- # Example: User->users, MediaItem->media_items, Fish->fish
425
- def model_key
426
- model_class.model_name.plural
427
- end
428
-
429
475
  def model_name
430
476
  model_class.model_name
431
477
  end
@@ -474,15 +520,6 @@ module Avo
474
520
  record.present? && record_id.present?
475
521
  end
476
522
 
477
- def find_record(id, query: nil, params: nil)
478
- Avo::ExecutionContext.new(
479
- target: self.class.find_record_method,
480
- query: query || self.class.find_scope,
481
- id: id,
482
- params: params
483
- ).handle
484
- end
485
-
486
523
  def id_attribute
487
524
  :id
488
525
  end
@@ -499,14 +536,6 @@ module Avo
499
536
  }
500
537
  end
501
538
 
502
- def search_query
503
- self.class.search.dig(:query)
504
- end
505
-
506
- def fetch_search(key)
507
- Avo::ExecutionContext.new(target: self.class.search[key], resource: self, record: record).handle
508
- end
509
-
510
539
  private
511
540
 
512
541
  def entity_loader(entity)
@@ -33,6 +33,10 @@ module Avo
33
33
  def is_sidebar?
34
34
  self.class.ancestors.include?(Avo::Resources::Items::Sidebar)
35
35
  end
36
+
37
+ def is_row?
38
+ self.class.respond_to?(:item_type) && self.class.item_type == :row
39
+ end
36
40
  end
37
41
  end
38
42
  end
@@ -18,6 +18,10 @@ module Avo
18
18
  deprecated_dsl_api __method__, "fields"
19
19
  end
20
20
 
21
+ def row(**args, &block)
22
+ deprecated_dsl_api __method__, "fields"
23
+ end
24
+
21
25
  def tabs(**args, &block)
22
26
  deprecated_dsl_api __method__, "fields"
23
27
  end
@@ -42,6 +46,7 @@ module Avo
42
46
 
43
47
  delegate :field, to: :items_holder
44
48
  delegate :panel, to: :items_holder
49
+ delegate :row, to: :items_holder
45
50
  delegate :tabs, to: :items_holder
46
51
  delegate :tool, to: :items_holder
47
52
  delegate :heading, to: :items_holder
@@ -96,6 +101,10 @@ module Avo
96
101
  if item.is_field?
97
102
  fields << item
98
103
  end
104
+
105
+ if item.is_row?
106
+ fields << extract_fields_from_items(tab)
107
+ end
99
108
  end
100
109
 
101
110
  fields.flatten
@@ -213,8 +222,10 @@ module Avo
213
222
  panelfull_items.grep(Avo::Resources::Items::TabGroup).each do |tab_group|
214
223
  tab_group.items.grep(Avo::Resources::Items::Tab).each do |tab|
215
224
  tab.items.grep(Avo::Resources::Items::Panel).each do |panel|
216
- panel.items.grep(Avo::Fields::BelongsToField).each do |field|
217
- field.target = :_top
225
+ set_target_to_top panel.items.grep(Avo::Fields::BelongsToField)
226
+
227
+ panel.items.grep(Avo::Resources::Items::Row).each do |row|
228
+ set_target_to_top row.items.grep(Avo::Fields::BelongsToField)
218
229
  end
219
230
  end
220
231
  end
@@ -250,9 +261,6 @@ module Avo
250
261
 
251
262
  item
252
263
  end
253
- .select do |item|
254
- item.visible_in_view?(view: view)
255
- end
256
264
  .select do |item|
257
265
  item.visible?
258
266
  end
@@ -264,7 +272,8 @@ module Avo
264
272
  end
265
273
  end
266
274
  .select do |item|
267
- if !item.is_heading? && view.in?([:edit, :update, :new, :create])
275
+ # On location field we can have field coordinates and setters with different names like latitude and longitude
276
+ if !item.is_a?(Avo::Fields::LocationField) && !item.is_heading? && view.in?([:edit, :update, :new, :create])
268
277
  if item.respond_to?(:id)
269
278
  item.resource.record.respond_to?("#{item.id}=")
270
279
  else
@@ -290,25 +299,21 @@ module Avo
290
299
  visible_items.blank?
291
300
  end
292
301
 
293
- def hydrate(**args)
294
- super(**args)
302
+ private
295
303
 
296
- items_holder.items.each do |item|
297
- item.hydrate(**args)
304
+ def set_target_to_top(fields)
305
+ fields.each do |field|
306
+ field.target = :_top
298
307
  end
299
-
300
- self
301
308
  end
302
309
 
303
- private
304
-
305
310
  def extract_fields_from_items(thing)
306
311
  fields = []
307
312
 
308
313
  thing.items.each do |item|
309
314
  if item.is_field?
310
315
  fields << item
311
- elsif item.is_panel?
316
+ elsif item.is_panel? || item.is_row?
312
317
  fields << extract_fields_from_items(item)
313
318
  end
314
319
  end
@@ -4,8 +4,6 @@ module Avo
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  class_methods do
7
- attr_reader :model_class
8
-
9
7
  # Cast the model class to a constantized version and memoize it like that
10
8
  def model_class=(value)
11
9
  @model_class = case value
data/lib/avo/current.rb CHANGED
@@ -1,9 +1,30 @@
1
1
  class Avo::Current < ActiveSupport::CurrentAttributes
2
+ # if Rails.env.development?
3
+ # singleton_class.attr_accessor :previous_attributes
4
+ # before_reset {
5
+ # puts ["before_reset->", self.previous_attributes].inspect
6
+ # if attributes.present?
7
+ # puts ["has attributes->"].inspect
8
+ # self.previous_attributes = attributes
9
+ # end
10
+ # puts ["before_reset->", self.previous_attributes].inspect
11
+ # }
12
+
13
+ # attr_accessor :previous_attributes
14
+
15
+ # def previous_attributes=(value)
16
+ # @previous_attributes = value
17
+ # end
18
+ # end
19
+
2
20
  attribute :app
3
21
  attribute :license
4
22
  attribute :context, :current_user, :view_context
23
+ attribute :error_manager
24
+ attribute :resource_manager
25
+ attribute :tool_manager
26
+ attribute :plugin_manager
5
27
 
6
- delegate :request, to: :view_context
7
28
  delegate :params, to: :request
8
29
 
9
30
  def request
@@ -72,7 +72,7 @@ module Avo
72
72
  end
73
73
 
74
74
  def field_class_from_symbol(symbol)
75
- matched_field = Avo::App.fields.all.find do |field|
75
+ matched_field = Avo.field_manager.all.find do |field|
76
76
  field[:name].to_s == symbol.to_s
77
77
  end
78
78
 
@@ -1,12 +1,23 @@
1
1
  module Avo
2
2
  class DynamicRouter
3
+ def self.eager_load(entity)
4
+ paths = Avo::ENTITIES.fetch entity
5
+
6
+ return unless paths.present?
7
+
8
+ pathname = Rails.root.join(*paths)
9
+ if pathname.directory?
10
+ Rails.autoloaders.main.eager_load_dir(pathname.to_s)
11
+ end
12
+ end
13
+
3
14
  def self.routes
4
15
  Avo::Engine.routes.draw do
5
16
  scope "resources", as: "resources" do
6
17
  # Check if the user chose to manually register the resource files.
7
18
  # If so, eager_load the resources dir.
8
19
  if Avo.configuration.resources.nil?
9
- Avo::App.eager_load(:resources) unless Rails.application.config.eager_load
20
+ Avo::DynamicRouter.eager_load(:resources) unless Rails.application.config.eager_load
10
21
  end
11
22
 
12
23
  Avo::Resources::ResourceManager.fetch_resources
data/lib/avo/engine.rb CHANGED
@@ -3,6 +3,8 @@ Gem.loaded_specs["avo"].dependencies.each do |d|
3
3
  case d.name
4
4
  when "activerecord"
5
5
  require "active_record/railtie"
6
+ when "activesupport"
7
+ require "active_support/railtie"
6
8
  when "actionview"
7
9
  require "action_view/railtie"
8
10
  when "activestorage"
@@ -23,7 +25,7 @@ module Avo
23
25
  ::Avo.asset_manager.reset
24
26
 
25
27
  # Boot Avo
26
- ::Avo::App.boot
28
+ ::Avo.boot
27
29
 
28
30
  # After deploy we want to make sure the license response is being cleared.
29
31
  # We need a fresh license response.
@@ -40,7 +42,7 @@ module Avo
40
42
  # Ensure we reboot the app when something changes
41
43
  config.to_prepare do
42
44
  # Boot Avo
43
- ::Avo::App.boot
45
+ ::Avo.boot
44
46
  end
45
47
 
46
48
  initializer "avo.autoload" do |app|
@@ -55,10 +57,6 @@ module Avo
55
57
  end
56
58
  end
57
59
 
58
- initializer "avo.init_fields" do |app|
59
- ::Avo::App.init_fields
60
- end
61
-
62
60
  initializer "avo.reloader" do |app|
63
61
  Avo::Reloader.new.tap do |reloader|
64
62
  reloader.execute
@@ -94,7 +92,6 @@ module Avo
94
92
 
95
93
  initializer "avo.locales" do |app|
96
94
  I18n.load_path += Dir[Avo::Engine.root.join("lib", "generators", "avo", "templates", "locales", "*.{rb,yml}")]
97
- I18n.load_path += Dir[Rails.root.join("config", "locales", "*.{rb,yml}")]
98
95
  end
99
96
  end
100
97
  end
@@ -65,6 +65,7 @@ module Avo
65
65
  @nullable = args[:nullable] || false
66
66
  @null_values = args[:null_values] || [nil, ""]
67
67
  @format_using = args[:format_using] || nil
68
+ @update_using = args[:update_using] || nil
68
69
  @placeholder = args[:placeholder]
69
70
  @autocomplete = args[:autocomplete] || nil
70
71
  @help = args[:help] || nil
@@ -170,8 +171,8 @@ module Avo
170
171
  target: format_using,
171
172
  value: final_value,
172
173
  record: record,
173
- resource: @resource,
174
- view: @view,
174
+ resource: resource,
175
+ view: view,
175
176
  field: self,
176
177
  include: self.class.included_modules
177
178
  ).handle
@@ -180,10 +181,22 @@ module Avo
180
181
  final_value
181
182
  end
182
183
 
184
+ # Fills the record with the received value on create and update actions.
183
185
  def fill_field(record, key, value, params)
184
186
  return record unless record.methods.include? key.to_sym
185
187
 
186
- record.send("#{key}=", value)
188
+ if @update_using.present?
189
+ value = Avo::ExecutionContext.new(
190
+ target: @update_using,
191
+ record: record,
192
+ key: key,
193
+ value: value,
194
+ resource: resource,
195
+ field: self
196
+ ).handle
197
+ end
198
+
199
+ record.public_send("#{key}=", value)
187
200
 
188
201
  record
189
202
  end
@@ -264,6 +277,15 @@ module Avo
264
277
  !is_disabled? && visible?
265
278
  end
266
279
 
280
+ # Used by Avo to fill the record with the default value on :new and :edit views
281
+ def assign_value(record:, value:)
282
+ id = type == "belongs_to" ? foreign_key : database_id
283
+
284
+ if record.send(id).nil?
285
+ record.send("#{id}=", value)
286
+ end
287
+ end
288
+
267
289
  private
268
290
 
269
291
  def model_or_class(model)