avo 0.5.0.beta2 → 0.5.0.beta7

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 (136) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -4
  3. data/Gemfile.lock +1 -5
  4. data/app/components/avo/index/grid_item_component.html.erb +8 -8
  5. data/app/components/avo/index/grid_item_component.rb +6 -12
  6. data/app/components/avo/index/resource_grid_component.html.erb +1 -1
  7. data/app/components/avo/index/resource_table_component.html.erb +2 -2
  8. data/app/components/avo/index/table_row_component.html.erb +1 -1
  9. data/app/controllers/avo/actions_controller.rb +2 -5
  10. data/app/controllers/avo/application_controller.rb +12 -4
  11. data/app/controllers/avo/base_controller.rb +1 -9
  12. data/app/controllers/avo/relations_controller.rb +1 -1
  13. data/app/controllers/avo/search_controller.rb +1 -1
  14. data/app/frontend/packs/application.js +1 -1
  15. data/app/views/avo/base/_actions.html.erb +0 -6
  16. data/app/views/avo/base/_boolean_filter.html.erb +1 -1
  17. data/app/views/avo/base/_filters.html.erb +3 -11
  18. data/app/views/avo/base/_select_filter.html.erb +1 -1
  19. data/app/views/avo/home/_resources.html.erb +0 -1
  20. data/app/views/avo/partials/_table_header.html.erb +1 -1
  21. data/app/views/layouts/avo/_filter_wrapper.html.erb +1 -1
  22. data/app/views/layouts/avo/application.html.erb +5 -0
  23. data/avo.gemspec +0 -1
  24. data/config/webpacker.yml +10 -23
  25. data/lib/avo.rb +5 -16
  26. data/lib/avo/app.rb +204 -0
  27. data/lib/avo/base_action.rb +123 -0
  28. data/lib/avo/base_resource.rb +326 -0
  29. data/lib/avo/configuration.rb +8 -0
  30. data/lib/avo/engine.rb +12 -31
  31. data/lib/avo/{app/fields → fields}/badge_field.rb +1 -1
  32. data/lib/avo/{app/fields/field.rb → fields/base_field.rb} +3 -7
  33. data/lib/avo/{app/fields/belongs_to.rb → fields/belongs_to_field.rb} +3 -3
  34. data/lib/avo/{app/fields → fields}/boolean_field.rb +1 -1
  35. data/lib/avo/{app/fields → fields}/boolean_group_field.rb +1 -1
  36. data/lib/avo/{app/fields → fields}/code_field.rb +1 -1
  37. data/lib/avo/{app/fields → fields}/country_field.rb +1 -1
  38. data/lib/avo/{app/fields → fields}/currency_field.rb +1 -1
  39. data/lib/avo/{app/fields → fields}/date_field.rb +0 -2
  40. data/lib/avo/{app/fields → fields}/date_time_field.rb +0 -2
  41. data/lib/avo/{app/fields → fields}/external_image_field.rb +1 -1
  42. data/lib/avo/{app/fields → fields}/field_extensions/has_field_name.rb +0 -0
  43. data/lib/avo/{app/fields → fields}/field_extensions/visible_in_different_views.rb +1 -1
  44. data/lib/avo/{app/fields → fields}/file_field.rb +1 -2
  45. data/lib/avo/{app/fields → fields}/files_field.rb +1 -2
  46. data/lib/avo/{app/fields → fields}/gravatar_field.rb +1 -1
  47. data/lib/avo/{app/fields/has_and_belongs_to_many.rb → fields/has_and_belongs_to_many_field.rb} +1 -1
  48. data/lib/avo/{app/fields/has_many.rb → fields/has_many_field.rb} +1 -1
  49. data/lib/avo/{app/fields/has_one.rb → fields/has_one_field.rb} +1 -1
  50. data/lib/avo/{app/fields → fields}/heading_field.rb +1 -1
  51. data/lib/avo/{app/fields → fields}/hidden_field.rb +0 -2
  52. data/lib/avo/{app/fields → fields}/id_field.rb +1 -1
  53. data/lib/avo/{app/fields → fields}/key_value_field.rb +1 -1
  54. data/lib/avo/{app/fields → fields}/markdown_field.rb +1 -3
  55. data/lib/avo/{app/fields → fields}/number_field.rb +0 -2
  56. data/lib/avo/{app/fields → fields}/password_field.rb +0 -2
  57. data/lib/avo/{app/fields → fields}/select_field.rb +1 -1
  58. data/lib/avo/{app/fields → fields}/status_field.rb +1 -1
  59. data/lib/avo/{app/fields → fields}/text_field.rb +1 -1
  60. data/lib/avo/{app/fields → fields}/textarea_field.rb +0 -2
  61. data/lib/avo/{app/fields → fields}/trix_field.rb +1 -3
  62. data/lib/avo/filters/base_filter.rb +20 -0
  63. data/lib/avo/filters/boolean_filter.rb +7 -0
  64. data/lib/avo/filters/select_filter.rb +7 -0
  65. data/lib/avo/{app/grid_fields → grid_fields}/body_field.rb +0 -2
  66. data/lib/avo/{app/grid_fields → grid_fields}/grid_field.rb +0 -0
  67. data/lib/avo/{app/grid_fields → grid_fields}/preview_field.rb +0 -2
  68. data/lib/avo/{app/grid_fields → grid_fields}/title_field.rb +0 -2
  69. data/lib/avo/licensing/community_license.rb +6 -0
  70. data/lib/avo/licensing/h_q.rb +88 -0
  71. data/lib/avo/licensing/license.rb +50 -0
  72. data/lib/avo/licensing/license_manager.rb +22 -0
  73. data/lib/avo/licensing/null_license.rb +14 -0
  74. data/lib/avo/licensing/pro_license.rb +11 -0
  75. data/lib/avo/loaders/actions_loader.rb +6 -0
  76. data/lib/avo/loaders/fields_loader.rb +27 -0
  77. data/lib/avo/loaders/filters_loader.rb +6 -0
  78. data/lib/avo/loaders/loader.rb +15 -0
  79. data/lib/avo/services/authorization_service.rb +93 -0
  80. data/lib/avo/services/panel_service.rb +27 -0
  81. data/lib/avo/{app/tools_manager.rb → tools_manager.rb} +0 -0
  82. data/lib/avo/version.rb +1 -1
  83. data/lib/generators/avo/action_generator.rb +11 -5
  84. data/lib/generators/avo/controller_generator.rb +14 -8
  85. data/lib/generators/avo/filter_generator.rb +14 -8
  86. data/lib/generators/avo/install_generator.rb +14 -10
  87. data/lib/generators/avo/locales_generator.rb +11 -7
  88. data/lib/generators/avo/partials_generator.rb +11 -5
  89. data/lib/generators/avo/resource_generator.rb +33 -15
  90. data/lib/generators/avo/templates/{action.rb → action.tt} +2 -4
  91. data/lib/generators/avo/templates/filters/boolean_filter.tt +11 -0
  92. data/lib/generators/avo/templates/filters/select_filter.tt +11 -0
  93. data/lib/generators/avo/templates/initializer/{avo.rb → avo.tt} +6 -0
  94. data/lib/generators/avo/templates/resource/controller.tt +2 -0
  95. data/lib/generators/avo/templates/resource/resource.tt +17 -0
  96. data/public/avo-packs/css/{application-3598cfbb.css → application-c75ac28c.css} +1 -1
  97. data/public/avo-packs/css/application-c75ac28c.css.br +0 -0
  98. data/public/avo-packs/css/{application-3598cfbb.css.gz → application-c75ac28c.css.gz} +0 -0
  99. data/public/avo-packs/js/{application-6ba2bd1bca05d69b96e0.js → application-8849c6e2c8f75d55c666.js} +3 -3
  100. data/public/avo-packs/js/{application-6ba2bd1bca05d69b96e0.js.LICENSE.txt → application-8849c6e2c8f75d55c666.js.LICENSE.txt} +0 -0
  101. data/public/avo-packs/js/application-8849c6e2c8f75d55c666.js.br +0 -0
  102. data/public/avo-packs/js/application-8849c6e2c8f75d55c666.js.gz +0 -0
  103. data/public/avo-packs/js/{application-6ba2bd1bca05d69b96e0.js.map → application-8849c6e2c8f75d55c666.js.map} +1 -1
  104. data/public/avo-packs/js/application-8849c6e2c8f75d55c666.js.map.br +0 -0
  105. data/public/avo-packs/js/{application-6ba2bd1bca05d69b96e0.js.map.gz → application-8849c6e2c8f75d55c666.js.map.gz} +0 -0
  106. data/public/avo-packs/manifest.json +6 -6
  107. data/public/avo-packs/manifest.json.br +0 -0
  108. data/public/avo-packs/manifest.json.gz +0 -0
  109. metadata +72 -87
  110. data/config/webpacker_packed.yml +0 -86
  111. data/lib/avo/app/action.rb +0 -145
  112. data/lib/avo/app/actions_loader.rb +0 -11
  113. data/lib/avo/app/app.rb +0 -181
  114. data/lib/avo/app/fields_loader.rb +0 -29
  115. data/lib/avo/app/filter.rb +0 -23
  116. data/lib/avo/app/filters/boolean_filter.rb +0 -13
  117. data/lib/avo/app/filters/select_filter.rb +0 -13
  118. data/lib/avo/app/licensing/community_license.rb +0 -4
  119. data/lib/avo/app/licensing/hq.rb +0 -86
  120. data/lib/avo/app/licensing/license.rb +0 -48
  121. data/lib/avo/app/licensing/license_manager.rb +0 -25
  122. data/lib/avo/app/licensing/null_license.rb +0 -12
  123. data/lib/avo/app/licensing/pro_license.rb +0 -9
  124. data/lib/avo/app/resource.rb +0 -291
  125. data/lib/avo/app/resource_grid_fields.rb +0 -30
  126. data/lib/avo/app/services/authorization_service.rb +0 -110
  127. data/lib/avo/app/services/panel_service.rb +0 -25
  128. data/lib/avo/app/tool.rb +0 -5
  129. data/lib/generators/avo/templates/filters/boolean_filter.rb +0 -17
  130. data/lib/generators/avo/templates/filters/select_filter.rb +0 -17
  131. data/lib/generators/avo/templates/resource/%plural_name%_controller.tt +0 -2
  132. data/lib/generators/avo/templates/resource/%singular_name%.tt +0 -20
  133. data/public/avo-packs/css/application-3598cfbb.css.br +0 -0
  134. data/public/avo-packs/js/application-6ba2bd1bca05d69b96e0.js.br +0 -0
  135. data/public/avo-packs/js/application-6ba2bd1bca05d69b96e0.js.gz +0 -0
  136. data/public/avo-packs/js/application-6ba2bd1bca05d69b96e0.js.map.br +0 -0
data/lib/avo.rb CHANGED
@@ -1,20 +1,9 @@
1
- require_relative 'avo/configuration'
1
+ require 'zeitwerk'
2
2
  require_relative 'avo/version'
3
+ require_relative 'avo/engine' if defined?(Rails)
3
4
 
4
- require_relative 'avo/app/fields/field'
5
-
6
- require_relative 'avo/app/action'
7
-
8
- require_relative 'avo/app/filter'
9
- require_relative 'avo/app/filters/boolean_filter'
10
- require_relative 'avo/app/filters/select_filter'
11
-
12
- require_relative 'avo/app/fields_loader'
13
- require_relative 'avo/app/actions_loader'
14
- # require_relative 'avo/app/fields_loader_helper'
15
- require_relative 'avo/app/resource'
16
-
17
- require_relative 'avo/app/licensing/license_manager'
5
+ loader = Zeitwerk::Loader.for_gem
6
+ loader.setup
18
7
 
19
8
  module Avo
20
9
  ROOT_PATH = Pathname.new(File.join(__dir__, '..'))
@@ -31,4 +20,4 @@ module Avo
31
20
  end
32
21
  end
33
22
 
34
- require_relative 'avo/engine' if defined?(Rails)
23
+ loader.eager_load
data/lib/avo/app.rb ADDED
@@ -0,0 +1,204 @@
1
+ module Avo
2
+ class App
3
+ class_attribute :app, default: {
4
+ resources: [],
5
+ cache_store: nil
6
+ }
7
+ class_attribute :fields, default: []
8
+ class_attribute :request, default: nil
9
+ class_attribute :context, default: nil
10
+ class_attribute :license, default: nil
11
+
12
+ class << self
13
+ # def load_controllers
14
+ # BaseResource.descendants.each do |resource|
15
+ # controller_name = "#{resource.to_s.gsub('Resource', '').pluralize}Controller"
16
+ # puts controller_name.inspect
17
+ # # eval <<DYNAMIC
18
+ # # class #{controller_name} < Avo::ResourcesController
19
+ # # # title :Person
20
+ # # # attribute :name, String
21
+ # # # ...or substitute other stuff in here.
22
+ # # end
23
+ # # DYNAMIC
24
+
25
+ # Avo.const_set(controller_name.to_sym, Class.new(Avo::ResourcesController))
26
+ # # puts controller_name.inspect
27
+ # end
28
+ # end
29
+
30
+ def boot
31
+ init_fields
32
+
33
+ I18n.locale = Avo.configuration.language_code
34
+
35
+ if Rails.cache.class == ActiveSupport::Cache::NullStore
36
+ self.app[:cache_store] ||= ActiveSupport::Cache::MemoryStore.new
37
+ else
38
+ self.app[:cache_store] = Rails.cache
39
+ end
40
+ end
41
+
42
+ def init(request:, context:)
43
+ self.request = request
44
+ self.context = context
45
+
46
+ # Set the current host for ActiveStorage
47
+ ActiveStorage::Current.host = request.base_url
48
+
49
+ init_resources
50
+
51
+ self.license = Licensing::LicenseManager.new(Licensing::HQ.new(request).response).license
52
+ end
53
+
54
+ def cache_store
55
+ self.app[:cache_store]
56
+ end
57
+
58
+ # This method will find all fields available in the Avo::Fields namespace and add them to the fields class_variable array
59
+ # so later we can instantiate them on our resources.
60
+ #
61
+ # If the field has their `def_method` set up it will follow that convention, if not it will snake_case the name:
62
+ #
63
+ # Avo::Fields::TextField -> text
64
+ # Avo::Fields::DateTimeField -> date_time
65
+ def init_fields
66
+ Avo::Fields::BaseField.descendants.each do |class_name|
67
+ next if class_name.to_s == 'BaseField'
68
+
69
+ if class_name.to_s.end_with? 'Field'
70
+ load_field class_name.get_field_name, class_name
71
+ end
72
+ end
73
+ end
74
+
75
+ def load_field(method_name, klass)
76
+ self.fields.push(
77
+ name: method_name,
78
+ class: klass,
79
+ )
80
+
81
+ # Avo::Loaders::FieldsLoader.define_method method_name.to_sym do |*args, &block|
82
+ # puts ['Avo::Loaders::FieldsLoader.define_method->', args, block.present?].inspect
83
+
84
+ # if block.present?
85
+ # puts '111->'.inspect
86
+ # field = klass.new(args[0], **args[1] || {}, &block)
87
+ # else
88
+ # puts '222->'.inspect
89
+ # field = klass.new(args[0], **args[1] || {})
90
+ # end
91
+
92
+ # puts field.inspect
93
+
94
+ # self.bag.push field
95
+
96
+ # # field
97
+ # # if block.present?
98
+ # # field_class = klass::new(args[0], **args[1] || {}, &block)
99
+ # # else
100
+ # # field_class = klass::new(args[0], **args[1] || {})
101
+ # # end
102
+
103
+ # # klass_entity.add_field(self, field_class)
104
+ # end
105
+ end
106
+
107
+ def init_resources
108
+ self.app[:resources] = BaseResource.descendants
109
+ .select do |resource|
110
+ resource != BaseResource
111
+ end
112
+ .map do |resource|
113
+ if resource.is_a? Class
114
+ resource.new
115
+ end
116
+ end
117
+ end
118
+
119
+ def get_resources
120
+ self.app[:resources]
121
+ end
122
+
123
+ # Returns the Avo resource by camelized name
124
+ #
125
+ # get_resource_by_name('User') => UserResource
126
+ def get_resource(resource)
127
+ self.app[:resources].find do |available_resource|
128
+ "#{resource}Resource".safe_constantize == available_resource.class
129
+ end
130
+ end
131
+
132
+ # Returns the Avo resource by singular snake_cased name
133
+ #
134
+ # get_resource_by_name('user') => UserResource
135
+ def get_resource_by_name(name)
136
+ self.get_resource name.singularize.camelize
137
+ end
138
+
139
+ # Returns the Avo resource by singular snake_cased name
140
+ #
141
+ # get_resource_by_name('User') => UserResource
142
+ # get_resource_by_name(User) => UserResource
143
+ def get_resource_by_model_name(name)
144
+ get_resources.find do |resource|
145
+ resource.model_class.model_name.name == name.to_s
146
+ end
147
+ end
148
+
149
+ # Returns the Avo resource by singular snake_cased name
150
+ #
151
+ # get_resource_by_controller_name('delayed_backend_active_record_jobs') => DelayedJobResource
152
+ # get_resource_by_controller_name('users') => UserResource
153
+ def get_resource_by_controller_name(name)
154
+ get_resources.find do |resource|
155
+ resource.model_class.to_s.pluralize.underscore.gsub('/', '_') == name.to_s
156
+ end
157
+ end
158
+
159
+ # Returns the Rails model class by singular snake_cased name
160
+ #
161
+ # get_model_class_by_name('user') => User
162
+ def get_model_class_by_name(name)
163
+ name.to_s.camelize.singularize
164
+ end
165
+
166
+ def get_available_resources(user = nil)
167
+ App.get_resources
168
+ .select do |resource|
169
+ Services::AuthorizationService.authorize user, resource.model, Avo.configuration.authorization_methods.stringify_keys['index'], raise_exception: false
170
+ end
171
+ .sort_by { |r| r.name }
172
+ end
173
+
174
+ def get_navigation_items(user = nil)
175
+ get_available_resources(user).select do |resource|
176
+ resource.model_class.present?
177
+ end
178
+ end
179
+
180
+ def draw_routes
181
+ # We should eager load all the classes so we find all descendants
182
+ Rails.application.eager_load!
183
+
184
+ Proc.new do
185
+ BaseResource.descendants
186
+ .select do |resource|
187
+ resource != :BaseResource
188
+ end
189
+ .map do |resource|
190
+ if resource.is_a? Class
191
+ if resource.model_class.present?
192
+ route_key = resource.model_class.model_name.route_key
193
+ else
194
+ route_key = resource.to_s.underscore.gsub('_resource', '').downcase.pluralize.to_sym
195
+ end
196
+
197
+ resources route_key
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,123 @@
1
+ module Avo
2
+ class BaseAction
3
+ class_attribute :name, default: self.class.to_s.demodulize.underscore.humanize(keep_id_suffix: true)
4
+ class_attribute :message
5
+ class_attribute :confirm_text
6
+ class_attribute :cancel_text
7
+ class_attribute :no_confirmation, default: false
8
+ class_attribute :fields_loader
9
+
10
+ attr_accessor :response
11
+ attr_accessor :model
12
+ attr_accessor :resource
13
+ attr_accessor :user
14
+
15
+ class << self
16
+ def fields(&block)
17
+ self.fields_loader ||= Avo::Loaders::FieldsLoader.new
18
+
19
+ yield(fields_loader)
20
+ end
21
+
22
+ def context
23
+ App.context
24
+ end
25
+ end
26
+
27
+ def initialize
28
+ self.class.message ||= I18n.t('avo.are_you_sure_you_want_to_run_this_option')
29
+ self.class.confirm_text ||= I18n.t('avo.run')
30
+ self.class.cancel_text ||= I18n.t('avo.cancel')
31
+
32
+ @response ||= {}
33
+ @response[:message_type] ||= :notice
34
+ @response[:message] ||= I18n.t('avo.action_ran_successfully')
35
+ end
36
+
37
+ def get_fields(view_type: :table)
38
+ get_field_definitions.map do |field|
39
+ field.hydrate(action: self, model: @model)
40
+ end
41
+ .select do |field|
42
+ field.can_see.present? ? field.can_see.call : true
43
+ end
44
+ end
45
+
46
+ def get_field_definitions
47
+ return [] if self.class.fields_loader.blank?
48
+
49
+ self.class.fields_loader.bag.map do |field|
50
+ field.hydrate(action: self)
51
+ end
52
+ end
53
+
54
+ def hydrate(model: nil, resource: nil, user: nil)
55
+ @model = model if model.present?
56
+ @resource = resource if resource.present?
57
+ @user = user if user.present?
58
+
59
+ self
60
+ end
61
+
62
+ def handle_action(request, models, raw_fields)
63
+ avo_fields = get_fields.map { |field| [field.id, field] }.to_h
64
+
65
+ if raw_fields.present?
66
+ fields = raw_fields.to_unsafe_h.map do |name, value|
67
+ [name, avo_fields[name.to_sym].resolve_attribute(value)]
68
+ end
69
+
70
+ fields = fields.to_h
71
+ else
72
+ fields = {}
73
+ end
74
+
75
+ result = self.handle request, models, fields
76
+
77
+ self
78
+ end
79
+
80
+ def param_id
81
+ self.class.to_s.demodulize.underscore.gsub '/', '_'
82
+ end
83
+
84
+ def succeed(text)
85
+ self.response[:message_type] = :notice
86
+ self.response[:message] = text
87
+
88
+ self
89
+ end
90
+
91
+ def fail(text)
92
+ self.response[:message_type] = :alert
93
+ self.response[:message] = text
94
+
95
+ self
96
+ end
97
+
98
+ def redirect_to(path = nil, &block)
99
+ self.response[:type] = :redirect
100
+ if block.present?
101
+ self.response[:path] = block
102
+ else
103
+ self.response[:path] = path
104
+ end
105
+
106
+ self
107
+ end
108
+
109
+ def reload
110
+ self.response[:type] = :reload
111
+
112
+ self
113
+ end
114
+
115
+ def download(path, filename)
116
+ self.response[:type] = :download
117
+ self.response[:path] = path
118
+ self.response[:filename] = filename
119
+
120
+ self
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,326 @@
1
+ module Avo
2
+ class BaseResource
3
+ extend ActiveSupport::DescendantsTracker
4
+
5
+ attr_accessor :view
6
+ attr_accessor :model
7
+ attr_accessor :user
8
+ attr_accessor :params
9
+
10
+ class_attribute :id, default: :id
11
+ class_attribute :title, default: :id
12
+ class_attribute :search, default: [:id]
13
+ class_attribute :includes, default: []
14
+ class_attribute :model_class
15
+ class_attribute :translation_key
16
+ class_attribute :default_view_type, default: :table
17
+ class_attribute :devise_password_optional, default: false
18
+ class_attribute :fields_loader
19
+ class_attribute :actions_loader
20
+ class_attribute :filters_loader
21
+ class_attribute :grid_cover_loader
22
+ class_attribute :grid_title_loader
23
+ class_attribute :grid_body_loader
24
+
25
+ class << self
26
+ def fields(&block)
27
+ self.fields_loader ||= Avo::Loaders::FieldsLoader.new
28
+
29
+ yield(fields_loader)
30
+ end
31
+
32
+ def actions(&block)
33
+ self.actions_loader ||= Avo::Loaders::ActionsLoader.new
34
+
35
+ yield(actions_loader)
36
+ end
37
+
38
+ def filters(&block)
39
+ self.filters_loader ||= Avo::Loaders::FiltersLoader.new
40
+
41
+ yield(filters_loader)
42
+ end
43
+
44
+ def grid(&block)
45
+ self.grid_cover_loader ||= Avo::Loaders::FieldsLoader.new
46
+ self.grid_title_loader ||= Avo::Loaders::FieldsLoader.new
47
+ self.grid_body_loader ||= Avo::Loaders::FieldsLoader.new
48
+
49
+ yield(grid_cover_loader, grid_title_loader, grid_body_loader)
50
+ end
51
+
52
+ def context
53
+ App.context
54
+ end
55
+ end
56
+
57
+ def hydrate(model: nil, view: nil, user: nil, params: nil)
58
+ @view = view if view.present?
59
+ @user = user if user.present?
60
+ @params = params if params.present?
61
+
62
+ if model.present?
63
+ @model = model
64
+
65
+ hydrate_model_with_default_values if @view == :new
66
+ end
67
+
68
+ self
69
+ end
70
+
71
+ def get_fields(panel: nil, reflection: nil)
72
+ fields = get_field_definitions.select do |field|
73
+ field.send("show_on_#{@view.to_s}")
74
+ end
75
+ .select do |field|
76
+ field.can_see.present? ? field.can_see.call : true
77
+ end
78
+ .select do |field|
79
+ unless field.respond_to?(:foreign_key) &&
80
+ reflection.present? &&
81
+ reflection.respond_to?(:foreign_key) &&
82
+ reflection.foreign_key == field.foreign_key
83
+ true
84
+ end
85
+ end
86
+
87
+ if panel.present?
88
+ fields = fields.select do |field|
89
+ field.panel_name == panel
90
+ end
91
+ end
92
+
93
+ fields = fields.map do |field|
94
+ field.hydrate(model: @model, view: @view, resource: self)
95
+ end
96
+
97
+ fields
98
+ end
99
+
100
+ def get_grid_fields
101
+ return if self.class.grid_title_loader.blank?
102
+
103
+ card = {
104
+ cover: [*self.class.grid_cover_loader.bag],
105
+ title: [*self.class.grid_title_loader.bag],
106
+ body: [*self.class.grid_body_loader.bag],
107
+ }
108
+
109
+ card.each do |section, fields|
110
+ fields = fields.map do |field|
111
+ field.hydrate(model: @model, view: @view, resource: self)
112
+ end
113
+ end
114
+ end
115
+
116
+ def get_filters
117
+ return [] if self.class.filters_loader.blank?
118
+
119
+ self.class.filters_loader.bag
120
+ end
121
+
122
+ def get_actions
123
+ return [] if self.class.actions_loader.blank?
124
+
125
+ self.class.actions_loader.bag
126
+ end
127
+
128
+ def get_field_definitions
129
+ return [] if self.class.fields_loader.blank?
130
+
131
+ self.class.fields_loader.bag.map do |field|
132
+ field.hydrate(resource: self, panel_name: default_panel_name, user: user)
133
+ end
134
+ end
135
+
136
+ def default_panel_name
137
+ return @params[:related_name].capitalize if @params[:related_name].present?
138
+
139
+ case @view
140
+ when :show
141
+ I18n.t('avo.resource_details', item: self.name.downcase, title: model_title).upcase_first
142
+ when :edit
143
+ I18n.t('avo.update_item', item: self.name.downcase, title: model_title).upcase_first
144
+ when :new
145
+ I18n.t('avo.create_new_item', item: self.name.downcase).upcase_first
146
+ end
147
+ end
148
+
149
+ def panels
150
+ panels = [
151
+ {
152
+ name: default_panel_name,
153
+ type: :fields,
154
+ in_panel: true,
155
+ }
156
+ ]
157
+
158
+ panels
159
+ end
160
+
161
+ def model_class
162
+ return self.class.model_class if self.class.model_class.present?
163
+
164
+ return @model.class if @model.present?
165
+
166
+ self.class.name.demodulize.chomp('Resource').safe_constantize
167
+ end
168
+
169
+ def model_title
170
+ return @model.send title if @model.present?
171
+
172
+ name
173
+ end
174
+
175
+ def name
176
+ return @name if @name.present?
177
+
178
+ return I18n.t(@translation_key, count: 1).capitalize if @translation_key
179
+
180
+ self.class.name.demodulize.chomp('Resource').titlecase
181
+ end
182
+
183
+ def singular_name
184
+ name
185
+ end
186
+
187
+ def plural_name
188
+ return I18n.t(@translation_key, count: 2).capitalize if @translation_key
189
+
190
+ name.pluralize
191
+ end
192
+
193
+ def underscore_name
194
+ return @name if @name.present?
195
+
196
+ self.class.name.demodulize.underscore
197
+ end
198
+
199
+ def available_view_types
200
+ view_types = [:table]
201
+
202
+ view_types << :grid if get_grid_fields.present?
203
+
204
+ view_types
205
+ end
206
+
207
+ def route_key
208
+ model_class.model_name.route_key
209
+ end
210
+
211
+ def context
212
+ App.context
213
+ end
214
+
215
+ # def fields
216
+ # raise NotImplementedError
217
+ # end
218
+
219
+ def query_search(query: '', via_resource_name: , via_resource_id:, user:)
220
+ # model_class = self.model
221
+
222
+ db_query = AuthorizationService.apply_policy(user, model_class)
223
+
224
+ if via_resource_name.present?
225
+ related_model = App.get_resource_by_name(via_resource_name).model
226
+
227
+ db_query = related_model.find(via_resource_id).public_send(self.plural_name.downcase)
228
+ end
229
+
230
+ new_query = []
231
+
232
+ [self.search].flatten.each_with_index do |search_by, index|
233
+ new_query.push 'or' if index != 0
234
+
235
+ new_query.push "text(#{search_by}) ILIKE '%#{query}%'"
236
+ end
237
+
238
+ db_query.where(new_query.join(' '))
239
+ end
240
+
241
+ def attached_file_fields
242
+ get_field_definitions.select do |field|
243
+ [Avo::Fields::FileField, Avo::Fields::FilesField].include? field.class
244
+ end
245
+ end
246
+
247
+ def fill_model(model, params)
248
+ # Map the received params to their actual fields
249
+ fields_by_database_id = self.get_field_definitions.map { |field| [field.database_id(model).to_s, field] }.to_h
250
+
251
+ params.each do |key, value|
252
+ field = fields_by_database_id[key]
253
+
254
+ next unless field
255
+
256
+ model = field.fill_field model, key, value
257
+ end
258
+
259
+ model
260
+ end
261
+
262
+ def authorization
263
+ Avo::Services::AuthorizationService.new(user, model)
264
+ end
265
+
266
+ def file_hash
267
+ content_to_be_hashed = ''
268
+
269
+ # resource file hash
270
+ resource_path = Rails.root.join('app', 'avo', 'resources', "#{name.underscore}.rb").to_s
271
+ if File.file? resource_path
272
+ content_to_be_hashed += File.read(resource_path)
273
+ end
274
+
275
+ # policy file hash
276
+ policy_path = Rails.root.join('app', 'policies', "#{name.underscore}_policy.rb").to_s
277
+ if File.file? policy_path
278
+ content_to_be_hashed += File.read(policy_path)
279
+ end
280
+
281
+ Digest::MD5.hexdigest(content_to_be_hashed)
282
+ end
283
+
284
+ def cache_hash(parent_model)
285
+ if parent_model.present?
286
+ [self.model, self.file_hash, parent_model]
287
+ else
288
+ [self.model, self.file_hash]
289
+ end
290
+ end
291
+
292
+ # For :new views we're hydrating the model with the values from the resource's default attribute.
293
+ # We will not overwrite any attributes that come pre-filled in the model.
294
+ def hydrate_model_with_default_values
295
+ default_values = get_fields.select do |field|
296
+ !field.computed
297
+ end
298
+ .map do |field|
299
+ id = field.id
300
+ value = field.value
301
+
302
+ if field.respond_to? :foreign_key
303
+ id = field.foreign_key.to_sym
304
+
305
+ reflection = @model._reflections[@params[:via_relation]]
306
+
307
+ if reflection.present? && reflection.foreign_key.present?
308
+ value = @params[:via_resource_id]
309
+ end
310
+ end
311
+
312
+ [id, value]
313
+ end
314
+ .to_h
315
+ .select do |id, value|
316
+ value.present?
317
+ end
318
+
319
+ default_values.each do |id, value|
320
+ if @model.send(id).nil?
321
+ @model.send("#{id}=", value)
322
+ end
323
+ end
324
+ end
325
+ end
326
+ end