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.

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



155
156
157
158
159
160
161
162
163
# File 'lib/glimmer/dsl/engine.rb', line 155

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



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/glimmer/dsl/engine.rb', line 76

def add_dynamic_expressions(dsl_namespace, expression_names)
  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



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/glimmer/dsl/engine.rb', line 88

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.define_method(keyword) do |*args, &block|
    begin
      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?
      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
          return 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
      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}"
        return 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
    rescue => e
#               Glimmer::DSL::Engine.dsl_stack.pop
        Glimmer::DSL::Engine.reset
      raise e
    end
  end
end

.disable_dsl(dsl_name) ⇒ Object



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

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

.disabled_dslsObject



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

def disabled_dsls
  @disabled_dsls ||= []
end

.dslObject



23
24
25
# File 'lib/glimmer/dsl/engine.rb', line 23

def dsl
  dsl_stack.last
end

.dsl=(dsl_name) ⇒ Object



14
15
16
17
18
19
20
21
# File 'lib/glimmer/dsl/engine.rb', line 14

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



182
183
184
# File 'lib/glimmer/dsl/engine.rb', line 182

def dsl_stack
  @dsl_stack ||= []
end

.dslsObject



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

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

.dynamic_expression_chains_of_responsibilityObject

Dynamic expression chains of responsibility indexed by dsl



60
61
62
# File 'lib/glimmer/dsl/engine.rb', line 60

def dynamic_expression_chains_of_responsibility
  @dynamic_expression_chains_of_responsibility ||= {}
end

.enable_dsl(dsl_name) ⇒ Object



36
37
38
39
# File 'lib/glimmer/dsl/engine.rb', line 36

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

.enabled_dsls=(dsl_names) ⇒ Object



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

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



126
127
128
# File 'lib/glimmer/dsl/engine.rb', line 126

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



130
131
132
# File 'lib/glimmer/dsl/engine.rb', line 130

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



135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/glimmer/dsl/engine.rb', line 135

def interpret(keyword, *args, &block)
  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 => e
#           dsl_stack.pop
  reset
  raise e
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



169
170
171
# File 'lib/glimmer/dsl/engine.rb', line 169

def parent
  parent_stack.last
end

.parent_stackObject



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

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

.parent_stacksObject



177
178
179
# File 'lib/glimmer/dsl/engine.rb', line 177

def parent_stacks
  @parent_stacks ||= {}
end

.resetObject

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



51
52
53
54
55
56
57
# File 'lib/glimmer/dsl/engine.rb', line 51

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



65
66
67
# File 'lib/glimmer/dsl/engine.rb', line 65

def static_expressions
  @static_expressions ||= {}
end