Module: Origen::Loader::ModuleConstMissing
- Defined in:
- lib/origen/loader.rb
Overview
This is inspired by Rails' ActiveSupport::Dependencies module.
Class Method Summary collapse
Instance Method Summary collapse
- #_load_const(file, name, altname = nil) ⇒ Object
- #_raise_uninitialized_constant_error(name, msg = nil) ⇒ Object private
-
#_sub_derivatives_from_end(new_val, dirs) ⇒ Object
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.
-
#const_missing(name) ⇒ Object
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.
Class Method Details
.append_features(base) ⇒ Object
212 213 214 215 216 217 218 219 220 221 |
# 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) ⇒ Object
223 224 225 226 227 228 |
# 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 |
Instance Method Details
#_load_const(file, name, altname = nil) ⇒ Object
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
# 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) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
391 392 393 394 395 396 |
# 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) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
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.
234 235 236 237 238 239 240 241 242 243 244 245 |
# 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) ⇒ Object
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.
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# 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 |