Class: DSL::Maker
- Inherits:
-
Object
- Object
- DSL::Maker
- Defined in:
- lib/dsl/maker.rb,
lib/dsl/maker/version.rb
Overview
This is the base class we provide.
Defined Under Namespace
Modules: Boolean Classes: Base
Constant Summary collapse
- VERSION =
The current version of this library
'0.0.4'- Yes =
On = True = true
- No =
Off = False = false
- @@dsl_elements =
FIXME: This may have to be changed when the elements can be altered because it is global to the hierarchy. But, that may be desirable.
{ String => ->(klass, name, type) { as_attr = '@' + name.to_s klass.class_eval do define_method(name.to_sym) do |*args| ___set(as_attr, args[0].to_s) unless args.empty? ___get(as_attr) end end }, DSL::Maker::Boolean => ->(klass, name, type) { as_attr = '@' + name.to_s klass.class_eval do define_method(name.to_sym) do |*args| ___set(as_attr, Boolean.coerce(args[0])) unless args.empty? # Ensure that the default nil returns as false. !!___get(as_attr) end end }, }
Class Method Summary collapse
-
.add_entrypoint(name, args = {}, &defn_block) ⇒ Class
Add an entrypoint (top-level DSL element) to this class's DSL.
- .add_helper(name, &block) ⇒ Object
-
.build_dsl_element(klass, name, type) ⇒ Object
Add a single element of a DSL to a class representing a level in a DSL.
-
.entrypoint(name) ⇒ Class
This returns the DSL corresponding to the entrypoint's name.
-
.execute_dsl(&block) ⇒ Object
Execute the DSL provided in the block.
-
.generate_dsl(args = {}, &defn_block) ⇒ Class
Add the meat of a DSL block to some level of this class's DSL.
-
.get_binding ⇒ Binding
Returns the binding as needed by parse_dsl() and execute_dsl().
-
.parse_dsl(dsl) ⇒ Object
Parse the DSL provided in the parameter.
Class Method Details
.add_entrypoint(name, args = {}, &defn_block) ⇒ Class
args could be a Hash (to be passed to generate_dsl()) or the result
Add an entrypoint (top-level DSL element) to this class's DSL.
This delegates to generate_dsl() for the majority of the work.
of a call to generate_dsl().
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/dsl/maker.rb', line 214 def self.add_entrypoint(name, args={}, &defn_block) # Without defn_block, there's no way to give back the result of the # DSL parsing. So, raise an error if we don't get one. # TODO: Provide a default block that returns the datastructure as a HoH. raise "Block required for add_entrypoint" unless block_given? if self.respond_to?(name.to_sym) raise "'#{name.to_s}' is already an entrypoint" end # FIXME: This is a wart. Really, we should be pulling out name, then # yielding to generate_dsl() in some fashion. if $is_dsl.call(args) dsl_class = args else dsl_class = generate_dsl(args, &defn_block) end define_singleton_method(name.to_sym) do |*args, &dsl_block| obj = dsl_class.new Docile.dsl_eval(obj, &dsl_block) if dsl_block rv = obj.instance_exec(*args, &defn_block) @accumulator.push(rv) return rv end @entrypoints ||= {} return @entrypoints[name.to_sym] = dsl_class end |
.add_helper(name, &block) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 |
# File 'lib/dsl/maker.rb', line 257 def self.add_helper(name, &block) raise "Block required for add_helper" unless block_given? if DSL::Maker::Base.new.respond_to? name.to_sym raise "'#{name.to_s}' is already a helper" end DSL::Maker::Base.class_eval do define_method(name.to_sym, &block) end end |
.build_dsl_element(klass, name, type) ⇒ Object
Add a single element of a DSL to a class representing a level in a DSL.
Each of the types represents a coercion - a guarantee and check of the value in that name. The standard coercions are:
- String - whatever you give is returned.
- Boolean - the truthiness of whatever you give is returned.
- generate_dsl() - this represents a new level of the DSL.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/dsl/maker.rb', line 137 def self.build_dsl_element(klass, name, type) if @@dsl_elements.has_key?(type) @@dsl_elements[type].call(klass, name, type) elsif $is_dsl.call(type) as_attr = '@' + name.to_s klass.class_eval do define_method(name.to_sym) do |*args, &dsl_block| unless (args.empty? && !dsl_block) obj = type.new Docile.dsl_eval(obj, &dsl_block) if dsl_block # I don't know why this code doesn't work, but it's why __apply(). #___set(as_attr, obj.instance_exec(*args, &defn_block)) ___set(as_attr, obj.__apply(*args)) end ___get(as_attr) end end else raise "Unrecognized element type '#{type}'" end return end |
.entrypoint(name) ⇒ Class
This returns the DSL corresponding to the entrypoint's name.
249 250 251 252 253 254 255 |
# File 'lib/dsl/maker.rb', line 249 def self.entrypoint(name) unless self.respond_to?(name.to_sym) raise "'#{name.to_s}' is not an entrypoint" end return @entrypoints[name.to_sym] end |
.execute_dsl(&block) ⇒ Object
If the DSL contains multiple entrypoints, then this will return an
Execute the DSL provided in the block.
Array. This is desirable.
78 79 80 81 82 83 84 85 |
# File 'lib/dsl/maker.rb', line 78 def self.execute_dsl(&block) @accumulator = [] instance_eval(&block) if @accumulator.length <= 1 return @accumulator[0] end return @accumulator end |
.generate_dsl(args = {}, &defn_block) ⇒ Class
Add the meat of a DSL block to some level of this class's DSL.
In order for Docile to parse a DSL, each level must be represented by a different class. This method creates anonymous classes that each represents a different level in the DSL's structure.
The creation of each DSL element is delegated to build_dsl_element.
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 |
# File 'lib/dsl/maker.rb', line 174 def self.generate_dsl(args={}, &defn_block) raise 'Block required for generate_dsl' unless block_given? # Inherit from the Boolean class to gain access to the useful methods # TODO: Convert DSL::Maker::Boolean into a Role # TODO: Create a DSL::Maker::Base class to inherit from dsl_class = Class.new(DSL::Maker::Base) do include DSL::Maker::Boolean # This instance method exists because we cannot seem to inline its work # where we call it. Could it be a problem of incorrect binding? # It has to be defined here because it needs access to &defn_block define_method(:__apply) do |*args| instance_exec(*args, &defn_block) end end args.each do |name, type| if dsl_class.new.respond_to? name.to_sym raise "Illegal attribute name '#{name}'" end build_dsl_element(dsl_class, name, type) end return dsl_class end |
.get_binding ⇒ Binding
Returns the binding as needed by parse_dsl() and execute_dsl()
90 91 92 |
# File 'lib/dsl/maker.rb', line 90 def self.get_binding binding end |
.parse_dsl(dsl) ⇒ Object
If the DSL contains multiple entrypoints, then this will return an
Parse the DSL provided in the parameter.
Array. This is desirable.
59 60 61 62 63 64 65 66 67 68 |
# File 'lib/dsl/maker.rb', line 59 def self.parse_dsl(dsl) # add_entrypoint() will use @accumulator to handle multiple entrypoints. # Reset it here so that we're only handling the values from this run. @accumulator = [] eval dsl, self.get_binding if @accumulator.length <= 1 return @accumulator[0] end return @accumulator end |