Class: DSLHelper
Overview
Provides a simple but powerful way to create non intrusive DSLs. Typical use cases are described below.
Punctual DSL usage
A given user block uses your DSL punctually. As the later installs potentially intrusive methods in some Ruby core classes, you would like your DSL to be installed, user block executed and Ruby methods restored immediately after.
DSLHelper.new(String => [:upcase], Symbol => [:/, :&, :|]) do
load 'mydsl.rb' # install your DSL first
yield if block_given? # yield user block
end
After the DSLHelper new invocation, all Ruby methods are correctly restored.
Middle-case DSL usage
Your DSL must be installed punctually but you cannot encapsulate its usage in a single DSLHelper.new invocation. Calling save and restore methods is the alternative. However, their invocation MUST respect (save restore)* invocation regexp otherwise a RuntimeError is raised.
helper = DSLHelper.new(String => [:upcase], Symbol => [:/, :&, :|])
helper.save # save Ruby methods that you plan to override
load 'mydsl.rb' # install your DSL
[...] # do anything you want, here of somewhere
helper.restore # restore Ruby methods when finished
Long-term DSL usage
Your DSL (or more generically, ruby extensions you’ve written) is used during the whole execution of the program. You hope that it is not intrusive (you only add methods that doesn’t already exist to some Ruby classes). You would like to check this hypothesis (because Ruby evolves, your code may be used in conjunction with third party libraries that install their own methods, etc.)
if DSLHelper.is_intrusive?(String => [:upcase], Symbol => [:/, :&, :|])
STDERR << "WARNING: this library seems to override Ruby existing methods"
end
load 'ruby_extensions.rb'
Class Method Summary collapse
-
.find_instance_method(mod, method) ⇒ Object
Finds a method inside a Module, or returns nil.
-
.is_intrusive?(hash) ⇒ Boolean
Checks if a set of methods that your DSL is planning to install makes it an intrusive DSL (overriding existing Ruby or third party methods).
Instance Method Summary collapse
-
#initialize(hash) ⇒ DSLHelper
constructor
Creates a DSLHelper instance.
-
#restore ⇒ Object
Restores all methods previously saved.
-
#save ⇒ Object
Saves all methods of the definition hash in an internal data structure.
Constructor Details
#initialize(hash) ⇒ DSLHelper
Creates a DSLHelper instance. hash must be a Hash instance mapping modules to array of symbols. If a block is given, the Ruby state is immediately saved, block is yield and state is restored after that.
60 61 62 63 64 65 66 67 68 |
# File 'lib/waw/utils/dsl_helper.rb', line 60 def initialize(hash) @definition = hash @saved = nil if block_given? save yield restore end end |
Class Method Details
.find_instance_method(mod, method) ⇒ Object
Finds a method inside a Module, or returns nil.
83 84 85 86 87 |
# File 'lib/waw/utils/dsl_helper.rb', line 83 def self.find_instance_method(mod, method) mod.instance_method(method) rescue NameError nil end |
.is_intrusive?(hash) ⇒ Boolean
Checks if a set of methods that your DSL is planning to install makes it an intrusive DSL (overriding existing Ruby or third party methods)
73 74 75 76 77 78 79 80 |
# File 'lib/waw/utils/dsl_helper.rb', line 73 def self.is_intrusive?(hash) hash.each_pair do |mod, methods| methods.each do |method| return true if mod.method_defined?(method) end end false end |
Instance Method Details
#restore ⇒ Object
Restores all methods previously saved. This method raises an error if no save call has been made before.
102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/waw/utils/dsl_helper.rb', line 102 def restore raise "save has not been called previously." if @saved.nil? @definition.each_pair do |mod, methods| methods.zip(@saved[mod]).each do |name, saved| if saved.nil? mod.send(:remove_method, name) if mod.method_defined?(name) elsif mod.send(:define_method, name, saved) end end end @saved = nil end |
#save ⇒ Object
Saves all methods of the definition hash in an internal data structure. This method raises an error is a save is already pending.
92 93 94 95 96 97 98 |
# File 'lib/waw/utils/dsl_helper.rb', line 92 def save raise "save already pending" unless @saved.nil? @saved = {} @definition.each_pair do |mod, methods| @saved[mod] = methods.collect {|m| DSLHelper.find_instance_method(mod,m)} end end |