glimmer 2.5.1 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +21 -0
- data/LICENSE.txt +1 -1
- data/PROCESS.md +1 -1
- data/README.md +201 -153
- data/VERSION +1 -1
- data/glimmer.gemspec +5 -4
- data/lib/glimmer/config.rb +1 -1
- data/lib/glimmer/data_binding/model_binding.rb +4 -4
- data/lib/glimmer/data_binding/observable.rb +1 -1
- data/lib/glimmer/data_binding/observable_array.rb +3 -3
- data/lib/glimmer/data_binding/observable_hash.rb +2 -2
- data/lib/glimmer/data_binding/observable_hashable.rb +1 -1
- data/lib/glimmer/data_binding/observable_model.rb +26 -17
- data/lib/glimmer/data_binding/observer.rb +3 -1
- data/lib/glimmer/data_binding/shine.rb +2 -2
- data/lib/glimmer/dsl/bind_expression.rb +1 -1
- data/lib/glimmer/dsl/engine.rb +6 -2
- data/lib/glimmer/dsl/expression.rb +1 -1
- data/lib/glimmer/dsl/expression_handler.rb +1 -1
- data/lib/glimmer/dsl/observe_expression.rb +1 -1
- data/lib/glimmer/dsl/parent_expression.rb +1 -1
- data/lib/glimmer/dsl/shine_data_binding_expression.rb +39 -0
- data/lib/glimmer/dsl/static_expression.rb +1 -1
- data/lib/glimmer/dsl/top_level_expression.rb +1 -1
- data/lib/glimmer/error.rb +1 -1
- data/lib/glimmer/invalid_keyword_error.rb +1 -1
- data/lib/glimmer.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0270d930a2379147403b0ed98ad22d38fc737a4bd45cac8a079b873db3b26b0
|
4
|
+
data.tar.gz: 002ea9f8c526d059ec0401397c0a2feb624f2333222b40e628a814275568ae7a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8034dbe6953955e23c2a355f211337fb9593e8722251ed35152af4b587422c675e675dd93e0d6b8ef9a04145f5a2ebbf364ed83f8a17296f7f4c4a83c711eb3c
|
7
|
+
data.tar.gz: 6f43bac5604f4759bbdcea11a0b1a84b68c24e7e087862f00f4bb7145ecfe0085ae1cbabe379c96a6f19c22465faa62c13bcfaeedc1f8390129e177d5318c0f5
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,27 @@
|
|
3
3
|
Related Change Logs:
|
4
4
|
- [glimmer-dsl-swt/CHANGELOG.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/CHANGELOG.md)
|
5
5
|
|
6
|
+
### 2.6.0
|
7
|
+
|
8
|
+
- If a static expression cannot handle keyword, it is tried with available dynamic expressions instead of erroring out.
|
9
|
+
|
10
|
+
### 2.5.5
|
11
|
+
|
12
|
+
- Support observing an array object index directly (e.g. `ModelBinding.new(array, '[0]')` as opposed to `ModelBinding.new(self, 'array[0]')`)
|
13
|
+
- Extract Shine data-binding expression from Glimmer DSL for SWT to Glimmer as a reusable class: `Glimmer::DSL::ShineDataBindingExpression`
|
14
|
+
- Avoid compacting model with property in `ModelBinding` yet only compact property/args and keep model as is get a better error message in the case of a `nil` model
|
15
|
+
- Document data-binding options like on_read, after_read, etc...
|
16
|
+
|
17
|
+
### 2.5.4
|
18
|
+
|
19
|
+
- `Glimmer::DataBinding::ObservableModel` support for observing model attributes with specified `:attribute_writer_type` option (default: `:attribute=`), which can be a single symbol or an array (e.g. `attribute_writer_type: [:attribute=, :set_attribute]`). Glimmer automatches attribute name and automatically generates observer notification attribute writer methods.
|
20
|
+
|
21
|
+
### 2.5.3
|
22
|
+
|
23
|
+
- Provide `Observer#observe` option to tolerate not being able to extend an object for observation by silently not observing such object
|
24
|
+
- Use `Concurrent::Hash` for `ObservableModel#property_observer_hash`
|
25
|
+
- Use `Concurrent::Hash` for `ObservableHash#key_observer_hash`
|
26
|
+
|
6
27
|
### 2.5.1
|
7
28
|
|
8
29
|
- Fix issue with referencing `OpenStruct` without 'ostruct' library being required (it now checks if `OpenStruct` is loaded first and avoids referencing otherwise).
|
data/LICENSE.txt
CHANGED
data/PROCESS.md
CHANGED
data/README.md
CHANGED
@@ -16,7 +16,7 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
|
|
16
16
|
|
17
17
|
[**Glimmer**](https://rubygems.org/gems/glimmer) is a DSL (Domain-Specific Language) Framework that consists of two things:
|
18
18
|
- [DSL Engine](#dsl-engine): enables building internal DSLs embedded in Ruby (e.g. for GUI, XML, or CSS).
|
19
|
-
- [Data-Binding Library](#data-binding-library): enables synchronizing GUI with Model Attributes bidirectionally **(now with [Shine](
|
19
|
+
- [Data-Binding Library](#data-binding-library): enables synchronizing GUI with Model Attributes bidirectionally **(now with [Shine](#shine-data-binding-syntax) syntax support in v2)**.
|
20
20
|
|
21
21
|
[**Glimmer**](https://rubygems.org/gems/glimmer) is ***the cream of the crop*** when it comes to building DSLs in Ruby:
|
22
22
|
- Supports building the tersest most concise domain specific language syntax in Ruby.
|
@@ -27,13 +27,13 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
|
|
27
27
|
- Multiple DSLs may be [mixed](#multi-dsl-support) together safely to achieve maximum expressability, composability, and productivity.
|
28
28
|
- DSLs are fully configurable, so you may activate and deactivate DSLs as per your current needs only.
|
29
29
|
|
30
|
-
Start by checking out Glimmer's original GUI DSL, which got extracted into its own gem
|
30
|
+
Start by checking out [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt), Glimmer's original GUI DSL (for [JRuby](https://www.jruby.org/)), which got extracted into its own gem; and [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui), Glimmer's CRuby GUI DSL, which has no prerequisites beyond installing the Ruby gem.
|
31
31
|
|
32
32
|
[**Glimmer**](https://rubygems.org/gems/glimmer) supports the following DSLs:
|
33
33
|
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
|
34
34
|
- [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
|
35
|
-
- [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
|
36
35
|
- [glimmer-dsl-libui](https://github.com/AndyObtiva/glimmer-dsl-libui): Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
|
36
|
+
- [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
|
37
37
|
- [glimmer-dsl-gtk](https://github.com/AndyObtiva/glimmer-dsl-gtk): Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)
|
38
38
|
- [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
|
39
39
|
- [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
|
@@ -48,8 +48,8 @@ Start by checking out Glimmer's original GUI DSL, which got extracted into its o
|
|
48
48
|
- [Official DSLs](#official-dsls)
|
49
49
|
- [Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](#glimmer-dsl-for-swt-jruby-desktop-development-gui-framework)
|
50
50
|
- [Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)](#glimmer-dsl-for-opal-pure-ruby-web-gui-and-auto-webifier-of-desktop-apps)
|
51
|
-
- [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
|
52
51
|
- [Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](#glimmer-dsl-for-libui-prerequisite-free-ruby-desktop-development-gui-library)
|
52
|
+
- [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
|
53
53
|
- [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
|
54
54
|
- [Glimmer DSL for CSS](#glimmer-dsl-for-css)
|
55
55
|
- [Data-Binding Library](#data-binding-library)
|
@@ -99,7 +99,7 @@ The Glimmer DSL Engine's architecture is based on the following Design Patterns
|
|
99
99
|
- **Stack Data Structure**: to handle processing parent/child nesting of DSL keyword expressions in the correct order
|
100
100
|
- **Proxy Design Pattern**: to shield consumers of GUI libraries built with Glimmer from low-level GUI widget details
|
101
101
|
|
102
|
-
Glimmer's use of the **Interpreter Design Pattern** in processing DSLs is also known as the **Virtual Machine Architectural Style**. After all, DSL expressions are virtual machine opcodes that process nested keywords stored in a stack. I built Glimmer's original DSL back in 2007 without knowing the **Virtual Machine Architectural Style** (except perhaps as an esoteric technology powering Java), but stumbled upon it anyways through following the Gang of Four Design Patterns mentioned above, chiefly the **Interpreter Design Pattern**.
|
102
|
+
Glimmer's use of the **Interpreter Design Pattern** in processing DSLs is also known as the **Virtual Machine Architectural Style**. After all, DSL expressions are virtual machine opcodes that process nested keywords stored in a stack. I built Glimmer's original DSL back in 2007 without knowing the **Virtual Machine Architectural Style** (except perhaps as an esoteric technology powering Java), but stumbled upon it anyways through following the Gang of Four Design Patterns mentioned above, chiefly the **Interpreter Design Pattern** and the **Chain of Responsibility Design Pattern**.
|
103
103
|
|
104
104
|
Every keyword in a Glimmer DSL is represented by a DSL expression that is processed by an `Expression` subclass selected from a chain of expressions (interpreters) pre-configured in a DSL chain of responsibility via `Glimmer::DSL::Engine.add_dynamic_expressions(DSLNameModule, expression_names_array)`.
|
105
105
|
|
@@ -177,9 +177,11 @@ module Glimmer
|
|
177
177
|
end
|
178
178
|
```
|
179
179
|
|
180
|
-
|
181
|
-
|
182
|
-
|
180
|
+
Extra convenience expression mixins/superclasses for use via inclusion/subclassing in Glimmer GUI libraries:
|
181
|
+
- `Glimmer::DSL::BindExpression`: enables usage of `bind` data-binding keyword to build a `Glimmer::DataBinding::ModelBinding` object for [data-binding](#data-binding) purposes.
|
182
|
+
- `Glimmer::DSL::ShineDataBindingExpression`: enables [Shine data-binding syntax](#shine-data-binding-syntax) via `Glimmer::DataBinding::Shine`, a facade for the `bind` keyword, hiding it with the `<=>` operator for bidirectional (two-way) data-binding and the `<=` operator for unidirectional (one-way) data-binding.
|
183
|
+
- `Glimmer::DSL::ObserveExpression`: enables a one-way `observe` operation.
|
184
|
+
You may learn more about them by looking at how [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) uses them.
|
183
185
|
|
184
186
|
DSL expressions go into the `glimmer/dsl/{dsl_name}` namespace directory.
|
185
187
|
|
@@ -230,7 +232,7 @@ end
|
|
230
232
|
### Setup
|
231
233
|
|
232
234
|
Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL:
|
233
|
-
- Add `gem 'glimmer', '~> 2.
|
235
|
+
- Add `gem 'glimmer', '~> 2.6.0'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.6.0` and add `require 'glimmer'`
|
234
236
|
- 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)
|
235
237
|
- Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static
|
236
238
|
|
@@ -754,146 +756,6 @@ You should see "Apple Calculator Theme"
|
|
754
756
|
|
755
757
|
[![Glimmer Calculator Opal Apple Calculator Theme](https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot-opal-apple.png)](http://glimmer-cs-calculator-server.herokuapp.com/welcomes/apple)
|
756
758
|
|
757
|
-
#### Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
|
758
|
-
|
759
|
-
[Tcl/Tk](https://www.tcl.tk/) has evolved into a practical desktop GUI toolkit due to gaining truely native looking widgets on Mac, Windows, and Linux in [Tk version 8.5](https://www.tcl.tk/software/tcltk/8.5.html#:~:text=Highlights%20of%20Tk%208.5&text=Font%20rendering%3A%20Now%20uses%20anti,and%20window%20layout%2C%20and%20more.).
|
760
|
-
|
761
|
-
Additionally, [Ruby](https://www.ruby-lang.org/en/) 3.0 Ractor (formerly known as [Guilds](https://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/)) supports truly parallel multi-threading, making both [MRI](https://github.com/ruby/ruby) and [Tk](https://www.tcl.tk/) finally viable for support in [Glimmer](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library) as an alternative to [JRuby on SWT](https://github.com/AndyObtiva/glimmer-dsl-swt).
|
762
|
-
|
763
|
-
The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a plethora of high quality reusable widgets for the Enterprise (such as [Nebula](https://www.eclipse.org/nebula/)), [Tk](https://www.tcl.tk/) enables very fast app startup time and a small memory footprint via [MRI Ruby](https://www.ruby-lang.org/en/).
|
764
|
-
|
765
|
-
[Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
|
766
|
-
- Declarative DSL syntax that visually maps to the GUI widget hierarchy
|
767
|
-
- Convention over configuration via smart defaults and automation of low-level details
|
768
|
-
- Requiring the least amount of syntax possible to build GUI
|
769
|
-
- Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
770
|
-
- Custom Widget support
|
771
|
-
- Scaffolding for new custom widgets, apps, and gems
|
772
|
-
- Native-Executable packaging on Mac, Windows, and Linux
|
773
|
-
|
774
|
-
To get started, visit the [Glimmer DSL for Tk project page](https://github.com/AndyObtiva/glimmer-dsl-tk#pre-requisites) for instructions on installing the [glimmer-dsl-tk gem](https://rubygems.org/gems/glimmer-dsl-tk).
|
775
|
-
|
776
|
-
##### Glimmer DSL for Tk Samples
|
777
|
-
|
778
|
-
###### Hello, World!
|
779
|
-
|
780
|
-
Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
|
781
|
-
|
782
|
-
```ruby
|
783
|
-
include Glimmer
|
784
|
-
|
785
|
-
root {
|
786
|
-
label {
|
787
|
-
text 'Hello, World!'
|
788
|
-
}
|
789
|
-
}.open
|
790
|
-
```
|
791
|
-
|
792
|
-
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
793
|
-
|
794
|
-
```
|
795
|
-
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"
|
796
|
-
```
|
797
|
-
|
798
|
-
Glimmer app:
|
799
|
-
|
800
|
-
![glimmer dsl tk screenshot sample hello world](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-world.png)
|
801
|
-
|
802
|
-
###### Hello, Notebook!
|
803
|
-
|
804
|
-
Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
|
805
|
-
|
806
|
-
```ruby
|
807
|
-
include Glimmer
|
808
|
-
|
809
|
-
root {
|
810
|
-
title 'Hello, Notebook!'
|
811
|
-
|
812
|
-
notebook {
|
813
|
-
frame(text: 'English') {
|
814
|
-
label {
|
815
|
-
text 'Hello, World!'
|
816
|
-
}
|
817
|
-
}
|
818
|
-
|
819
|
-
frame(text: 'French') {
|
820
|
-
label {
|
821
|
-
text 'Bonjour, Univers!'
|
822
|
-
}
|
823
|
-
}
|
824
|
-
}
|
825
|
-
}.open
|
826
|
-
```
|
827
|
-
|
828
|
-
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
829
|
-
|
830
|
-
```
|
831
|
-
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_notebook.rb'"
|
832
|
-
```
|
833
|
-
|
834
|
-
Glimmer app:
|
835
|
-
|
836
|
-
![glimmer dsl tk screenshot sample hello notebook English](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-notebook-english.png)
|
837
|
-
![glimmer dsl tk screenshot sample hello notebook French](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-notebook-french.png)
|
838
|
-
|
839
|
-
###### Hello, Combobox!
|
840
|
-
|
841
|
-
Glimmer code (from [samples/hello/hello_combobox.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combobox.rb)):
|
842
|
-
|
843
|
-
```ruby
|
844
|
-
require 'glimmer-dsl-tk'
|
845
|
-
|
846
|
-
class Person
|
847
|
-
attr_accessor :country, :country_options
|
848
|
-
|
849
|
-
def initialize
|
850
|
-
self.country_options=["", "Canada", "US", "Mexico"]
|
851
|
-
self.country = "Canada"
|
852
|
-
end
|
853
|
-
|
854
|
-
def reset_country
|
855
|
-
self.country = "Canada"
|
856
|
-
end
|
857
|
-
end
|
858
|
-
|
859
|
-
class HelloCombobox
|
860
|
-
include Glimmer
|
861
|
-
|
862
|
-
def launch
|
863
|
-
person = Person.new
|
864
|
-
|
865
|
-
root {
|
866
|
-
title 'Hello, Combobox!'
|
867
|
-
|
868
|
-
combobox {
|
869
|
-
readonly true # this applies to text editing only (item selection still triggers a write to model)
|
870
|
-
text <=> [person, :country]
|
871
|
-
}
|
872
|
-
|
873
|
-
button {
|
874
|
-
text "Reset Selection"
|
875
|
-
command {
|
876
|
-
person.reset_country
|
877
|
-
}
|
878
|
-
}
|
879
|
-
}.open
|
880
|
-
end
|
881
|
-
end
|
882
|
-
|
883
|
-
HelloCombobox.new.launch
|
884
|
-
```
|
885
|
-
|
886
|
-
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
887
|
-
|
888
|
-
```
|
889
|
-
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combobox.rb'"
|
890
|
-
```
|
891
|
-
|
892
|
-
Glimmer app:
|
893
|
-
|
894
|
-
![glimmer dsl tk screenshot sample hello combobox](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combobox.png)
|
895
|
-
![glimmer dsl tk screenshot sample hello combobox dropdown](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combobox-dropdown.png)
|
896
|
-
|
897
759
|
#### Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
|
898
760
|
|
899
761
|
[Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) is a prerequisite-free Ruby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!
|
@@ -1099,6 +961,144 @@ Linux
|
|
1099
961
|
|
1100
962
|
![glimmer-dsl-libui-linux-area-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-area-gallery.png)
|
1101
963
|
|
964
|
+
#### Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
|
965
|
+
|
966
|
+
[Tcl/Tk](https://www.tcl.tk/) has recently improved by gaining native looking themed widgets on Mac, Windows, and Linux in [Tk version 8.5](https://www.tcl.tk/software/tcltk/8.5.html#:~:text=Highlights%20of%20Tk%208.5&text=Font%20rendering%3A%20Now%20uses%20anti,and%20window%20layout%2C%20and%20more.). Additionally, [Ruby](https://www.ruby-lang.org/en/) 3.0 Ractor (formerly known as [Guilds](https://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/)) supports truly parallel multi-threading, making both [MRI](https://github.com/ruby/ruby) and [Tk](https://www.tcl.tk/) finally viable for support in [Glimmer](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library) as an alternative to [JRuby on SWT](https://github.com/AndyObtiva/glimmer-dsl-swt).
|
967
|
+
|
968
|
+
The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a plethora of high quality reusable widgets for the Enterprise (such as [Nebula](https://www.eclipse.org/nebula/)), [Tk](https://www.tcl.tk/) enables very fast app startup time and a small memory footprint via [MRI Ruby](https://www.ruby-lang.org/en/).
|
969
|
+
|
970
|
+
[Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
|
971
|
+
- Declarative DSL syntax that visually maps to the GUI widget hierarchy
|
972
|
+
- Convention over configuration via smart defaults and automation of low-level details
|
973
|
+
- Requiring the least amount of syntax possible to build GUI
|
974
|
+
- Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
975
|
+
- Custom Widget support
|
976
|
+
- Scaffolding for new custom widgets, apps, and gems
|
977
|
+
- Native-Executable packaging on Mac, Windows, and Linux
|
978
|
+
|
979
|
+
To get started, visit the [Glimmer DSL for Tk project page](https://github.com/AndyObtiva/glimmer-dsl-tk#pre-requisites) for instructions on installing the [glimmer-dsl-tk gem](https://rubygems.org/gems/glimmer-dsl-tk).
|
980
|
+
|
981
|
+
##### Glimmer DSL for Tk Samples
|
982
|
+
|
983
|
+
###### Hello, World!
|
984
|
+
|
985
|
+
Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
|
986
|
+
|
987
|
+
```ruby
|
988
|
+
include Glimmer
|
989
|
+
|
990
|
+
root {
|
991
|
+
label {
|
992
|
+
text 'Hello, World!'
|
993
|
+
}
|
994
|
+
}.open
|
995
|
+
```
|
996
|
+
|
997
|
+
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
998
|
+
|
999
|
+
```
|
1000
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"
|
1001
|
+
```
|
1002
|
+
|
1003
|
+
Glimmer app:
|
1004
|
+
|
1005
|
+
![glimmer dsl tk screenshot sample hello world](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-world.png)
|
1006
|
+
|
1007
|
+
###### Hello, Notebook!
|
1008
|
+
|
1009
|
+
Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
|
1010
|
+
|
1011
|
+
```ruby
|
1012
|
+
include Glimmer
|
1013
|
+
|
1014
|
+
root {
|
1015
|
+
title 'Hello, Notebook!'
|
1016
|
+
|
1017
|
+
notebook {
|
1018
|
+
frame(text: 'English') {
|
1019
|
+
label {
|
1020
|
+
text 'Hello, World!'
|
1021
|
+
}
|
1022
|
+
}
|
1023
|
+
|
1024
|
+
frame(text: 'French') {
|
1025
|
+
label {
|
1026
|
+
text 'Bonjour, Univers!'
|
1027
|
+
}
|
1028
|
+
}
|
1029
|
+
}
|
1030
|
+
}.open
|
1031
|
+
```
|
1032
|
+
|
1033
|
+
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
1034
|
+
|
1035
|
+
```
|
1036
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_notebook.rb'"
|
1037
|
+
```
|
1038
|
+
|
1039
|
+
Glimmer app:
|
1040
|
+
|
1041
|
+
![glimmer dsl tk screenshot sample hello notebook English](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-notebook-english.png)
|
1042
|
+
![glimmer dsl tk screenshot sample hello notebook French](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-notebook-french.png)
|
1043
|
+
|
1044
|
+
###### Hello, Combobox!
|
1045
|
+
|
1046
|
+
Glimmer code (from [samples/hello/hello_combobox.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combobox.rb)):
|
1047
|
+
|
1048
|
+
```ruby
|
1049
|
+
require 'glimmer-dsl-tk'
|
1050
|
+
|
1051
|
+
class Person
|
1052
|
+
attr_accessor :country, :country_options
|
1053
|
+
|
1054
|
+
def initialize
|
1055
|
+
self.country_options=["", "Canada", "US", "Mexico"]
|
1056
|
+
self.country = "Canada"
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
def reset_country
|
1060
|
+
self.country = "Canada"
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
class HelloCombobox
|
1065
|
+
include Glimmer
|
1066
|
+
|
1067
|
+
def launch
|
1068
|
+
person = Person.new
|
1069
|
+
|
1070
|
+
root {
|
1071
|
+
title 'Hello, Combobox!'
|
1072
|
+
|
1073
|
+
combobox {
|
1074
|
+
readonly true # this applies to text editing only (item selection still triggers a write to model)
|
1075
|
+
text <=> [person, :country]
|
1076
|
+
}
|
1077
|
+
|
1078
|
+
button {
|
1079
|
+
text "Reset Selection"
|
1080
|
+
command {
|
1081
|
+
person.reset_country
|
1082
|
+
}
|
1083
|
+
}
|
1084
|
+
}.open
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
HelloCombobox.new.launch
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
1092
|
+
|
1093
|
+
```
|
1094
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combobox.rb'"
|
1095
|
+
```
|
1096
|
+
|
1097
|
+
Glimmer app:
|
1098
|
+
|
1099
|
+
![glimmer dsl tk screenshot sample hello combobox](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combobox.png)
|
1100
|
+
![glimmer dsl tk screenshot sample hello combobox dropdown](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combobox-dropdown.png)
|
1101
|
+
|
1102
1102
|
#### Glimmer DSL for XML (& HTML)
|
1103
1103
|
|
1104
1104
|
[Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) provides Ruby syntax for building XML (eXtensible Markup Language) documents.
|
@@ -1180,18 +1180,22 @@ body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2
|
|
1180
1180
|
|
1181
1181
|
Data-Binding enables mapping GUI properties (like text and color) to Model attributes (like name and age) for bidirectional or unidirectional synchronization and conversion as needed.
|
1182
1182
|
|
1183
|
+
Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) flavor of [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) by observing both the View and a Presenter for changes and updating the opposite side upon encountering them. This enables writing more decoupled cleaner code that keeps View code and Model code disentangled and highly maintainable.
|
1184
|
+
|
1185
|
+
![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
|
1186
|
+
|
1183
1187
|
Glimmer enhances observed models automatically (including array operations like `<<`, `delete`, and `reject!`) on first observation. As such, you get automatic observable support, including nested and computed observations. No need to change your model code to data-bind it to the view or add repetitive boilerplate modules. View data-binding is truly decoupled from model logic by being able to observe any model attribute (Ruby attribute reader/writer combo or Ruby attribute reader alone for read-only data-binding when needed)
|
1184
1188
|
|
1185
1189
|
This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
|
1186
1190
|
|
1187
1191
|
These are the main classes concerning data-binding:
|
1188
|
-
- `Glimmer::DataBinding::Observer`: Provides general observer support including unique registration and deregistration for cleanup and prevention of memory leaks. Main methods concerned are: `call`, `register` (alias: `observe`), and `unregister` (alias: `unobserve` or `deregister`)
|
1192
|
+
- `Glimmer::DataBinding::Observer`: Provides general observer support including unique registration and deregistration for cleanup and prevention of memory leaks. Main methods concerned are: `call`, `register` (alias: `observe`), and `unregister` (alias: `unobserve` or `deregister`). Passing the option `ignore_frozen: true` at the end of the args of `register` (alias: `observe`) method results in silently ignoring any passed frozen observable without raising an error (it raises an error otherwise for frozen/immutable objects).
|
1189
1193
|
- `Glimmer::DataBinding::Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
1190
|
-
- `Glimmer::DataBinding::ObservableModel`: Mixin module for any observable model (`Object`, `Struct` or `OpenStruct`) with observable attributes (observes attribute writers and `Struct`/`OpenStruct` `:[]=` method). In addition to `Observable` methods, it has a `notify_observers` method to be called when changes occur. It automatically enhances all attribute setters (ending with `=`) to notify observers on changes. Also, it automatically handles observing array attributes using `ObservableArray` appropriately so they would notify observers upon array mutation changes.
|
1194
|
+
- `Glimmer::DataBinding::ObservableModel`: Mixin module for any observable model (`Object`, `Struct` or `OpenStruct`) with observable attributes (observes attribute writers and `Struct`/`OpenStruct` `:[]=` method). In addition to `Observable` methods, it has a `notify_observers` method to be called when changes occur. It automatically enhances all attribute setters (ending with `=`) to notify observers on changes. Also, it automatically handles observing array attributes using `ObservableArray` appropriately so they would notify observers upon array mutation changes. `:attribute_writer_type` option can be specified (default: `:attribute=`) to observe different attribute styles (e.g. `attribute_writer_type: [:attribute=, :set_attribute]`).
|
1191
1195
|
- `Glimmer::DataBinding::ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push`, `select!`, or `delete`) recursively (meaning if an array contained arrays and they changed, observers are notified). Accepts `recursive: true` option in `add_observer` method to recursively observe nested arrays all the way down. Alternatively, pass `recursive: [integer]` to limit recursion in `Array` observation to a specific number of levels beyond the first level (which is always included).
|
1192
1196
|
- `Glimmer::DataBinding::ObservableHash`: Mixin module for any observable hash that automatically handles notifying observers upon performing hash mutation operations (e.g. `hash[key]=value`, `select!`, `merge!`). Also, it automatically handles observing array values using `ObservableArray` appropriately so they would notify observers upon array mutation changes.
|
1193
1197
|
- `Glimmer::DataBinding::ModelBinding`: a higher-level abstraction that relies on all the other observer/observable classes to support basic data-binding, nested data-binding, and computed data-binding
|
1194
|
-
- `Glimmer::DataBinding::Shine`: enables highly intuitive and visually expressive syntax to perform bidirectional (two-way) data-binding with `<=>` and unidirectional (one-way) data-binding with `<=`
|
1198
|
+
- `Glimmer::DataBinding::Shine`: enables [highly intuitive and visually expressive syntax](#shine-data-binding-syntax) to perform bidirectional (two-way) data-binding with `<=>` and unidirectional (one-way) data-binding with `<=`
|
1195
1199
|
|
1196
1200
|
To do simple observation of models, arrays, or hashes, you can use the `Glimmer::DataBinding::Observer::proc` method, which builds an observer from a block. When invoking the `#observe` method on it, it automatically enhances the object argument being observed into an `Observable` (whether `ObservableModel`, `ObservableArray`, or `ObervableHash`).
|
1197
1201
|
|
@@ -1253,12 +1257,56 @@ ModelBinding.new(model, "grid[5][7]").add_observer do |new_grid_cell_value|
|
|
1253
1257
|
end
|
1254
1258
|
```
|
1255
1259
|
|
1256
|
-
|
1260
|
+
Data-bound `ModelBinding` attribute can be:
|
1261
|
+
- **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
|
1262
|
+
- **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
|
1263
|
+
- **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
|
1264
|
+
|
1265
|
+
Data-binding options include:
|
1266
|
+
- `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
|
1267
|
+
- `on_read {|value| ...}`: converts value read from Model to update the View.
|
1268
|
+
- `after_read {|converted_value| ...}`: performs an operation after read from Model to update View.
|
1269
|
+
- `before_write {|value| ...}`: performs an operation before writing data to Model from View.
|
1270
|
+
- `on_write {|value| ...}`: converts value read from View to update the Model.
|
1271
|
+
- `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
|
1272
|
+
- `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated. That is known as "computed data-binding".
|
1273
|
+
|
1274
|
+
Note that if an observed model attribute or hash key is an `Array`, it is automatically observed for `Array` changes (e.g. via mutation methods `<<`, `delete`, `map!`), not just attribute/key-value changes.
|
1257
1275
|
|
1258
1276
|
All of the features above make Glimmer's data-binding library one of the most sophisticated and advanced in the industry since it automates everything instead of requiring endless manual configuration, thus resulting in some of the tersest most declarative syntax for using observers and data-binding.
|
1259
1277
|
|
1260
1278
|
You may learn more by looking into [data-binding specs](/Users/andy/code/glimmer/spec/lib/glimmer/data_binding) as well as [Data-Binding](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#data-binding) and [Observer](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#observer) usage in [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)
|
1261
1279
|
|
1280
|
+
### Shine Data-Binding Syntax
|
1281
|
+
|
1282
|
+
The Shine data-binding syntax is a highly intuitive and visually expressive way of data-binding that enables performing bidirectional (two-way) data-binding with the `<=>` operator and unidirectional (one-way) data-binding with the `<=` operator.
|
1283
|
+
|
1284
|
+
It is facilitated by the combination of the `Glimmer::DSL::ShineDataBindingExpression` and `Glimmer::DataBinding::Shine` classes, which depend on `Glimmer::DSL::BindExpression` and `Glimmer::DataBinding::ModelBinding`.
|
1285
|
+
|
1286
|
+
Below are some examples of Shine usage in GUI DSLs:
|
1287
|
+
|
1288
|
+
`text <=> [contact, :first_name]`
|
1289
|
+
|
1290
|
+
This example bidirectionally binds the text property of a widget like label to the first name of a contact model.
|
1291
|
+
|
1292
|
+
`text <=> [contact, 'address.street']`
|
1293
|
+
|
1294
|
+
This example binds the text property of a widget like label to the nested street of the address of a contact. This is called nested property data binding.
|
1295
|
+
|
1296
|
+
`text <=> [contact, 'address.street', on_read: :upcase, on_write: :downcase]`
|
1297
|
+
|
1298
|
+
This example adds on the one above it by specifying converters on read and write of the model property, like in the case of a text widget. The text widget will then displays the street upper case and the model will store it lower case. When specifying converters, read and write operations must be symmetric.
|
1299
|
+
|
1300
|
+
`enabled <= [user, :logged_in]`
|
1301
|
+
|
1302
|
+
This example unidirectionally binds the enabled property of a widget like button to the logged in status of a user.
|
1303
|
+
|
1304
|
+
`enabled <= [user, :logged_in, on_read: :!]`
|
1305
|
+
|
1306
|
+
This example unidirectionally binds the enabled property of a widget like entry to the negated logged in status of a user. Note that when using a single on read converter with unidirectional data-binding, there is no need for a symmetric on_write converter as well since writing is never done with unidirectional (one-way) data-binding.
|
1307
|
+
|
1308
|
+
[Learn more about Shine data-binding syntax from its usage in Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#shine)
|
1309
|
+
|
1262
1310
|
## Glimmer Process
|
1263
1311
|
|
1264
1312
|
[Glimmer Process](PROCESS.md) is the lightweight software development process used for building Glimmer libraries and Glimmer apps, which goes beyond Agile, rendering all Agile processes obsolete. [Glimmer Process](PROCESS.md) is simply made up of 7 guidelines to pick and choose as necessary until software development needs are satisfied.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.0
|
data/glimmer.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer 2.
|
5
|
+
# stub: glimmer 2.6.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer".freeze
|
9
|
-
s.version = "2.
|
9
|
+
s.version = "2.6.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["AndyMaleh".freeze]
|
14
|
-
s.date = "
|
14
|
+
s.date = "2022-01-22"
|
15
15
|
s.description = "Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of a DSL Engine and an Observable / Observer / Data-Binding Library (including Observable Model, Observable Array, and Observable Hash). Used in Glimmer DSL for SWT (JRuby Desktop Development GUI Framework), Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps), Glimmer DSL for Tk (Ruby Desktop Development GUI Library), Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library), Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library), Glimmer DSL for XML (& HTML), and Glimmer DSL for CSS.".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -43,6 +43,7 @@ Gem::Specification.new do |s|
|
|
43
43
|
"lib/glimmer/dsl/expression_handler.rb",
|
44
44
|
"lib/glimmer/dsl/observe_expression.rb",
|
45
45
|
"lib/glimmer/dsl/parent_expression.rb",
|
46
|
+
"lib/glimmer/dsl/shine_data_binding_expression.rb",
|
46
47
|
"lib/glimmer/dsl/static_expression.rb",
|
47
48
|
"lib/glimmer/dsl/top_level_expression.rb",
|
48
49
|
"lib/glimmer/error.rb",
|
@@ -52,7 +53,7 @@ Gem::Specification.new do |s|
|
|
52
53
|
]
|
53
54
|
s.homepage = "http://github.com/AndyObtiva/glimmer".freeze
|
54
55
|
s.licenses = ["MIT".freeze]
|
55
|
-
s.rubygems_version = "3.
|
56
|
+
s.rubygems_version = "3.3.1".freeze
|
56
57
|
s.summary = "Glimmer - DSL Engine for Ruby GUI and More".freeze
|
57
58
|
|
58
59
|
if s.respond_to? :specification_version then
|
data/lib/glimmer/config.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -75,7 +75,7 @@ module Glimmer
|
|
75
75
|
# If there are any indexed property names, this returns indexes as properties.
|
76
76
|
# e.g. property name expression "addresses[1].state" gives ['addresses', '[1]', 'state']
|
77
77
|
def nested_property_names
|
78
|
-
@nested_property_names ||= Concurrent::Array.new(property_name_expression.split(/\[|\./).map {|pne| pne.end_with?(']') ? "[#{pne}" : pne })
|
78
|
+
@nested_property_names ||= Concurrent::Array.new(property_name_expression.split(/\[|\./).map {|pne| pne.end_with?(']') ? "[#{pne}" : pne }.reject {|pne| pne.empty? })
|
79
79
|
end
|
80
80
|
|
81
81
|
# Final nested property name
|
@@ -85,7 +85,7 @@ module Glimmer
|
|
85
85
|
end
|
86
86
|
|
87
87
|
# Model representing nested property names
|
88
|
-
# e.g. property name expression "address.state" gives [
|
88
|
+
# e.g. property name expression "address.state" gives ['address']
|
89
89
|
def model_property_names
|
90
90
|
Concurrent::Array.new(nested_property_names[0...-1])
|
91
91
|
end
|
@@ -137,7 +137,7 @@ module Glimmer
|
|
137
137
|
apply_processor(@binding_options[:after_read], converted_value)
|
138
138
|
end
|
139
139
|
end
|
140
|
-
observer_registration = model_binding_observer.observe(*[model
|
140
|
+
observer_registration = model_binding_observer.observe(*([model] + [property_name, observation_options].compact))
|
141
141
|
my_registration = observer.registration_for(self)
|
142
142
|
observer.add_dependent(my_registration => observer_registration)
|
143
143
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -51,7 +51,7 @@ module Glimmer
|
|
51
51
|
element_properties = element_properties.flatten.compact.uniq
|
52
52
|
return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
|
53
53
|
property_observer_list[observer] = options
|
54
|
-
observer_element_properties[observer] = element_properties_for(observer) + Concurrent::
|
54
|
+
observer_element_properties[observer] = Concurrent::Set.new(Concurrent::Array.new(element_properties_for(observer).to_a) + Concurrent::Array.new(element_properties)) # converting to Array as a workaround to jruby-9.3.2.0 issue TODO remove this workaround when no longer needed
|
55
55
|
if !options.empty? && options[:recursive].is_a?(Integer)
|
56
56
|
options = options.clone
|
57
57
|
options[:recursive] = options[:recursive] - 1
|
@@ -97,7 +97,7 @@ module Glimmer
|
|
97
97
|
element_properties = element_properties.flatten.compact.uniq
|
98
98
|
if !element_properties.empty?
|
99
99
|
old_element_properties = element_properties_for(observer)
|
100
|
-
observer_element_properties[observer] = element_properties_for(observer) - Concurrent::
|
100
|
+
observer_element_properties[observer] = Concurrent::Set.new(Concurrent::Array.new(element_properties_for(observer).to_a) - Concurrent::Array.new(element_properties)) # TODO remove this workaround when no longer needed (it is for jruby-9.3.2.0 issue)
|
101
101
|
each { |element| element_properties.each { |property| observer.unobserve(element, property) } }
|
102
102
|
end
|
103
103
|
if element_properties_for(observer).empty?
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -84,7 +84,7 @@ module Glimmer
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def key_observer_hash
|
87
|
-
@key_observers ||= Hash.new
|
87
|
+
@key_observers ||= Concurrent::Hash.new
|
88
88
|
end
|
89
89
|
|
90
90
|
def key_observer_list(key)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -40,8 +40,7 @@ module Glimmer
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
PROPERTY_WRITER_FACTORY = lambda do |property_name, options|
|
44
|
-
property_writer_name = "#{property_name}="
|
43
|
+
PROPERTY_WRITER_FACTORY = lambda do |property_name, property_writer_name, options|
|
45
44
|
lambda do |value|
|
46
45
|
old_value = self.send(property_name)
|
47
46
|
unregister_dependent_observers(property_name, old_value) # remove dependent observers previously installed in ensure_array_object_observer
|
@@ -52,6 +51,7 @@ module Glimmer
|
|
52
51
|
end
|
53
52
|
|
54
53
|
def add_observer(observer, property_name, options = {})
|
54
|
+
initialize_observer_options(options)
|
55
55
|
return observer if has_observer?(observer, property_name)
|
56
56
|
property_observer_list(property_name) << observer
|
57
57
|
add_property_writer_observers(property_name, options)
|
@@ -93,7 +93,7 @@ module Glimmer
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def property_observer_hash
|
96
|
-
@property_observers ||= Hash.new
|
96
|
+
@property_observers ||= Concurrent::Hash.new
|
97
97
|
end
|
98
98
|
|
99
99
|
def property_observer_list(property_name)
|
@@ -113,20 +113,24 @@ module Glimmer
|
|
113
113
|
end
|
114
114
|
|
115
115
|
def add_property_writer_observers(property_name, options)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
116
|
+
options[:attribute_writer_type].each do |attribute_writer_type|
|
117
|
+
begin
|
118
|
+
property_writer_name = attribute_writer_type.to_s.gsub('attribute', property_name.to_s)
|
119
|
+
method(property_writer_name)
|
120
|
+
ensure_array_object_observer(property_name, send(property_name), nil, options)
|
121
|
+
begin
|
122
|
+
method("__original__#{property_writer_name}")
|
123
|
+
rescue
|
124
|
+
define_singleton_method("__original__#{property_writer_name}", property_writer_method(property_writer_name))
|
125
|
+
# Note the limitation that the first observe call options apply to all subsequent observations meaning even if unobserve was called, options do not change from initial ones
|
126
|
+
# It is good enough for now. If there is a need to address this in the future, this is where to start the work
|
127
|
+
define_singleton_method(property_writer_name, &PROPERTY_WRITER_FACTORY.call(property_name, property_writer_name, options))
|
128
|
+
end
|
129
|
+
rescue => e
|
130
|
+
#ignore writing if no property writer exists
|
131
|
+
Glimmer::Config.logger.debug {"No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"}
|
132
|
+
end
|
126
133
|
end
|
127
|
-
rescue => e
|
128
|
-
#ignore writing if no property writer exists
|
129
|
-
Glimmer::Config.logger.debug {"No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"}
|
130
134
|
end
|
131
135
|
|
132
136
|
def property_writer_method(property_writer_name)
|
@@ -157,6 +161,11 @@ module Glimmer
|
|
157
161
|
@array_object_observers[property_name] = Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
|
158
162
|
@array_object_observers[property_name]
|
159
163
|
end
|
164
|
+
|
165
|
+
def initialize_observer_options(options)
|
166
|
+
options[:attribute_writer_type] ||= [:attribute=]
|
167
|
+
options[:attribute_writer_type] = [options[:attribute_writer_type]] if !options[:attribute_writer_type].is_a?(Array)
|
168
|
+
end
|
160
169
|
end
|
161
170
|
end
|
162
171
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -83,7 +83,9 @@ module Glimmer
|
|
83
83
|
# registers observer in an observable on args usually containing a property and options (optional)
|
84
84
|
# observer maintains registration list to unregister later
|
85
85
|
def observe(observable, *args)
|
86
|
+
options = args.last.is_a?(Hash) ? args.last : {}
|
86
87
|
return if observable.nil?
|
88
|
+
return if options[:ignore_frozen] && observable.frozen?
|
87
89
|
unless observable.is_a?(Observable)
|
88
90
|
# TODO refactor code to be more smart/polymorphic/automated and honor open/closed principle (e.g. for SomeClass, search if there is ObservableSomeClass)
|
89
91
|
if observable.is_a?(Array)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -28,7 +28,7 @@ module Glimmer
|
|
28
28
|
@parent = parent
|
29
29
|
@parent_attribute = parent_attribute
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def <=>(other)
|
33
33
|
if other.is_a?(Array)
|
34
34
|
args_clone = other.clone
|
data/lib/glimmer/dsl/engine.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) 2007-
|
1
|
+
# Copyright (c) 2007-2022 Andy Maleh
|
2
2
|
#
|
3
3
|
# Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
# a copy of this software and associated documentation files (the
|
@@ -59,7 +59,11 @@ module Glimmer
|
|
59
59
|
static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
|
60
60
|
static_expression_can_interpret = nil
|
61
61
|
if static_expression.nil? || !(static_expression_can_interpret = static_expression.can_interpret?(Glimmer::DSL::Engine.parent, keyword, *args, &block))
|
62
|
-
|
62
|
+
begin
|
63
|
+
Glimmer::DSL::Engine.interpret(keyword, *args, &block)
|
64
|
+
rescue => e
|
65
|
+
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} and no dynamic expressions to be able to handle either!"
|
66
|
+
end
|
63
67
|
else
|
64
68
|
Glimmer::Config.logger.info {"#{static_expression.class.name} will handle expression keyword #{keyword}"}
|
65
69
|
Glimmer::DSL::Engine.interpret_expression(static_expression, keyword, *args, &block)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright (c) 2007-2022 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.
|
21
|
+
|
22
|
+
require 'glimmer/dsl/expression'
|
23
|
+
require 'glimmer/data_binding/shine'
|
24
|
+
|
25
|
+
module Glimmer
|
26
|
+
module DSL
|
27
|
+
class ShineDataBindingExpression < Expression
|
28
|
+
# Including class can override can_interpret? and call super to augment it
|
29
|
+
def can_interpret?(parent, keyword, *args, &block)
|
30
|
+
args.size == 0 and
|
31
|
+
block.nil?
|
32
|
+
end
|
33
|
+
|
34
|
+
def interpret(parent, keyword, *args, &block)
|
35
|
+
Glimmer::DataBinding::Shine.new(parent, keyword)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/glimmer/error.rb
CHANGED
data/lib/glimmer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: array_include_methods
|
@@ -241,6 +241,7 @@ files:
|
|
241
241
|
- lib/glimmer/dsl/expression_handler.rb
|
242
242
|
- lib/glimmer/dsl/observe_expression.rb
|
243
243
|
- lib/glimmer/dsl/parent_expression.rb
|
244
|
+
- lib/glimmer/dsl/shine_data_binding_expression.rb
|
244
245
|
- lib/glimmer/dsl/static_expression.rb
|
245
246
|
- lib/glimmer/dsl/top_level_expression.rb
|
246
247
|
- lib/glimmer/error.rb
|
@@ -266,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
266
267
|
- !ruby/object:Gem::Version
|
267
268
|
version: '0'
|
268
269
|
requirements: []
|
269
|
-
rubygems_version: 3.
|
270
|
+
rubygems_version: 3.3.1
|
270
271
|
signing_key:
|
271
272
|
specification_version: 4
|
272
273
|
summary: Glimmer - DSL Engine for Ruby GUI and More
|