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, 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?
      puts Glimmer::DSL::Engine::MESSAGE_NO_DSLS # TODO consider switching to an error log statement
    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)
        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))
          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}"
        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



113
114
115
# File 'lib/glimmer/dsl/engine.rb', line 113

def dynamic_expression_chains_of_responsibility
  @dynamic_expression_chains_of_responsibility ||= Concurrent::Hash.new
end

.static_expressionsObject

Static expressions indexed by keyword and dsl



118
119
120
# File 'lib/glimmer/dsl/engine.rb', line 118

def static_expressions
  @static_expressions ||= Concurrent::Hash.new
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



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/glimmer/dsl/engine.rb', line 188

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



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

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



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

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] ||= Concurrent::Hash.new
  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



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

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

.disabled_dslsObject



90
91
92
# File 'lib/glimmer/dsl/engine.rb', line 90

def disabled_dsls
  @disabled_dsls ||= Concurrent::Array.new
end

.dsl_stackObject

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



216
217
218
# File 'lib/glimmer/dsl/engine.rb', line 216

def dsl_stack
  @dsl_stack ||= Concurrent::Array.new
end

.dslsObject



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

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

.enable_dsl(dsl_name) ⇒ Object



85
86
87
88
# File 'lib/glimmer/dsl/engine.rb', line 85

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

.enabled_dsls=(dsl_names) ⇒ Object



94
95
96
97
# File 'lib/glimmer/dsl/engine.rb', line 94

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



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

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



161
162
163
# File 'lib/glimmer/dsl/engine.rb', line 161

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



166
167
168
169
170
171
172
173
174
# File 'lib/glimmer/dsl/engine.rb', line 166

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)
  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



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

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)


108
109
110
# File 'lib/glimmer/dsl/engine.rb', line 108

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

.parent_stackObject



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

def parent_stack
  parent_stacks[dsl] ||= Concurrent::Array.new
end

.parent_stacksObject



211
212
213
# File 'lib/glimmer/dsl/engine.rb', line 211

def parent_stacks
  @parent_stacks ||= Concurrent::Hash.new
end

.resetObject

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



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

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