glimmer 1.1.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c181d967c5d8c834867570a112937498dd4d45c9585f357a683368de979967c1
4
- data.tar.gz: 50a1ed33fd43a34e500de9ce2954d02ffc511204108c8d80b99eb31b885c8f1a
3
+ metadata.gz: 351035b802dcf014c087c908f8481dbbf13d8479fdd1766b47e25be2177ffc98
4
+ data.tar.gz: 7960514819570b96106dcde2c71889feb0bd706dbe5803e3e62bf02c28f4c1f1
5
5
  SHA512:
6
- metadata.gz: 8a1d46f0e7f051000acbcf5756dd3c15a892583557388a181d47186e74a39f4eeb27c3c05b94780cae47c67f8b5a3e4c8a237cd5ce94c551cbb0eec8a6494af3
7
- data.tar.gz: 54ce9ae27a6f5f4b320783ec1e107cde725435da3e3bb3199bb6ff44a89f647de81339654be98d06a903aed3feb4caf096bbc80d9c9e9df8ef194e3c9789b8a4
6
+ metadata.gz: f127846dc71e67d3667cd738a96f11d667718d93f28494e31414a29a6e93b323f04af33bcc022bb8a4cc96e413622209589f1f2ab2ba5efc10bf5bce6baf33f7
7
+ data.tar.gz: 6a300696cb179c0d01173fd4a652d6ef78219ad944ff2af9ef7fbfec19e9a853365631f92bae5c7b2353946a2c2e498d178f14f96ee357f61a02b872b1e17537
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
+ ### 1.3.1
7
+
8
+ - Use `Concurrent::Array` instead of `Array` in `Glimmer::DataBinding::ModelBinding`
9
+
10
+ ### 1.3.0
11
+
12
+ - Support the `Expression#around` hook, which executes code around both `interpret` and `add_content`
13
+
14
+ ## 1.2.0
15
+
16
+ - Alter `Engine`/`Expression`/`ParentExpression` API to pass `keyword` and `args` to `#add_content` method. This enables consumers to vary behavior based on `keyword` and `args`, not just `parent` and presence of `block`.
17
+
18
+ ## 1.1.2
19
+
20
+ - Add more logging for which DSL is assumed before interpreting expressions
21
+ - Switch DSL Engine puts statement to a log statement for the error about no DSLs available
22
+
23
+ ## 1.1.1
24
+
25
+ - Ensured after_read hook truly happens after notifying observers in ModelBinding
26
+
6
27
  ## 1.1.0
7
28
 
8
29
  - ModelBinding hooks for before_read, after_read, before_write, after_write
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<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 1.1.0 - DSL Framework for GUI and More
1
+ # [<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 - DSL Framework for Ruby GUI and More
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer.svg)](http://badge.fury.io/rb/glimmer)
3
3
  [![rspec](https://github.com/AndyObtiva/glimmer/workflows/rspec/badge.svg)](https://github.com/AndyObtiva/glimmer/actions?query=workflow%3Arspec)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
@@ -7,7 +7,7 @@
7
7
 
8
8
  **[Contributors Wanted! (Submit a Glimmer App Sample to Get Started)](#contributing)**
9
9
 
10
- **(The Original Glimmer Library Since 2007. Beware of Imitators!)**
10
+ **(The Original Glimmer Library Handling the World’s Ruby GUI Needs Since 2007. Beware of Imitators!)**
11
11
 
12
12
  [**Glimmer**](https://rubygems.org/gems/glimmer) started out as a [GUI Library](https://github.com/AndyObtiva/glimmer-dsl-swt) and grew into a full-fledged [DSL Framework](#dsl-engine) with support for multiple GUI DSLs. Glimmer's namesake is referring to the Glimmer of Ruby in Graphical User Interfaces (contrary to [popular myth](http://blog.headius.com/2007/11/tab-sweep.html) perpetrated by [Charles Nutter](http://blog.headius.com/2007/11/tab-sweep.html), Glimmer has nothing to do with the ill-fated Whitney Houston movie, which does not in fact share the same name)
13
13
 
@@ -27,6 +27,8 @@ 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: [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)
31
+
30
32
  [**Glimmer**](https://rubygems.org/gems/glimmer) supports the following DSLs:
31
33
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
32
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)
@@ -38,15 +40,17 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
38
40
 
39
41
  ## Table of Contents
40
42
 
41
- - [Glimmer 1.1.0](#-glimmer-110---dsl-framework-for-gui-and-more)
42
- - [Official DSLs](#official-dsls)
43
- - [Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](#glimmer-dsl-for-swt-jruby-desktop-development-gui-framework)
44
- - [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)
45
- - [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
46
- - [Glimmer DSL for CSS](#glimmer-dsl-for-css)
47
- - [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
43
+ - [Glimmer](#-glimmer---dsl-framework-for-ruby-gui-and-more)
48
44
  - [DSL Engine](#dsl-engine)
45
+ - [Setup](#setup)
46
+ - [Configuration](#configuration)
49
47
  - [Multi-DSL Support](#multi-dsl-support)
48
+ - [Official DSLs](#official-dsls)
49
+ - [Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](#glimmer-dsl-for-swt-jruby-desktop-development-gui-framework)
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 XML (& HTML)](#glimmer-dsl-for-xml--html)
52
+ - [Glimmer DSL for CSS](#glimmer-dsl-for-css)
53
+ - [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
50
54
  - [Data-Binding Library](#data-binding-library)
51
55
  - [Glimmer Process](#glimmer-process)
52
56
  - [Resources](#resources)
@@ -60,21 +64,301 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
60
64
  - [Hire Me](#hire-me)
61
65
  - [License](#license)
62
66
 
63
- ## Official DSLs
67
+ ## DSL Engine
68
+
69
+ Glimmer is fundamentally a DSL Engine that can support any number of DSLs like the official Glimmer DSLs (gems starting with the `glimmer-dsl-` prefix like `glimmer-dsl-swt`) or any DSLs for that matter.
70
+
71
+ Glimmer DSL syntax consists mainly of:
72
+ - **keywords** (e.g. `table` for a table widget)
73
+ - **style/args** (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
74
+ - **content** (e.g. `{ table_column { text 'Name'} }` as in `table(:multi) { table_column { text 'Name'} }` for a multi-line selection table widget with a table column having header text property `'Name'` as content)
75
+
76
+ The Glimmer DSL Engine's architecture is based on the following Design Patterns and Data Structures:
77
+ - **Interpreter Design Pattern**: to define interpretable expressions of DSL keywords
78
+ - **Chain of Responsibility Design Pattern / Queue Data Structure**: to chain expression handlers in order of importance for processing DSL keywords
79
+ - **Adapter Design Pattern**: to adapt expressions into handlers in a chain of responsibility
80
+ - **Stack Data Structure**: to handle processing parent/child nesting of DSL keyword expressions in the correct order
81
+
82
+ 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**.
83
+
84
+ 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)`.
85
+
86
+ Expressions are either:
87
+ - **Static** (subclass of `StaticExpression`, which is a subclass of `Expression`): if they represent a single pre-identified keyword (e.g. `color` or `display`)
88
+ - **Dynamic** (subclass of `Expression`): if they represent keywords calculated on the fly during processing (e.g. an SWT widget like `label` or a random XML element called `folder` representing `<folder></folder>`)
89
+
90
+ Optionally, expressions can be parent expressions that contain other expressions, and must include the `ParentExpression` mixin module as such.
91
+
92
+ Additionally, every expression that serves as a top-level entry point into the DSL must mixin `TopLevelExpression`
93
+
94
+ Static expressions are optimized in performance since they pre-define methods on the `Glimmer` module matching the static keywords they represent (e.g. `color` causes creating a `Glimmer#color` method for processing `color` expressions) and completely bypass as a result the Glimmer DSL Engine Chain of Responsibility. That said, they must be avoided if the same keyword might occur multiple times, but with different requirements for arguments, block, and parenthood type.
95
+
96
+ Every `Expression` sublcass must specify two methods at least:
97
+ - `can_interpret?(parent, keyword, *args, &block)`: to quickly test if the keyword and arg/block/parent combination qualifies for interpretation by the current `Expression` or to otherwise delegate to the next expression in the chain of responsibility.
98
+ - `interpret(parent, keyword, *args, &block)`: to go ahead and interpret a DSL expression that qualified for interpretation
99
+
100
+ `StaticExpression` sublcasses may skip the `can_interpret?` method since they include a default implementation for it that matches the name of the keyword from the class name by convention. For example, a `color` keyword would have a `ColorExpression` class, so `color` is inferred automatically from class name and used in deciding whether the class can handle a `color` keyword or not.
101
+
102
+ `ParentExpression` subclasses can optionally override this extra method, which is included by default and simply invokes the parent's passed block to process its children:
103
+ - `add_content(parent, keyword, *args, &block)`
104
+
105
+ For example, some parent widgets use their block for other reasons or process their children at very specific times, so they may override that method and disable it, or otherwise call `super` and do additional work.
106
+
107
+ Otherwise, all expressions support the `around` hook method:
108
+ - `around(parent, keyword, args, block, &interpret_and_add_content)`: a hook for executing code around both `interpret` and `add_content`. Clients may invoke `interpret_and_add_content.call` or `yield` when ready for interpretation. `parent`, `keyword`, `args`, and `block` are supplied in case they are needed in the `around` logic.
109
+
110
+ Example of a dynamic expression:
111
+
112
+ ```ruby
113
+ module Glimmer
114
+ module DSL
115
+ module SWT
116
+ class WidgetExpression < Expression
117
+ include ParentExpression
118
+
119
+ EXCLUDED_KEYWORDS = %w[shell display tab_item]
120
+
121
+ def can_interpret?(parent, keyword, *args, &block)
122
+ !EXCLUDED_KEYWORDS.include?(keyword) and
123
+ parent.respond_to?(:swt_widget) and
124
+ Glimmer::SWT::WidgetProxy.widget_exists?(keyword)
125
+ end
126
+
127
+ def interpret(parent, keyword, *args, &block)
128
+ Glimmer::SWT::WidgetProxy.create(keyword, parent, args)
129
+ end
130
+
131
+ def add_content(parent, keyword, *args, &block)
132
+ super
133
+ parent.post_add_content
134
+ end
135
+
136
+ end
137
+ end
138
+ end
139
+ end
140
+ ```
141
+
142
+ Example of a static expression (does not need `can_interpret?`):
143
+
144
+ ```ruby
145
+ module Glimmer
146
+ module DSL
147
+ module Opal
148
+ class ColorExpression < StaticExpression
149
+ include TopLevelExpression
150
+
151
+ def interpret(parent, keyword, *args, &block)
152
+ Glimmer::SWT::ColorProxy.new(*args)
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ ```
159
+
160
+ DSL expressions go into the `glimmer/dsl/{dsl_name}` namespace directory.
161
+
162
+ Also, every DSL requires a `glimmer/dsl/{dsl_name}/dsl.rb` file, which configures the DSL into Glimmer via a call to:
163
+ ```ruby
164
+ Glimmer::DSL::Engine.add_dynamic_expressions(DSLNameModule, expression_names_array)
165
+ ```
166
+
167
+ Expression names are underscored verions of `Expression` subclass names minus the `_expression` suffix.
168
+
169
+ For example, here is an SWT DSL configuration:
170
+
171
+ ```ruby
172
+ require 'glimmer/launcher'
173
+ require Glimmer::Launcher.swt_jar_file
174
+ require 'glimmer/dsl/engine'
175
+ Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f}
176
+
177
+ module Glimmer
178
+ module DSL
179
+ module SWT
180
+ Engine.add_dynamic_expressions(
181
+ SWT,
182
+ %w[
183
+ layout
184
+ widget_listener
185
+ combo_selection_data_binding
186
+ checkbox_group_selection_data_binding
187
+ radio_group_selection_data_binding
188
+ list_selection_data_binding
189
+ tree_items_data_binding
190
+ table_items_data_binding
191
+ data_binding
192
+ cursor
193
+ font
194
+ image
195
+ property
196
+ block_property
197
+ widget
198
+ custom_widget
199
+ ]
200
+ )
201
+ end
202
+ end
203
+ end
204
+ ```
205
+
206
+ ### Setup
207
+
208
+ Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL:
209
+ - Add `gem 'glimmer', '~> 1.3.1'` to `Gemfile` and run `bundle` or run `gem install glimmer -v1.3.1` and add `require 'glimmer'`
210
+ - 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)
211
+ - Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static
212
+
213
+ ### Configuration
214
+
215
+ Glimmer configuration may be done via the [`Glimmer::Config`](https://github.com/AndyObtiva/glimmer/blob/master/lib/glimmer/config.rb) module.
216
+
217
+ #### logger
218
+
219
+ The Glimmer DSL engine supports logging via a standard `STDOUT` Ruby `Logger` configured in the `Glimmer::Config.logger` config option.
220
+ It is set to level Logger::ERROR by default.
221
+ Log level may be adjusted via `Glimmer::Config.logger.level` just like any other Ruby Logger.
222
+
223
+ Example:
224
+
225
+ ```ruby
226
+ Glimmer::Config.logger.level = :debug
227
+ ```
228
+ This results in more verbose debug loggging to `STDOUT`, which is very helpful in troubleshooting Glimmer DSL syntax when needed.
229
+
230
+ Example log:
231
+ ```
232
+ D, [2017-07-21T19:23:12.587870 #35707] DEBUG -- : method: shell and args: []
233
+ D, [2017-07-21T19:23:12.594405 #35707] DEBUG -- : ShellCommandHandler will handle command: shell with arguments []
234
+ D, [2017-07-21T19:23:12.844775 #35707] DEBUG -- : method: composite and args: []
235
+ D, [2017-07-21T19:23:12.845388 #35707] DEBUG -- : parent is a widget: true
236
+ D, [2017-07-21T19:23:12.845833 #35707] DEBUG -- : on listener?: false
237
+ D, [2017-07-21T19:23:12.864395 #35707] DEBUG -- : WidgetCommandHandler will handle command: composite with arguments []
238
+ D, [2017-07-21T19:23:12.864893 #35707] DEBUG -- : widget styles are: []
239
+ D, [2017-07-21T19:23:12.874296 #35707] DEBUG -- : method: list and args: [:multi]
240
+ D, [2017-07-21T19:23:12.874969 #35707] DEBUG -- : parent is a widget: true
241
+ D, [2017-07-21T19:23:12.875452 #35707] DEBUG -- : on listener?: false
242
+ D, [2017-07-21T19:23:12.878434 #35707] DEBUG -- : WidgetCommandHandler will handle command: list with arguments [:multi]
243
+ D, [2017-07-21T19:23:12.878798 #35707] DEBUG -- : widget styles are: [:multi]
244
+ ```
245
+
246
+ The `logger` instance may be replaced with a custom logger via `Glimmer::Config.logger = custom_logger`
247
+
248
+ To reset `logger` to the default instance, you may call `Glimmer::Config.reset_logger!`
249
+
250
+ All logging is done lazily via blocks (e.g. `logger.debug {message}`) to avoid affecting app performance with logging when below the configured logging level threshold.
251
+
252
+ [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) enhances Glimmer default logging support via the Ruby [`logging`](https://github.com/TwP/logging) gem, enabling buffered asynchronous logging in a separate thread, thus completely unhindering normal desktop app performance.
253
+
254
+ #### loop_max_count
255
+
256
+ Glimmer has infinite loop detection support.
257
+ It can detect when an infinite loop is about to occur in method_missing and stops it.
258
+ It detects potential infinite loops when the same keyword and args repeat more than 100 times, which is unusual in a GUI app.
259
+
260
+ The max limit can be changed via the `Glimmer::Config::loop_max_count=(count)` config option.
261
+
262
+ Infinite loop detection may be disabled altogether if needed by setting `Glimmer::Config::loop_max_count` to `-1`
263
+
264
+ #### excluded_keyword_checkers
265
+
266
+ Glimmer permits consumers to exclude keywords from DSL processing by its engine via the `excluded_keyword_checkers` config option.
267
+
268
+ To do so, add a proc to it that returns a boolean indicating if a keyword is excluded or not.
269
+
270
+ Note that this proc runs within the context of the Glimmer object (as in the object mixing in the Glimmer module), so checker can can pretend to run there with its `self` object assumption.
271
+
272
+ Example of keywords excluded by [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt):
273
+
274
+ ```ruby
275
+ Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
276
+ method = method_symbol.to_s
277
+ result = false
278
+ result ||= method.start_with?('on_swt_') && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
279
+ result ||= method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
280
+ result ||= ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
281
+ result ||= method == 'post_initialize_child'
282
+ result ||= method.end_with?('=')
283
+ result ||= ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
284
+ end
285
+ ```
286
+
287
+ #### log_excluded_keywords
288
+
289
+ (default = false)
290
+
291
+ This just tells Glimmer whether to log excluded keywords or not (at the debug level). It is off by default.
292
+
293
+
294
+ ### Multi-DSL Support
295
+
296
+ The Glimmer [DSL Engine](#dsl-engine) allows mixing DSLs, which comes in handy when doing things like rendering a desktop GUI DSL `browser` widget additionally leveraging the HTML DSL and CSS DSL for its content.
297
+
298
+ DSLs are activated by top-level keywords (expressions denoted as `TopLevelExpression`). For example, the `html` keyword activates the [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) and the `css` keyword activates the [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css). Glimmer automatically recognizes top-level keywords in each DSL and activates the DSL accordingly. Once done processing a nested DSL top-level keyword, Glimmer switches back to the prior DSL automatically.
299
+
300
+ By default, all loaded DSLs (required glimmer DSL gems) are enabled.
301
+
302
+ For example, this shows "Hello, World!" inside a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) desktop app `browser` widget using `html` and `css` from [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) and [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css):
303
+
304
+ ```ruby
305
+ require 'glimmer-dsl-swt'
306
+ require 'glimmer-dsl-xml'
307
+ require 'glimmer-dsl-css'
308
+
309
+ include Glimmer
310
+
311
+ shell {
312
+ minimum_size 130, 130
313
+ @browser = browser {
314
+ text html {
315
+ head {
316
+ meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
317
+ style {
318
+ css {
319
+ h1 {
320
+ background 'yellow'
321
+ }
322
+ }
323
+ }
324
+ }
325
+ body {
326
+ h1 { "Hello, World!" }
327
+ }
328
+ }
329
+ }
330
+ }.open
331
+ ```
332
+
333
+ **API methods to enable/disable DSLs:**
334
+
335
+ `Glimmer::DSL::Engine.disable_dsl(dsl)`: disables a particular DSL
336
+
337
+ Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
338
+
339
+ `Glimmer::DSL::Engine.enable_dsl(dsl)`: enables a particular DSL
340
+
341
+ Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
342
+
343
+ `Glimmer::DSL::Engine.enabled_dsls=(dsls)`: enables only the specified DSLs, disabling all other loaded DSLs
344
+
345
+ Example: `Glimmer::DSL::Engine.enabled_dsls = [:xml, :css]`
346
+
347
+ ### Official DSLs
64
348
 
65
349
  Here, we showcase official Glimmer DSLs; that is [gems starting with the `glimmer-dsl-` prefix](https://rubygems.org/search?query=glimmer-dsl-).
66
350
 
67
351
  (you can skip ahead if you prefer to learn more about the Glimmer [DSL Engine](#dsl-engine) or [Data-Binding Library](#data-binding-library) first)
68
352
 
69
- ### Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
353
+ #### Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
70
354
 
71
355
  [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) is a native-GUI cross-platform desktop development library written in [JRuby](https://www.jruby.org/), an OS-threaded faster version of [Ruby](https://www.ruby-lang.org/en/). [Glimmer](https://rubygems.org/gems/glimmer)'s main innovation is a declarative [Ruby DSL](https://github.com/AndyObtiva/glimmer-dsl-swt#glimmer-dsl-syntax) that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust [Eclipse SWT library](https://www.eclipse.org/swt/). [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) additionally innovates by having built-in [data-binding](https://github.com/AndyObtiva/glimmer-dsl-swt#data-binding) support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. To get started quickly, [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) offers [scaffolding](https://github.com/AndyObtiva/glimmer-dsl-swt#scaffolding) options for [Apps](https://github.com/AndyObtiva/glimmer-dsl-swt#in-production), [Gems](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-shell-gem), and [Custom Widgets](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-widgets). [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) also includes native-executable [packaging](https://github.com/AndyObtiva/glimmer-dsl-swt#packaging--distribution) support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in [Ruby](https://www.ruby-lang.org/en/) as truly native DMG/PKG/APP files on the [Mac](https://www.apple.com/ca/macos) + [App Store](https://developer.apple.com/macos/distribution/) and MSI/EXE files on [Windows](https://www.microsoft.com/en-ca/windows).
72
356
 
73
357
  To get started, visit the [Glimmer DSL for SWT project page](https://github.com/AndyObtiva/glimmer-dsl-swt#pre-requisites) for instructions on installing the [glimmer-dsl-swt gem](https://rubygems.org/gems/glimmer-dsl-swt).
74
358
 
75
- #### Glimmer DSL for SWT Samples
359
+ ##### Glimmer DSL for SWT Samples
76
360
 
77
- ##### Hello, World!
361
+ ###### Hello, World!
78
362
 
79
363
  ![Hello World](images/glimmer-hello-world.png)
80
364
 
@@ -90,7 +374,7 @@ shell {
90
374
  }.open
91
375
  ```
92
376
 
93
- ##### Glimmer Tetris
377
+ ###### Glimmer Tetris
94
378
 
95
379
  ![Tetris](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/v4.18.3.1/images/glimmer-tetris.png)
96
380
 
@@ -118,15 +402,11 @@ Glimmer GUI code (from [samples/elaborate/tetris.rb](https://github.com/AndyObti
118
402
  score_lane(game: game, block_size: BLOCK_SIZE) {
119
403
  layout_data(:fill, :fill, true, true)
120
404
  }
121
-
122
- on_widget_disposed {
123
- deregister_observers
124
- }
125
405
  }
126
406
  # ...
127
407
  ```
128
408
 
129
- ##### Hello, Table!
409
+ ###### Hello, Table!
130
410
 
131
411
  ![Hello Table](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-hello-table.png)
132
412
 
@@ -225,7 +505,7 @@ Glimmer GUI code (from [samples/hello/hello_table.rb](https://github.com/AndyObt
225
505
  # ...
226
506
  ```
227
507
 
228
- #### Production Desktop Apps Built with Glimmer DSL for SWT
508
+ ##### Production Desktop Apps Built with Glimmer DSL for SWT
229
509
 
230
510
  [<img alt="Are We There Yet Logo" src="https://raw.githubusercontent.com/AndyObtiva/are-we-there-yet/master/are-we-there-yet-logo.svg" width="40" />Are We There Yet?](https://github.com/AndyObtiva/are-we-there-yet) - Small Project Tracking App
231
511
 
@@ -239,7 +519,7 @@ Glimmer GUI code (from [samples/hello/hello_table.rb](https://github.com/AndyObt
239
519
 
240
520
  [![Garderie Rainbow Daily Agenda App Screenshot](https://raw.githubusercontent.com/AndyObtiva/garderie_rainbow_daily_agenda/master/images/garderie_rainbow_daily_agenda_screenshot.png)](https://github.com/AndyObtiva/garderie_rainbow_daily_agenda)
241
521
 
242
- ### Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
522
+ #### Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
243
523
 
244
524
  [Glimmer DSL for Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) is an experimental proof-of-concept web GUI adapter for [Glimmer](https://github.com/AndyObtiva/glimmer) desktop apps (i.e. apps built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)). It webifies them via [Rails](https://rubyonrails.org/), allowing Ruby desktop apps to run on the web via [Opal Ruby](https://opalrb.com/) without changing a line of code. Apps may then be custom-styled for the web with standard CSS.
245
525
 
@@ -247,9 +527,9 @@ Glimmer DSL for Opal webifier successfully reuses the entire [Glimmer](https://g
247
527
 
248
528
  To get started, visit the [Glimmer DSL for Opal project page](https://github.com/AndyObtiva/glimmer-dsl-opal) for instructions on installing the [glimmer-dsl-opal gem](https://rubygems.org/gems/glimmer-dsl-opal).
249
529
 
250
- #### Glimmer DSL for Opal Samples
530
+ ##### Glimmer DSL for Opal Samples
251
531
 
252
- ##### Hello, Computed!
532
+ ###### Hello, Computed!
253
533
 
254
534
  Add the following require statement to `app/assets/javascripts/application.rb`
255
535
 
@@ -368,7 +648,7 @@ You should see "Hello, Computed!"
368
648
 
369
649
  ![Glimmer DSL for Opal Hello Computed](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-computed.png)
370
650
 
371
- ##### Glimmer Calculator
651
+ ###### Glimmer Calculator
372
652
 
373
653
  Add the [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) gem to `Gemfile` (without requiring):
374
654
 
@@ -385,6 +665,7 @@ require 'glimmer-cs-calculator/launch'
385
665
  Sample GUI code (relies on custom widgets `command_button`, `operation_button`, and `number_button`):
386
666
 
387
667
  ```ruby
668
+ # ...
388
669
  shell {
389
670
  minimum_size (OS.mac? ? 320 : (OS.windows? ? 390 : 520)), 240
390
671
  image File.join(APP_ROOT, 'package', 'windows', "Glimmer Calculator.ico") if OS.windows?
@@ -403,9 +684,9 @@ shell {
403
684
  caret nil
404
685
  }
405
686
  command_button('AC')
406
- operation_button('÷')
407
- operation_button('�')
408
- operation_button('−')
687
+ operation_button('÷')
688
+ operation_button('×')
689
+ operation_button('')
409
690
  (7..9).each { |number|
410
691
  number_button(number)
411
692
  }
@@ -420,6 +701,7 @@ shell {
420
701
  number_button(0, horizontal_span: 2)
421
702
  operation_button('.')
422
703
  }
704
+ # ...
423
705
  ```
424
706
 
425
707
  Glimmer app on the desktop (using the [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
@@ -448,13 +730,13 @@ You should see "Apple Calculator Theme"
448
730
 
449
731
  [![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)
450
732
 
451
- ### Glimmer DSL for XML (& HTML)
733
+ #### Glimmer DSL for XML (& HTML)
452
734
 
453
735
  [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) provides Ruby syntax for building XML (eXtensible Markup Language) documents.
454
736
 
455
737
  Within the context of desktop development, Glimmer DSL for XML is useful in providing XML data for the [SWT Browser widget](https://github.com/AndyObtiva/glimmer/tree/master#browser-widget).
456
738
 
457
- #### XML DSL
739
+ ##### XML DSL
458
740
 
459
741
  Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
460
742
  Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
@@ -486,13 +768,13 @@ Output:
486
768
  <html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
487
769
  ```
488
770
 
489
- ### Glimmer DSL for CSS
771
+ #### Glimmer DSL for CSS
490
772
 
491
773
  [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) provides Ruby syntax for building CSS (Cascading Style Sheets).
492
774
 
493
775
  Within the context of [Glimmer](https://github.com/AndyObtiva/glimmer) app development, Glimmer DSL for CSS is useful in providing CSS for the [SWT Browser widget](https://github.com/AndyObtiva/glimmer/tree/master#browser-widget).
494
776
 
495
- #### CSS DSL
777
+ ##### CSS DSL
496
778
 
497
779
  Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
498
780
  Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
@@ -525,7 +807,7 @@ Output:
525
807
  body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2em}
526
808
  ```
527
809
 
528
- ### Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
810
+ #### Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
529
811
 
530
812
  [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.).
531
813
 
@@ -544,9 +826,9 @@ The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a pleth
544
826
 
545
827
  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).
546
828
 
547
- #### Glimmer DSL for Tk Samples
829
+ ##### Glimmer DSL for Tk Samples
548
830
 
549
- ##### Hello, World!
831
+ ###### Hello, World!
550
832
 
551
833
  Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
552
834
 
@@ -570,7 +852,7 @@ Glimmer app:
570
852
 
571
853
  ![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)
572
854
 
573
- ##### Hello, Tab!
855
+ ###### Hello, Tab!
574
856
 
575
857
  Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
576
858
 
@@ -607,7 +889,7 @@ Glimmer app:
607
889
  ![glimmer dsl tk screenshot sample hello tab English](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-tab-english.png)
608
890
  ![glimmer dsl tk screenshot sample hello tab French](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-tab-french.png)
609
891
 
610
- ##### Hello, Combo!
892
+ ###### Hello, Combo!
611
893
 
612
894
  Glimmer code (from [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combo.rb)):
613
895
 
@@ -642,204 +924,11 @@ Glimmer app:
642
924
  ![glimmer dsl tk screenshot sample hello combo](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combo.png)
643
925
  ![glimmer dsl tk screenshot sample hello combo dropdown](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combo-dropdown.png)
644
926
 
645
- ## DSL Engine
646
-
647
- Glimmer is fundamentally a DSL Engine that can support any number of DSLs like the official Glimmer DSLs (gems starting with the `glimmer-dsl-` prefix like `glimmer-dsl-swt`) or any DSLs for that matter.
648
-
649
- Glimmer DSL syntax consists mainly of:
650
- - **keywords** (e.g. `table` for a table widget)
651
- - **style/args** (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
652
- - **content** (e.g. `{ table_column { text 'Name'} }` as in `table(:multi) { table_column { text 'Name'} }` for a multi-line selection table widget with a table column having header text property `'Name'` as content)
653
-
654
- The Glimmer DSL Engine's architecture is based on the following Design Patterns and Data Structures:
655
- - **Interpreter Design Pattern**: to define interpretable expressions of DSL keywords
656
- - **Chain of Responsibility Design Pattern / Queue Data Structure**: to chain expression handlers in order of importance for processing DSL keywords
657
- - **Adapter Design Pattern**: to adapt expressions into handlers in a chain of responsibility
658
- - **Stack Data Structure**: to handle processing parent/child nesting of DSL keyword expressions in the correct order
659
-
660
- 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**.
661
-
662
- 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)`.
663
-
664
- Expressions are either:
665
- - **Static** (subclass of `StaticExpression`, which is a subclass of `Expression`): if they represent a single pre-identified keyword (e.g. `color` or `display`)
666
- - **Dynamic** (subclass of `Expression`): if they represent keywords calculated on the fly during processing (e.g. an SWT widget like `label` or a random XML element called `folder` representing `<folder></folder>`)
667
-
668
- Optionally, expressions can be parent expressions that contain other expressions, and must include the `ParentExpression` mixin module as such.
669
-
670
- Additionally, every expression that serves as a top-level entry point into the DSL must mixin `TopLevelExpression`
671
-
672
- Static expressions are optimized in performance since they pre-define methods on the `Glimmer` module matching the static keywords they represent (e.g. `color` causes creating a `Glimmer#color` method for processing `color` expressions) and completely bypass as a result the Glimmer DSL Engine Chain of Responsibility. That said, they must be avoided if the same keyword might occur multiple times, but with different requirements for arguments, block, and parenthood type.
673
-
674
- Every `Expression` sublcass must specify two methods at least:
675
- - `can_interpret?(parent, keyword, *args, &block)`: to quickly test if the keyword and arg/block/parent combination qualifies for interpretation by the current `Expression` or to otherwise delegate to the next expression in the chain of responsibility.
676
- - `interpret(parent, keyword, *args, &block)`: to go ahead and interpret a DSL expression that qualified for interpretation
677
-
678
- `StaticExpression` sublcasses may skip the `can_interpret?` method since they include a default implementation for it that matches the name of the keyword from the class name by convention. For example, a `color` keyword would have a `ColorExpression` class, so `color` is inferred automatically from class name and used in deciding whether the class can handle a `color` keyword or not.
679
-
680
- `ParentExpression` subclasses can optionally override this extra method, which is included by default and simply invokes the parent's passed block to process its children:
681
- - `add_content(parent, &block)`
682
-
683
- For example, some parent widgets use their block for other reasons or process their children at very specific times, so they may override that method and disable it, or otherwise call `super` and do additional work.
684
-
685
- Example of a dynamic expression:
686
-
687
- ```ruby
688
- module Glimmer
689
- module DSL
690
- module SWT
691
- class WidgetExpression < Expression
692
- include ParentExpression
693
-
694
- EXCLUDED_KEYWORDS = %w[shell display tab_item]
695
-
696
- def can_interpret?(parent, keyword, *args, &block)
697
- !EXCLUDED_KEYWORDS.include?(keyword) and
698
- parent.respond_to?(:swt_widget) and
699
- Glimmer::SWT::WidgetProxy.widget_exists?(keyword)
700
- end
701
-
702
- def interpret(parent, keyword, *args, &block)
703
- Glimmer::SWT::WidgetProxy.create(keyword, parent, args)
704
- end
705
-
706
- def add_content(parent, &block)
707
- super
708
- parent.post_add_content
709
- end
710
-
711
- end
712
- end
713
- end
714
- end
715
- ```
716
-
717
- Example of a static expression (does not need `can_interpret?`):
718
-
719
- ```ruby
720
- module Glimmer
721
- module DSL
722
- module Opal
723
- class ColorExpression < StaticExpression
724
- include TopLevelExpression
725
-
726
- def interpret(parent, keyword, *args, &block)
727
- Glimmer::SWT::ColorProxy.new(*args)
728
- end
729
- end
730
- end
731
- end
732
- end
733
- ```
734
-
735
- DSL expressions go into the `glimmer/dsl/{dsl_name}` namespace directory.
736
-
737
- Also, every DSL requires a `glimmer/dsl/{dsl_name}/dsl.rb` file, which configures the DSL into Glimmer via a call to:
738
- ```ruby
739
- Glimmer::DSL::Engine.add_dynamic_expressions(DSLNameModule, expression_names_array)
740
- ```
741
-
742
- Expression names are underscored verions of `Expression` subclass names minus the `_expression` suffix.
743
-
744
- For example, here is an SWT DSL configuration:
745
-
746
- ```ruby
747
- require 'glimmer/launcher'
748
- require Glimmer::Launcher.swt_jar_file
749
- require 'glimmer/dsl/engine'
750
- Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f}
751
-
752
- module Glimmer
753
- module DSL
754
- module SWT
755
- Engine.add_dynamic_expressions(
756
- SWT,
757
- %w[
758
- layout
759
- widget_listener
760
- combo_selection_data_binding
761
- checkbox_group_selection_data_binding
762
- radio_group_selection_data_binding
763
- list_selection_data_binding
764
- tree_items_data_binding
765
- table_items_data_binding
766
- data_binding
767
- cursor
768
- font
769
- image
770
- property
771
- block_property
772
- widget
773
- custom_widget
774
- ]
775
- )
776
- end
777
- end
778
- end
779
- ```
780
-
781
- In summary, these are the files needed to author a Glimmer DSL:
782
- - `glimmer/dsl/[dsl_name]/dsl.rb`: requires and adds all dynamic expressions to [dsl_name] Glimmer DSL
783
- - `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb`: needed for every [expresion_name] expression, whether dynamic or static
784
-
785
- ### Multi-DSL Support
786
-
787
- The Glimmer [DSL Engine](#dsl-engine) allows mixing DSLs, which comes in handy when doing things like rendering a desktop GUI DSL `browser` widget additionally leveraging the HTML DSL and CSS DSL for its content.
788
-
789
- DSLs are activated by top-level keywords (expressions denoted as `TopLevelExpression`). For example, the `html` keyword activates the [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) and the `css` keyword activates the [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css). Glimmer automatically recognizes top-level keywords in each DSL and activates the DSL accordingly. Once done processing a nested DSL top-level keyword, Glimmer switches back to the prior DSL automatically.
790
-
791
- By default, all loaded DSLs (required glimmer DSL gems) are enabled.
792
-
793
- For example, this shows "Hello, World!" inside a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) desktop app `browser` widget using `html` and `css` from [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) and [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css):
794
-
795
- ```ruby
796
- require 'glimmer-dsl-swt'
797
- require 'glimmer-dsl-xml'
798
- require 'glimmer-dsl-css'
799
-
800
- include Glimmer
801
-
802
- shell {
803
- minimum_size 130, 130
804
- @browser = browser {
805
- text html {
806
- head {
807
- meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
808
- style {
809
- css {
810
- h1 {
811
- background 'yellow'
812
- }
813
- }
814
- }
815
- }
816
- body {
817
- h1 { "Hello, World!" }
818
- }
819
- }
820
- }
821
- }.open
822
- ```
823
-
824
- **API methods to enable/disable DSLs:**
825
-
826
- `Glimmer::DSL::Engine.disable_dsl(dsl)`: disables a particular DSL
827
-
828
- Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
829
-
830
- `Glimmer::DSL::Engine.enable_dsl(dsl)`: enables a particular DSL
831
-
832
- Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
833
-
834
- `Glimmer::DSL::Engine.enabled_dsls=(dsls)`: enables only the specified DSLs, disabling all other loaded DSLs
835
-
836
- Example: `Glimmer::DSL::Engine.enabled_dsls = [:xml, :css]`
837
-
838
927
  ## Data-Binding Library
839
928
 
840
- Data-Binding enables mapping GUI properties (like text and color) to Model attributes (like name and age).
929
+ 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.
841
930
 
842
- Glimmer enhances observed models automatically (including array operations like `<<` 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)
931
+ 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)
843
932
 
844
933
  This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
845
934
 
@@ -850,6 +939,8 @@ These are the main classes concerning data-binding:
850
939
  - `ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push` or `delete`)
851
940
  - `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
852
941
 
942
+ You may learn more from [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)
943
+
853
944
  ## Glimmer Process
854
945
 
855
946
  [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.