Class: Glimmer::DSL::Engine

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/glimmer/dsl/engine.rb

Overview

Glimmer DSL Engine

Follows Interpreter and Chain of Responsibility 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?
      puts Glimmer::DSL::Engine::MESSAGE_NO_DSLS
    else
      retrieved_static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
      static_expression_dsl = (Glimmer::DSL::Engine.static_expressions[keyword].keys - Glimmer::DSL::Engine.disabled_dsls).first if retrieved_static_expression.nil?
      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)
        static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
        if !static_expression.can_interpret?(Glimmer::DSL::Engine.parent, keyword, *args, &block)
          raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args} under parent #{Glimmer::DSL::Engine.parent}"
        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

Class Method Summary collapse

Class Attribute Details

.dynamic_expression_chains_of_responsibilityObject

Dynamic expression chains of responsibility indexed by dsl



111
112
113
# File 'lib/glimmer/dsl/engine.rb', line 111

def dynamic_expression_chains_of_responsibility
  @dynamic_expression_chains_of_responsibility ||= {}
end

.static_expressionsObject

Static expressions indexed by keyword and dsl



116
117
118
# File 'lib/glimmer/dsl/engine.rb', line 116

def static_expressions
  @static_expressions ||= {}
end

Class Method Details

.add_content(parent, expression, &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



185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/glimmer/dsl/engine.rb', line 185

def add_content(parent, expression, &block)
  if block_given? && expression.is_a?(ParentExpression)
    dsl_stack.push(expression.class.dsl)
    parent_stack.push(parent)
    begin
      expression.add_content(parent, &block)
    ensure
      parent_stack.pop
      dsl_stack.pop
    end
  end
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



133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/glimmer/dsl/engine.rb', line 133

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_static_expression(static_expression) ⇒ Object



146
147
148
149
150
151
152
153
# File 'lib/glimmer/dsl/engine.rb', line 146

def add_static_expression(static_expression)
  Glimmer::Config.logger.info {"Adding static expression: #{static_expression.class.name}"}
  keyword = static_expression.class.keyword
  static_expression_dsl = static_expression.class.dsl
  static_expressions[keyword] ||= {}
  static_expressions[keyword][static_expression_dsl] = static_expression
  Glimmer.send(:define_method, keyword, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword))
end

.disable_dsl(dsl_name) ⇒ Object



78
79
80
81
# File 'lib/glimmer/dsl/engine.rb', line 78

def disable_dsl(dsl_name)
  dsl_name = dsl_name.to_sym
  disabled_dsls << dsl_name
end

.disabled_dslsObject



88
89
90
# File 'lib/glimmer/dsl/engine.rb', line 88

def disabled_dsls
  @disabled_dsls ||= []
end

.dsl_stackObject

Enables multiple DSLs to play well with each other when mixing together



213
214
215
# File 'lib/glimmer/dsl/engine.rb', line 213

def dsl_stack
  @dsl_stack ||= []
end

.dslsObject



74
75
76
# File 'lib/glimmer/dsl/engine.rb', line 74

def dsls
  static_expressions.values.map(&:keys).flatten.uniq
end

.enable_dsl(dsl_name) ⇒ Object



83
84
85
86
# File 'lib/glimmer/dsl/engine.rb', line 83

def enable_dsl(dsl_name)
  dsl_name = dsl_name.to_sym
  disabled_dsls.delete(dsl_name)
end

.enabled_dsls=(dsl_names) ⇒ Object



92
93
94
95
# File 'lib/glimmer/dsl/engine.rb', line 92

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



155
156
157
# File 'lib/glimmer/dsl/engine.rb', line 155

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



159
160
161
# File 'lib/glimmer/dsl/engine.rb', line 159

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) { … })



164
165
166
167
168
169
170
171
# File 'lib/glimmer/dsl/engine.rb', line 164

def interpret(keyword, *args, &block)
  return puts(MESSAGE_NO_DSLS) if no_dsls?
  keyword = keyword.to_s
  dynamic_expression_dsl = (dynamic_expression_chains_of_responsibility.keys - disabled_dsls).first if dsl.nil?
  dsl_stack.push(dynamic_expression_dsl || dsl)
  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



173
174
175
176
177
178
# File 'lib/glimmer/dsl/engine.rb', line 173

def interpret_expression(expression, keyword, *args, &block)
  expression.interpret(parent, keyword, *args, &block).tap do |ui_object|
    add_content(ui_object, expression, &block)
    dsl_stack.pop
  end
end

.no_dsls?Boolean

Returns:

  • (Boolean)


106
107
108
# File 'lib/glimmer/dsl/engine.rb', line 106

def no_dsls?
  static_expressions.empty? && dynamic_expression_chains_of_responsibility.empty?
end

.parent_stackObject



204
205
206
# File 'lib/glimmer/dsl/engine.rb', line 204

def parent_stack
  parent_stacks[dsl] ||= []
end

.parent_stacksObject



208
209
210
# File 'lib/glimmer/dsl/engine.rb', line 208

def parent_stacks
  @parent_stacks ||= {}
end

.resetObject

Resets Glimmer’s engine activity and configuration. Useful in rspec before or after blocks in tests.



98
99
100
101
102
103
104
# File 'lib/glimmer/dsl/engine.rb', line 98

def reset
  parent_stacks.values.each do |a_parent_stack|
    a_parent_stack.clear
  end
  dsl_stack.clear
  disabled_dsls.clear
end