Class: Decidim::DependencyResolver

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
decidim-core/lib/decidim/dependency_resolver.rb

Overview

The dependency resolver provides information about the Decidim module dependencies. For instance, it can be used to check if a particular Decidim module is needed by the instance or not.

This is a singleton utility in order to make it perform better when it is called from different places throughout the application.

Examples:

Checking whether a module is needed by the instance

Decidim::DependencyResolver.instance.needed?("decidim-proposals")

Defined Under Namespace

Classes: Lookup

Instance Method Summary collapse

Constructor Details

#initializeDependencyResolver

Returns a new instance of DependencyResolver.



16
17
18
# File 'decidim-core/lib/decidim/dependency_resolver.rb', line 16

def initialize
  @cache = []
end

Instance Method Details

#available?(spec) ⇒ Boolean

Resolves whether the module for the gem specification defines the main load file.

Parameters:

  • spec (Bundler::LazySpecification, Gem::Specification, String)

    The gem specification to test or the name of the gem.

Returns:

  • (Boolean)

    A boolean indicating if the gem defined by the spec is available for loading.



82
83
84
85
86
87
# File 'decidim-core/lib/decidim/dependency_resolver.rb', line 82

def available?(spec)
  path = module_path(spec)
  return false unless path

  File.exist?(path)
end

#loaded?(spec) ⇒ Boolean

Checks if the module for the gem speficiation has been loaded through ‘require “decidim/foo”`.

Parameters:

  • spec (Bundler::LazySpecification, Gem::Specification, String)

    The gem specification to test or the name of the gem.

Returns:

  • (Boolean)

    A boolean indicating if the gem defined by the spec is loaded.



96
97
98
99
100
# File 'decidim-core/lib/decidim/dependency_resolver.rb', line 96

def loaded?(spec)
  return false unless available?(spec)

  $LOADED_FEATURES.include?(module_path(spec))
end

#lookup(gem) ⇒ Gem::Specification?

Finds a gem specification for the gem with the name provided as the argument.

When bundler is available, searches whether the gem is listed in the Gemfile or required as a dependency for one of the gems in the Gemfile or their dependencies.

When bundler is not available, gets the gem specification from the loaded specs which means any gems available in the gem install folder.

Parameters:

  • gem (String)

    The name for the gem to be looked up.

Returns:

  • (Gem::Specification, nil)

    Returns the gem specification for the given gem name. The specification implements the necessary methods for the other checks. In both cases returns nil if the gem is not found.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'decidim-core/lib/decidim/dependency_resolver.rb', line 34

def lookup(gem)
  # In case the lookup method is called with a spec definition, return the
  # definition itself.
  return gem unless gem.is_a?(String)

  if bundler?
    lookup = Lookup.new
    return lookup.spec(gem) if cache.include?(gem)

    # Keep a local cache of the already processed gems in order to avoid
    # double lookups.
    lookup.find(Bundler.definition.dependencies, gem) do |dependency|
      if potential_module?(dependency.name)
        cache_miss = cache.exclude?(dependency.name)
        cache << dependency.name if cache_miss
        cache_miss
      else
        false
      end
    end
  else
    return unless potential_module?(gem)

    Gem.loaded_specs[gem]
  end
end

#module_path(spec) ⇒ String

Resolves the main load file path for module defined by the gem specification.

Parameters:

  • spec (Bundler::LazySpecification, Gem::Specification, String)

    The gem specification to test or the name of the gem.

Returns:

  • (String)

    A string pointing the path to the module’s main load file.



68
69
70
71
72
73
# File 'decidim-core/lib/decidim/dependency_resolver.rb', line 68

def module_path(spec)
  spec = lookup(spec)
  return unless spec

  "#{spec.full_gem_path}/lib/#{spec.name.gsub("-", "/")}.rb"
end

#needed?(spec) ⇒ Boolean

Resolves whether the target gem is needed or not, i.e. does it need to be loaded.

When bundler is available, the dependencies are resolved based on the modules defined in the Gemfile which means that the gem is always needed when it is reported as available by the resolver (i.e. defined as a dependency in the Gemfile itself or one of those gems depend on it).

When bundler is not available, this will check whether the Decidim module provided by the gem has been already required/loaded or not. In this situation, it is impossible to know whether the implementer has intended the gem to be used or not, so we assume in these situations implementers will load all the gems they want before running any initializers. This means that the ‘require “decidim/foo”` statements have to be listed for all modules before the initialization process.

Parameters:

  • spec (Bundler::LazySpecification, Gem::Specification, String)

    The gem specification to test or the name of the gem.

Returns:

  • (Boolean)

    A boolean indicating if the gem defined by the spec is needed.



122
123
124
125
126
127
128
129
130
131
# File 'decidim-core/lib/decidim/dependency_resolver.rb', line 122

def needed?(spec)
  spec = lookup(spec)
  return false unless spec

  if bundler?
    available?(spec)
  else
    loaded?(spec)
  end
end