Class Tap::Env::Constant
In: lib/tap/env/constant.rb
Parent: Object

A Constant serves as a placeholder for an actual constant, sort of like autoload. Use the constantize method to retrieve the actual constant; if it doesn‘t exist, constantize requires require_path and tries again.

  Object.const_defined?(:Net)                      # => false
  $".grep(/net\/http.rb$/).empty?                  # => true

  http = Constant.new('Net::HTTP', 'net/http.rb')
  http.constantize                                 # => Net::HTTP
  $".grep(/net\/http.rb$/).empty?                  # => false

Unloading

Constant also supports constant unloading. Unloading can be useful in various development modes, but may cause code to behave unpredictably. When a Constant unloads, the constant value is removed from the nesting constant and the require paths are removed from $". This allows a require statement to re-require, and in theory, reload the constant.

  # [simple.rb]
  # class Simple
  # end

  const = Constant.new('Simple', 'simple')
  const.constantize                                # => Simple
  Object.const_defined?(:Simple)                   # => true

  const.unload                                     # => Simple
  Object.const_defined?(:Simple)                   # => false

  const.constantize                                # => Simple
  Object.const_defined?(:Simple)                   # => true

Unloading and reloading works best for scripts that have no side effects; ie scripts that do not require other files and only define the specified class or module.

Methods

<=>   ==   basename   cast   constantize   constantize   dirname   inspect   name   nesting   nesting_depth   new   path   path_match?   register_as   relative_path   scan   to_s   type_match?   unload  

Constants

CONST_REGEXP = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/   Matches a valid constant. After the match:
  $1:: The unqualified constant (ex 'Const' for '::Const')

Attributes

const_name  [R]  The full constant name
require_paths  [R]  An array of paths that will be required when the constantize is called and the constant does not exist. Require paths are required in order.
types  [R]  A hash of (type, summary) pairs used to classify self.

Public Class methods

[Source]

     # File lib/tap/env/constant.rb, line 113
113:         def cast(obj)
114:           case obj
115:           when String   then new(obj)
116:           when Module   then new(obj.to_s)
117:           when Constant then obj
118:           else raise ArgumentError, "not a constant or constant name: #{obj.inspect}"
119:           end
120:         end

Constantize tries to look up the specified constant under const. A block may be given to manually look up missing constants; the last existing const and any non-existant constant names are yielded to the block, which is expected to return the desired constant. For instance in the example ‘Non::Existant’ is essentially mapping to ConstName.

  module ConstName; end

  Constant.constantize('ConstName')                     # => ConstName
  Constant.constantize('Non::Existant') { ConstName }   # => ConstName

Raises a NameError for invalid/missing constants.

[Source]

    # File lib/tap/env/constant.rb, line 69
69:         def constantize(const_name, const=Object) # :yields: const, missing_const_names
70:           unless CONST_REGEXP =~ const_name
71:             raise NameError, "#{const_name.inspect} is not a valid constant name!"
72:           end
73:         
74:           constants = $1.split(/::/)
75:           while !constants.empty?
76:             unless const_is_defined?(const, constants[0])
77:               if block_given? 
78:                 return yield(const, constants)
79:               else
80:                 raise NameError.new("uninitialized constant #{const_name}", constants[0]) 
81:               end
82:             end
83:             const = const.const_get(constants.shift)
84:           end
85:           const
86:         end

Initializes a new Constant with the specified constant name, and require_paths. Raises an error if const_name is not valid.

[Source]

     # File lib/tap/env/constant.rb, line 157
157:       def initialize(const_name, *require_paths)
158:         @types = {}
159:         @const_name = normalize(const_name)
160:         @require_paths = require_paths
161:       end

Scans the directory and pattern for constants.

[Source]

     # File lib/tap/env/constant.rb, line 89
 89:         def scan(dir, pattern="**/*.rb")
 90:           constants = {}
 91:           
 92:           root = Root.new(dir)
 93:           root.glob(pattern).each do |path|
 94:             Lazydoc::Document.scan(File.read(path)) do |const_name, type, summary|
 95:               require_path = root.relative_path(path)
 96:               
 97:               if const_name.empty?
 98:                 extname = File.extname(path)
 99:                 const_name = require_path.chomp(extname).camelize
100:               end
101:               
102:               constant = (constants[const_name] ||= new(const_name))
103:               constant.register_as(type, summary)
104:               constant.require_paths << require_path
105:             end
106:           end
107: 
108:           constants = constants.values
109:           constants.each {|constant| constant.require_paths.uniq! }
110:           constants
111:         end

Public Instance methods

Peforms comparison of the const_name of self vs another.

[Source]

     # File lib/tap/env/constant.rb, line 224
224:       def <=>(another)
225:         const_name <=> another.const_name
226:       end

True if another is a Constant with the same const_name, require_path, and comment as self.

[Source]

     # File lib/tap/env/constant.rb, line 217
217:       def ==(another)
218:         another.kind_of?(Constant) && 
219:         another.const_name == self.const_name &&
220:         another.require_paths == self.require_paths
221:       end

Returns the basename of path.

  Constant.new("Const::Name").basename       # => 'name'

[Source]

     # File lib/tap/env/constant.rb, line 179
179:       def basename
180:         @basename ||= File.basename(path)
181:       end

Looks up and returns the constant indicated by const_name. If the constant cannot be found, constantize requires the require_paths in order and tries again.

Raises a NameError if the constant cannot be found.

[Source]

     # File lib/tap/env/constant.rb, line 244
244:       def constantize(autorequire=true)
245:         Constant.constantize(const_name) do
246:           break unless autorequire
247:           
248:           require_paths.each do |require_path|
249:             require require_path
250:           end
251:           
252:           Constant.constantize(const_name)
253:         end
254:       end

Returns the path, minus the basename of path.

  Constant.new("Const::Name").dirname        # => '/const'

[Source]

     # File lib/tap/env/constant.rb, line 187
187:       def dirname
188:         @dirname ||= File.dirname(path)
189:       end

Returns a string like:

  "#<Tap::Env::Constant:object_id Const::Name (require_path)>"

[Source]

     # File lib/tap/env/constant.rb, line 299
299:       def inspect
300:         "#<#{self.class}:#{object_id} #{const_name} #{require_paths.inspect}>"
301:       end

Returns the name of the constant, minus nesting.

  Constant.new("Const::Name").name           # => 'Name'

[Source]

     # File lib/tap/env/constant.rb, line 195
195:       def name
196:         @name ||= (const_name =~ /.*::(.*)\z/ ? $1 : const_name)
197:       end

Returns the nesting constant of const_name.

  Constant.new("Const::Name").nesting        # => 'Const'

[Source]

     # File lib/tap/env/constant.rb, line 203
203:       def nesting
204:         @nesting ||= (const_name =~ /(.*)::.*\z/ ? $1 : '')
205:       end

Returns the number of constants in nesting.

  Constant.new("Const::Name").nesting_depth  # => 1

[Source]

     # File lib/tap/env/constant.rb, line 211
211:       def nesting_depth
212:         @nesting_depth ||= nesting.split(/::/).length
213:       end

Returns the underscored const_name.

  Constant.new("Const::Name").path           # => '/const/name'

[Source]

     # File lib/tap/env/constant.rb, line 171
171:       def path
172:         @path ||= "/#{relative_path}"
173:       end

[Source]

     # File lib/tap/env/constant.rb, line 283
283:       def path_match?(head, tail=nil)
284:         (head.nil? || head.empty? || head_match(head)) && (tail.nil? || tail.empty? || tail_match(tail))
285:       end

Registers the type and summary with self. Raises an error if self is already registerd as the type and override is false.

[Source]

     # File lib/tap/env/constant.rb, line 230
230:       def register_as(type, summary=nil, override=false)
231:         if types.include?(type) && types[type] != summary && !override
232:           raise "already registered as a #{type.inspect} (#{const_name})"
233:         end
234:         
235:         types[type] = summary
236:         self
237:       end

[Source]

     # File lib/tap/env/constant.rb, line 163
163:       def relative_path
164:         @relative_path ||= const_name.underscore
165:       end

Returns const_name

[Source]

     # File lib/tap/env/constant.rb, line 304
304:       def to_s
305:         const_name
306:       end

[Source]

     # File lib/tap/env/constant.rb, line 287
287:       def type_match?(type)
288:         case type
289:         when nil   then true
290:         when Array then type.any? {|t| types.has_key?(t) } 
291:         else types.has_key?(type)
292:         end
293:       end

Undefines the constant indicated by const_name. The nesting constants are not removed. If specified, the require_paths will be removed from $".

When removing require_path, unload will add ’.rb’ to the require_path if require_path has no extension (this echos the behavior of require). Other extension names like ’.so’, ’.dll’, etc. are not tried and will not be removed.

Does nothing if const_name doesn‘t exist. Returns the unloaded constant. Obviously, this method should be used with caution.

[Source]

     # File lib/tap/env/constant.rb, line 266
266:       def unload(unrequire=true)
267:         const = nesting.empty? ? Object : Constant.constantize(nesting) { Object }
268:       
269:         if const.const_defined?(name)
270:           require_paths.each do |require_path|
271:             require_path = File.extname(require_path).empty? ? "#{require_path}.rb" : require_path
272:             regexp = /#{require_path}$/
273:             
274:             $".delete_if {|path| path =~ regexp }
275:           end if unrequire
276:         
277:           return const.send(:remove_const, name)
278:         end
279:       
280:         nil
281:       end

[Validate]