Class: DSLHelper

Inherits:
Object show all
Defined in:
lib/waw/utils/dsl_helper.rb

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

Instance Method Summary collapse

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)

Returns:



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

#restoreObject

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

#saveObject

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