Module: Metamorphosis
- Extended by:
- Metamorphosis
- Included in:
- Metamorphosis
- Defined in:
- lib/metamorphosis/core.rb,
lib/metamorphosis/helpers.rb,
lib/metamorphosis/version.rb
Overview
This module, the main one, is responsible for all hooks setup and extension mechanisms. Therefore, if you have a module or a class which would benefit from Metamorphosis features, just extend Metamorphosis:
MyProject.extend Metamorphosis
Obviously this may look more like this in real-code:
module MyProject
extend Metamorphosis
end
By extending Metamorphosis, MyModule is able to call activate
and a bunch of attr_reader as class methods:
receiver(would returnMyProjectconstant, ie.self)base_path(Pathname path of the directory of the file whereMyProjectextended Metamorphosis)spells_path(Pathname path of the spells root directory)redefinable(list of allMyProject's modules and classes spells may be defined against, ie. the public API from Metamorphosis standing point)spells(list of all activated spells forMyProject).
Be aware of the fact that, since Metamorphosis is extended, the receiver does not gain Metamorphosis class methods, which are part of the private API somehow. They are documented nontheless so as to give you some more hints about Metamorphosis internals.
Defined Under Namespace
Modules: RedefInit Classes: MetamorphosisError
Constant Summary
- CALLERS_TO_IGNORE =
paths to be ignored when looking for the receiver's path
[ /\/metamorphosis(\/(core|helpers))?\.rb$/, # all metamorphosis code #/\(.*\)/, # any generated code /custom_require\.rb$/, # rubygems require hacks ]
- MAJOR =
0- MINOR =
1- PATCH =
0- VERSION =
[MAJOR, MINOR, PATCH].join('.')
Class Method Summary (collapse)
-
+ (Boolean) activate!(spell_name, receiver, *syms)
The activation process really takes place here.
-
+ (Object) activate_on_instance(instance, spell_name, spell_module)
Activate a plugin for a specific instance object.
-
+ (Object) caller_files
Like Kernel#caller but excluding certain magic entries and without line/method information; the resulting array contains filenames only.
- + (Object) caller_locations
- + (Object) included(base)
-
+ (String) inner_spell_module_from(name)
Extracts a sub-const string matching a spell module from a valid receiver constant.
-
+ (Object) receiver_base_path
Returns the full path of the script Metamorphosis has been called from, following the
extend Metamorphosisinstruction. -
+ (Const?) receiver_constant_for(name, *syms)
Retrieve the constant under the receiver namespace for a given name.
Instance Method Summary (collapse)
-
- (Boolean) activate(spell_name, *syms)
Activate a spell.
Class Method Details
+ (Boolean) activate!(spell_name, receiver, *syms)
The activation process really takes place here.
Called by activate which is part of the public API. This method registers hooks between the receiver and the spell, taking general or specific configuration settings into account.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/metamorphosis/core.rb', line 106 def self.activate!(spell_name, receiver, *syms) #begin = syms.flatten!. # TODO: handle camelcased or underscored or capitalized spell name spell_name = spell_name.capitalize # TODO: read config file (generic or specific) # first, load the spell begin spell_path = Pathname.new(@@spells_path.to_s + "/" + spell_name.downcase) require spell_path.to_s rescue LoadError => e puts e abort "You tried to load a spell which does not exist (#{spell_name})." end # then, fetch the spell const begin spell = @@receiver.constant("Spells").constant(spell_name) rescue => e puts e abort "Invalid definition for spell \"#{spell_name}\". Please check #{spell_path.to_s + ".rb"}" end # process what's inside the spell definition spell.fetch_nested(recursive: true, only: :modules) do |e| #puts "************************" #puts "#{e.inspect} (#{receiver_constant_for inner_spell_module_from e.name})" #puts "************************" if receiver_constant_for inner_spell_module_from e.name # this module exists within the receiver, we're heading to redefs # let's say e is Receiver::Spells::ASpell::AModule::Nested::Again, e = e.name.split("::")[3..-1] # now e is AModule::Nested::Again # some special cases related to "Convention over Configuration" case e.last when "InstanceMethods" # this module handles redefs for instance methods of e[-2] #puts "case: InstanceMethods" #puts e.inspect #puts @@redefinable e_match = receiver_constant_for(e[0..-2].join("::")) #puts e_match if [:retroactive] ObjectSpace.each_object(e_match) { |x| activate_on_instance x, spell_name, e } end e_match.extend RedefInit unless @@redefinable[e_match][spell] and @@redefinable[e_match][spell].include? :instance_methods (@@redefinable[e_match][spell] ||= []) << :instance_methods end e.pop #puts when "ClassMethods" #puts "case: ClassMethods" #puts e.inspect #pp @@redefinable e_match = receiver_constant_for(e[0..-2].join("::")) e.pop unless @@redefinable[e_match][spell] and @@redefinable[e_match][spell].include? :class_methods (@@redefinable[e_match][spell] ||= []) << :class_methods end # pending #puts else #puts "case: No smart-convention provided" #puts e.inspect #puts @@redefinable # pending e_match = receiver_constant_for(e[0..-1].join("::")) #puts e_match.inspect unless @@redefinable[e_match][spell] and @@redefinable[e_match][spell].include? :fresh (@@redefinable[e_match][spell] ||= []) << :fresh end #puts end #puts @@redefinable #puts #puts else # this module does not exist within the receiver #puts "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Connard" end end @@spells << spell_name # TODO: unpack as an alternative to the default hook processing #spell.unpack if spell.respond_to?(:unpack) return true #rescue Exception => msg #puts msg #return false #end end |
+ (Object) activate_on_instance(instance, spell_name, spell_module)
Activate a plugin for a specific instance object.
An instance method accessor may be provided so the receiver can play too.
286 287 288 |
# File 'lib/metamorphosis/core.rb', line 286 def self.activate_on_instance instance, spell_name, spell_module instance.extend receiver_constant_for("Spells").constant(spell_name).constant(spell_module.join("::")) end |
+ (Object) caller_files
Like Kernel#caller but excluding certain magic entries and without line/method information; the resulting array contains filenames only
29 30 31 |
# File 'lib/metamorphosis/helpers.rb', line 29 def self.caller_files caller_locations.map { |file,line| file } end |
+ (Object) caller_locations
20 21 22 23 |
# File 'lib/metamorphosis/helpers.rb', line 20 def self.caller_locations caller.map { |line| line.split(/:(?=\d|in )/)[0,2] } .reject { |file, line| CALLERS_TO_IGNORE.any? { |pattern| file =~ pattern } } end |
+ (Object) included(base)
60 61 62 |
# File 'lib/metamorphosis/core.rb', line 60 def self.included base raise MetamorphosisError, "Metamorphosis must be extended, not included (for #{base})." end |
+ (String) inner_spell_module_from(name)
Extracts a sub-const string matching a spell module from a valid receiver constant.
269 270 271 272 273 274 275 276 |
# File 'lib/metamorphosis/core.rb', line 269 def self.inner_spell_module_from name begin receiver_constant_for name # check validity return name.split("::")[3..-1].join("::") rescue Exception => msg raise ArgumentError, "Invalid receiver constant (#{msg})" end end |
+ (Object) receiver_base_path
Returns the full path of the script Metamorphosis has
been called from, following the extend Metamorphosis
instruction.
36 37 38 |
# File 'lib/metamorphosis/helpers.rb', line 36 def self.receiver_base_path Pathname.new(caller_files.first).realpath.dirname end |
+ (Const?) receiver_constant_for(name, *syms)
Retrieve the constant under the receiver namespace for a given name. Handles nested namespaces. This method does not perform any smart look-up, hence name must be a complete [nested] namespace[s] path under the receiver's domain.
Given the following structure:
module Base
module Foo
module Bar
Class ChunkyBacon
# ...
end
end
end
end
and calls performed inside +Base+:
252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/metamorphosis/core.rb', line 252 def self.receiver_constant_for name, *syms = syms. name = name.join("::") if name.is_a? Array begin name.gsub!(/::(InstanceMethods|ClassMethods)$/, "") unless [:full_path] @@receiver.constant name rescue Exception return nil end end |
Instance Method Details
- (Boolean) activate(spell_name, *syms)
Activate a spell.
Must be called by the receiver, ie. the module or class which called
extend Metamorphosis.
53 54 55 |
# File 'lib/metamorphosis/core.rb', line 53 def activate spell_name, *syms return Metamorphosis.activate!(spell_name, self, syms) end |