From d4035b47a2316dbc1875109c93e99984ed4586ff Mon Sep 17 00:00:00 2001 From: Andy Maleh Date: Sun, 14 Jul 2024 14:57:17 -0400 Subject: [PATCH] - Support ability to interpret multiple DSL expression hierarchies, by suspending interpretation of a hierarchy, interpreting another until done, and then resuming interpretation of previous hierarchy until done too. That is done by starting a new expression hierarchy interpretation using `Glimmer::DSL::Engine::new_parent_stack` in the middle of interpreting another expression hierarchy. - Refactor/rename `Glimmer::DSL::Engine::parent_stacks` to `Glimmer::DSL::Engine::dsl_parent_stacks` and have it include an `Array` of `Array`s of parent stacks per DSL. --- CHANGELOG.md | 5 +++++ README.md | 4 ++-- TODO.md | 3 ++- VERSION | 2 +- lib/glimmer/dsl/engine.rb | 32 +++++++++++++++++++++++------ spec/lib/glimmer/dsl/engine_spec.rb | 18 ++++++++++++++++ 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6502dcce2..f32d7be40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ Related Change Logs: - [glimmer-dsl-swt/CHANGELOG.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/CHANGELOG.md) +### 2.8.0 + +- Support ability to interpret multiple DSL expression hierarchies, by suspending interpretation of a hierarchy, interpreting another until done, and then resuming interpretation of previous hierarchy until done too. That is done by starting a new expression hierarchy interpretation using `Glimmer::DSL::Engine::new_parent_stack` in the middle of interpreting another expression hierarchy. +- Refactor/rename `Glimmer::DSL::Engine::parent_stacks` to `Glimmer::DSL::Engine::dsl_parent_stacks` and have it include an `Array` of `Array`s of parent stacks per DSL. + ### 2.7.9 - Optimize performance of `Glimmer::DataBinding::ObservableModel#add_observer` by removing `OpenStruct` constant check. diff --git a/README.md b/README.md index e19c0a1d4..571c8d7fa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# [](https://rubygems.org/gems/glimmer) Glimmer 2.7.9 +# [](https://rubygems.org/gems/glimmer) Glimmer 2.8.0 ## DSL Framework for Ruby GUI and More [![Gem Version](https://badge.fury.io/rb/glimmer.svg)](http://badge.fury.io/rb/glimmer) [![rspec](https://github.com/AndyObtiva/glimmer/workflows/rspec/badge.svg)](https://github.com/AndyObtiva/glimmer/actions?query=workflow%3Arspec) @@ -268,7 +268,7 @@ end ### Setup Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL: -- Add `gem 'glimmer', '~> 2.7.9'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.7.9` and add `require 'glimmer'` +- Add `gem 'glimmer', '~> 2.8.0'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.8.0` and add `require 'glimmer'` - Create `glimmer/dsl/[dsl_name]/dsl.rb`, which requires and adds all dynamic expressions for the [dsl_name] Glimmer DSL module as per the code shown in the previous section (or [Official DSLs](#official-dsls) as examples) - Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static diff --git a/TODO.md b/TODO.md index 6dee303a6..74482ef6e 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,7 @@ Related TODO files: - Observe multiple attributes (e.g. observe(@game, [:width, :height]) returns |value, attribute_name|) - Fix issue with computed data-binding when combined with nested/indexed data-binding (it looks up computed attributes on root object instead of nested object, it seems) - Support nested computed attributes (e.g. computed_by: {address1: [:street, :city, :state, :zip]}) -- Consider the idea of having Observer#observe accept an optional block to do observation without implementing `call` (kinda like when using the `observe` keyword in the Glimmer DSL). +- Consider the idea of having Observer#observe accept an optional block to do observation without implementing `call` (kinda like when using the `observe` keyword in the Glimmer DSL). - Ensure removing observers from hash in ObservableModel when removed from observable - Avoid `< Struct.new` in specs (tests) - Add built-in support to Glimmer::DSL::Engine to memoize/cache expressions similar to how that is supported for StaticExpression (but allowing an outside keyword and non-static expression to be used) @@ -57,6 +57,7 @@ Related TODO files: - Support `recursive: true` with models that have nested models - Observe multiple attributes or indexed/keyed/nested attribute expressions with a single observer - Consider the idea of having before_read and before_write support cancelling a data-binding operation by returning `false`, returning `:cancel`, Or by receiving an extra arg that enables calling arg.cancel on or something similar to avoid having normal code cancel the data-binding operation just because it returns nil. +- Consider supporting concurrent/parallel interpretation of expression hierarchies by assigning each one an ID that is used to identify it if multiple hierarchies were interpreted in multiple concurrent/parallel threads. ### Miscellaneous diff --git a/VERSION b/VERSION index 3e651609d..834f26295 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.7.9 +2.8.0 diff --git a/lib/glimmer/dsl/engine.rb b/lib/glimmer/dsl/engine.rb index deed91cb7..a78325ac9 100644 --- a/lib/glimmer/dsl/engine.rb +++ b/lib/glimmer/dsl/engine.rb @@ -103,7 +103,7 @@ def enabled_dsls=(dsl_names) # Resets Glimmer's engine activity and configuration. Useful in rspec before or after blocks in tests. def reset - parent_stacks.values.each do |a_parent_stack| + dsl_parent_stacks.values.each do |a_parent_stack| a_parent_stack.clear end dsl_stack.clear @@ -230,11 +230,11 @@ def interpret_expression(expression, keyword, *args, &block) def add_content(new_parent, expression, keyword, *args, &block) if block_given? && expression.is_a?(ParentExpression) dsl_stack.push(expression.class.dsl) - parent_stack.push(new_parent) + push_parent_into_parent_stack(new_parent) begin expression.add_content(new_parent, keyword, *args, &block) ensure - parent_stack.pop + pop_parent_from_parent_stack dsl_stack.pop end end @@ -244,14 +244,34 @@ def add_content(new_parent, expression, keyword, *args, &block) # # Parents are maintained in a stack while evaluating Glimmer DSL # to ensure properly ordered interpretation of DSL syntax - def_delegator :parent_stack, :last, :parent + def parent + parent_stack.last + end + + def push_parent_into_parent_stack(parent) + parent_stack.push(parent) + end + def pop_parent_from_parent_stack + parent_stack.pop + parent_stacks.pop if parent_stacks.size > 1 && parent_stacks.last.empty? + end + def parent_stack - parent_stacks[dsl] ||= Concurrent::Array.new + new_parent_stack if parent_stacks.last.nil? + parent_stacks.last + end + + def new_parent_stack + parent_stacks.push(Concurrent::Array.new) end def parent_stacks - @parent_stacks ||= Concurrent::Hash.new + dsl_parent_stacks[dsl] ||= Concurrent::Array.new # TODO insted of having one array, we need to nest it within an array of arrays + end + + def dsl_parent_stacks + @dsl_parent_stacks ||= Concurrent::Hash.new end # Enables multiple DSLs to play well with each other when mixing together diff --git a/spec/lib/glimmer/dsl/engine_spec.rb b/spec/lib/glimmer/dsl/engine_spec.rb index 2a90cade8..d300b50fd 100644 --- a/spec/lib/glimmer/dsl/engine_spec.rb +++ b/spec/lib/glimmer/dsl/engine_spec.rb @@ -61,6 +61,24 @@ module GlimmerSpec expect(GLIMMER_TOP_LEVEL_TARGET.to_s).to eq('SWT shell { SWT Dynamic browser(XML html { XML Dynamic body { XML Dynamic input({:type=>"text", :value=>"Hello, World!"}) } }) }') end + it 'interprets another expression hierarchy in the middle of interpreting an expression hierarchy' do + @target1 = shell { + spinner { + # start a second hierarchy + Glimmer::DSL::Engine.new_parent_stack + @target2 = shell { + table { + } + } + # continue back in first hierarchy + progress + } + } + + expect(@target1.to_s).to eq('SWT shell { SWT Dynamic spinner { SWT Dynamic progress } }') + expect(@target2.to_s).to eq('SWT shell { SWT Dynamic table }') + end + it 'standard static expression' do @target = shell { }