Class: Tap::Env::Constant
- Inherits:
-
Object
- Object
- Tap::Env::Constant
- Defined in:
- lib/tap/env/constant.rb
Overview
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.
–
Rationale for that last statement
Scripts that require other files will not re-require the other files because unload doesn’t remove the other files from $“. Likewise scripts that define other constants effectively overwrite the existing constant; that may or may not be a big deal, but it can cause warnings. Moreover, if a script actually DOES something (like create a file), that something will be repeated when it gets re-required.
Constant Summary collapse
- CONST_REGEXP =
Matches a valid constant. After the match:
$1:: The unqualified constant (ex 'Const' for '::Const')
/\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/
Instance Attribute Summary collapse
-
#const_name ⇒ Object
readonly
The full constant name.
-
#require_paths ⇒ Object
readonly
An array of paths that will be required when the constantize is called and the constant does not exist.
-
#types ⇒ Object
readonly
A hash of (type, summary) pairs used to classify self.
Class Method Summary collapse
- .cast(obj) ⇒ Object
-
.constantize(const_name, const = Object) ⇒ Object
Constantize tries to look up the specified constant under const.
-
.scan(dir, pattern = "**/*.rb") ⇒ Object
Scans the directory and pattern for constants.
Instance Method Summary collapse
-
#<=>(another) ⇒ Object
Peforms comparison of the const_name of self vs another.
-
#==(another) ⇒ Object
True if another is a Constant with the same const_name, require_path, and comment as self.
-
#basename ⇒ Object
Returns the basename of path.
-
#constantize(autorequire = true) ⇒ Object
Looks up and returns the constant indicated by const_name.
-
#dirname ⇒ Object
Returns the path, minus the basename of path.
-
#initialize(const_name, *require_paths) ⇒ Constant
constructor
Initializes a new Constant with the specified constant name, and require_paths.
-
#inspect ⇒ Object
Returns a string like:.
-
#name ⇒ Object
Returns the name of the constant, minus nesting.
-
#nesting ⇒ Object
Returns the nesting constant of const_name.
-
#nesting_depth ⇒ Object
Returns the number of constants in nesting.
-
#path ⇒ Object
Returns the underscored const_name.
- #path_match?(head, tail = nil) ⇒ Boolean
-
#register_as(type, summary = nil, override = false) ⇒ Object
Registers the type and summary with self.
- #relative_path ⇒ Object
-
#to_s ⇒ Object
Returns const_name.
- #type_match?(type) ⇒ Boolean
-
#unload(unrequire = true) ⇒ Object
Undefines the constant indicated by const_name.
Constructor Details
#initialize(const_name, *require_paths) ⇒ Constant
Initializes a new Constant with the specified constant name, and require_paths. Raises an error if const_name is not valid.
157 158 159 160 161 |
# File 'lib/tap/env/constant.rb', line 157 def initialize(const_name, *require_paths) @types = {} @const_name = normalize(const_name) @require_paths = require_paths end |
Instance Attribute Details
#const_name ⇒ Object (readonly)
The full constant name
146 147 148 |
# File 'lib/tap/env/constant.rb', line 146 def const_name @const_name end |
#require_paths ⇒ Object (readonly)
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.
150 151 152 |
# File 'lib/tap/env/constant.rb', line 150 def require_paths @require_paths end |
#types ⇒ Object (readonly)
A hash of (type, summary) pairs used to classify self.
153 154 155 |
# File 'lib/tap/env/constant.rb', line 153 def types @types end |
Class Method Details
.cast(obj) ⇒ Object
113 114 115 116 117 118 119 120 |
# File 'lib/tap/env/constant.rb', line 113 def cast(obj) case obj when String then new(obj) when Module then new(obj.to_s) when Constant then obj else raise ArgumentError, "not a constant or constant name: #{obj.inspect}" end end |
.constantize(const_name, const = Object) ⇒ Object
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.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/tap/env/constant.rb', line 69 def constantize(const_name, const=Object) # :yields: const, missing_const_names unless CONST_REGEXP =~ const_name raise NameError, "#{const_name.inspect} is not a valid constant name!" end constants = $1.split(/::/) while !constants.empty? unless const_is_defined?(const, constants[0]) if block_given? return yield(const, constants) else raise NameError.new("uninitialized constant #{const_name}", constants[0]) end end const = const.const_get(constants.shift) end const end |
.scan(dir, pattern = "**/*.rb") ⇒ Object
Scans the directory and pattern for constants.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/tap/env/constant.rb', line 89 def scan(dir, pattern="**/*.rb") constants = {} root = Root.new(dir) root.glob(pattern).each do |path| Lazydoc::Document.scan(File.read(path)) do |const_name, type, summary| require_path = root.relative_path(path) if const_name.empty? extname = File.extname(path) const_name = require_path.chomp(extname).camelize end constant = (constants[const_name] ||= new(const_name)) constant.register_as(type, summary) constant.require_paths << require_path end end constants = constants.values constants.each {|constant| constant.require_paths.uniq! } constants end |
Instance Method Details
#<=>(another) ⇒ Object
Peforms comparison of the const_name of self vs another.
224 225 226 |
# File 'lib/tap/env/constant.rb', line 224 def <=>(another) const_name <=> another.const_name end |
#==(another) ⇒ Object
True if another is a Constant with the same const_name, require_path, and comment as self.
217 218 219 220 221 |
# File 'lib/tap/env/constant.rb', line 217 def ==(another) another.kind_of?(Constant) && another.const_name == self.const_name && another.require_paths == self.require_paths end |
#basename ⇒ Object
179 180 181 |
# File 'lib/tap/env/constant.rb', line 179 def basename @basename ||= File.basename(path) end |
#constantize(autorequire = true) ⇒ Object
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.
244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/tap/env/constant.rb', line 244 def constantize(autorequire=true) Constant.constantize(const_name) do break unless autorequire require_paths.each do |require_path| require require_path end Constant.constantize(const_name) end end |
#dirname ⇒ Object
187 188 189 |
# File 'lib/tap/env/constant.rb', line 187 def dirname @dirname ||= File.dirname(path) end |
#inspect ⇒ Object
Returns a string like:
"#<Tap::Env::Constant:object_id Const::Name (require_path)>"
299 300 301 |
# File 'lib/tap/env/constant.rb', line 299 def inspect "#<#{self.class}:#{object_id} #{const_name} #{require_paths.inspect}>" end |
#name ⇒ Object
195 196 197 |
# File 'lib/tap/env/constant.rb', line 195 def name @name ||= (const_name =~ /.*::(.*)\z/ ? $1 : const_name) end |
#nesting ⇒ Object
203 204 205 |
# File 'lib/tap/env/constant.rb', line 203 def nesting @nesting ||= (const_name =~ /(.*)::.*\z/ ? $1 : '') end |
#nesting_depth ⇒ Object
211 212 213 |
# File 'lib/tap/env/constant.rb', line 211 def nesting_depth @nesting_depth ||= nesting.split(/::/).length end |
#path ⇒ Object
171 172 173 |
# File 'lib/tap/env/constant.rb', line 171 def path @path ||= "/#{relative_path}" end |
#path_match?(head, tail = nil) ⇒ Boolean
283 284 285 |
# File 'lib/tap/env/constant.rb', line 283 def path_match?(head, tail=nil) (head.nil? || head.empty? || head_match(head)) && (tail.nil? || tail.empty? || tail_match(tail)) end |
#register_as(type, summary = nil, override = false) ⇒ Object
Registers the type and summary with self. Raises an error if self is already registerd as the type and override is false.
230 231 232 233 234 235 236 237 |
# File 'lib/tap/env/constant.rb', line 230 def register_as(type, summary=nil, override=false) if types.include?(type) && types[type] != summary && !override raise "already registered as a #{type.inspect} (#{const_name})" end types[type] = summary self end |
#relative_path ⇒ Object
163 164 165 |
# File 'lib/tap/env/constant.rb', line 163 def relative_path @relative_path ||= const_name.underscore end |
#to_s ⇒ Object
Returns const_name
304 305 306 |
# File 'lib/tap/env/constant.rb', line 304 def to_s const_name end |
#type_match?(type) ⇒ Boolean
287 288 289 290 291 292 293 |
# File 'lib/tap/env/constant.rb', line 287 def type_match?(type) case type when nil then true when Array then type.any? {|t| types.has_key?(t) } else types.has_key?(type) end end |
#unload(unrequire = true) ⇒ Object
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.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/tap/env/constant.rb', line 266 def unload(unrequire=true) const = nesting.empty? ? Object : Constant.constantize(nesting) { Object } if const.const_defined?(name) require_paths.each do |require_path| require_path = File.extname(require_path).empty? ? "#{require_path}.rb" : require_path regexp = /#{require_path}$/ $".delete_if {|path| path =~ regexp } end if unrequire return const.send(:remove_const, name) end nil end |