avo 3.0.0.beta1 → 3.0.0.pre1

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 (215) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -5
  3. data/Gemfile.lock +81 -92
  4. data/{public/avo-assets/avo.css → app/assets/builds/avo.base.css} +686 -728
  5. data/app/assets/builds/avo.base.js +93804 -0
  6. data/app/assets/builds/avo.base.js.map +7 -0
  7. data/app/assets/stylesheets/avo.base.css +2 -1
  8. data/app/assets/svgs/failed_to_load.svg +1 -0
  9. data/app/assets/svgs/grid-empty-state.svg +1 -0
  10. data/app/assets/svgs/table-empty-state.svg +1 -0
  11. data/app/assets/svgs/triangle-up.svg +1 -1
  12. data/app/components/avo/actions_component.html.erb +1 -1
  13. data/app/components/avo/actions_component.rb +16 -42
  14. data/app/components/avo/alert_component.html.erb +1 -1
  15. data/app/components/avo/base_component.rb +7 -7
  16. data/app/components/avo/field_wrapper_component.html.erb +4 -4
  17. data/app/components/avo/field_wrapper_component.rb +1 -1
  18. data/app/components/avo/fields/belongs_to_field/edit_component.html.erb +5 -5
  19. data/app/components/avo/fields/belongs_to_field/edit_component.rb +4 -8
  20. data/app/components/avo/fields/boolean_field/edit_component.html.erb +0 -1
  21. data/app/components/avo/fields/boolean_group_field/edit_component.html.erb +1 -1
  22. data/app/components/avo/fields/code_field/edit_component.html.erb +0 -1
  23. data/app/components/avo/fields/common/files_list_viewer_component.html.erb +5 -0
  24. data/app/components/avo/fields/common/files_list_viewer_component.rb +8 -0
  25. data/app/components/avo/fields/common/heading_component.html.erb +1 -1
  26. data/app/components/avo/fields/common/single_file_viewer_component.html.erb +56 -0
  27. data/app/components/avo/fields/common/single_file_viewer_component.rb +55 -0
  28. data/app/components/avo/fields/country_field/edit_component.html.erb +1 -3
  29. data/app/components/avo/fields/file_field/edit_component.html.erb +2 -4
  30. data/app/components/avo/fields/file_field/edit_component.rb +0 -1
  31. data/app/components/avo/fields/file_field/index_component.rb +2 -2
  32. data/app/components/avo/fields/file_field/show_component.html.erb +1 -1
  33. data/app/components/avo/fields/files_field/edit_component.html.erb +2 -4
  34. data/app/components/avo/fields/files_field/edit_component.rb +0 -1
  35. data/app/components/avo/fields/files_field/show_component.html.erb +1 -1
  36. data/app/components/avo/fields/has_many_field/show_component.html.erb +1 -1
  37. data/app/components/avo/fields/has_one_field/show_component.html.erb +4 -5
  38. data/app/components/avo/fields/has_one_field/show_component.rb +2 -6
  39. data/app/components/avo/fields/index_component.rb +0 -1
  40. data/app/components/avo/fields/markdown_field/edit_component.html.erb +3 -4
  41. data/app/components/avo/fields/markdown_field/show_component.html.erb +3 -3
  42. data/app/components/avo/fields/number_field/edit_component.html.erb +1 -3
  43. data/app/components/avo/fields/password_field/edit_component.html.erb +1 -3
  44. data/app/components/avo/fields/progress_bar_field/edit_component.html.erb +0 -1
  45. data/app/components/avo/fields/select_field/edit_component.html.erb +1 -2
  46. data/app/components/avo/fields/status_field/edit_component.html.erb +1 -1
  47. data/app/components/avo/fields/text_field/edit_component.html.erb +2 -3
  48. data/app/components/avo/fields/textarea_field/edit_component.html.erb +0 -1
  49. data/app/components/avo/fields/trix_field/edit_component.html.erb +1 -2
  50. data/app/components/avo/fields/trix_field/show_component.html.erb +1 -1
  51. data/app/components/avo/index/field_wrapper_component.html.erb +1 -1
  52. data/app/components/avo/index/field_wrapper_component.rb +12 -0
  53. data/app/components/avo/index/grid_item_component.html.erb +35 -9
  54. data/app/components/avo/index/grid_item_component.rb +10 -36
  55. data/app/components/avo/index/resource_controls_component.rb +11 -8
  56. data/app/components/avo/index/resource_table_component.rb +1 -1
  57. data/app/components/avo/item_switcher_component.html.erb +5 -10
  58. data/app/components/avo/item_switcher_component.rb +1 -2
  59. data/app/components/avo/modal_component.html.erb +1 -1
  60. data/app/components/avo/panel_component.html.erb +1 -6
  61. data/app/components/avo/panel_component.rb +0 -1
  62. data/app/components/avo/profile_item_component.html.erb +2 -17
  63. data/app/components/avo/profile_item_component.rb +1 -13
  64. data/app/components/avo/referrer_params_component.html.erb +0 -2
  65. data/app/components/avo/resource_component.rb +6 -69
  66. data/app/components/avo/resource_sidebar_component.rb +1 -1
  67. data/app/components/avo/sidebar/link_component.html.erb +0 -2
  68. data/app/components/avo/sidebar/link_component.rb +3 -5
  69. data/app/components/avo/sidebar_component.html.erb +3 -3
  70. data/app/components/avo/sidebar_component.rb +4 -4
  71. data/app/components/avo/sidebar_profile_component.html.erb +27 -27
  72. data/app/components/avo/views/resource_edit_component.html.erb +5 -5
  73. data/app/components/avo/views/resource_edit_component.rb +1 -1
  74. data/app/components/avo/views/resource_index_component.html.erb +10 -19
  75. data/app/components/avo/views/resource_index_component.rb +16 -22
  76. data/app/components/avo/views/resource_show_component.html.erb +4 -4
  77. data/app/controllers/avo/actions_controller.rb +20 -20
  78. data/app/controllers/avo/application_controller.rb +67 -90
  79. data/app/controllers/avo/associations_controller.rb +7 -5
  80. data/app/controllers/avo/attachments_controller.rb +7 -22
  81. data/app/controllers/avo/base_controller.rb +35 -47
  82. data/app/controllers/avo/home_controller.rb +1 -1
  83. data/app/controllers/avo/search_controller.rb +16 -20
  84. data/app/controllers/concerns/avo/initializes_avo.rb +8 -3
  85. data/app/helpers/avo/application_helper.rb +6 -13
  86. data/app/javascript/js/application.js +0 -2
  87. data/app/javascript/js/controllers/fields/{easy_mde_controller.js → simple_mde_controller.js} +3 -4
  88. data/app/javascript/js/controllers/search_controller.js +1 -3
  89. data/app/javascript/js/controllers.js +2 -2
  90. data/app/views/avo/actions/show.html.erb +3 -5
  91. data/app/views/avo/associations/new.html.erb +3 -3
  92. data/app/views/avo/debug/status.html.erb +5 -6
  93. data/app/views/avo/home/index.html.erb +1 -1
  94. data/app/views/avo/partials/_custom_tools_alert.html.erb +2 -2
  95. data/app/views/avo/partials/_footer.html.erb +1 -1
  96. data/app/views/avo/partials/_javascript.html.erb +1 -1
  97. data/app/views/avo/partials/_navbar.html.erb +1 -1
  98. data/app/views/avo/partials/_table_header.html.erb +8 -0
  99. data/app/views/avo/partials/_view_toggle_button.html.erb +0 -9
  100. data/app/views/avo/private/design.html.erb +2 -2
  101. data/app/views/layouts/avo/application.html.erb +3 -2
  102. data/avo.gemspec +1 -2
  103. data/config/initializers/pagy.rb +10 -12
  104. data/config/routes.rb +5 -5
  105. data/db/factories.rb +0 -17
  106. data/lib/avo/app.rb +165 -0
  107. data/lib/avo/base_action.rb +18 -31
  108. data/lib/avo/base_resource.rb +213 -238
  109. data/lib/avo/concerns/breadcrumbs.rb +2 -2
  110. data/lib/avo/concerns/can_replace_items.rb +7 -3
  111. data/lib/avo/concerns/filters_session_handler.rb +4 -5
  112. data/lib/avo/concerns/has_item_type.rb +0 -4
  113. data/lib/avo/concerns/has_items.rb +115 -93
  114. data/lib/avo/concerns/is_visible.rb +1 -1
  115. data/lib/avo/concerns/model_class_constantized.rb +2 -0
  116. data/lib/avo/configuration.rb +8 -9
  117. data/lib/avo/current.rb +1 -35
  118. data/lib/avo/dsl/field_parser.rb +1 -1
  119. data/lib/avo/dynamic_router.rb +2 -13
  120. data/lib/avo/engine.rb +13 -11
  121. data/lib/avo/execution_context.rb +2 -4
  122. data/lib/avo/fields/base_field.rb +14 -51
  123. data/lib/avo/fields/belongs_to_field.rb +13 -20
  124. data/lib/avo/fields/concerns/is_searchable.rb +1 -1
  125. data/lib/avo/fields/concerns/use_resource.rb +1 -1
  126. data/lib/avo/fields/date_field.rb +3 -16
  127. data/lib/avo/fields/field_manager.rb +3 -13
  128. data/lib/avo/fields/file_field.rb +0 -2
  129. data/lib/avo/fields/files_field.rb +0 -6
  130. data/lib/avo/fields/has_base_field.rb +5 -5
  131. data/lib/avo/fields/has_one_field.rb +1 -2
  132. data/lib/avo/fields/id_field.rb +1 -2
  133. data/lib/avo/filters/base_filter.rb +0 -9
  134. data/lib/avo/grid_collector.rb +40 -0
  135. data/lib/avo/html/builder.rb +1 -3
  136. data/lib/avo/licensing/h_q.rb +6 -11
  137. data/lib/avo/licensing/license.rb +1 -1
  138. data/lib/avo/licensing/license_manager.rb +1 -1
  139. data/lib/avo/licensing/{nil_license.rb → null_license.rb} +1 -1
  140. data/lib/avo/loaders/fields_loader.rb +1 -7
  141. data/lib/avo/plugin.rb +0 -10
  142. data/lib/avo/plugin_manager.rb +4 -2
  143. data/lib/avo/reloader.rb +1 -1
  144. data/lib/avo/resources/controls/actions_list.rb +1 -2
  145. data/lib/avo/resources/controls/create_button.rb +1 -1
  146. data/lib/avo/resources/controls/delete_button.rb +1 -1
  147. data/lib/avo/resources/controls/detach_button.rb +1 -1
  148. data/lib/avo/resources/controls/edit_button.rb +1 -1
  149. data/lib/avo/resources/controls/show_button.rb +1 -1
  150. data/lib/avo/resources/items/holder.rb +5 -13
  151. data/lib/avo/resources/items/item_group.rb +0 -1
  152. data/lib/avo/resources/resource_manager.rb +18 -11
  153. data/lib/avo/services/debug_service.rb +5 -6
  154. data/lib/avo/services/telemetry_service.rb +2 -3
  155. data/lib/avo/version.rb +1 -1
  156. data/lib/avo.rb +25 -109
  157. data/lib/generators/avo/action_generator.rb +8 -8
  158. data/lib/generators/avo/card/chartkick_generator.rb +18 -0
  159. data/lib/generators/avo/card/metric_generator.rb +18 -0
  160. data/lib/generators/avo/card/partial_generator.rb +19 -0
  161. data/lib/generators/avo/eject_generator.rb +0 -1
  162. data/lib/generators/avo/filter_generator.rb +8 -8
  163. data/lib/generators/avo/install_generator.rb +1 -11
  164. data/lib/generators/avo/resource_generator.rb +4 -22
  165. data/lib/generators/avo/tailwindcss/install_generator.rb +1 -4
  166. data/lib/generators/avo/templates/action.tt +5 -7
  167. data/lib/generators/avo/templates/cards/chartkick_card.tt +1 -1
  168. data/lib/generators/avo/templates/cards/chartkick_card_sample.tt +1 -1
  169. data/lib/generators/avo/templates/cards/metric_card.tt +1 -1
  170. data/lib/generators/avo/templates/cards/metric_card_sample.tt +1 -1
  171. data/lib/generators/avo/templates/cards/partial_card.tt +1 -1
  172. data/lib/generators/avo/templates/cards/partial_card_sample.tt +1 -1
  173. data/lib/generators/avo/templates/dashboards/dashboard.tt +3 -5
  174. data/lib/generators/avo/templates/initializer/avo.tt +2 -4
  175. data/lib/generators/avo/templates/resource/resource.tt +6 -6
  176. data/lib/generators/avo/templates/scope.tt +1 -1
  177. data/lib/generators/avo/templates/standalone_action.tt +8 -0
  178. data/lib/generators/avo/templates/tailwindcss/Procfile.dev +1 -1
  179. data/lib/tasks/avo_tasks.rake +0 -5
  180. metadata +19 -56
  181. data/app/assets/svgs/map-empty-state.svg +0 -35
  182. data/app/assets/svgs/map-view-type.svg +0 -3
  183. data/app/components/avo/fields/area_field/edit_component.html.erb +0 -7
  184. data/app/components/avo/fields/area_field/edit_component.rb +0 -4
  185. data/app/components/avo/fields/area_field/show_component.html.erb +0 -8
  186. data/app/components/avo/fields/area_field/show_component.rb +0 -4
  187. data/app/components/avo/fields/common/files/controls_component.html.erb +0 -29
  188. data/app/components/avo/fields/common/files/controls_component.rb +0 -19
  189. data/app/components/avo/fields/common/files/list_viewer_component.html.erb +0 -20
  190. data/app/components/avo/fields/common/files/list_viewer_component.rb +0 -41
  191. data/app/components/avo/fields/common/files/view_type/grid_component.html.erb +0 -27
  192. data/app/components/avo/fields/common/files/view_type/grid_component.rb +0 -51
  193. data/app/components/avo/fields/common/files/view_type/list_component.html.erb +0 -22
  194. data/app/components/avo/fields/common/files/view_type/list_component.rb +0 -15
  195. data/app/components/avo/fields/location_field/edit_component.html.erb +0 -22
  196. data/app/components/avo/fields/location_field/edit_component.rb +0 -4
  197. data/app/components/avo/fields/location_field/show_component.html.erb +0 -7
  198. data/app/components/avo/fields/location_field/show_component.rb +0 -4
  199. data/app/components/avo/index/resource_map_component.html.erb +0 -16
  200. data/app/components/avo/index/resource_map_component.rb +0 -109
  201. data/app/components/avo/row_component.html.erb +0 -3
  202. data/app/components/avo/row_component.rb +0 -12
  203. data/app/views/avo/attachments/destroy.turbo_stream.erb +0 -7
  204. data/app/views/avo/partials/_profile_menu_extra.html.erb +0 -2
  205. data/lib/avo/concerns/has_description.rb +0 -23
  206. data/lib/avo/fields/area_field.rb +0 -39
  207. data/lib/avo/fields/concerns/file_authorization.rb +0 -31
  208. data/lib/avo/fields/location_field.rb +0 -86
  209. data/lib/avo/resources/items/row.rb +0 -54
  210. data/lib/generators/avo/card_generator.rb +0 -27
  211. data/public/avo-assets/avo.base.css +0 -10542
  212. data/public/avo-assets/avo.base.js +0 -949
  213. data/public/avo-assets/avo.base.js.map +0 -7
  214. data/public/avo-assets/avo.js +0 -513
  215. data/public/avo-assets/avo.js.map +0 -7
@@ -8,63 +8,54 @@ module Avo
8
8
  include Avo::Concerns::HasControls
9
9
  include Avo::Concerns::HasStimulusControllers
10
10
  include Avo::Concerns::ModelClassConstantized
11
- include Avo::Concerns::HasDescription
12
11
 
13
- # Avo::Current methods
14
12
  delegate :context, to: Avo::Current
15
- def curent_user
16
- Avo::Current.user
17
- end
13
+ delegate :current_user, to: Avo::Current
18
14
  delegate :params, to: Avo::Current
19
15
  delegate :request, to: Avo::Current
20
16
  delegate :view_context, to: Avo::Current
21
-
22
- # view_context methods
23
17
  delegate :simple_format, :content_tag, to: :view_context
24
18
  delegate :main_app, to: :view_context
25
19
  delegate :avo, to: :view_context
26
20
  delegate :resource_path, to: :view_context
27
21
  delegate :resources_path, to: :view_context
28
-
29
- # I18n methods
30
22
  delegate :t, to: ::I18n
31
23
 
32
- # class methods
33
- delegate :class_name, to: :class
34
- delegate :route_key, to: :class
35
- delegate :singular_route_key, to: :class
36
-
37
24
  attr_accessor :view
38
25
  attr_accessor :reflection
39
26
  attr_accessor :user
27
+ attr_accessor :params
40
28
  attr_accessor :record
41
29
 
42
30
  class_attribute :id, default: :id
43
- class_attribute :title
44
- class_attribute :search, default: {}
31
+ class_attribute :title, default: :id
32
+ class_attribute :description, default: :id
33
+ class_attribute :search_query, default: nil
34
+ class_attribute :search_query_help, default: ""
45
35
  class_attribute :includes, default: []
46
36
  class_attribute :authorization_policy
47
37
  class_attribute :translation_key
48
38
  class_attribute :default_view_type, default: :table
49
39
  class_attribute :devise_password_optional, default: false
40
+ class_attribute :actions_loader
50
41
  class_attribute :scopes_loader
51
42
  class_attribute :filters_loader
52
- class_attribute :view_types
53
- class_attribute :grid_view
43
+ class_attribute :grid_loader
54
44
  class_attribute :visible_on_sidebar, default: true
55
- class_attribute :index_query, default: -> {
56
- query
57
- }
58
- class_attribute :find_record_method, default: -> {
59
- query.find id
45
+ class_attribute :unscoped_queries_on_index, default: false
46
+ class_attribute :resolve_index_query
47
+ class_attribute :resolve_find_scope
48
+ # TODO: refactor this into a Host without args
49
+ class_attribute :find_record_method, default: ->(model_class:, id:, params:) {
50
+ model_class.find id
60
51
  }
52
+ class_attribute :hide_from_global_search, default: false
61
53
  class_attribute :after_create_path, default: :show
62
54
  class_attribute :after_update_path, default: :show
63
55
  class_attribute :record_selector, default: true
64
56
  class_attribute :keep_filters_panel_open, default: false
65
57
  class_attribute :extra_params
66
58
  class_attribute :link_to_child_resource, default: false
67
- class_attribute :map_view
68
59
 
69
60
  # EXTRACT:
70
61
  class_attribute :ordering
@@ -73,37 +64,65 @@ module Avo
73
64
  delegate :t, to: ::I18n
74
65
  delegate :context, to: ::Avo::Current
75
66
 
67
+ def grid(&block)
68
+ grid_collector = GridCollector.new
69
+ grid_collector.instance_eval(&block)
70
+
71
+ self.grid_loader = grid_collector
72
+ end
73
+
76
74
  def action(action_class, arguments: {})
77
- deprecated_dsl_api __method__, "actions"
75
+ self.actions_loader ||= Avo::Loaders::Loader.new
76
+
77
+ action = {class: action_class, arguments: arguments}
78
+ self.actions_loader.use action
78
79
  end
79
80
 
80
81
  def filter(filter_class, arguments: {})
81
- deprecated_dsl_api __method__, "filters"
82
+ self.filters_loader ||= Avo::Loaders::Loader.new
83
+
84
+ filter = { class: filter_class , arguments: arguments }
85
+ self.filters_loader.use filter
82
86
  end
83
87
 
84
- def scope(scope_class)
85
- deprecated_dsl_api __method__, "scopes"
88
+ # This is the search_query scope
89
+ # This should be removed and passed to the search block
90
+ def scope
91
+ query_scope
92
+ end
93
+
94
+ def scopes(*args)
95
+ self.scopes_loader ||= Avo::Loaders::Loader.new
96
+
97
+ args.each do |scope_class|
98
+ self.scopes_loader.use scope_class
99
+ end
86
100
  end
87
101
 
88
102
  # This resolves the scope when doing "where" queries (not find queries)
89
- #
90
- # It's used to apply the authorization feature.
91
103
  def query_scope
92
- authorization.apply_policy Avo::ExecutionContext.new(
93
- target: index_query,
94
- query: model_class
95
- ).handle
104
+ final_scope = if resolve_index_query.present?
105
+ Avo::ExecutionContext.new(target: resolve_index_query, model_class: model_class).handle
106
+ else
107
+ model_class
108
+ end
109
+
110
+ authorization.apply_policy final_scope
96
111
  end
97
112
 
98
113
  # This resolves the scope when finding records (not "where" queries)
99
- #
100
- # It's used to apply the authorization feature.
101
114
  def find_scope
102
- authorization.apply_policy model_class
115
+ final_scope = if resolve_find_scope.present?
116
+ Avo::ExecutionContext.new(target: resolve_find_scope, model_class: model_class).handle
117
+ else
118
+ model_class
119
+ end
120
+
121
+ authorization.apply_policy final_scope
103
122
  end
104
123
 
105
124
  def authorization
106
- Avo::Services::AuthorizationService.new Avo::Current.user, model_class, policy_class: authorization_policy
125
+ Avo::Services::AuthorizationService.new Avo::Current.current_user, model_class, policy_class: authorization_policy
107
126
  end
108
127
 
109
128
  def get_record_associations(record)
@@ -132,106 +151,19 @@ module Avo
132
151
  m.to_s == model_name.to_s
133
152
  end
134
153
  end
154
+ end
135
155
 
136
- # Returns the model class being used for this resource.
137
- #
138
- # The Resource instance has a model_class method too so it can support the STI use cases
139
- # where we figure out the model class from the record
140
- def model_class(record_class: nil)
141
- # get the model class off of the static property
142
- return @model_class if @model_class.present?
143
-
144
- # get the model class off of the record for STI models
145
- return record_class if record_class.present?
146
-
147
- # generate a model class
148
- class_name.safe_constantize
149
- end
150
-
151
- # This is used as the model class ID
152
- # We use this instead of the route_key to maintain compatibility with uncountable models
153
- # With uncountable models route key appends an _index suffix (Fish->fish_index)
154
- # Example: User->users, MediaItem->media_items, Fish->fish
155
- def model_key
156
- model_class.model_name.plural
157
- end
158
-
159
- def class_name
160
- to_s.demodulize
161
- end
162
-
163
- def route_key
164
- class_name.underscore.pluralize
165
- end
166
-
167
- def singular_route_key
168
- route_key.singularize
169
- end
170
-
171
- def translation_key
172
- @translation_key || "avo.resource_translations.#{class_name.underscore}"
173
- end
174
-
175
- def name
176
- default = class_name.underscore.humanize
177
-
178
- if translation_key
179
- t(translation_key, count: 1, default: default).humanize
180
- else
181
- default
182
- end
183
- end
184
- alias_method :singular_name, :name
185
-
186
- def plural_name
187
- default = name.pluralize
156
+ delegate :context, to: ::Avo::Current
188
157
 
189
- if translation_key
190
- t(translation_key, count: 2, default: default).humanize
191
- else
192
- default
158
+ def initialize
159
+ unless self.class.model_class.present?
160
+ if model_class.present? && model_class.respond_to?(:base_class)
161
+ self.class.model_class = model_class.base_class
193
162
  end
194
163
  end
195
-
196
- def underscore_name
197
- return @name if @name.present?
198
-
199
- name.demodulize.underscore
200
- end
201
-
202
- def navigation_label
203
- plural_name.humanize
204
- end
205
-
206
- def find_record(id, query: nil, params: nil)
207
- Avo::ExecutionContext.new(
208
- target: find_record_method,
209
- query: query || find_scope, # If no record is given we'll use the default
210
- id: id,
211
- params: params
212
- ).handle
213
- end
214
-
215
- def search_query
216
- search.dig(:query)
217
- end
218
-
219
- def fetch_search(key, record: nil)
220
- # self.class.fetch_search
221
- Avo::ExecutionContext.new(target: search[key], resource: self, record: record).handle
222
- end
223
164
  end
224
165
 
225
- delegate :context, to: ::Avo::Current
226
- delegate :name, to: :class
227
- delegate :singular_name, to: :class
228
- delegate :plural_name, to: :class
229
- delegate :underscore_name, to: :class
230
- delegate :underscore_name, to: :class
231
- delegate :find_record, to: :class
232
- delegate :model_key, to: :class
233
-
234
- def initialize(record: nil, view: nil, user: nil, params: nil)
166
+ def hydrate(record: nil, view: nil, user: nil, params: nil)
235
167
  @view = view if view.present?
236
168
  @user = user if user.present?
237
169
  @params = params if params.present?
@@ -242,73 +174,43 @@ module Avo
242
174
  hydrate_model_with_default_values if @view == :new
243
175
  end
244
176
 
245
- detect_fields
246
-
247
- unless self.class.model_class.present?
248
- if model_class.present? && model_class.respond_to?(:base_class)
249
- self.class.model_class = model_class.base_class
250
- end
251
- end
252
- end
253
-
254
- def detect_fields
255
- self.items_holder = Avo::Resources::Items::Holder.new
256
-
257
- # Used in testing to replace items
258
- if temporary_items.present?
259
- instance_eval(&temporary_items)
260
- else
261
- fields
262
- end
263
-
264
177
  self
265
178
  end
266
179
 
267
- def fields
268
- # blank fields method
269
- end
180
+ def get_grid_fields
181
+ return if self.class.grid_loader.blank?
270
182
 
271
- [:action, :filter, :scope].each do |entity|
272
- plural_entity = entity.to_s.pluralize
183
+ self.class.grid_loader.hydrate(record: @record, view: @view, resource: self)
184
+ end
273
185
 
274
- # def actions / def filters / def scopes
275
- define_method plural_entity do
276
- # blank entity method
277
- end
186
+ def get_filters
187
+ return [] if self.class.filters_loader.blank?
278
188
 
279
- # def action / def filter / def scope
280
- define_method entity do |entity_class, arguments: {}|
281
- entity_loader(entity).use({class: entity_class, arguments: arguments})
282
- end
189
+ self.class.filters_loader.bag
190
+ end
283
191
 
284
- # def get_actions / def get_filters / def get_scopes
285
- define_method "get_#{plural_entity}" do
286
- return entity_loader(entity).bag if entity_loader(entity).present?
192
+ def get_filter_arguments(filter_class)
193
+ filter = get_filters.find { |filter| filter[:class] == filter_class.constantize }
287
194
 
288
- instance_variable_set("@#{plural_entity}_loader", Avo::Loaders::Loader.new)
289
- send plural_entity
195
+ filter[:arguments]
196
+ end
290
197
 
291
- entity_loader(entity).bag
292
- end
198
+ def get_actions
199
+ return [] if self.class.actions_loader.blank?
293
200
 
294
- # def get_action_arguments / def get_filter_arguments / def get_scope_arguments
295
- define_method "get_#{entity}_arguments" do |entity_class|
296
- send("get_#{plural_entity}").find { |entity| entity[:class].to_s == entity_class.to_s }[:arguments]
297
- end
201
+ self.class.actions_loader.bag
298
202
  end
299
203
 
300
- def hydrate(record: nil, view: nil, user: nil, params: nil)
301
- @view = view if view.present?
302
- @user = user if user.present?
303
- @params = params if params.present?
204
+ def get_action_arguments(action_class)
205
+ action = get_actions.find { |action| action[:class].to_s == action_class.to_s }
304
206
 
305
- if record.present?
306
- @record = record
207
+ action[:arguments]
208
+ end
307
209
 
308
- hydrate_model_with_default_values if @view == :new
309
- end
210
+ def get_scopes
211
+ return [] if self.class.scopes_loader.blank?
310
212
 
311
- self
213
+ self.class.scopes_loader.bag
312
214
  end
313
215
 
314
216
  def default_panel_name
@@ -320,63 +222,103 @@ module Avo
320
222
  when :edit
321
223
  record_title
322
224
  when :new
323
- t("avo.create_new_item", item: name.humanize(capitalize: false)).upcase_first
225
+ t("avo.create_new_item", item: name.downcase).upcase_first
324
226
  end
325
227
  end
326
228
 
327
- # Returns the model class being used for this resource.
328
- #
329
- # We use the class method as a fallback but we pass it the record too so it can support the STI use cases
330
- # where we figure out the model class from that record.
229
+ def class_name
230
+ self.class.name.demodulize
231
+ end
232
+
331
233
  def model_class
332
- record_class = @record&.class
234
+ # get the model class off of the static property
235
+ return self.class.model_class if self.class.model_class.present?
333
236
 
334
- self.class.model_class record_class: record_class
237
+ # get the model class off of the record
238
+ return @record.base_class if @record.present?
239
+
240
+ # generate a model class
241
+ class_name.safe_constantize
335
242
  end
336
243
 
337
244
  def record_title
338
245
  return name if @record.nil?
339
246
 
340
- # Get the title from the record if title is not set, try to get the name, title or label, or fallback to the id
341
- return @record.try(:name) || @record.try(:title) || @record.try(:label) || @record.id if title.nil?
247
+ the_title = @record.send title
248
+ return the_title if the_title.present?
249
+
250
+ @record.id
251
+ rescue
252
+ name
253
+ end
342
254
 
343
- # If the title is a symbol, get the value from the record else execute the block/string
344
- case title
345
- when Symbol
346
- @record.send title
347
- when Proc
348
- Avo::ExecutionContext.new(target: title, resource: self, record: @record).handle
255
+ def resource_description
256
+ return instance_exec(&self.class.description) if self.class.description.respond_to? :call
257
+
258
+ # Show the description only on the resource index view.
259
+ # If the user wants to conditionally it on all pages, they should use a block.
260
+ if view == :index
261
+ return self.class.description if self.class.description.is_a? String
349
262
  end
350
263
  end
351
264
 
352
- def available_view_types
353
- if self.class.view_types.present?
354
- return Array(
355
- Avo::ExecutionContext.new(
356
- target: self.class.view_types,
357
- resource: self,
358
- record: record
359
- ).handle
360
- )
265
+ def translation_key
266
+ self.class.translation_key || "avo.resource_translations.#{class_name.underscore}"
267
+ end
268
+
269
+ def name
270
+ default = class_name.underscore.humanize
271
+
272
+ return @name if @name.present?
273
+
274
+ if translation_key
275
+ t(translation_key, count: 1, default: default).capitalize
276
+ else
277
+ default
278
+ end
279
+ end
280
+
281
+ def singular_name
282
+ name
283
+ end
284
+
285
+ def plural_name
286
+ default = name.pluralize
287
+
288
+ if translation_key
289
+ t(translation_key, count: 2, default: default).capitalize
290
+ else
291
+ default
361
292
  end
293
+ end
294
+
295
+ def underscore_name
296
+ return @name if @name.present?
297
+
298
+ self.class.name.demodulize.underscore
299
+ end
362
300
 
301
+ def navigation_label
302
+ plural_name.humanize
303
+ end
304
+
305
+ def available_view_types
363
306
  view_types = [:table]
364
307
 
365
- view_types << :grid if self.class.grid_view.present?
366
- view_types << :map if map_view.present?
308
+ view_types << :grid if get_grid_fields.present?
367
309
 
368
310
  view_types
369
311
  end
370
312
 
371
- def attachment_fields
313
+ def attached_file_fields
372
314
  get_field_definitions.select do |field|
373
315
  [Avo::Fields::FileField, Avo::Fields::FilesField].include? field.class
374
316
  end
375
317
  end
376
318
 
377
- # Map the received params to their actual fields
378
- def fields_by_database_id
379
- get_field_definitions
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
380
322
  .reject do |field|
381
323
  field.computed
382
324
  end
@@ -384,9 +326,7 @@ module Avo
384
326
  [field.database_id.to_s, field]
385
327
  end
386
328
  .to_h
387
- end
388
329
 
389
- def fill_record(record, params, extra_params: [])
390
330
  # Write the field values
391
331
  params.each do |key, value|
392
332
  field = fields_by_database_id[key]
@@ -406,7 +346,7 @@ module Avo
406
346
  end
407
347
 
408
348
  def authorization(user: nil)
409
- current_user = user || Avo::Current.user
349
+ current_user = user || Avo::Current.current_user
410
350
  Avo::Services::AuthorizationService.new(current_user, record || model_class, policy_class: authorization_policy)
411
351
  end
412
352
 
@@ -443,18 +383,19 @@ module Avo
443
383
  !field.computed
444
384
  end
445
385
  .map do |field|
386
+ id = field.id
446
387
  value = field.value
447
388
 
448
389
  if field.type == "belongs_to"
390
+ id = field.foreign_key.to_sym
449
391
 
450
392
  reflection = @record._reflections[@params[:via_relation]]
451
393
 
452
394
  if field.polymorphic_as.present? && field.types.map(&:to_s).include?(@params[:via_relation_class])
453
395
  # set the value to the actual record
454
- via_resource = Avo.resource_manager.get_resource_by_model_class(@params[:via_relation_class])
455
- value = via_resource.find_record(@params[:via_record_id])
396
+ value = @params[:via_relation_class].safe_constantize.find(@params[:via_record_id])
456
397
  elsif reflection.present? && reflection.foreign_key.present? && field.id.to_s == @params[:via_relation].to_s
457
- resource = Avo.resource_manager.get_resource_by_model_class params[:via_relation_class]
398
+ resource = Avo::App.resources.get_resource_by_model_class params[:via_relation_class]
458
399
  record = resource.find_record @params[:via_record_id], params: params
459
400
  id_param = reflection.options[:primary_key] || :id
460
401
 
@@ -462,18 +403,36 @@ module Avo
462
403
  end
463
404
  end
464
405
 
465
- [field, value]
406
+ [id, value]
466
407
  end
467
408
  .to_h
468
- .select do |_, value|
409
+ .select do |id, value|
469
410
  value.present?
470
411
  end
471
412
 
472
- default_values.each do |field, value|
473
- field.assign_value record: @record, value: value
413
+ default_values.each do |id, value|
414
+ if @record.send(id).nil?
415
+ @record.send("#{id}=", value)
416
+ end
474
417
  end
475
418
  end
476
419
 
420
+ def route_key
421
+ class_name.underscore.pluralize
422
+ end
423
+
424
+ def singular_route_key
425
+ route_key.singularize
426
+ end
427
+
428
+ # This is used as the model class ID
429
+ # We use this instead of the route_key to maintain compatibility with uncountable models
430
+ # With uncountable models route key appends an _index suffix (Fish->fish_index)
431
+ # Example: User->users, MediaItem->media_items, Fish->fish
432
+ def model_key
433
+ model_class.model_name.plural
434
+ end
435
+
477
436
  def model_name
478
437
  model_class.model_name
479
438
  end
@@ -490,6 +449,18 @@ module Avo
490
449
  resources_path(resource: self)
491
450
  end
492
451
 
452
+ def label_field
453
+ get_field_definitions.find do |field|
454
+ field.as_label.present?
455
+ end
456
+ rescue
457
+ nil
458
+ end
459
+
460
+ def label
461
+ label_field&.value || record_title
462
+ end
463
+
493
464
  def avatar_field
494
465
  get_field_definitions.find do |field|
495
466
  field.as_avatar.present?
@@ -514,6 +485,18 @@ module Avo
514
485
  nil
515
486
  end
516
487
 
488
+ def description_field
489
+ get_field_definitions.find do |field|
490
+ field.as_description.present?
491
+ end
492
+ rescue
493
+ nil
494
+ end
495
+
496
+ def description
497
+ description_field&.value
498
+ end
499
+
517
500
  def form_scope
518
501
  model_class.base_class.to_s.underscore.downcase
519
502
  end
@@ -522,6 +505,12 @@ module Avo
522
505
  record.present? && record_id.present?
523
506
  end
524
507
 
508
+ def find_record(id, query: nil, params: nil)
509
+ query ||= self.class.find_scope
510
+
511
+ self.class.find_record_method.call(model_class: query, id: id, params: params)
512
+ end
513
+
525
514
  def id_attribute
526
515
  :id
527
516
  end
@@ -529,19 +518,5 @@ module Avo
529
518
  def record_id
530
519
  record.send(id_attribute)
531
520
  end
532
-
533
- def description_attributes
534
- {
535
- view: view,
536
- resource: self,
537
- record: record
538
- }
539
- end
540
-
541
- private
542
-
543
- def entity_loader(entity)
544
- instance_variable_get("@#{entity.to_s.pluralize}_loader")
545
- end
546
521
  end
547
522
  end
@@ -50,7 +50,7 @@ module Avo
50
50
  when Symbol
51
51
  context.send(name)
52
52
  when Proc
53
- Avo::ExecutionContext.new(target: name, context: context).handle
53
+ name.call(context)
54
54
  else
55
55
  name.to_s
56
56
  end
@@ -61,7 +61,7 @@ module Avo
61
61
  when Symbol
62
62
  context.send(path)
63
63
  when Proc
64
- Avo::ExecutionContext.new(target: path, context: context).handle
64
+ path.call(context)
65
65
  else
66
66
  context.url_for(path)
67
67
  end
@@ -4,17 +4,21 @@ module Avo
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class_attribute :temporary_items
7
+ class_attribute :backup_items_holder
8
8
  end
9
9
 
10
10
  class_methods do
11
11
  def with_temporary_items(&block)
12
12
  # back-up the previous items
13
- self.temporary_items = block
13
+ self.backup_items_holder = items_holder
14
+
15
+ self.items_holder = Avo::Resources::Items::Holder.new
16
+
17
+ instance_eval(&block)
14
18
  end
15
19
 
16
20
  def restore_items_from_backup
17
- self.temporary_items = nil
21
+ self.items_holder = backup_items_holder if backup_items_holder.present?
18
22
  end
19
23
 
20
24
  def with_new_items(&block)