module Origen::Loader::ModuleConstMissing
This is inspired by Rails’ ActiveSupport::Dependencies module.
Public Class Methods
append_features(base)
click to toggle source
Calls superclass method
# File lib/origen/loader.rb, line 212 def self.append_features(base) base.class_eval do # Emulate #exclude via an ivar return if defined?(@_const_missing) && @_const_missing @_const_missing = instance_method(:const_missing) remove_method(:const_missing) end super end
exclude_from(base)
click to toggle source
# File lib/origen/loader.rb, line 223 def self.exclude_from(base) base.class_eval do define_method :const_missing, @_const_missing @_const_missing = nil end end
Public Instance Methods
_load_const(file, name, altname = nil)
click to toggle source
@api_private
# File lib/origen/loader.rb, line 374 def _load_const(file, name, altname = nil) load file if defined?(@@pre_loading_controller) return if @@pre_loading_controller end @_checking_name = altname || name const = eval(altname || name) @_checking_name = nil if const Origen::Loader.record_const(altname || name) return const end msg ||= "uninitialized constant #{name} (expected it to be defined in: #{file})" _raise_uninitialized_constant_error(name, msg) end
_raise_uninitialized_constant_error(name, msg = nil)
click to toggle source
@api private
# File lib/origen/loader.rb, line 391 def _raise_uninitialized_constant_error(name, msg = nil) msg ||= "uninitialized constant #{name}" name_error = NameError.new(msg, name) name_error.set_backtrace(caller.reject { |l| l =~ /^#{__FILE__}/ }) fail name_error end
_sub_derivatives_from_end(new_val, dirs)
click to toggle source
@api private Substitutes a single occurrence of ‘derivatives’ in the given dirs array, starting from the end of it and replacing it with the given new value. Returns true if a substitution is made, else false.
# File lib/origen/loader.rb, line 234 def _sub_derivatives_from_end(new_val, dirs) subbed = false size = dirs.size - 1 dirs.reverse_each.with_index do |val, i| if val == 'derivatives' dirs[size - i] = new_val subbed = true break end end dirs if subbed end
const_missing(name)
click to toggle source
Allows classes and modules to be defined in app/blocks and app/lib without needing to require them and in the case of app/blocks to use a custom directory structure.
The first time a reference is made to a class or module name it will trigger this hook, and we then work out what the file name should be and require it.
# File lib/origen/loader.rb, line 252 def const_missing(name) if Origen.in_app_workspace? if self == Object name = name.to_s else name = "#{self}::#{name}" end return nil if @_checking_name == name names = name.split('::') namespace = names.shift if app = Origen::Application.from_namespace(namespace) # First we are going to check for a match in the app/blocks directory, this needs to be handled # specially since it follows a non-std structure, e.g. use of derivatives/ and sub_blocks/ folders # for organization without having them as part of the class name-spacing altname = nil dirs = [app.root, 'app', 'blocks'] names.each_with_index do |name, i| dirs << 'derivatives' unless i == 0 dirs << name.underscore end # Is this a reference to a model? if File.exist?(f = File.join(*dirs, 'model.rb')) model = _load_const(f, name) # Also load the model's controller if it exists if File.exist?(f = File.join(*dirs, 'controller.rb')) controller = _load_const(f, name + 'Controller') end return model end # Is this a reference to a controller? if dirs.last.to_s =~ /_controller$/ controller_reference = true dirs << dirs.pop.sub(/_controller$/, '') if File.exist?(f = File.join(*dirs, 'controller.rb')) return _load_const(f, name) end end # Is this a reference to a sub-block model or controller that is nested within a block? dirs_ = dirs.dup while dirs_ = _sub_derivatives_from_end('sub_blocks', dirs_) if controller_reference if File.exist?(f = File.join(*dirs_, 'controller.rb')) return _load_const(f, name) end elsif File.exist?(f = File.join(*dirs_, 'model.rb')) model = _load_const(f, name) # Also load the model's controller if it exists if File.exist?(f = File.join(*dirs_, 'controller.rb')) controller = _load_const(f, name + 'Controller') end return model end end # Is this a reference to a module that has been added to a model or controller? # In this case dirs contains something like: # [..., "my_model", "derivatives", "my_module"] # [..., "my_model_controller", "derivatives", "my_module"] # So let's try by transforming these into: # [..., "my_model", "model"] + "my_module.rb" # [..., "my_model", "controller"] + "my_module.rb" filename = dirs.pop + '.rb' dirs.pop # Lose 'derivatives' if dirs.last.to_s =~ /_controller$/ dirs << dirs.pop.sub(/_controller$/, '') dirs << 'controller' else dirs << 'model' end if File.exist?(f = File.join(*dirs, filename)) return _load_const(f, name) end # Now that we have established that it is not a reference to a block (which has a non-std code # organization structure), we can now check for a match in the app/lib directory following std # Ruby code organization conventions until names.empty? path = File.join(*names.map(&:underscore)) + '.rb' f = File.join(app.root, 'app', 'lib', namespace.underscore, path) if File.exist?(f) model = _load_const(f, name, altname) # Try and reference the controller to load it too, though don't raise an error if it # doesn't exist @@pre_loading_controller = true eval "#{altname || name}Controller" return model # If a folder exists that is named after this constant, then assume it is an otherwise # undeclared namespace module and declare it now elsif File.exist?(f.sub('.rb', '')) return const_set path.sub('.rb', '').camelcase, Module.new end # Don't waste time looking up the namespace hierarchy for the controller, if it exists it # should be within the exact same namespace as the model return nil if defined?(@@pre_loading_controller) && @@pre_loading_controller # Remove the highest level namespace and then search again in the parent namespace if discarded_namespace = names.delete_at(-2) altname ||= name altname = altname.sub("#{discarded_namespace}::", '') else names.pop end end _raise_uninitialized_constant_error(name) else _raise_uninitialized_constant_error(name) end else _raise_uninitialized_constant_error(name) end ensure @@pre_loading_controller = false end