Skip to content

Commit

Permalink
- Support ability to interpret multiple DSL expression hierarchies, b…
Browse files Browse the repository at this point in the history
…y 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.
  • Loading branch information
AndyObtiva committed Jul 14, 2024
1 parent 914e274 commit 8c695b7
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 style="position: relative; top: 20px;" />](https://rubygems.org/gems/glimmer) Glimmer 2.7.9
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 style="position: relative; top: 20px;" />](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)
Expand Down Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ Related TODO files:

## Next

- Shine Data Binding Expression (+ optimize shine to avoid calling bind)
- Observe an array or nested arrays for all children changes on a specific property (e.g. observe(@game, 'tetris_blocks[][].color') ; returns |new_color, tetris_block| / observe(@game.players, '[].name') ; returns |name, player|)
- 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)
Expand Down Expand Up @@ -56,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

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.7.9
2.8.0
6 changes: 3 additions & 3 deletions glimmer.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-
# stub: glimmer 2.7.9 ruby lib
# stub: glimmer 2.8.0 ruby lib

Gem::Specification.new do |s|
s.name = "glimmer".freeze
s.version = "2.7.9"
s.version = "2.8.0"

s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
s.require_paths = ["lib".freeze]
s.authors = ["AndyMaleh".freeze]
s.date = "2024-07-02"
s.date = "2024-07-14"
s.description = "Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of a DSL Engine and a Data-Binding Library (including Observer Design Pattern, Observable Model, Observable Array, and Observable Hash). Used in Glimmer DSL for SWT (JRuby Desktop Development GUI Framework), Glimmer DSL for Web (Ruby in the Browser Web Frontend Framework), Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library and Winner of Fukuoka Ruby Award Competition 2022 Special Award), Glimmer DSL for Tk (Ruby Tk Desktop Development GUI Library), Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library), Glimmer DSL for FX (FOX Toolkit Ruby Desktop Development GUI Library), Glimmer DSL for WX (wxWidgets Ruby Desktop Development GUI Library), Glimmer DSL for Swing (JRuby Swing Desktop Development GUI Library), Glimmer DSL for JFX (JRuby JavaFX Desktop Development GUI Library), Glimmer DSL for XML (& HTML), and Glimmer DSL for CSS.".freeze
s.email = "andy.am@gmail.com".freeze
s.extra_rdoc_files = [
Expand Down
32 changes: 26 additions & 6 deletions lib/glimmer/dsl/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions spec/lib/glimmer/dsl/engine_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
}
Expand Down

0 comments on commit 8c695b7

Please sign in to comment.