Class: Avo::Resources::ResourceManager

Inherits:
Object
  • Object
show all
Defined in:
lib/avo/resources/resource_manager.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeResourceManager

Returns a new instance of ResourceManager.



65
66
67
# File 'lib/avo/resources/resource_manager.rb', line 65

def initialize
  @resources = self.class.fetch_resources
end

Instance Attribute Details

#resourcesObject Also known as: all

Returns the value of attribute resources.



4
5
6
# File 'lib/avo/resources/resource_manager.rb', line 4

def resources
  @resources
end

Class Method Details

.buildObject



9
10
11
12
13
# File 'lib/avo/resources/resource_manager.rb', line 9

def build
  instance = new
  instance.check_bad_resources
  instance
end

.fetch_resourcesObject

Fetches the resources available to the application. We have two ways of doing that.

  1. Through eager loading.

We automatically eager load the resources directory and fetch the descendants from the scanned files. This is the simple way to get started.

  1. Manually, declared by the user.

We have this option to load the resources because when they are loaded automatically through eager loading, those Resource classes and their methods may trigger loading other classes. And that may disrupt Rails booting process. Ex: AdminResource may use self.model_class = User. That will trigger Ruby to load the User class and itself load other classes in a chain reaction. The scenario that comes up most often is when Rails boots, the routes are being computed which eager loads the resource files. At that boot time some migration might have not been run yet, but Rails tries to access them through model associations, and they are not available.

To enable this feature add a ‘resources` array config in your Avo initializer. config.resources = [

"UserResource",
"FishResource",

]



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/avo/resources/resource_manager.rb', line 36

def fetch_resources
  # Mark the BaseResource as abstract so it doesn't get loaded by the resource manager
  # This is made here instead of in the BaseResource class because BaseResource class can be overridden
  # And we don't want to force the developer that overrides this class to add the abstract resource flag to the overridden class.
  Avo::BaseResource.abstract_resource!

  if Avo.configuration.resources.present?
    load_configured_resources
  else
    load_resources_namespace
  end

  # All descendants from Avo::Resources::Base except the internal abstract ones
  Base.descendants.reject { _1.is_abstract? }
end

.load_configured_resourcesObject



56
57
58
59
60
61
62
# File 'lib/avo/resources/resource_manager.rb', line 56

def load_configured_resources
  raise "Resources configuration must be an array" unless Avo.configuration.resources.is_a? Array

  Avo.configuration.resources.each do |resource|
    resource.to_s.safe_constantize
  end
end

.load_resources_namespaceObject



52
53
54
# File 'lib/avo/resources/resource_manager.rb', line 52

def load_resources_namespace
  Rails.autoloaders.main.eager_load_namespace(Avo::Resources)
end

Instance Method Details

#check_bad_resourcesObject



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/avo/resources/resource_manager.rb', line 69

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/3.0/resources.html#self_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

#get_available_resources(user = nil) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/avo/resources/resource_manager.rb', line 159

def get_available_resources(user = nil)
  valid_resources
    .select do |resource|
      resource.authorization.class.authorize(
        user,
        resource.model_class,
        Avo.configuration.authorization_methods.stringify_keys["index"],
        policy_class: resource.authorization_policy,
        raise_exception: false
      )
    end
    .sort_by { |r| r.name }
end

#get_model_class_by_name(name) ⇒ Object

Returns the Rails model class by singular snake_cased name

get_model_class_by_name(‘user’) => User



155
156
157
# File 'lib/avo/resources/resource_manager.rb', line 155

def get_model_class_by_name(name)
  name.to_s.camelize.singularize
end

#get_resource(resource) ⇒ Object

Returns the Avo resource by camelized name

get_resource_by_name(‘User’) => instance of Avo::Resources::User



94
95
96
97
98
99
100
# File 'lib/avo/resources/resource_manager.rb', line 94

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

#get_resource_by_controller_name(name) ⇒ Object

Returns the Avo resource by singular snake_cased name

get_resource_by_controller_name(‘delayed_backend_active_record_jobs’) => instance of Avo::Resources::DelayedJob get_resource_by_controller_name(‘users’) => instance of Avo::Resources::User



140
141
142
143
144
145
# File 'lib/avo/resources/resource_manager.rb', line 140

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

#get_resource_by_model_class(klass) ⇒ Object

Returns the Avo resource by singular snake_cased name From all the resources that use the same model_class, it will fetch the first one in alphabetical order

get_resource_by_name(‘User’) => instance of Avo::Resources::User get_resource_by_name(User) => instance of Avo::Resources::User



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/avo/resources/resource_manager.rb', line 124

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

#get_resource_by_name(name) ⇒ Object

Returns the Avo resource by singular snake_cased name

get_resource_by_name(‘user’) => instance of Avo::Resources::User



105
106
107
# File 'lib/avo/resources/resource_manager.rb', line 105

def get_resource_by_name(name)
  get_resource name.singularize.camelize
end

#get_resource_by_plural_name(name) ⇒ Object

Returns the Avo resource by singular snake_cased name

get_resource_by_name(‘z posts’) => instance of Avo::Resources::ZPost



112
113
114
115
116
# File 'lib/avo/resources/resource_manager.rb', line 112

def get_resource_by_plural_name(name)
  resources.find do |resource|
    resource.plural_name == name
  end
end

#guess_resource(name) ⇒ Object

Returns the Avo resource by some name



148
149
150
# File 'lib/avo/resources/resource_manager.rb', line 148

def guess_resource(name)
  get_resource_by_name(name.to_s) || get_resource_by_model_class(name)
end

#resources_for_navigation(user = nil) ⇒ Object



173
174
175
176
177
178
# File 'lib/avo/resources/resource_manager.rb', line 173

def resources_for_navigation(user = nil)
  get_available_resources(user)
    .select do |resource|
      resource.visible_on_sidebar
    end
end

#valid_resourcesObject

Filters out the resources that are missing the model_class



87
88
89
# File 'lib/avo/resources/resource_manager.rb', line 87

def valid_resources
  resources.select { |resource| resource.model_class.present? }.sort_by(&:name)
end