class Avo::Resources::Base
def action(action_class, arguments: {})
def action(action_class, arguments: {}) deprecated_dsl_api __method__, "actions" end
def attachment_fields
def attachment_fields get_field_definitions.select do |field| [Avo::Fields::FileField, Avo::Fields::FilesField].include? field.class end end
def authorization
def authorization Avo::Services::AuthorizationService.new Avo::Current.user, model_class, policy_class: authorization_policy end
def authorization(user: nil)
def authorization(user: nil) current_user = user || Avo::Current.user Avo::Services::AuthorizationService.new(current_user, record || model_class, policy_class: authorization_policy) end
def available_view_types
def available_view_types @available_view_types ||= begin if self.class.view_types.present? return Array( Avo::ExecutionContext.new( target: self.class.view_types, resource: self, record: record ).handle ) end view_types = [:table] view_types << :grid if self.class.grid_view.present? view_types << :map if map_view.present? view_types end end
def cache_hash(parent_record)
def cache_hash(parent_record) result = [record, file_hash] if parent_record.present? result << parent_record end result end
def class_name
def class_name @class_name ||= to_s.demodulize end
def current_user
def current_user Avo::Current.user end
def custom_components
def custom_components @custom_components ||= Avo::ExecutionContext.new( target: components, resource: self, record: @record, view: @view ).handle.with_indifferent_access end
def default_panel_name
def default_panel_name return @params[:related_name].capitalize if @params.present? && @params[:related_name].present? case @view.to_sym when :show record_title when :edit record_title when :new t("avo.create_new_item", item: name.humanize(capitalize: false)).upcase_first end end
def description_attributes
def description_attributes { view: view, resource: self, record: record } end
def detect_fields
def detect_fields self.items_holder = Avo::Resources::Items::Holder.new(parent: self) # Used in testing to replace items if temporary_items.present? instance_eval(&temporary_items) else fetch_fields end self end
def divider(**kwargs)
def divider(**kwargs) entity_loader(:action).use({class: Divider, **kwargs}.compact) end
def entity_loader(entity)
def entity_loader(entity) instance_variable_get(:"@#{entity.to_s.pluralize}_loader") end
def fetch_cards
def fetch_cards cards end
def fetch_fields
def fetch_fields if view.preview? [:fields, :index_fields, :show_fields, :display_fields].each do |fields_method| send(fields_method) if respond_to?(fields_method) end return end possible_methods_for_view = VIEW_METHODS_MAPPING[view.to_sym] # Safe navigation operator is used because the view can be "destroy" possible_methods_for_view&.each do |method_for_view| return send(method_for_view) if respond_to?(method_for_view) end fields end
def fetch_record_title
def fetch_record_title return name if @record.nil? # Get the title from the record if title is not set, try to get the name, title or label, or fallback to the to_param return @record.try(:name) || @record.try(:title) || @record.try(:label) || @record.to_param if title.nil? # If the title is a symbol, get the value from the record else execute the block/string case title when Symbol @record.send title when Proc Avo::ExecutionContext.new(target: title, resource: self, record: @record).handle end end
def fetch_search(key, record: nil)
def fetch_search(key, record: nil) # self.class.fetch_search Avo::ExecutionContext.new(target: search[key], resource: self, record: record).handle end
def fields_by_database_id(resource: self)
Map the received params to their actual fields
def fields_by_database_id(resource: self) resource.get_field_definitions .reject do |field| field.computed end .map do |field| [field.database_id.to_s, field] end .to_h end
def file_hash
def file_hash content_to_be_hashed = "" resource_path = Rails.root.join("app", "avo", "resources", "#{file_name}.rb").to_s if File.file? resource_path content_to_be_hashed += File.read(resource_path) end # policy file hash policy_path = Rails.root.join("app", "policies", "#{file_name.gsub("_resource", "")}_policy.rb").to_s if File.file? policy_path content_to_be_hashed += File.read(policy_path) end Digest::MD5.hexdigest(content_to_be_hashed) end
def file_name
def file_name @file_name ||= self.class.underscore_name.tr(" ", "_") end
def fill_record(record, permitted_params, extra_params: [], fields: nil)
def fill_record(record, permitted_params, extra_params: [], fields: nil) # Write the field values permitted_params.each do |key, value| field = if fields.present? fields.find { |f| f.id == key.to_sym } else fields_by_database_id[key] end next unless field.present? record = field.fill_field record, key, value, permitted_params end # Write the user configured extra params to the record if extra_params.present? # Pick only the extra params # params at this point are already permitted, only need the keys to access them extra_attributes = permitted_params.slice(*flatten_keys(extra_params)) # Let Rails fill in the rest of the params record.assign_attributes extra_attributes end safe_call(:fill_nested_records, record:, permitted_params:) || record end
def filter(filter_class, arguments: {})
def filter(filter_class, arguments: {}) deprecated_dsl_api __method__, "filters" end
def find_record(id, query: nil, params: nil)
def find_record(id, query: nil, params: nil) query ||= find_scope # If no record is given we'll use the default if single_includes.present? query = query.includes(*single_includes) end if single_attachments.present? single_attachments.each do |attachment| query = query.send(:"with_attached_#{attachment}") end end Avo::ExecutionContext.new( target: find_record_method, query: query, id: id, params: params ).handle end
def find_scope
This resolves the scope when finding records (not "where" queries)
def find_scope authorization.apply_policy model_class end
def flatten_keys(array)
def flatten_keys(array) # [:fish_type, information: [:name, :history], reviews_attributes: [:body, :user_id]] # becomes # [:fish_type, :information, :reviews_attributes] array.flat_map do |item| case item when Hash item.keys else item end end end
def form_scope
def form_scope model_class.base_class.to_s.underscore.downcase end
def get_available_models
def get_available_models ApplicationRecord.descendants end
def get_external_link
def get_external_link return unless record.persisted? Avo::ExecutionContext.new(target: external_link, resource: self, record: record).handle end
def get_model_by_name(model_name)
def get_model_by_name(model_name) get_available_models.find do |m| m.to_s == model_name.to_s end end
def has_record_id?
def has_record_id? record.present? && record_id.present? end
def hydrate(...)
def hydrate(...) super if @record.present? hydrate_model_with_default_values if @view&.new? end self end
def hydrate_model_with_default_values
def hydrate_model_with_default_values default_values = get_fields .select do |field| !field.computed && !field.is_a?(Avo::Fields::HeadingField) end .map do |field| value = field.value if field.type == "belongs_to" reflection = @record.class.reflect_on_association(@params[:via_relation]) if @params[:via_relation].present? if field.polymorphic_as.present? && field.types.map(&:to_s).include?(@params[:via_relation_class]) # set the value to the actual record via_resource = Avo.resource_manager.get_resource_by_model_class(@params[:via_relation_class]) value = via_resource.find_record(@params[:via_record_id]) elsif reflection.present? && reflection.foreign_key.present? && field.id.to_s == @params[:via_relation].to_s resource = Avo.resource_manager.get_resource_by_model_class params[:via_relation_class] record = resource.find_record @params[:via_record_id], params: params id_param = reflection.options[:primary_key] || :id value = record.send(id_param) end end [field, value] end .to_h .select do |_, value| value.present? end default_values.each do |field, value| field.assign_value record: @record, value: value end end
def id_attribute
def id_attribute :id end
def initialize(record: nil, view: nil, user: nil, params: nil)
def initialize(record: nil, view: nil, user: nil, params: nil) @view = Avo::ViewInquirer.new(view) if view.present? @user = user if user.present? @params = params if params.present? if record.present? @record = record hydrate_model_with_default_values if @view&.new? end unless self.class.model_class.present? if model_class.present? && model_class.respond_to?(:base_class) self.class.model_class = model_class.base_class end end end
def model_class(record_class: nil)
The Resource instance has a model_class method too so it can support the STI use cases
Returns the model class being used for this resource.
def model_class(record_class: nil) # get the model class off of the static property return constantized_model_class if @model_class.present? # get the model class off of the record for STI models return record_class if record_class.present? # generate a model class class_name.safe_constantize end
def model_class
We use the class method as a fallback but we pass it the record too so it can support the STI use cases
Returns the model class being used for this resource.
def model_class record_class = @record&.class self.class.model_class record_class: record_class end
def model_key
With uncountable models route key appends an _index suffix (Fish->fish_index)
We use this instead of the route_key to maintain compatibility with uncountable models
This is used as the model class ID
def model_key @model_key ||= model_class.model_name.plural end
def model_name
def model_name model_class.model_name end
def name
def name name_from_translation_key(count: 1, default: class_name.underscore.humanize) end
def name_from_translation_key(count:, default:)
product:
resource_translations:
avo:
en:
---
Example:
It can raise I18n::InvalidPluralizationData when using only resource_translation without pluralization keys like: one, two or other key
Get the name from the translation_key and fallback to default
def name_from_translation_key(count:, default:) t(translation_key, count:, default:).humanize rescue I18n::InvalidPluralizationData default end
def navigation_label
def navigation_label plural_name.humanize end
def plural_name
def plural_name name_from_translation_key(count: 2, default: name.pluralize) end
def query_scope
This resolves the scope when doing "where" queries (not find queries)
def query_scope authorization.apply_policy Avo::ExecutionContext.new( target: index_query, query: model_class ).handle end
def record_id
def record_id record.send(id_attribute) end
def record_param
def record_param @record_param ||= @record.persisted? ? @record.to_param : nil end
def record_path
def record_path resource_path(record: record, resource: self) end
def record_title
def record_title fetch_record_title.to_s end
def records_path
def records_path resources_path(resource: self) end
def resolve_component(original_component)
def resolve_component(original_component) custom_components.dig(original_component.to_s)&.to_s&.safe_constantize || original_component end
def resource_type_array? = false
def resource_type_array? = false
def route_key
def route_key class_name.underscore.pluralize end
def scope(scope_class)
def scope(scope_class) deprecated_dsl_api __method__, "scopes" end
def search_query
def search_query search.dig(:query) end
def search_results_count
def search_results_count search.dig(:results_count) end
def singular_model_key
def singular_model_key model_class.model_name.singular end
def singular_route_key
def singular_route_key route_key.singularize end
def sort_by_param
def sort_by_param available_columns = model_class.column_names if available_columns.include?(default_sort_column.to_s) default_sort_column elsif available_columns.include?("created_at") :created_at end end
def sorting_supported? = true
def sorting_supported? = true
def translation_key
def translation_key custom_translation_key || "avo.resource_translations.#{class_name.underscore}" end
def underscore_name
def underscore_name name.demodulize.underscore end
def valid_association_name(record, association_name)
def valid_association_name(record, association_name) association_name if record.class.reflect_on_association(association_name).present? end
def valid_attachment_name(record, association_name)
def valid_attachment_name(record, association_name) association_name if record.class.reflect_on_attachment(association_name).present? end
def view_type
def view_type @view_type ||= if @params[:view_type].present? @params[:view_type] elsif available_view_types.size == 1 available_view_types.first else Avo::ExecutionContext.new( target: default_view_type || Avo.configuration.default_view_type, resource: self, view: @view ).handle end end