class Glimmer::DSL::Engine

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.

Constants

MESSAGE_NO_DSLS
STATIC_EXPRESSION_METHOD_FACTORY

Attributes

dynamic_expression_chains_of_responsibility[W]

Sets dynamic expression chains of responsibility. Useful for internal testing

static_expressions[W]

Sets static expressions. Useful for internal testing

Public Class Methods

add_capitalized_static_expression(static_expression) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 183
def add_capitalized_static_expression(static_expression)
  if static_expression.class.capitalized?
    Glimmer::Config.logger.info {"Adding capitalized static expression: #{static_expression.class.name}"}
    keyword = static_expression.class.keyword
    static_expression_dsl = static_expression.class.dsl
    static_expressions[keyword.capitalize] ||= Concurrent::Hash.new
    static_expressions[keyword.capitalize][static_expression_dsl] = static_expression
    Glimmer.send(:define_method, keyword.capitalize, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword.capitalize))
  end
end
add_content(new_parent, expression, keyword, *args, &block) click to toggle source

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

# File lib/glimmer/dsl/engine.rb, line 230
def add_content(new_parent, expression, keyword, *args, &block)
  if block_given? && expression.is_a?(ParentExpression)
    dsl_stack.push(expression.class.dsl)
    push_parent_into_parent_stack(new_parent)
    begin
      expression.add_content(new_parent, keyword, *args, &block)
    ensure
      pop_parent_from_parent_stack
      dsl_stack.pop
    end
  end
end
add_downcased_static_expression(static_expression) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 153
def add_downcased_static_expression(static_expression)
  Glimmer::Config.logger.info {"Adding static expression: #{static_expression.class.name}"}
  keyword = static_expression.class.keyword
  static_expressions[keyword] ||= Concurrent::Hash.new
  static_expression_dsl = static_expression.class.dsl
  static_expressions[keyword][static_expression_dsl] = static_expression
  Glimmer.send(:define_method, keyword, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword))
end
Also aliased as: add_static_expression
add_dynamic_expressions(dsl_namespace, *expression_names) click to toggle source

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

# File lib/glimmer/dsl/engine.rb, line 140
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)
add_upcased_static_expression(static_expression) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 172
def add_upcased_static_expression(static_expression)
  if static_expression.class.upcased?
    Glimmer::Config.logger.info {"Adding upcased static expression: #{static_expression.class.name}"}
    keyword = static_expression.class.keyword
    static_expression_dsl = static_expression.class.dsl
    static_expressions[keyword.upcase] ||= Concurrent::Hash.new
    static_expressions[keyword.upcase][static_expression_dsl] = static_expression
    Glimmer.send(:define_method, keyword.upcase, &STATIC_EXPRESSION_METHOD_FACTORY.call(keyword.upcase))
  end
end
disable_dsl(dsl_name) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 85
def disable_dsl(dsl_name)
  dsl_name = dsl_name.to_sym
  disabled_dsls << dsl_name
end
disabled_dsls() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 95
def disabled_dsls
  @disabled_dsls ||= Concurrent::Array.new
end
dsl_parent_stacks() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 273
def dsl_parent_stacks
  @dsl_parent_stacks ||= Concurrent::Hash.new
end
dsl_stack() click to toggle source

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

# File lib/glimmer/dsl/engine.rb, line 278
def dsl_stack
  @dsl_stack ||= Concurrent::Array.new
end
dsls() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 81
def dsls
  static_expressions.values.map(&:keys).flatten.uniq
end
dynamic_expression_chains_of_responsibility() click to toggle source

Dynamic expression chains of responsibility indexed by dsl

# File lib/glimmer/dsl/engine.rb, line 118
def dynamic_expression_chains_of_responsibility
  @dynamic_expression_chains_of_responsibility ||= Concurrent::Hash.new
end
enable_dsl(dsl_name) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 90
def enable_dsl(dsl_name)
  dsl_name = dsl_name.to_sym
  disabled_dsls.delete(dsl_name)
end
enabled_dsls=(dsl_names) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 99
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) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 194
def expression_class(dsl_namespace, expression_name)
  dsl_namespace.const_get(expression_class_name(expression_name).to_sym)
end
expression_class_name(expression_name) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 198
def expression_class_name(expression_name)
  "#{expression_name}_expression".camelcase(:upper)
end
interpret(keyword, *args, &block) click to toggle source

Interprets Glimmer dynamic DSL expression consisting of keyword, args, and block (e.g. shell(:no_resize) { … })

# File lib/glimmer/dsl/engine.rb, line 203
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)
  Glimmer::Config.logger.info {"Assuming DSL: #{dsl_stack.last}"}
  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) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 214
def interpret_expression(expression, keyword, *args, &block)
  new_parent = nil
  expression.around(parent, keyword, args, block) do
    new_parent = expression.interpret(parent, keyword, *args, &block).tap do |new_parent|
      add_content(new_parent, expression, keyword, *args, &block)
      dsl_stack.pop
    end
  end
  new_parent
end
new_parent_stack() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 265
def new_parent_stack
  parent_stacks.push(Concurrent::Array.new)
end
no_dsls?() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 113
def no_dsls?
  static_expressions.empty? && dynamic_expression_chains_of_responsibility.empty?
end
parent() click to toggle source

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

# File lib/glimmer/dsl/engine.rb, line 247
def parent
  parent_stack.last
end
parent_stack() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 260
def parent_stack
  new_parent_stack if parent_stacks.last.nil?
  parent_stacks.last
end
parent_stacks() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 269
def parent_stacks
  dsl_parent_stacks[dsl] ||= Concurrent::Array.new # TODO insted of having one array, we need to nest it within an array of arrays
end
pop_parent_from_parent_stack() click to toggle source
# File lib/glimmer/dsl/engine.rb, line 255
def pop_parent_from_parent_stack
  parent_stack.pop
  parent_stacks.pop if parent_stacks.size > 1 && parent_stacks.last.empty?
end
push_parent_into_parent_stack(parent) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 251
def push_parent_into_parent_stack(parent)
  parent_stack.push(parent)
end
remove_downcased_static_expression(static_expression) click to toggle source
# File lib/glimmer/dsl/engine.rb, line 163
def remove_downcased_static_expression(static_expression)
  if !static_expression.class.downcased?
    keyword = static_expression.class.keyword
    static_expressions[keyword].delete(static_expression_dsl) if static_expressions[keyword]
    static_expressions.delete(keyword) if static_expressions[keyword].empty?
    Glimmer.send(:undef_method, keyword) if (Glimmer.method(keyword) rescue nil)
  end
end
reset() click to toggle source

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

# File lib/glimmer/dsl/engine.rb, line 105
def reset
  dsl_parent_stacks.values.each do |a_parent_stack|
    a_parent_stack.clear
  end
  dsl_stack.clear
  disabled_dsls.clear
end
static_expressions() click to toggle source

Static expressions indexed by keyword and dsl

# File lib/glimmer/dsl/engine.rb, line 123
def static_expressions
  @static_expressions ||= Concurrent::Hash.new
end