Module: Blockenspiel::DSLSetupMethods
- Defined in:
- lib/blockenspiel/dsl_setup.rb
Overview
DSL setup methods
These class methods are available after you have included the Blockenspiel::DSL module.
By default, a class that has DSL capability will automatically make
all public methods available to parameterless blocks, except for the
initialize method, any methods whose names begin with an underscore,
and any methods whose names end with an equals sign.
If you want to change this behavior, use the directives defined here to control exactly which methods are available to parameterless blocks.
Class Method Summary collapse
-
._setup_class(klass_) ⇒ Object
Set up a class.
-
.extended(klass_) ⇒ Object
Called when DSLSetupMethods extends a class.
Instance Method Summary collapse
-
#_blockenspiel_auto_dsl_method(symbol_) ⇒ Object
Automatically make the given method a DSL method according to the current setting.
-
#_blockenspiel_custom_include(*modules_) ⇒ Object
Custom include method.
-
#_get_blockenspiel_delegate(name_) ⇒ Object
Get information on the given DSL method name.
-
#_get_blockenspiel_module ⇒ Object
Get this class's corresponding DSL module.
-
#dsl_attr_accessor(*names_) ⇒ Object
A DSL-friendly attr_accessor.
-
#dsl_attr_writer(*names_) ⇒ Object
A DSL-friendly attr_writer.
-
#dsl_method(name_, delegate_ = nil) ⇒ Object
Make a particular method available to parameterless DSL blocks.
-
#dsl_methods(*names_) ⇒ Object
Control the behavior of methods with respect to parameterless blocks, or make a list of methods available to parameterless blocks in bulk.
-
#method_added(symbol_) ⇒ Object
Hook called when a method is added.
Class Method Details
._setup_class(klass_) ⇒ Object
Set up a class. Creates a DSL module for this class, optionally delegating to the superclass's module. Also initializes the class's methods hash and active flag.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/blockenspiel/dsl_setup.rb', line 88 def self._setup_class(klass_) # :nodoc: superclass_ = klass_.superclass superclass_ = nil unless superclass_.respond_to?(:_get_blockenspiel_module) mod_ = ::Module.new if superclass_ mod_.module_eval do include superclass_._get_blockenspiel_module end end klass_.instance_variable_set(:@_blockenspiel_superclass, superclass_) klass_.instance_variable_set(:@_blockenspiel_module, mod_) klass_.instance_variable_set(:@_blockenspiel_methods, {}) klass_.instance_variable_set(:@_blockenspiel_active, nil) end |
.extended(klass_) ⇒ Object
Called when DSLSetupMethods extends a class. This sets up the current class, and adds a hook that causes any subclass of the current class also to be set up.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/blockenspiel/dsl_setup.rb', line 65 def self.extended(klass_) unless klass_.instance_variable_defined?(:@_blockenspiel_module) _setup_class(klass_) def klass_.inherited(subklass_) ::Blockenspiel::DSLSetupMethods._setup_class(subklass_) super end class << klass_ unless private_method_defined?(:_blockenspiel_default_include) alias_method :_blockenspiel_default_include, :include alias_method :include, :_blockenspiel_custom_include end end end end |
Instance Method Details
#_blockenspiel_auto_dsl_method(symbol_) ⇒ Object
Automatically make the given method a DSL method according to the current setting.
106 107 108 109 110 111 112 113 114 |
# File 'lib/blockenspiel/dsl_setup.rb', line 106 def _blockenspiel_auto_dsl_method(symbol_) # :nodoc: if @_blockenspiel_active dsl_method(symbol_) elsif @_blockenspiel_active.nil? if symbol_ != :initialize && symbol_.to_s !~ /^_/ && symbol_.to_s !~ /=$/ dsl_method(symbol_) end end end |
#_blockenspiel_custom_include(*modules_) ⇒ Object
Custom include method. Calls the main include implementation, but also goes through the public methods of the included module and calls _blockenspiel_auto_dsl_method on each to make them DSL methods (possibly) according to the current setting.
132 133 134 135 136 137 138 139 |
# File 'lib/blockenspiel/dsl_setup.rb', line 132 def _blockenspiel_custom_include(*modules_) # :nodoc: _blockenspiel_default_include(*modules_) modules_.reverse_each do |mod_| mod_.public_instance_methods.each do |method_| _blockenspiel_auto_dsl_method(method_) end end end |
#_get_blockenspiel_delegate(name_) ⇒ Object
Get information on the given DSL method name. Possible values are the name of the delegate method, false for method disabled, or nil for method never defined.
153 154 155 156 157 158 159 160 |
# File 'lib/blockenspiel/dsl_setup.rb', line 153 def _get_blockenspiel_delegate(name_) # :nodoc: delegate_ = @_blockenspiel_methods[name_] if delegate_.nil? && @_blockenspiel_superclass @_blockenspiel_superclass._get_blockenspiel_delegate(name_) else delegate_ end end |
#_get_blockenspiel_module ⇒ Object
Get this class's corresponding DSL module
144 145 146 |
# File 'lib/blockenspiel/dsl_setup.rb', line 144 def _get_blockenspiel_module # :nodoc: @_blockenspiel_module end |
#dsl_attr_accessor(*names_) ⇒ Object
A DSL-friendly attr_accessor.
This creates the usual "name" and "name=" methods in the current class that can be used in the usual way. However, its implementation of the "name" method (the getter) also takes an optional parameter that causes it to behave as a setter. This is done because the usual setter syntax cannot be used in a parameterless block, since it is syntactically indistinguishable from a local variable assignment. The "name" method is exposed as a dsl_method.
For example:
dsl_attr_accessor :foo
enables the following:
my_block do |param| param.foo = 1 # Usual setter syntax works param.foo 2 # Alternate setter syntax also works puts param.foo # Usual getter syntax still works end
my_block do
foo = 1 # Usual setter syntax does NOT work since it
# looks like a local variable assignment
foo 2 # Alternate setter syntax does work
puts foo # Usual getter syntax still works
end
263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/blockenspiel/dsl_setup.rb', line 263 def dsl_attr_accessor(*names_) names_.each do |name_| unless name_.kind_of?(::String) || name_.kind_of?(::Symbol) raise ::TypeError, "#{name_.inspect} is not a symbol" end unless name_.to_s =~ /^[_a-zA-Z]\w+$/ raise ::NameError, "invalid attribute name #{name_.inspect}" end module_eval("def #{name_}(value_=::Blockenspiel::NO_VALUE); ::Blockenspiel::NO_VALUE.equal?(value_) ? @#{name_} : @#{name_} = value_; end\n") alias_method("#{name_}=", name_) dsl_method(name_) end end |
#dsl_attr_writer(*names_) ⇒ Object
A DSL-friendly attr_writer.
This creates the usual "name=" method in the current class that can be used in the usual way. However, it also creates the method "name", which also functions as a setter (but not a getter). This is done because the usual setter syntax cannot be used in a parameterless block, since it is syntactically indistinguishable from a local variable assignment. The "name" method is exposed as a dsl_method.
For example:
dsl_attr_writer :foo
is functionally equivalent to:
attr_writer :foo alias_method :foo, :foo= dsl_method :foo
which enables the following:
my_block do |param| param.foo = 1 # Usual setter syntax works param.foo 2 # Alternate setter syntax also works end my_block do
foo = 1 # Usual setter syntax does NOT work since it
# looks like a local variable assignment
foo(2) # Alternate setter syntax does work
end
309 310 311 312 313 314 315 |
# File 'lib/blockenspiel/dsl_setup.rb', line 309 def dsl_attr_writer(*names_) names_.each do |name_| attr_writer(name_) alias_method(name_, "#{name_}=") dsl_method(name_) end end |
#dsl_method(name_, delegate_ = nil) ⇒ Object
Make a particular method available to parameterless DSL blocks.
To explicitly make a method available to parameterless blocks: dsl_method :my_method
To explicitly exclude a method from parameterless blocks: dsl_method :my_method, false
To explicitly make a method available to parameterless blocks, but point it to a method of a different name on the target class: dsl_method :my_method, :target_class_method
175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/blockenspiel/dsl_setup.rb', line 175 def dsl_method(name_, delegate_=nil) name_ = name_.to_sym if delegate_ delegate_ = delegate_.to_sym elsif delegate_.nil? delegate_ = name_ end @_blockenspiel_methods[name_] = delegate_ unless @_blockenspiel_module.public_method_defined?(name_) @_blockenspiel_module.module_eval("def #{name_}(*params_, &block_); val_ = ::Blockenspiel._target_dispatch(self, :#{name_}, params_, block_); ::Blockenspiel::NO_VALUE.equal?(val_) ? super(*params_, &block_) : val_; end\n") end end |
#dsl_methods(*names_) ⇒ Object
Control the behavior of methods with respect to parameterless blocks, or make a list of methods available to parameterless blocks in bulk.
To enable automatic exporting of methods to parameterless blocks. After executing this command, all public methods defined in the class will be available on parameterless blocks, until dsl_methods false is called: dsl_methods true
To disable automatic exporting of methods to parameterless blocks. After executing this command, methods defined in this class will be excluded from parameterless blocks, until dsl_methods true is called: dsl_methods false
To make a list of methods available to parameterless blocks in bulk: dsl_methods :my_method1, :my_method2, ...
You can also point dsl methods to a method of a different name on the target class, by using a hash syntax, as follows: dsl_methods :my_method1 => :target_class_method1, :my_method2 => :target_class_method2
You can mix non-renamed and renamed method declarations as long as the renamed (hash) methods are at the end. e.g.: dsl_methods :my_method1, :my_method2 => :target_class_method2
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/blockenspiel/dsl_setup.rb', line 216 def dsl_methods(*names_) if names_.size == 0 || names_ == [true] @_blockenspiel_active = true elsif names_ == [false] @_blockenspiel_active = false else if names_.last.kind_of?(::Hash) names_.pop.each do |name_, delegate_| dsl_method(name_, delegate_) end end names_.each do |name_| dsl_method(name_, name_) end end end |
#method_added(symbol_) ⇒ Object
Hook called when a method is added. This calls _blockenspiel_auto_dsl_method to auto-handle the method, possibly making it a DSL method according to the current setting.
121 122 123 124 |
# File 'lib/blockenspiel/dsl_setup.rb', line 121 def method_added(symbol_) # :nodoc: _blockenspiel_auto_dsl_method(symbol_) super end |