Module: Namespace

Defined in:
lib/namespace.rb

Overview

Namespaces for ruby.

See Namespace.open and Namespace#import for more details

Defined Under Namespace

Modules: Ext Classes: ConflictError, ImportError

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.cacheObject (readonly)

Returns the value of attribute cache.



8
9
10
# File 'lib/namespace.rb', line 8

def cache
  @cache
end

.load_pathObject (readonly)

Returns the value of attribute load_path.



9
10
11
# File 'lib/namespace.rb', line 9

def load_path
  @load_path
end

Class Method Details

.basename(namespace) ⇒ Object

Returns the top name of a namespace

Example: basename(“some::deep::namespace”) => “namespace”



65
66
67
# File 'lib/namespace.rb', line 65

def basename(namespace)
  normalize(namespace).gsub(/.*::/,'')
end

.included(mod) ⇒ Object

Raises a ScriptError if the module is included

Raises:

  • (ScriptError)


70
71
72
# File 'lib/namespace.rb', line 70

def included(mod)
  raise ScriptError, "Namespace is not designed to be included, only extended. See #{mod}"
end

.normalize(namespace) ⇒ Object

Transforms a string into a normalized namespace identifier



58
59
60
# File 'lib/namespace.rb', line 58

def normalize(namespace)
  namespace.to_s.sub(/^[\/:\s]*/, '').sub(/[\/:\s]*$/, '').gsub(/[\/\\:]+/, '::')
end

.open(namespace) ⇒ Object

Loads a file according to the namespace in the context of a module, and returns that object.

It’s not a Python import, where the namespace is available. What you get here, is a sort of Sandbox where constants defined in the file are not polluting the global namespace but available on the returned object. Since the returned object is a Module, it can be included in the current module if you’re also in the context of a “Sandbox”.

#import has been chosen because the ruby namespace is already crowded (require, load and include are already taken)

Raises:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/namespace.rb', line 22

def open(namespace)
  namespace = normalize(namespace)
  
  # Cache lookup
  ns = @cache[namespace]
  return ns if ns
  
  # File lookup
  file_path = namespace.gsub(':', File::SEPARATOR) + '.rb'
  file = @load_path.inject(nil) do |file, load_path|
    path = File.join(load_path, file_path)
    File.exists?(path) ? path : file
  end

  raise ImportError, "no such file to load -- #{file_path}" unless file

  file_content = File.read(file)
  # Pre-process __NAMESPACE__ keyword to act like __FILE__
  # in know... it's not exactly the same... (eg "__FILE__") but it should do the trick
  file_content.gsub!('__NAMESPACE__', namespace.inspect)

  ns = Module.new
  ns.instance_variable_set("@__namespace__", namespace)
  # Allow calling methods in self, like in global namespace
  ns.extend(ns)
  # Adds the #import methods from the DSL
  ns.extend(Namespace, Namespace::Ext)
  ns.module_eval(file_content, file)

  # Cache
  @cache[namespace] = ns

  return ns
end

Instance Method Details

#import(namespace, options = {}) ⇒ Object

options = select the imported name

THINK: since variable is already returned, is :as necessary?

Raises:



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/namespace.rb', line 80

def import(namespace, options={})
  mod = Namespace::open(namespace)

  name = options[:as] || options['as'] || Namespace.basename(namespace)
  raise ConflictError, "name `#{name}` is already taken" if method_defined?(name)
  instance_variable_set("@#{name}", mod)
  attr_reader name
  private name

  mod
end