glimmer-dsl-css 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +111 -0
  4. data/VERSION +1 -0
  5. data/lib/glimmer-dsl-css.rb +7 -0
  6. data/lib/glimmer/config.rb +22 -0
  7. data/lib/glimmer/css/rule.rb +25 -0
  8. data/lib/glimmer/css/style_sheet.rb +19 -0
  9. data/lib/glimmer/dsl/css/css_expression.rb +21 -0
  10. data/lib/glimmer/dsl/css/dsl.rb +21 -0
  11. data/lib/glimmer/dsl/css/dynamic_property_expression.rb +12 -0
  12. data/lib/glimmer/dsl/css/property_expression.rb +22 -0
  13. data/lib/glimmer/dsl/css/pv_expression.rb +17 -0
  14. data/lib/glimmer/dsl/css/rule_expression.rb +26 -0
  15. data/lib/glimmer/dsl/css/s_expression.rb +26 -0
  16. data/lib/glimmer/dsl/engine.rb +193 -0
  17. data/lib/glimmer/dsl/expression.rb +42 -0
  18. data/lib/glimmer/dsl/expression_handler.rb +48 -0
  19. data/lib/glimmer/dsl/opal/dsl.rb +41 -0
  20. data/lib/glimmer/dsl/opal/label_expression.rb +17 -0
  21. data/lib/glimmer/dsl/opal/property_expression.rb +19 -0
  22. data/lib/glimmer/dsl/opal/shell_expression.rb +19 -0
  23. data/lib/glimmer/dsl/parent_expression.rb +12 -0
  24. data/lib/glimmer/dsl/static_expression.rb +36 -0
  25. data/lib/glimmer/dsl/top_level_expression.rb +7 -0
  26. data/lib/glimmer/dsl/xml/dsl.rb +23 -0
  27. data/lib/glimmer/dsl/xml/html_expression.rb +25 -0
  28. data/lib/glimmer/dsl/xml/meta_expression.rb +23 -0
  29. data/lib/glimmer/dsl/xml/name_space_expression.rb +37 -0
  30. data/lib/glimmer/dsl/xml/node_parent_expression.rb +33 -0
  31. data/lib/glimmer/dsl/xml/tag_expression.rb +29 -0
  32. data/lib/glimmer/dsl/xml/text_expression.rb +22 -0
  33. data/lib/glimmer/dsl/xml/xml_expression.rb +21 -0
  34. data/lib/glimmer/error.rb +6 -0
  35. data/lib/glimmer/invalid_keyword_error.rb +6 -0
  36. data/lib/glimmer/opal/label.rb +31 -0
  37. data/lib/glimmer/opal/shell.rb +34 -0
  38. data/lib/glimmer/xml/depth_first_search_iterator.rb +22 -0
  39. data/lib/glimmer/xml/name_space_visitor.rb +21 -0
  40. data/lib/glimmer/xml/node.rb +75 -0
  41. data/lib/glimmer/xml/node_visitor.rb +13 -0
  42. data/lib/glimmer/xml/xml_visitor.rb +65 -0
  43. metadata +201 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b8bc05a543f2281ad44c1073149fda8c8f947ebf4205d98cc106219e1477f14e
4
+ data.tar.gz: e4f674a887a0c6d231a6ed6e259662de616eb3a6be770e20ddff30ca52946a00
5
+ SHA512:
6
+ metadata.gz: 8c0aadaad6a38cabfea3b4e2d62aea9d429389b298c3b89187a5548ed50ad90a91a00dce46773edc9b93248561c55f2966bba19f2af3f2031b7eec14e777f04f
7
+ data.tar.gz: ffe9a44219bca30c59fa961df2792792c3957e49118ea16da617fb026e843088b5ffa8c22f7cfded1a794a018c61b2fb082284bbe86c961bdf417edba71a2bf6
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2020 Andy Maleh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,111 @@
1
+ # Glimmer DSL for CSS 0.1.0 Beta (Cascading Style Sheets)
2
+ [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-css.svg)](http://badge.fury.io/rb/glimmer-dsl-css)
3
+ [![Travis CI](https://travis-ci.com/AndyObtiva/glimmer-dsl-css.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer-dsl-css)
4
+
5
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for CSS provides Ruby syntax for building CSS (Cascading Style Sheets).
6
+
7
+ Within the context of desktop development, Glimmer DSL for CSS is useful in providing CSS for the [SWT Browser widget](https://github.com/AndyObtiva/glimmer/tree/master#browser-widget).
8
+
9
+ ## Setup
10
+
11
+ Please follow these instructions to make the `glimmer` command available on your system.
12
+
13
+ ### Option 1: Direct Install
14
+
15
+ Run this command to install directly:
16
+ ```
17
+ jgem install glimmer-dsl-xml -v 0.1.0
18
+ ```
19
+
20
+ `jgem` is JRuby's version of `gem` command.
21
+ RVM allows running `gem` as an alias.
22
+ Otherwise, you may also run `jruby -S gem install ...`
23
+
24
+ Add `require 'glimmer-dsl-xml'` to your code after `require glimmer-dsl-swt` or `require glimmer-dsl-opal`
25
+
26
+ ### Option 2: Bundler
27
+
28
+ Add the following to `Gemfile` after `glimmer-dsl-swt` or `glimmer-dsl-opal`:
29
+ ```
30
+ gem 'glimmer-dsl-xml', '~> 0.1.0'
31
+ ```
32
+
33
+ And, then run:
34
+ ```
35
+ jruby -S bundle install
36
+ ```
37
+
38
+ That's it! Requiring the gem activates the Glimmer XML DSL automatically.
39
+
40
+ ## CSS DSL
41
+
42
+ Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
43
+ Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
44
+
45
+ `css` is the only top-level keyword in the Glimmer CSS DSL
46
+
47
+ Selectors may be specified by `s` keyword or HTML element keyword directly (e.g. `body`)
48
+ Rule property values may be specified by `pv` keyword or underscored property name directly (e.g. `font_size`)
49
+
50
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
51
+
52
+ ```ruby
53
+ @css = css {
54
+ body {
55
+ font_size '1.1em'
56
+ pv 'background', 'white'
57
+ }
58
+
59
+ s('body > h1') {
60
+ background_color :red
61
+ pv 'font-size', '2em'
62
+ }
63
+ }
64
+ puts @css
65
+ ```
66
+
67
+ ## Multi-DSL Support
68
+
69
+ Learn more about how to use this DSL alongside other Glimmer DSLs:
70
+
71
+ [Glimmer Multi-DSL Support](https://github.com/AndyObtiva/glimmer/tree/master#multi-dsl-support)
72
+
73
+ ## Help
74
+
75
+ ### Issues
76
+
77
+ You may submit [issues](https://github.com/AndyObtiva/glimmer/issues) on [GitHub](https://github.com/AndyObtiva/glimmer/issues).
78
+
79
+ [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer/issues)
80
+
81
+ ### IRC Channel
82
+
83
+ If you need live help, try the [#glimmer](http://widget.mibbit.com/?settings=7514b8a196f8f1de939a351245db7aa8&server=irc.mibbit.net&channel=%23glimmer) IRC channel on [irc.mibbit.net](http://widget.mibbit.com/?settings=7514b8a196f8f1de939a351245db7aa8&server=irc.mibbit.net&channel=%23glimmer). If no one was available, you may [leave a GitHub issue](https://github.com/AndyObtiva/glimmer/issues) to schedule a meetup on IRC.
84
+
85
+ [Click here to connect to #glimmer IRC channel immediately via a web interface.](http://widget.mibbit.com/?settings=7514b8a196f8f1de939a351245db7aa8&server=irc.mibbit.net&channel=%23glimmer)
86
+
87
+ ## Feature Suggestions
88
+
89
+ These features have been suggested. You might see them in a future version of Glimmer. You are welcome to contribute more feature suggestions.
90
+
91
+ [TODO.md](TODO.md)
92
+
93
+ ## Change Log
94
+
95
+ [CHANGELOG.md](CHANGELOG.md)
96
+
97
+ ## Contributing
98
+
99
+ [CONTRIBUTING.md](CONTRIBUTING.md)
100
+
101
+ ## Contributors
102
+
103
+ * [Andy Maleh](https://github.com/AndyObtiva) (Founder)
104
+ * [Dennis Theisen](https://github.com/Soleone) (Contributor)
105
+
106
+ [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer/graphs/contributors)
107
+
108
+ ## License
109
+
110
+ Copyright (c) 2007-2020 Andy Maleh.
111
+ See LICENSE.txt for further details.
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
2
+
3
+ require 'facets/string/underscore'
4
+ require 'facets/string/camelcase'
5
+ require 'glimmer'
6
+
7
+ require 'glimmer/dsl/css/dsl'
@@ -0,0 +1,22 @@
1
+ module Glimmer
2
+ module Config
3
+ class << self
4
+ # Returns Glimmer logger (standard Ruby logger)
5
+ def logger
6
+ # unless defined? @@logger
7
+ # @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
8
+ # end
9
+ @@logger if defined? @@logger
10
+ end
11
+
12
+ def enable_logging
13
+ @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ if ENV['GLIMMER_LOGGER_LEVEL']
20
+ Glimmer::Config.enable_logging
21
+ Glimmer::Config.logger.level = ENV['GLIMMER_LOGGER_LEVEL'].downcase
22
+ end
@@ -0,0 +1,25 @@
1
+ module Glimmer
2
+ module CSS
3
+ class Rule
4
+ attr_reader :selector, :properties
5
+
6
+ def initialize(selector)
7
+ @selector = selector
8
+ @properties = {}
9
+ end
10
+
11
+ def add_property(keyword, *args)
12
+ keyword = keyword.to_s.downcase.gsub('_', '-')
13
+ @properties[keyword] = args.first
14
+ end
15
+
16
+ def to_css
17
+ css = "#{@selector}{"
18
+ css << @properties.map { |name, value| "#{name}:#{value}" }.join(';')
19
+ css << "}"
20
+ end
21
+
22
+ alias to_s to_css
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ require 'glimmer/css/rule'
2
+
3
+ module Glimmer
4
+ module CSS
5
+ class StyleSheet
6
+ attr_reader :rules
7
+
8
+ def initialize
9
+ @rules = []
10
+ end
11
+
12
+ def to_css
13
+ rules.map(&:to_css).join
14
+ end
15
+
16
+ alias to_s to_css
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/top_level_expression'
3
+ require 'glimmer/dsl/parent_expression'
4
+ require 'glimmer/css/style_sheet'
5
+
6
+ module Glimmer
7
+ module DSL
8
+ module CSS
9
+ # This static html expression flips the DSL switch on for
10
+ # XML DSL in Glimmer
11
+ class CssExpression < StaticExpression
12
+ include TopLevelExpression
13
+ include ParentExpression
14
+
15
+ def interpret(parent, keyword, *args, &block)
16
+ Glimmer::CSS::StyleSheet.new
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ require 'glimmer/dsl/engine'
2
+ # Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f} # cannot in Opal
3
+ require 'glimmer/dsl/css/rule_expression'
4
+ require 'glimmer/dsl/css/dynamic_property_expression'
5
+ require 'glimmer/dsl/css/css_expression'
6
+ require 'glimmer/dsl/css/s_expression'
7
+ require 'glimmer/dsl/css/pv_expression'
8
+
9
+ module Glimmer
10
+ module DSL
11
+ module CSS
12
+ Engine.add_dynamic_expressions(
13
+ CSS,
14
+ %w[
15
+ rule
16
+ dynamic_property
17
+ ]
18
+ )
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ require 'glimmer/dsl/expression'
2
+ require 'glimmer/dsl/css/property_expression'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ module CSS
7
+ class DynamicPropertyExpression < Expression
8
+ include PropertyExpression
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ require 'glimmer/dsl/expression'
2
+ require 'glimmer/css/rule'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ module CSS
7
+ module PropertyExpression
8
+ include ParentExpression
9
+
10
+ def can_interpret?(parent, keyword, *args, &block)
11
+ parent.is_a?(Glimmer::CSS::Rule) and
12
+ !block_given? and
13
+ !args.empty?
14
+ end
15
+
16
+ def interpret(parent, keyword, *args, &block)
17
+ parent.add_property(keyword, *args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/dsl/css/property_expression'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ module CSS
7
+ # Static keyword 'pv' version of CSS DSL dynamic property expression
8
+ class PVExpression < StaticExpression
9
+ include PropertyExpression
10
+
11
+ def interpret(parent, keyword, *args, &block)
12
+ parent.add_property(args[0], args[1])
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require 'glimmer/dsl/expression'
2
+ require 'glimmer/dsl/parent_expression'
3
+ require 'glimmer/css/style_sheet'
4
+ require 'glimmer/css/rule'
5
+
6
+ module Glimmer
7
+ module DSL
8
+ module CSS
9
+ class RuleExpression < Expression
10
+ include ParentExpression
11
+
12
+ def can_interpret?(parent, keyword, *args, &block)
13
+ parent.is_a?(Glimmer::CSS::StyleSheet) and
14
+ block_given? and
15
+ args.empty?
16
+ end
17
+
18
+ def interpret(parent, keyword, *args, &block)
19
+ Glimmer::CSS::Rule.new(keyword.to_s.downcase).tap do |rule|
20
+ parent.rules << rule
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/css/style_sheet'
3
+ require 'glimmer/css/rule'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module CSS
8
+ class SExpression < StaticExpression
9
+ include ParentExpression
10
+
11
+ def can_interpret?(parent, keyword, *args, &block)
12
+ keyword == 's' and
13
+ parent.is_a?(Glimmer::CSS::StyleSheet) and
14
+ block_given? and
15
+ !args.empty?
16
+ end
17
+
18
+ def interpret(parent, keyword, *args, &block)
19
+ Glimmer::CSS::Rule.new(args.first.to_s).tap do |rule|
20
+ parent.rules << rule
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,193 @@
1
+ require 'glimmer'
2
+ require 'glimmer/dsl/expression_handler'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ # Glimmer DSL Engine
7
+ #
8
+ # Follows Interpreter and Chain of Responsibility Design Patterns
9
+ #
10
+ # When DSL engine interprets an expression, it attempts to handle
11
+ # with ordered expression array specified via `.expressions=` method.
12
+ class Engine
13
+ class << self
14
+ def dsl=(dsl_name)
15
+ dsl_name = dsl_name&.to_sym
16
+ if dsl_name
17
+ dsl_stack.push(dsl_name)
18
+ else
19
+ dsl_stack.clear
20
+ end
21
+ end
22
+
23
+ def dsl
24
+ dsl_stack.last
25
+ end
26
+
27
+ def dsls
28
+ static_expressions.values.map(&:keys).flatten.uniq
29
+ end
30
+
31
+ def disable_dsl(dsl_name)
32
+ dsl_name = dsl_name.to_sym
33
+ disabled_dsls << dsl_name
34
+ end
35
+
36
+ def enable_dsl(dsl_name)
37
+ dsl_name = dsl_name.to_sym
38
+ disabled_dsls.delete(dsl_name)
39
+ end
40
+
41
+ def disabled_dsls
42
+ @disabled_dsls ||= []
43
+ end
44
+
45
+ def enabled_dsls=(dsl_names)
46
+ dsls.each {|dsl_name| disable_dsl(dsl_name)}
47
+ dsl_names.each {|dsl_name| enable_dsl(dsl_name)}
48
+ end
49
+
50
+ # Resets Glimmer's engine activity and configuration. Useful in rspec before or after blocks in tests.
51
+ def reset
52
+ parent_stacks.values.each do |a_parent_stack|
53
+ a_parent_stack.clear
54
+ end
55
+ dsl_stack.clear
56
+ disabled_dsls.clear
57
+ end
58
+
59
+ # Dynamic expression chains of responsibility indexed by dsl
60
+ def dynamic_expression_chains_of_responsibility
61
+ @dynamic_expression_chains_of_responsibility ||= {}
62
+ end
63
+
64
+ # Static expressions indexed by keyword and dsl
65
+ def static_expressions
66
+ @static_expressions ||= {}
67
+ end
68
+
69
+ # Sets an ordered array of DSL expressions to support
70
+ #
71
+ # Every expression has an underscored name corresponding to an upper
72
+ # camelcase AbstractExpression subclass name in glimmer/dsl
73
+ #
74
+ # They are used in order following the Chain of Responsibility Design
75
+ # Pattern when interpretting a DSL expression
76
+ def add_dynamic_expressions(dsl_namespace, expression_names)
77
+ dsl = dsl_namespace.name.split("::").last.downcase.to_sym
78
+ dynamic_expression_chains_of_responsibility[dsl] = expression_names.reverse.map do |expression_name|
79
+ expression_class(dsl_namespace, expression_name).new
80
+ end.reduce(nil) do |last_expresion_handler, expression|
81
+ Glimmer::Config.logger&.debug "Adding dynamic expression: #{expression.class.name}"
82
+ expression_handler = ExpressionHandler.new(expression)
83
+ expression_handler.next = last_expresion_handler if last_expresion_handler
84
+ expression_handler
85
+ end
86
+ end
87
+
88
+ def add_static_expression(static_expression)
89
+ Glimmer::Config.logger&.debug "Adding static expression: #{static_expression.class.name}"
90
+ keyword = static_expression.class.keyword
91
+ static_expression_dsl = static_expression.class.dsl
92
+ static_expressions[keyword] ||= {}
93
+ static_expressions[keyword][static_expression_dsl] = static_expression
94
+ Glimmer.send(:define_method, keyword) do |*args, &block|
95
+ begin
96
+ retrieved_static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
97
+ static_expression_dsl = (Glimmer::DSL::Engine.static_expressions[keyword].keys - Glimmer::DSL::Engine.disabled_dsls).last if retrieved_static_expression.nil?
98
+ interpretation = nil
99
+ 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))
100
+ begin
101
+ interpretation = Glimmer::DSL::Engine.interpret(keyword, *args, &block)
102
+ rescue => e
103
+ Glimmer::DSL::Engine.reset
104
+ raise e if static_expression_dsl.nil? || !Glimmer::DSL::Engine.static_expressions[keyword][static_expression_dsl].is_a?(TopLevelExpression)
105
+ end
106
+ end
107
+ if interpretation
108
+ interpretation
109
+ else
110
+ raise Glimmer::Error, "Unsupported keyword: #{keyword}" unless static_expression_dsl || retrieved_static_expression
111
+ Glimmer::DSL::Engine.dsl_stack.push(static_expression_dsl || Glimmer::DSL::Engine.dsl)
112
+ static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
113
+ if !static_expression.can_interpret?(Glimmer::DSL::Engine.parent, keyword, *args, &block)
114
+ raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args} under parent #{Glimmer::DSL::Engine.parent}"
115
+ else
116
+ Glimmer::Config.logger&.debug "#{static_expression.class.name} will handle expression keyword #{keyword}"
117
+ static_expression.interpret(Glimmer::DSL::Engine.parent, keyword, *args, &block).tap do |ui_object|
118
+ Glimmer::DSL::Engine.add_content(ui_object, static_expression, &block) unless block.nil?
119
+ Glimmer::DSL::Engine.dsl_stack.pop
120
+ end
121
+ end
122
+ end
123
+ rescue StandardError => e
124
+ # Glimmer::DSL::Engine.dsl_stack.pop
125
+ Glimmer::DSL::Engine.reset
126
+ raise e
127
+ end
128
+ end
129
+ end
130
+
131
+ def expression_class(dsl_namespace, expression_name)
132
+ dsl_namespace.const_get(expression_class_name(expression_name).to_sym)
133
+ end
134
+
135
+ def expression_class_name(expression_name)
136
+ "#{expression_name}_expression".camelcase(:upper)
137
+ end
138
+
139
+ # Interprets Glimmer dynamic DSL expression consisting of keyword, args, and block (e.g. shell(:no_resize) { ... })
140
+ def interpret(keyword, *args, &block)
141
+ keyword = keyword.to_s
142
+ dynamic_expression_dsl = (dynamic_expression_chains_of_responsibility.keys - disabled_dsls).last if dsl.nil?
143
+ dsl_stack.push(dynamic_expression_dsl || dsl)
144
+ expression = dynamic_expression_chains_of_responsibility[dsl].handle(parent, keyword, *args, &block)
145
+ expression.interpret(parent, keyword, *args, &block).tap do |ui_object|
146
+ add_content(ui_object, expression, &block)
147
+ dsl_stack.pop
148
+ end
149
+ rescue StandardError => e
150
+ # dsl_stack.pop
151
+ reset
152
+ raise e
153
+ end
154
+
155
+ # Adds content block to parent UI object
156
+ #
157
+ # This allows evaluating parent UI object properties and children
158
+ #
159
+ # For example, a shell widget would get properties set and children added
160
+ def add_content(parent, expression, &block)
161
+ if block_given? && expression.is_a?(ParentExpression)
162
+ dsl_stack.push(expression.class.dsl)
163
+ parent_stack.push(parent)
164
+ expression.add_content(parent, &block)
165
+ parent_stack.pop
166
+ dsl_stack.pop
167
+ end
168
+ end
169
+
170
+ # Current parent while evaluating Glimmer DSL (nil if just started or done evaluatiing)
171
+ #
172
+ # Parents are maintained in a stack while evaluating Glimmer DSL
173
+ # to ensure properly ordered interpretation of DSL syntax
174
+ def parent
175
+ parent_stack.last
176
+ end
177
+
178
+ def parent_stack
179
+ parent_stacks[dsl] ||= []
180
+ end
181
+
182
+ def parent_stacks
183
+ @parent_stacks ||= {}
184
+ end
185
+
186
+ # Enables multiple DSLs to play well with each other when mixing together
187
+ def dsl_stack
188
+ @dsl_stack ||= []
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end