glimmer 1.0.9 → 1.1.2

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: 78b3aa3c65d53cbdca49fe3e5ac96b6daa602e54ba7a79f1195fc79a75057746
4
- data.tar.gz: 9f16f383e0c2aa7f3748dedae65225b85456276fc07aa00b2790dd3a314435f0
3
+ metadata.gz: 9d0de24c574be6e42a7cade68606d7dace341333cd2922307d15aee307f77a42
4
+ data.tar.gz: 3ae8fe64ba1fc958bca990d13c02966101f7eef91b03d60ddf28cd6f406b1af8
5
5
  SHA512:
6
- metadata.gz: 2b0c3e251536486ce4aacad9fa04332c7fd39be472593d01e3c4b10aa92ac90fb66c2bf3ca13147836d8e537f2edc62ac681a9a7cbec8d1d21d0fdaa2cfac2a1
7
- data.tar.gz: 3fa2edb9b9d68b82ef128869ad8a07a4caccbc2786d5c2bed99949819193f3d2952f865c67ac7ff57ad81ab6a953293e1a10c63c9a898726e8bee62a5ec9b320
6
+ metadata.gz: 3ea0f3c6733f9274b06067f6e3888a017f4ff4a7bf99b5c446f4cadf38eb705b6ddbc1f76a2b0e2d4ee9e6620626f07069d2c4ff199b682f7be38fda1a6d6749
7
+ data.tar.gz: 5ae1155117530b0e3808000057fae31a5ee5345dbc8fc28b4eae797d916d72ec1c7c643e2f314f8ffcb26121d33a7dc43080a2d30639ded865bb90c4a2bfbebc
data/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
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.1.2
7
+
8
+ - Add more logging for which DSL is assumed before interpreting expressions
9
+ - Switch DSL Engine puts statement to a log statement for the error about no DSLs available
10
+
11
+ ## 1.1.1
12
+
13
+ - Ensured after_read hook truly happens after notifying observers in ModelBinding
14
+
15
+ ## 1.1.0
16
+
17
+ - ModelBinding hooks for before_read, after_read, before_write, after_write
18
+
19
+ ## 1.0.11
20
+
21
+ - Alias unregister as deregister everywhere
22
+ - Optimize performance of `Glimmer::Config.excluded_keyword_checkers` in glimmer method_missing
23
+
24
+ ## 1.0.10
25
+
26
+ - Avoid using concurrent-ruby in Opal
27
+
6
28
  ## 1.0.9
7
29
 
8
30
  - Fixed issue with top-level static expressions sometimes not working (e.g. async_exec in DSL for SWT) with DSL detected as nil
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.0.9 - DSL Framework
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,9 +7,9 @@
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
- [**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 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)
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
 
14
14
  [<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
15
15
  Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) and [Chalmers/Gothenburg University Software Engineering Master's Lecture Material](http://www.cse.chalmers.se/~bergert/slides/guest_lecture_DSLs.pdf)
@@ -32,21 +32,23 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
32
32
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
33
33
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
34
34
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
35
- - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
35
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
36
36
 
37
37
  [Glimmer and/or Glimmer DSLs receive two updates per month](https://rubygems.org/gems/glimmer-dsl-swt/versions). You can trust [Glimmer](https://rubygems.org/gems/glimmer) with your Ruby development needs.
38
38
 
39
39
  ## Table of Contents
40
40
 
41
- - [Glimmer 1.0.9](#-glimmer-109---dsl-framework)
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 (Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-ruby-desktop-development-gui-library)
41
+ - [Glimmer](#-glimmer---dsl-framework-for-ruby-gui-and-more)
48
42
  - [DSL Engine](#dsl-engine)
43
+ - [Setup](#setup)
44
+ - [Configuration](#configuration)
49
45
  - [Multi-DSL Support](#multi-dsl-support)
46
+ - [Official DSLs](#official-dsls)
47
+ - [Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](#glimmer-dsl-for-swt-jruby-desktop-development-gui-framework)
48
+ - [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)
49
+ - [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
50
+ - [Glimmer DSL for CSS](#glimmer-dsl-for-css)
51
+ - [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
50
52
  - [Data-Binding Library](#data-binding-library)
51
53
  - [Glimmer Process](#glimmer-process)
52
54
  - [Resources](#resources)
@@ -60,21 +62,300 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
60
62
  - [Hire Me](#hire-me)
61
63
  - [License](#license)
62
64
 
63
- ## Official DSLs
65
+ ## DSL Engine
66
+
67
+ 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.
68
+
69
+ Glimmer DSL syntax consists mainly of:
70
+ - **keywords** (e.g. `table` for a table widget)
71
+ - **style/args** (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
72
+ - **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)
73
+
74
+ The Glimmer DSL Engine's architecture is based on the following Design Patterns and Data Structures:
75
+ - **Interpreter Design Pattern**: to define interpretable expressions of DSL keywords
76
+ - **Chain of Responsibility Design Pattern / Queue Data Structure**: to chain expression handlers in order of importance for processing DSL keywords
77
+ - **Adapter Design Pattern**: to adapt expressions into handlers in a chain of responsibility
78
+ - **Stack Data Structure**: to handle processing parent/child nesting of DSL keyword expressions in the correct order
79
+
80
+ 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**.
81
+
82
+ 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)`.
83
+
84
+ Expressions are either:
85
+ - **Static** (subclass of `StaticExpression`, which is a subclass of `Expression`): if they represent a single pre-identified keyword (e.g. `color` or `display`)
86
+ - **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>`)
87
+
88
+ Optionally, expressions can be parent expressions that contain other expressions, and must include the `ParentExpression` mixin module as such.
89
+
90
+ Additionally, every expression that serves as a top-level entry point into the DSL must mixin `TopLevelExpression`
91
+
92
+ 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.
93
+
94
+ Every `Expression` sublcass must specify two methods at least:
95
+ - `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.
96
+ - `interpret(parent, keyword, *args, &block)`: to go ahead and interpret a DSL expression that qualified for interpretation
97
+
98
+ `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.
99
+
100
+ `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:
101
+ - `add_content(parent, &block)`
102
+
103
+ 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.
104
+
105
+ Example of a dynamic expression:
106
+
107
+ ```ruby
108
+ module Glimmer
109
+ module DSL
110
+ module SWT
111
+ class WidgetExpression < Expression
112
+ include ParentExpression
113
+
114
+ EXCLUDED_KEYWORDS = %w[shell display tab_item]
115
+
116
+ def can_interpret?(parent, keyword, *args, &block)
117
+ !EXCLUDED_KEYWORDS.include?(keyword) and
118
+ parent.respond_to?(:swt_widget) and
119
+ Glimmer::SWT::WidgetProxy.widget_exists?(keyword)
120
+ end
121
+
122
+ def interpret(parent, keyword, *args, &block)
123
+ Glimmer::SWT::WidgetProxy.create(keyword, parent, args)
124
+ end
125
+
126
+ def add_content(parent, &block)
127
+ super
128
+ parent.post_add_content
129
+ end
130
+
131
+ end
132
+ end
133
+ end
134
+ end
135
+ ```
136
+
137
+ Example of a static expression (does not need `can_interpret?`):
138
+
139
+ ```ruby
140
+ module Glimmer
141
+ module DSL
142
+ module Opal
143
+ class ColorExpression < StaticExpression
144
+ include TopLevelExpression
145
+
146
+ def interpret(parent, keyword, *args, &block)
147
+ Glimmer::SWT::ColorProxy.new(*args)
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ ```
154
+
155
+ DSL expressions go into the `glimmer/dsl/{dsl_name}` namespace directory.
156
+
157
+ Also, every DSL requires a `glimmer/dsl/{dsl_name}/dsl.rb` file, which configures the DSL into Glimmer via a call to:
158
+ ```ruby
159
+ Glimmer::DSL::Engine.add_dynamic_expressions(DSLNameModule, expression_names_array)
160
+ ```
161
+
162
+ Expression names are underscored verions of `Expression` subclass names minus the `_expression` suffix.
163
+
164
+ For example, here is an SWT DSL configuration:
165
+
166
+ ```ruby
167
+ require 'glimmer/launcher'
168
+ require Glimmer::Launcher.swt_jar_file
169
+ require 'glimmer/dsl/engine'
170
+ Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f}
171
+
172
+ module Glimmer
173
+ module DSL
174
+ module SWT
175
+ Engine.add_dynamic_expressions(
176
+ SWT,
177
+ %w[
178
+ layout
179
+ widget_listener
180
+ combo_selection_data_binding
181
+ checkbox_group_selection_data_binding
182
+ radio_group_selection_data_binding
183
+ list_selection_data_binding
184
+ tree_items_data_binding
185
+ table_items_data_binding
186
+ data_binding
187
+ cursor
188
+ font
189
+ image
190
+ property
191
+ block_property
192
+ widget
193
+ custom_widget
194
+ ]
195
+ )
196
+ end
197
+ end
198
+ end
199
+ ```
200
+
201
+ ### Setup
202
+
203
+ Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL:
204
+ - Add `gem 'glimmer', '~> 1.1.2'` to `Gemfile` and run `bundle` or run `gem install glimmer -v1.1.2` and add `require 'glimmer'`
205
+ - 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)
206
+ - Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static
207
+
208
+ ### Configuration
209
+
210
+ Glimmer configuration may be done via the [`Glimmer::Config`](https://github.com/AndyObtiva/glimmer/blob/master/lib/glimmer/config.rb) module.
211
+
212
+ #### logger
213
+
214
+ The Glimmer DSL engine supports logging via a standard `STDOUT` Ruby `Logger` configured in the `Glimmer::Config.logger` config option.
215
+ It is set to level Logger::ERROR by default.
216
+ Log level may be adjusted via `Glimmer::Config.logger.level` just like any other Ruby Logger.
217
+
218
+ Example:
219
+
220
+ ```ruby
221
+ Glimmer::Config.logger.level = :debug
222
+ ```
223
+ This results in more verbose debug loggging to `STDOUT`, which is very helpful in troubleshooting Glimmer DSL syntax when needed.
224
+
225
+ Example log:
226
+ ```
227
+ D, [2017-07-21T19:23:12.587870 #35707] DEBUG -- : method: shell and args: []
228
+ D, [2017-07-21T19:23:12.594405 #35707] DEBUG -- : ShellCommandHandler will handle command: shell with arguments []
229
+ D, [2017-07-21T19:23:12.844775 #35707] DEBUG -- : method: composite and args: []
230
+ D, [2017-07-21T19:23:12.845388 #35707] DEBUG -- : parent is a widget: true
231
+ D, [2017-07-21T19:23:12.845833 #35707] DEBUG -- : on listener?: false
232
+ D, [2017-07-21T19:23:12.864395 #35707] DEBUG -- : WidgetCommandHandler will handle command: composite with arguments []
233
+ D, [2017-07-21T19:23:12.864893 #35707] DEBUG -- : widget styles are: []
234
+ D, [2017-07-21T19:23:12.874296 #35707] DEBUG -- : method: list and args: [:multi]
235
+ D, [2017-07-21T19:23:12.874969 #35707] DEBUG -- : parent is a widget: true
236
+ D, [2017-07-21T19:23:12.875452 #35707] DEBUG -- : on listener?: false
237
+ D, [2017-07-21T19:23:12.878434 #35707] DEBUG -- : WidgetCommandHandler will handle command: list with arguments [:multi]
238
+ D, [2017-07-21T19:23:12.878798 #35707] DEBUG -- : widget styles are: [:multi]
239
+ ```
240
+
241
+ The `logger` instance may be replaced with a custom logger via `Glimmer::Config.logger = custom_logger`
242
+
243
+ To reset `logger` to the default instance, you may call `Glimmer::Config.reset_logger!`
244
+
245
+ 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.
246
+
247
+ [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.
248
+
249
+ #### loop_max_count
250
+
251
+ Glimmer has infinite loop detection support.
252
+ It can detect when an infinite loop is about to occur in method_missing and stops it.
253
+ It detects potential infinite loops when the same keyword and args repeat more than 100 times, which is unusual in a GUI app.
254
+
255
+ The max limit can be changed via the `Glimmer::Config::loop_max_count=(count)` config option.
256
+
257
+ Infinite loop detection may be disabled altogether if needed by setting `Glimmer::Config::loop_max_count` to `-1`
258
+
259
+ #### excluded_keyword_checkers
260
+
261
+ Glimmer permits consumers to exclude keywords from DSL processing by its engine via the `excluded_keyword_checkers` config option.
262
+
263
+ To do so, add a proc to it that returns a boolean indicating if a keyword is excluded or not.
264
+
265
+ 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.
266
+
267
+ Example of keywords excluded by [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt):
268
+
269
+ ```ruby
270
+ Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
271
+ method = method_symbol.to_s
272
+ result = false
273
+ result ||= method.start_with?('on_swt_') && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
274
+ result ||= method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
275
+ result ||= ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
276
+ result ||= method == 'post_initialize_child'
277
+ result ||= method.end_with?('=')
278
+ result ||= ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
279
+ end
280
+ ```
281
+
282
+ #### log_excluded_keywords
283
+
284
+ (default = false)
285
+
286
+ This just tells Glimmer whether to log excluded keywords or not (at the debug level). It is off by default.
287
+
288
+
289
+ ### Multi-DSL Support
290
+
291
+ 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.
292
+
293
+ 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.
294
+
295
+ By default, all loaded DSLs (required glimmer DSL gems) are enabled.
296
+
297
+ 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):
298
+
299
+ ```ruby
300
+ require 'glimmer-dsl-swt'
301
+ require 'glimmer-dsl-xml'
302
+ require 'glimmer-dsl-css'
303
+
304
+ include Glimmer
305
+
306
+ shell {
307
+ minimum_size 130, 130
308
+ @browser = browser {
309
+ text html {
310
+ head {
311
+ meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
312
+ style {
313
+ css {
314
+ h1 {
315
+ background 'yellow'
316
+ }
317
+ }
318
+ }
319
+ }
320
+ body {
321
+ h1 { "Hello, World!" }
322
+ }
323
+ }
324
+ }
325
+ }.open
326
+ ```
327
+
328
+ **API methods to enable/disable DSLs:**
329
+
330
+ `Glimmer::DSL::Engine.disable_dsl(dsl)`: disables a particular DSL
331
+
332
+ Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
333
+
334
+ `Glimmer::DSL::Engine.enable_dsl(dsl)`: enables a particular DSL
335
+
336
+ Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
337
+
338
+ `Glimmer::DSL::Engine.enabled_dsls=(dsls)`: enables only the specified DSLs, disabling all other loaded DSLs
339
+
340
+ Example: `Glimmer::DSL::Engine.enabled_dsls = [:xml, :css]`
341
+
342
+ ### Official DSLs
64
343
 
65
344
  Here, we showcase official Glimmer DSLs; that is [gems starting with the `glimmer-dsl-` prefix](https://rubygems.org/search?query=glimmer-dsl-).
66
345
 
67
346
  (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
347
 
69
- ### Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
348
+ #### Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
70
349
 
71
350
  [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
351
 
73
352
  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
353
 
75
- #### Glimmer DSL for SWT Samples
354
+ ##### Glimmer DSL for SWT Samples
76
355
 
77
- ##### Hello, World!
356
+ ###### Hello, World!
357
+
358
+ ![Hello World](images/glimmer-hello-world.png)
78
359
 
79
360
  Glimmer GUI code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)):
80
361
  ```ruby
@@ -88,163 +369,138 @@ shell {
88
369
  }.open
89
370
  ```
90
371
 
91
- Glimmer app:
92
-
93
- ![Hello World](images/glimmer-hello-world.png)
372
+ ###### Glimmer Tetris
94
373
 
95
- ##### Tic Tac Toe
374
+ ![Tetris](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/v4.18.3.1/images/glimmer-tetris.png)
96
375
 
97
- Glimmer GUI code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)):
376
+ Glimmer GUI code (from [samples/elaborate/tetris.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/v4.18.3.1/samples/elaborate/tetris.rb)):
98
377
 
99
378
  ```ruby
100
379
  # ...
101
- @shell = shell {
102
- text "Tic-Tac-Toe"
103
- minimum_size 150, 178
104
- composite {
105
- grid_layout 3, true
106
- (1..3).each { |row|
107
- (1..3).each { |column|
108
- button {
109
- layout_data :fill, :fill, true, true
110
- text bind(@tic_tac_toe_board[row, column], :sign)
111
- enabled bind(@tic_tac_toe_board[row, column], :empty)
112
- font style: :bold, height: 20
113
- on_widget_selected {
114
- @tic_tac_toe_board.mark(row, column)
115
- }
116
- }
117
- }
118
- }
380
+ shell(:no_resize) {
381
+ grid_layout {
382
+ num_columns 2
383
+ make_columns_equal_width false
384
+ margin_width 0
385
+ margin_height 0
386
+ horizontal_spacing 0
387
+ }
388
+
389
+ text 'Glimmer Tetris'
390
+ minimum_size 475, 500
391
+ background :gray
392
+
393
+ tetris_menu_bar(game: game)
394
+
395
+ playfield(game_playfield: game.playfield, playfield_width: playfield_width, playfield_height: playfield_height, block_size: BLOCK_SIZE)
396
+
397
+ score_lane(game: game, block_size: BLOCK_SIZE) {
398
+ layout_data(:fill, :fill, true, true)
119
399
  }
120
400
  }
121
401
  # ...
122
402
  ```
123
403
 
124
- Glimmer app:
125
-
126
- ![Tic Tac Toe](images/glimmer-tic-tac-toe-in-progress.png)
404
+ ###### Hello, Table!
127
405
 
128
- ##### Contact Manager
406
+ ![Hello Table](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-hello-table.png)
129
407
 
130
- Glimmer GUI code (from [samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)):
408
+ Glimmer GUI code (from [samples/hello/hello_table.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_table.rb)):
131
409
 
132
410
  ```ruby
133
411
  # ...
134
412
  shell {
135
- text "Contact Manager"
136
- composite {
137
- group {
138
- grid_layout(2, false) {
139
- margin_width 0
140
- margin_height 0
141
- }
142
- layout_data :fill, :center, true, false
143
- text 'Lookup Contacts'
144
- font height: 24
145
-
146
- label {
147
- layout_data :right, :center, false, false
148
- text "First &Name: "
149
- font height: 16
150
- }
151
- text {
152
- layout_data :fill, :center, true, false
153
- text bind(@contact_manager_presenter, :first_name)
154
- on_key_pressed {|key_event|
155
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
156
- }
157
- }
158
-
159
- label {
160
- layout_data :right, :center, false, false
161
- text "&Last Name: "
162
- font height: 16
163
- }
164
- text {
165
- layout_data :fill, :center, true, false
166
- text bind(@contact_manager_presenter, :last_name)
167
- on_key_pressed {|key_event|
168
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
169
- }
170
- }
171
-
172
- label {
173
- layout_data :right, :center, false, false
174
- text "&Email: "
175
- font height: 16
176
- }
177
- text {
178
- layout_data :fill, :center, true, false
179
- text bind(@contact_manager_presenter, :email)
180
- on_key_pressed {|key_event|
181
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
182
- }
183
- }
184
-
185
- composite {
186
- row_layout {
187
- margin_width 0
188
- margin_height 0
189
- }
190
- layout_data(:right, :center, false, false) {
191
- horizontal_span 2
192
- }
193
-
194
- button {
195
- text "&Find"
196
- on_widget_selected { @contact_manager_presenter.find }
197
- on_key_pressed {|key_event|
198
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
199
- }
200
- }
413
+ grid_layout
414
+
415
+ text 'Hello, Table!'
416
+
417
+ label {
418
+ layout_data :center, :center, true, false
419
+
420
+ text 'Baseball Playoff Schedule'
421
+ font height: 30, style: :bold
422
+ }
423
+
424
+ combo(:read_only) {
425
+ layout_data :center, :center, true, false
426
+ selection bind(BaseballGame, :playoff_type)
427
+ font height: 16
428
+ }
429
+
430
+ table(:editable) { |table_proxy|
431
+ layout_data :fill, :fill, true, true
432
+
433
+ table_column {
434
+ text 'Game Date'
435
+ width 150
436
+ sort_property :date # ensure sorting by real date value (not `game_date` string specified in items below)
437
+ editor :date_drop_down, property: :date_time
438
+ }
439
+ table_column {
440
+ text 'Game Time'
441
+ width 150
442
+ sort_property :time # ensure sorting by real time value (not `game_time` string specified in items below)
443
+ editor :time, property: :date_time
444
+ }
445
+ table_column {
446
+ text 'Ballpark'
447
+ width 180
448
+ editor :none
449
+ }
450
+ table_column {
451
+ text 'Home Team'
452
+ width 150
453
+ editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
454
+ }
455
+ table_column {
456
+ text 'Away Team'
457
+ width 150
458
+ editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
459
+ }
460
+ table_column {
461
+ text 'Promotion'
462
+ width 150
463
+ # default text editor is used here
464
+ }
465
+
466
+ # Data-bind table items (rows) to a model collection property, specifying column properties ordering per nested model
467
+ items bind(BaseballGame, :schedule), column_properties(:game_date, :game_time, :ballpark, :home_team, :away_team, :promotion)
468
+
469
+ # Data-bind table selection
470
+ selection bind(BaseballGame, :selected_game)
471
+
472
+ # Default initial sort property
473
+ sort_property :date
474
+
475
+ # Sort by these additional properties after handling sort by the column the user clicked
476
+ additional_sort_properties :date, :time, :home_team, :away_team, :ballpark, :promotion
477
+
478
+ menu {
479
+ menu_item {
480
+ text 'Book'
201
481
 
202
- button {
203
- text "&List All"
204
- on_widget_selected { @contact_manager_presenter.list }
205
- on_key_pressed {|key_event|
206
- @contact_manager_presenter.list if key_event.keyCode == swt(:cr)
207
- }
482
+ on_widget_selected {
483
+ book_selected_game
208
484
  }
209
485
  }
210
486
  }
211
-
212
- table(:multi) { |table_proxy|
213
- layout_data {
214
- horizontal_alignment :fill
215
- vertical_alignment :fill
216
- grab_excess_horizontal_space true
217
- grab_excess_vertical_space true
218
- height_hint 200
219
- }
220
- table_column {
221
- text "First Name"
222
- width 80
223
- }
224
- table_column {
225
- text "Last Name"
226
- width 80
227
- }
228
- table_column {
229
- text "Email"
230
- width 200
231
- }
232
- items bind(@contact_manager_presenter, :results),
233
- column_properties(:first_name, :last_name, :email)
234
- on_mouse_up { |event|
235
- table_proxy.edit_table_item(event.table_item, event.column_index)
236
- }
487
+ }
488
+
489
+ button {
490
+ text 'Book Selected Game'
491
+ layout_data :center, :center, true, false
492
+ font height: 16
493
+ enabled bind(BaseballGame, :selected_game)
494
+
495
+ on_widget_selected {
496
+ book_selected_game
237
497
  }
238
498
  }
239
499
  }.open
240
500
  # ...
241
501
  ```
242
502
 
243
- Glimmer App:
244
-
245
- ![Contact Manager](images/glimmer-contact-manager.png)
246
-
247
- #### Production Desktop Apps Built with Glimmer DSL for SWT
503
+ ##### Production Desktop Apps Built with Glimmer DSL for SWT
248
504
 
249
505
  [<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
250
506
 
@@ -254,7 +510,11 @@ Glimmer App:
254
510
 
255
511
  [![Math Bowling App Screenshot](https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/Math-Bowling-Screenshot.png)](https://github.com/AndyObtiva/MathBowling)
256
512
 
257
- ### Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
513
+ [<img alt="Garderie Rainbow Daily Agenda Logo" src="https://raw.githubusercontent.com/AndyObtiva/garderie_rainbow_daily_agenda/master/images/garderie_rainbow_daily_agenda_logo.png" width="40" />Garderie Rainbow Daily Agenda](https://github.com/AndyObtiva/garderie_rainbow_daily_agenda) - A child nursery daily agenda reporting desktop app
514
+
515
+ [![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)
516
+
517
+ #### Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
258
518
 
259
519
  [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.
260
520
 
@@ -262,9 +522,9 @@ Glimmer DSL for Opal webifier successfully reuses the entire [Glimmer](https://g
262
522
 
263
523
  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).
264
524
 
265
- #### Glimmer DSL for Opal Samples
525
+ ##### Glimmer DSL for Opal Samples
266
526
 
267
- ##### Hello, Computed!
527
+ ###### Hello, Computed!
268
528
 
269
529
  Add the following require statement to `app/assets/javascripts/application.rb`
270
530
 
@@ -383,7 +643,7 @@ You should see "Hello, Computed!"
383
643
 
384
644
  ![Glimmer DSL for Opal Hello Computed](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-computed.png)
385
645
 
386
- ##### Glimmer Calculator
646
+ ###### Glimmer Calculator
387
647
 
388
648
  Add the [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) gem to `Gemfile` (without requiring):
389
649
 
@@ -400,6 +660,7 @@ require 'glimmer-cs-calculator/launch'
400
660
  Sample GUI code (relies on custom widgets `command_button`, `operation_button`, and `number_button`):
401
661
 
402
662
  ```ruby
663
+ # ...
403
664
  shell {
404
665
  minimum_size (OS.mac? ? 320 : (OS.windows? ? 390 : 520)), 240
405
666
  image File.join(APP_ROOT, 'package', 'windows', "Glimmer Calculator.ico") if OS.windows?
@@ -435,6 +696,7 @@ shell {
435
696
  number_button(0, horizontal_span: 2)
436
697
  operation_button('.')
437
698
  }
699
+ # ...
438
700
  ```
439
701
 
440
702
  Glimmer app on the desktop (using the [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
@@ -463,13 +725,13 @@ You should see "Apple Calculator Theme"
463
725
 
464
726
  [![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)
465
727
 
466
- ### Glimmer DSL for XML (& HTML)
728
+ #### Glimmer DSL for XML (& HTML)
467
729
 
468
730
  [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) provides Ruby syntax for building XML (eXtensible Markup Language) documents.
469
731
 
470
732
  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).
471
733
 
472
- #### XML DSL
734
+ ##### XML DSL
473
735
 
474
736
  Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
475
737
  Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
@@ -501,13 +763,13 @@ Output:
501
763
  <html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
502
764
  ```
503
765
 
504
- ### Glimmer DSL for CSS
766
+ #### Glimmer DSL for CSS
505
767
 
506
768
  [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) provides Ruby syntax for building CSS (Cascading Style Sheets).
507
769
 
508
770
  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).
509
771
 
510
- #### CSS DSL
772
+ ##### CSS DSL
511
773
 
512
774
  Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
513
775
  Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
@@ -540,13 +802,13 @@ Output:
540
802
  body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2em}
541
803
  ```
542
804
 
543
- ### Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
805
+ #### Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
544
806
 
545
807
  [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.).
546
808
 
547
809
  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).
548
810
 
549
- 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 via [MRI Ruby](https://www.ruby-lang.org/en/).
811
+ 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/).
550
812
 
551
813
  [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:
552
814
  - Declarative DSL syntax that visually maps to the GUI widget hierarchy
@@ -559,9 +821,9 @@ The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a pleth
559
821
 
560
822
  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).
561
823
 
562
- #### Glimmer DSL for Tk Samples
824
+ ##### Glimmer DSL for Tk Samples
563
825
 
564
- ##### Hello, World!
826
+ ###### Hello, World!
565
827
 
566
828
  Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
567
829
 
@@ -585,7 +847,7 @@ Glimmer app:
585
847
 
586
848
  ![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)
587
849
 
588
- ##### Hello, Tab!
850
+ ###### Hello, Tab!
589
851
 
590
852
  Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
591
853
 
@@ -622,7 +884,7 @@ Glimmer app:
622
884
  ![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)
623
885
  ![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)
624
886
 
625
- ##### Hello, Combo!
887
+ ###### Hello, Combo!
626
888
 
627
889
  Glimmer code (from [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combo.rb)):
628
890
 
@@ -657,204 +919,13 @@ Glimmer app:
657
919
  ![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)
658
920
  ![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)
659
921
 
660
- ## DSL Engine
661
-
662
- 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.
663
-
664
- Glimmer DSL syntax consists mainly of:
665
- - **keywords** (e.g. `table` for a table widget)
666
- - **style/args** (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
667
- - **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)
668
-
669
- The Glimmer DSL Engine's architecture is based on the following Design Patterns and Data Structures:
670
- - **Interpreter Design Pattern**: to define interpretable expressions of DSL keywords
671
- - **Chain of Responsibility Design Pattern / Queue Data Structure**: to chain expression handlers in order of importance for processing DSL keywords
672
- - **Adapter Design Pattern**: to adapt expressions into handlers in a chain of responsibility
673
- - **Stack Data Structure**: to handle processing parent/child nesting of DSL keyword expressions in the correct order
674
-
675
- 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**.
676
-
677
- 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)`.
678
-
679
- Expressions are either:
680
- - **Static** (subclass of `StaticExpression`, which is a subclass of `Expression`): if they represent a single pre-identified keyword (e.g. `color` or `display`)
681
- - **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>`)
682
-
683
- Optionally, expressions can be parent expressions that contain other expressions, and must include the `ParentExpression` mixin module as such.
684
-
685
- Additionally, every expression that serves as a top-level entry point into the DSL must mixin `TopLevelExpression`
686
-
687
- 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.
688
-
689
- Every `Expression` sublcass must specify two methods at least:
690
- - `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.
691
- - `interpret(parent, keyword, *args, &block)`: to go ahead and interpret a DSL expression that qualified for interpretation
692
-
693
- `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.
694
-
695
- `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:
696
- - `add_content(parent, &block)`
697
-
698
- 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.
699
-
700
- Example of a dynamic expression:
701
-
702
- ```ruby
703
- module Glimmer
704
- module DSL
705
- module SWT
706
- class WidgetExpression < Expression
707
- include ParentExpression
708
-
709
- EXCLUDED_KEYWORDS = %w[shell display tab_item]
710
-
711
- def can_interpret?(parent, keyword, *args, &block)
712
- !EXCLUDED_KEYWORDS.include?(keyword) and
713
- parent.respond_to?(:swt_widget) and
714
- Glimmer::SWT::WidgetProxy.widget_exists?(keyword)
715
- end
716
-
717
- def interpret(parent, keyword, *args, &block)
718
- Glimmer::SWT::WidgetProxy.create(keyword, parent, args)
719
- end
720
-
721
- def add_content(parent, &block)
722
- super
723
- parent.post_add_content
724
- end
725
-
726
- end
727
- end
728
- end
729
- end
730
- ```
731
-
732
- Example of a static expression (does not need `can_interpret?`):
733
-
734
- ```ruby
735
- module Glimmer
736
- module DSL
737
- module Opal
738
- class ColorExpression < StaticExpression
739
- include TopLevelExpression
740
-
741
- def interpret(parent, keyword, *args, &block)
742
- Glimmer::SWT::ColorProxy.new(*args)
743
- end
744
- end
745
- end
746
- end
747
- end
748
- ```
749
-
750
- DSL expressions go into the `glimmer/dsl/{dsl_name}` namespace directory.
751
-
752
- Also, every DSL requires a `glimmer/dsl/{dsl_name}/dsl.rb` file, which configures the DSL into Glimmer via a call to:
753
- ```ruby
754
- Glimmer::DSL::Engine.add_dynamic_expressions(DSLNameModule, expression_names_array)
755
- ```
756
-
757
- Expression names are underscored verions of `Expression` subclass names minus the `_expression` suffix.
758
-
759
- For example, here is an SWT DSL configuration:
760
-
761
- ```ruby
762
- require 'glimmer/launcher'
763
- require Glimmer::Launcher.swt_jar_file
764
- require 'glimmer/dsl/engine'
765
- Dir[File.expand_path('../*_expression.rb', __FILE__)].each {|f| require f}
766
-
767
- module Glimmer
768
- module DSL
769
- module SWT
770
- Engine.add_dynamic_expressions(
771
- SWT,
772
- %w[
773
- layout
774
- widget_listener
775
- combo_selection_data_binding
776
- checkbox_group_selection_data_binding
777
- radio_group_selection_data_binding
778
- list_selection_data_binding
779
- tree_items_data_binding
780
- table_items_data_binding
781
- data_binding
782
- cursor
783
- font
784
- image
785
- property
786
- block_property
787
- widget
788
- custom_widget
789
- ]
790
- )
791
- end
792
- end
793
- end
794
- ```
795
-
796
- In summary, these are the files needed to author a Glimmer DSL:
797
- - `glimmer/dsl/[dsl_name]/dsl.rb`: requires and adds all dynamic expressions to [dsl_name] Glimmer DSL
798
- - `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb`: needed for every [expresion_name] expression, whether dynamic or static
799
-
800
- ### Multi-DSL Support
801
-
802
- 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.
803
-
804
- 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.
805
-
806
- By default, all loaded DSLs (required glimmer DSL gems) are enabled.
807
-
808
- 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):
809
-
810
- ```ruby
811
- require 'glimmer-dsl-swt'
812
- require 'glimmer-dsl-xml'
813
- require 'glimmer-dsl-css'
814
-
815
- include Glimmer
816
-
817
- shell {
818
- minimum_size 130, 130
819
- @browser = browser {
820
- text html {
821
- head {
822
- meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
823
- style {
824
- css {
825
- h1 {
826
- background 'yellow'
827
- }
828
- }
829
- }
830
- }
831
- body {
832
- h1 { "Hello, World!" }
833
- }
834
- }
835
- }
836
- }.open
837
- ```
838
-
839
- **API methods to enable/disable DSLs:**
840
-
841
- `Glimmer::DSL::Engine.disable_dsl(dsl)`: disables a particular DSL
842
-
843
- Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
844
-
845
- `Glimmer::DSL::Engine.enable_dsl(dsl)`: enables a particular DSL
846
-
847
- Example: `Glimmer::DSL::Engine.disable_dsl(:swt)`
848
-
849
- `Glimmer::DSL::Engine.enabled_dsls=(dsls)`: enables only the specified DSLs, disabling all other loaded DSLs
850
-
851
- Example: `Glimmer::DSL::Engine.enabled_dsls = [:xml, :css]`
852
-
853
922
  ## Data-Binding Library
854
923
 
855
- Data-Binding enables binding GUI properties (like text and color) to Model attributes (like name and age).
924
+ Data-Binding enables mapping GUI properties (like text and color) to Model attributes (like name and age).
925
+
926
+ 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)
856
927
 
857
- It relies on the Observer Design Pattern and MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
928
+ This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
858
929
 
859
930
  These are the main classes concerning data-binding:
860
931
  - `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`)
@@ -914,8 +985,8 @@ If you would like to contribute to Glimmer, please study up on Glimmer and [SWT]
914
985
 
915
986
  You may apply for contributing to any of these Glimmer DSL gems whether you prefer to focus on the desktop or web:
916
987
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
917
- - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
918
- - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
988
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
989
+ - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
919
990
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
920
991
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
921
992