Class: Glimmer::DSL::Engine
- Inherits:
-
Object
- Object
- Glimmer::DSL::Engine
- Extended by:
- Forwardable
- Defined in:
- lib/glimmer/dsl/engine.rb
Overview
Glimmer DSL Engine
Follows Interpreter, Chain of Responsibility, and Singleton Design Patterns
When DSL engine interprets an expression, it attempts to handle with ordered expression array specified via ‘.expressions=` method.
Constant Summary collapse
- MESSAGE_NO_DSLS =
"Glimmer has no DSLs configured. Add glimmer-dsl-swt gem or visit https://github.com/AndyObtiva/glimmer#multi-dsl-support for more details.\n"
- STATIC_EXPRESSION_METHOD_FACTORY =
lambda do |keyword| lambda do |*args, &block| if Glimmer::DSL::Engine.no_dsls? Glimmer::Config.logger.error {Glimmer::DSL::Engine::MESSAGE_NO_DSLS} else retrieved_static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl] # TODO consider replacing Glimmer::DSL::Engine.static_expressions[keyword].keys - Glimmer::DSL::Engine.disabled_dsls with Glimmer::DSL::Engine.enabled_static_expression_dsls(keyword) static_expression_dsl = (Glimmer::DSL::Engine.static_expressions[keyword].keys - Glimmer::DSL::Engine.disabled_dsls).first interpretation = nil if retrieved_static_expression.nil? && Glimmer::DSL::Engine.dsl && (static_expression_dsl.nil? || !Glimmer::DSL::Engine.static_expressions[keyword][static_expression_dsl].is_a?(TopLevelExpression)) begin interpretation = Glimmer::DSL::Engine.interpret(keyword, *args, &block) rescue => e raise e if static_expression_dsl.nil? || !Glimmer::DSL::Engine.static_expressions[keyword][static_expression_dsl].is_a?(TopLevelExpression) end end if interpretation interpretation else raise Glimmer::Error, "Unsupported keyword: #{keyword}" unless static_expression_dsl || retrieved_static_expression Glimmer::DSL::Engine.dsl_stack.push(static_expression_dsl || Glimmer::DSL::Engine.dsl) Glimmer::Config.logger.info {"Assuming DSL: #{Glimmer::DSL::Engine.dsl_stack.last}"} static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl] static_expression_can_interpret = nil if static_expression.nil? || !(static_expression_can_interpret = static_expression.can_interpret?(Glimmer::DSL::Engine.parent, keyword, *args, &block)) begin Glimmer::DSL::Engine.interpret(keyword, *args, &block) rescue => e raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args} under parent #{Glimmer::DSL::Engine.parent.inspect} with DSL #{Glimmer::DSL::Engine.dsl.inspect} and static expression #{static_expression.inspect} having can_interpret? as #{static_expression_can_interpret.inspect} and no dynamic expressions to be able to handle either!" end else Glimmer::Config.logger.info {"#{static_expression.class.name} will handle expression keyword #{keyword}"} Glimmer::DSL::Engine.interpret_expression(static_expression, keyword, *args, &block) end end end end end
Class Attribute Summary collapse
-
.dynamic_expression_chains_of_responsibility ⇒ Object
Dynamic expression chains of responsibility indexed by dsl.
-
.static_expressions ⇒ Object
Static expressions indexed by keyword and dsl.
Class Method Summary collapse
- .add_capitalized_static_expression(static_expression) ⇒ Object
-
.add_content(new_parent, expression, keyword, *args, &block) ⇒ Object
Adds content block to parent UI object.
- .add_downcased_static_expression(static_expression) ⇒ Object (also: add_static_expression)
-
.add_dynamic_expressions(dsl_namespace, *expression_names) ⇒ Object
Sets an ordered array of DSL expressions to support.
- .add_upcased_static_expression(static_expression) ⇒ Object
- .disable_dsl(dsl_name) ⇒ Object
- .disabled_dsls ⇒ Object
- .dsl_parent_stacks ⇒ Object
-
.dsl_stack ⇒ Object
Enables multiple DSLs to play well with each other when mixing together.
- .dsls ⇒ Object
- .enable_dsl(dsl_name) ⇒ Object
- .enabled_dsls=(dsl_names) ⇒ Object
- .expression_class(dsl_namespace, expression_name) ⇒ Object
- .expression_class_name(expression_name) ⇒ Object
-
.interpret(keyword, *args, &block) ⇒ Object
Interprets Glimmer dynamic DSL expression consisting of keyword, args, and block (e.g. shell(:no_resize) { … }).
- .interpret_expression(expression, keyword, *args, &block) ⇒ Object
- .new_parent_stack ⇒ Object
- .no_dsls? ⇒ Boolean
-
.parent ⇒ Object
Current parent while evaluating Glimmer DSL (nil if just started or done evaluatiing).
- .parent_stack ⇒ Object
- .parent_stacks ⇒ Object
- .pop_parent_from_parent_stack ⇒ Object
- .push_parent_into_parent_stack(parent) ⇒ Object
- .remove_downcased_static_expression(static_expression) ⇒ Object
-
.reset ⇒ Object
Resets Glimmer’s engine activity and configuration.
Class Attribute Details
.dynamic_expression_chains_of_responsibility ⇒ Object
Dynamic expression chains of responsibility indexed by dsl
118 119 120 |
# File 'lib/glimmer/dsl/engine.rb', line 118 def dynamic_expression_chains_of_responsibility @dynamic_expression_chains_of_responsibility ||= Concurrent::Hash.new end |
.static_expressions ⇒ Object
Static expressions indexed by keyword and dsl
123 124 125 |
# File 'lib/glimmer/dsl/engine.rb', line 123 def static_expressions @static_expressions ||= Concurrent::Hash.new end |
Class Method Details
.add_capitalized_static_expression(static_expression) ⇒ Object
183 184 185 186 187 188 189 190 191 192 |
# File 'lib/glimmer/dsl/engine.rb', line 183 def add_capitalized_static_expression(static_expression) if static_expression.class.capitalized? Glimmer::Config.logger.info {"Adding capitalized static expression: #{static_expression.class.name}"} keyword = static_expression.class.keyword static_expression_dsl = static_expression.class.dsl static_expressions[keyword.capitalize] ||= Concurrent::Hash.new static_expressions[keyword.capitalize][static_expression_dsl] = static_expression Glimmer.send(:define_method, keyword.capitalize, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword.capitalize)) end end |
.add_content(new_parent, expression, keyword, *args, &block) ⇒ Object
Adds content block to parent UI object
This allows evaluating parent UI object properties and children
For example, a shell widget would get properties set and children added
230 231 232 233 234 235 236 237 238 239 240 241 |
# File 'lib/glimmer/dsl/engine.rb', line 230 def add_content(new_parent, expression, keyword, *args, &block) if block_given? && expression.is_a?(ParentExpression) dsl_stack.push(expression.class.dsl) push_parent_into_parent_stack(new_parent) begin expression.add_content(new_parent, keyword, *args, &block) ensure pop_parent_from_parent_stack dsl_stack.pop end end end |
.add_downcased_static_expression(static_expression) ⇒ Object Also known as: add_static_expression
153 154 155 156 157 158 159 160 |
# File 'lib/glimmer/dsl/engine.rb', line 153 def add_downcased_static_expression(static_expression) Glimmer::Config.logger.info {"Adding static expression: #{static_expression.class.name}"} keyword = static_expression.class.keyword static_expressions[keyword] ||= Concurrent::Hash.new static_expression_dsl = static_expression.class.dsl static_expressions[keyword][static_expression_dsl] = static_expression Glimmer.send(:define_method, keyword, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword)) end |
.add_dynamic_expressions(dsl_namespace, *expression_names) ⇒ Object
Sets an ordered array of DSL expressions to support
Every expression has an underscored name corresponding to an upper camelcase AbstractExpression subclass name in glimmer/dsl
They are used in order following the Chain of Responsibility Design Pattern when interpretting a DSL expression
140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/glimmer/dsl/engine.rb', line 140 def add_dynamic_expressions(dsl_namespace, *expression_names) expression_names = expression_names.flatten dsl = dsl_namespace.name.split("::").last.downcase.to_sym dynamic_expression_chains_of_responsibility[dsl] = expression_names.reverse.map do |expression_name| expression_class(dsl_namespace, expression_name).new end.reduce(nil) do |last_expresion_handler, expression| Glimmer::Config.logger.info {"Adding dynamic expression: #{expression.class.name}"} expression_handler = ExpressionHandler.new(expression) expression_handler.next = last_expresion_handler if last_expresion_handler expression_handler end end |
.add_upcased_static_expression(static_expression) ⇒ Object
172 173 174 175 176 177 178 179 180 181 |
# File 'lib/glimmer/dsl/engine.rb', line 172 def add_upcased_static_expression(static_expression) if static_expression.class.upcased? Glimmer::Config.logger.info {"Adding upcased static expression: #{static_expression.class.name}"} keyword = static_expression.class.keyword static_expression_dsl = static_expression.class.dsl static_expressions[keyword.upcase] ||= Concurrent::Hash.new static_expressions[keyword.upcase][static_expression_dsl] = static_expression Glimmer.send(:define_method, keyword.upcase, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword.upcase)) end end |
.disable_dsl(dsl_name) ⇒ Object
85 86 87 88 |
# File 'lib/glimmer/dsl/engine.rb', line 85 def disable_dsl(dsl_name) dsl_name = dsl_name.to_sym disabled_dsls << dsl_name end |
.disabled_dsls ⇒ Object
95 96 97 |
# File 'lib/glimmer/dsl/engine.rb', line 95 def disabled_dsls @disabled_dsls ||= Concurrent::Array.new end |
.dsl_parent_stacks ⇒ Object
273 274 275 |
# File 'lib/glimmer/dsl/engine.rb', line 273 def dsl_parent_stacks @dsl_parent_stacks ||= Concurrent::Hash.new end |
.dsl_stack ⇒ Object
Enables multiple DSLs to play well with each other when mixing together
278 279 280 |
# File 'lib/glimmer/dsl/engine.rb', line 278 def dsl_stack @dsl_stack ||= Concurrent::Array.new end |
.dsls ⇒ Object
81 82 83 |
# File 'lib/glimmer/dsl/engine.rb', line 81 def dsls static_expressions.values.map(&:keys).flatten.uniq end |
.enable_dsl(dsl_name) ⇒ Object
90 91 92 93 |
# File 'lib/glimmer/dsl/engine.rb', line 90 def enable_dsl(dsl_name) dsl_name = dsl_name.to_sym disabled_dsls.delete(dsl_name) end |
.enabled_dsls=(dsl_names) ⇒ Object
99 100 101 102 |
# File 'lib/glimmer/dsl/engine.rb', line 99 def enabled_dsls=(dsl_names) dsls.each {|dsl_name| disable_dsl(dsl_name)} dsl_names.each {|dsl_name| enable_dsl(dsl_name)} end |
.expression_class(dsl_namespace, expression_name) ⇒ Object
194 195 196 |
# File 'lib/glimmer/dsl/engine.rb', line 194 def expression_class(dsl_namespace, expression_name) dsl_namespace.const_get(expression_class_name(expression_name).to_sym) end |
.expression_class_name(expression_name) ⇒ Object
198 199 200 |
# File 'lib/glimmer/dsl/engine.rb', line 198 def expression_class_name(expression_name) "#{expression_name}_expression".camelcase(:upper) end |
.interpret(keyword, *args, &block) ⇒ Object
Interprets Glimmer dynamic DSL expression consisting of keyword, args, and block (e.g. shell(:no_resize) { … })
203 204 205 206 207 208 209 210 211 212 |
# File 'lib/glimmer/dsl/engine.rb', line 203 def interpret(keyword, *args, &block) return puts(MESSAGE_NO_DSLS) if no_dsls? # TODO consider switching to an error log statement keyword = keyword.to_s dynamic_expression_dsl = (dynamic_expression_chains_of_responsibility.keys - disabled_dsls).first if dsl.nil? # TODO consider pushing this code into interpret_expresion to provide hooks that work around it regardless of static vs dynamic dsl_stack.push(dynamic_expression_dsl || dsl) Glimmer::Config.logger.info {"Assuming DSL: #{dsl_stack.last}"} expression = dynamic_expression_chains_of_responsibility[dsl].handle(parent, keyword, *args, &block) interpret_expression(expression, keyword, *args, &block) end |
.interpret_expression(expression, keyword, *args, &block) ⇒ Object
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/glimmer/dsl/engine.rb', line 214 def interpret_expression(expression, keyword, *args, &block) new_parent = nil expression.around(parent, keyword, args, block) do new_parent = expression.interpret(parent, keyword, *args, &block).tap do |new_parent| add_content(new_parent, expression, keyword, *args, &block) dsl_stack.pop end end new_parent end |
.new_parent_stack ⇒ Object
265 266 267 |
# File 'lib/glimmer/dsl/engine.rb', line 265 def new_parent_stack parent_stacks.push(Concurrent::Array.new) end |
.no_dsls? ⇒ Boolean
113 114 115 |
# File 'lib/glimmer/dsl/engine.rb', line 113 def no_dsls? static_expressions.empty? && dynamic_expression_chains_of_responsibility.empty? end |
.parent ⇒ Object
Current parent while evaluating Glimmer DSL (nil if just started or done evaluatiing)
Parents are maintained in a stack while evaluating Glimmer DSL to ensure properly ordered interpretation of DSL syntax
247 248 249 |
# File 'lib/glimmer/dsl/engine.rb', line 247 def parent parent_stack.last end |
.parent_stack ⇒ Object
260 261 262 263 |
# File 'lib/glimmer/dsl/engine.rb', line 260 def parent_stack new_parent_stack if parent_stacks.last.nil? parent_stacks.last end |
.parent_stacks ⇒ Object
269 270 271 |
# File 'lib/glimmer/dsl/engine.rb', line 269 def parent_stacks dsl_parent_stacks[dsl] ||= Concurrent::Array.new # TODO insted of having one array, we need to nest it within an array of arrays end |
.pop_parent_from_parent_stack ⇒ Object
255 256 257 258 |
# File 'lib/glimmer/dsl/engine.rb', line 255 def pop_parent_from_parent_stack parent_stack.pop parent_stacks.pop if parent_stacks.size > 1 && parent_stacks.last.empty? end |
.push_parent_into_parent_stack(parent) ⇒ Object
251 252 253 |
# File 'lib/glimmer/dsl/engine.rb', line 251 def push_parent_into_parent_stack(parent) parent_stack.push(parent) end |
.remove_downcased_static_expression(static_expression) ⇒ Object
163 164 165 166 167 168 169 170 |
# File 'lib/glimmer/dsl/engine.rb', line 163 def remove_downcased_static_expression(static_expression) if !static_expression.class.downcased? keyword = static_expression.class.keyword static_expressions[keyword].delete(static_expression_dsl) if static_expressions[keyword] static_expressions.delete(keyword) if static_expressions[keyword].empty? Glimmer.send(:undef_method, keyword) if (Glimmer.method(keyword) rescue nil) end end |
.reset ⇒ Object
Resets Glimmer’s engine activity and configuration. Useful in rspec before or after blocks in tests.
105 106 107 108 109 110 111 |
# File 'lib/glimmer/dsl/engine.rb', line 105 def reset dsl_parent_stacks.values.each do |a_parent_stack| a_parent_stack.clear end dsl_stack.clear disabled_dsls.clear end |