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



160
161
162
163
164
165
166
167
168
# File 'lib/glimmer/dsl/engine.rb', line 160

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
125
126
127
128
129
# 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.send(: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).last 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
    rescue StandardError => 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



187
188
189
# File 'lib/glimmer/dsl/engine.rb', line 187

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



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

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



135
136
137
# File 'lib/glimmer/dsl/engine.rb', line 135

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



140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/glimmer/dsl/engine.rb', line 140

def interpret(keyword, *args, &block)
  keyword = keyword.to_s
  dynamic_expression_dsl = (dynamic_expression_chains_of_responsibility.keys - disabled_dsls).last 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

.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



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

def parent
  parent_stack.last
end

.parent_stackObject



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

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

.parent_stacksObject



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

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