class Avo::Resources::ResourceManager
def build
def build instance = new instance.init_resources instance.check_bad_resources instance end
def check_bad_resources
def check_bad_resources resources.each do |resource| has_model = resource.model_class.present? unless has_model possible_model = resource.to_s.gsub "Avo::Resources::", "" possible_model = possible_model.gsub "Resource", "" Avo.error_manager.add({ url: "https://docs.avohq.io/2.0/resources.html#custom-model-class", target: "_blank", message: "#{resource} does not have a valid model assigned. It failed to find the #{possible_model} model. \n\r Please create that model or assign one using self.model_class = YOUR_MODEL" }) end end end
def fetch_resources
"FishResource",
"UserResource",
config.resources = [
To enable this feature add a `resources` array config in your Avo initializer.
and they are not available.
At that boot time some migration might have not been run yet, but Rails tries to access them through model associations,
The scenario that comes up most often is when Rails boots, the routes are being computed which eager loads the resource files.
other classes in a chain reaction.
Ex: AdminResource may use self.model_class = User. That will trigger Ruby to load the User class and itself load
those Resource classes and their methods may trigger loading other classes. And that may disrupt Rails booting process.
We have this option to load the resources because when they are loaded automatically through eager loading,
2. Manually, declared by the user.
This is the simple way to get started.
We automatically eager load the resources directory and fetch the descendants from the scanned files.
1. Through eager loading.
We have two ways of doing that.
Fetches the resources available to the application.
def fetch_resources resources = if Avo.configuration.resources.nil? BaseResource.descendants else Avo.configuration.resources end resources.map do |resource| if resource.is_a?(Class) resource else resource.to_s.safe_constantize end end end
def get_available_resources(user = nil)
def get_available_resources(user = nil) valid_resources .select do |resource| Services::AuthorizationService.authorize user, resource.model_class, Avo.configuration.authorization_methods.stringify_keys["index"], raise_exception: false end .sort_by { |r| r.name } end
def get_mapping_for_model(klass)
def get_mapping_for_model(klass) (Avo.configuration.model_resource_mapping || {}).stringify_keys.transform_values(&:to_s)[klass.to_s] end
def get_model_class_by_name(name)
Returns the Rails model class by singular snake_cased name
def get_model_class_by_name(name) name.to_s.camelize.singularize end
def get_resource(resource)
Returns the Avo resource by camelized name
def get_resource(resource) resource = "Avo::Resources::#{resource}" unless resource.to_s.starts_with?("Avo::Resources::") resources.find do |available_resource| resource.to_s == available_resource.to_s end end
def get_resource_by_controller_name(name)
get_resource_by_controller_name('delayed_backend_active_record_jobs') => instance of Avo::Resources::DelayedJob
Returns the Avo resource by singular snake_cased name
def get_resource_by_controller_name(name) valid_resources .find do |resource| resource.model_class.to_s.pluralize.underscore.tr("/", "_") == name.to_s end end
def get_resource_by_model_class(klass)
def get_resource_by_model_class(klass) # Fetch the mappings imposed by the user. # If they are present, use those ones. mapping = get_mapping_for_model klass return get_resource(mapping) if mapping.present? valid_resources .find do |resource| resource.model_class.model_name.name == klass.to_s end end
def get_resource_by_name(name)
Returns the Avo resource by singular snake_cased name
def get_resource_by_name(name) get_resource name.singularize.camelize end
def guess_resource(name)
def guess_resource(name) get_resource_by_name(name.to_s) || get_resource_by_model_class(name) end
def init_resources
def init_resources self.resources = self.class.fetch_resources .reject do |resource| # Remove the BaseResource. We only need the descendants resource == Avo::BaseResource end .uniq do |klass| # On invalid resource configuration the resource classes get duplicated in `ObjectSpace` # We need to de-duplicate them klass.name end end
def initialize
def initialize @resources = [] end
def resources_for_navigation(user = nil)
def resources_for_navigation(user = nil) get_available_resources(user) .select do |resource| resource.visible_on_sidebar end end
def valid_resources
def valid_resources resources.select { |resource| resource.model_class.present? }.sort_by(&:name) end