Class: Glimmer::DSL::Engine

Inherits:
Object
  • Object
show all
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"

Class Method Summary collapse

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



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

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



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/glimmer/dsl/engine.rb', line 93

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&.debug "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



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/glimmer/dsl/engine.rb', line 106

def add_static_expression(static_expression)
  Glimmer::Config.logger&.debug "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) do |*args, &block|
    begin
      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
            Glimmer::DSL::Engine.reset
            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&.debug "#{static_expression.class.name} will handle expression keyword #{keyword}"
            static_expression.interpret(Glimmer::DSL::Engine.parent, keyword, *args, &block).tap do |ui_object|
              Glimmer::DSL::Engine.add_content(ui_object, static_expression, &block) unless block.nil?
              Glimmer::DSL::Engine.dsl_stack.pop
            end
          end
        end
      end              
    rescue StandardError => e
#               Glimmer::DSL::Engine.dsl_stack.pop
        Glimmer::DSL::Engine.reset
      raise e
    end
  end
end

.disable_dsl(dsl_name) ⇒ Object



34
35
36
37
# File 'lib/glimmer/dsl/engine.rb', line 34

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

.disabled_dslsObject



44
45
46
# File 'lib/glimmer/dsl/engine.rb', line 44

def disabled_dsls
  @disabled_dsls ||= []
end

.dslObject



26
27
28
# File 'lib/glimmer/dsl/engine.rb', line 26

def dsl
  dsl_stack.last
end

.dsl=(dsl_name) ⇒ Object



17
18
19
20
21
22
23
24
# File 'lib/glimmer/dsl/engine.rb', line 17

def dsl=(dsl_name)
  dsl_name = dsl_name&.to_sym
  if dsl_name
    dsl_stack.push(dsl_name)
  else
    dsl_stack.clear
  end
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



30
31
32
# File 'lib/glimmer/dsl/engine.rb', line 30

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

.dynamic_expression_chains_of_responsibilityObject

Dynamic expression chains of responsibility indexed by dsl



67
68
69
# File 'lib/glimmer/dsl/engine.rb', line 67

def dynamic_expression_chains_of_responsibility
  @dynamic_expression_chains_of_responsibility ||= {}
end

.dynamic_expression_chains_of_responsibility=(chains) ⇒ Object

Sets dynamic expression chains of responsibility. Useful for internal testing



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

def dynamic_expression_chains_of_responsibility=(chains)
  @dynamic_expression_chains_of_responsibility = chains
end

.enable_dsl(dsl_name) ⇒ Object



39
40
41
42
# File 'lib/glimmer/dsl/engine.rb', line 39

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

.enabled_dsls=(dsl_names) ⇒ Object



48
49
50
51
# File 'lib/glimmer/dsl/engine.rb', line 48

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



153
154
155
# File 'lib/glimmer/dsl/engine.rb', line 153

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



157
158
159
# File 'lib/glimmer/dsl/engine.rb', line 157

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



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/glimmer/dsl/engine.rb', line 162

def interpret(keyword, *args, &block)
  if no_dsls?
    puts MESSAGE_NO_DSLS
    return
  end
  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)
  expression.interpret(parent, keyword, *args, &block).tap do |ui_object|            
    add_content(ui_object, expression, &block)
    dsl_stack.pop
  end
rescue StandardError => e
#           dsl_stack.pop
  reset
  raise e
end

.no_dsls?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/glimmer/dsl/engine.rb', line 62

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

.parentObject

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



200
201
202
# File 'lib/glimmer/dsl/engine.rb', line 200

def parent
  parent_stack.last
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.



54
55
56
57
58
59
60
# File 'lib/glimmer/dsl/engine.rb', line 54

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

.static_expressionsObject

Static expressions indexed by keyword and dsl



72
73
74
# File 'lib/glimmer/dsl/engine.rb', line 72

def static_expressions
  @static_expressions ||= {}
end

.static_expressions=(expressions) ⇒ Object

Sets static expressions. Useful for internal testing



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

def static_expressions=(expressions)
  @static_expressions = expressions
end