glimmer 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +311 -227
- data/VERSION +1 -1
- data/glimmer.gemspec +21 -35
- data/lib/glimmer.rb +1 -0
- data/lib/glimmer/config.rb +2 -2
- data/lib/glimmer/dsl/engine.rb +3 -1
- metadata +22 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d0de24c574be6e42a7cade68606d7dace341333cd2922307d15aee307f77a42
|
4
|
+
data.tar.gz: 3ae8fe64ba1fc958bca990d13c02966101f7eef91b03d60ddf28cd6f406b1af8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ea0f3c6733f9274b06067f6e3888a017f4ff4a7bf99b5c446f4cadf38eb705b6ddbc1f76a2b0e2d4ee9e6620626f07069d2c4ff199b682f7be38fda1a6d6749
|
7
|
+
data.tar.gz: 5ae1155117530b0e3808000057fae31a5ee5345dbc8fc28b4eae797d916d72ec1c7c643e2f314f8ffcb26121d33a7dc43080a2d30639ded865bb90c4a2bfbebc
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,11 @@
|
|
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
|
+
|
6
11
|
## 1.1.1
|
7
12
|
|
8
13
|
- Ensured after_read hook truly happens after notifying observers in ModelBinding
|
data/README.md
CHANGED
@@ -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
|
|
@@ -39,14 +39,16 @@ Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) an
|
|
39
39
|
## Table of Contents
|
40
40
|
|
41
41
|
- [Glimmer](#-glimmer---dsl-framework-for-ruby-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)
|
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,298 @@ 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
|
-
##
|
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
|
-
|
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
|
-
|
354
|
+
##### Glimmer DSL for SWT Samples
|
76
355
|
|
77
|
-
|
356
|
+
###### Hello, World!
|
78
357
|
|
79
358
|
![Hello World](images/glimmer-hello-world.png)
|
80
359
|
|
@@ -90,7 +369,7 @@ shell {
|
|
90
369
|
}.open
|
91
370
|
```
|
92
371
|
|
93
|
-
|
372
|
+
###### Glimmer Tetris
|
94
373
|
|
95
374
|
![Tetris](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/v4.18.3.1/images/glimmer-tetris.png)
|
96
375
|
|
@@ -118,15 +397,11 @@ Glimmer GUI code (from [samples/elaborate/tetris.rb](https://github.com/AndyObti
|
|
118
397
|
score_lane(game: game, block_size: BLOCK_SIZE) {
|
119
398
|
layout_data(:fill, :fill, true, true)
|
120
399
|
}
|
121
|
-
|
122
|
-
on_widget_disposed {
|
123
|
-
deregister_observers
|
124
|
-
}
|
125
400
|
}
|
126
401
|
# ...
|
127
402
|
```
|
128
403
|
|
129
|
-
|
404
|
+
###### Hello, Table!
|
130
405
|
|
131
406
|
![Hello Table](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-hello-table.png)
|
132
407
|
|
@@ -225,7 +500,7 @@ Glimmer GUI code (from [samples/hello/hello_table.rb](https://github.com/AndyObt
|
|
225
500
|
# ...
|
226
501
|
```
|
227
502
|
|
228
|
-
|
503
|
+
##### Production Desktop Apps Built with Glimmer DSL for SWT
|
229
504
|
|
230
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
|
231
506
|
|
@@ -239,7 +514,7 @@ Glimmer GUI code (from [samples/hello/hello_table.rb](https://github.com/AndyObt
|
|
239
514
|
|
240
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)
|
241
516
|
|
242
|
-
|
517
|
+
#### Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
|
243
518
|
|
244
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.
|
245
520
|
|
@@ -247,9 +522,9 @@ Glimmer DSL for Opal webifier successfully reuses the entire [Glimmer](https://g
|
|
247
522
|
|
248
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).
|
249
524
|
|
250
|
-
|
525
|
+
##### Glimmer DSL for Opal Samples
|
251
526
|
|
252
|
-
|
527
|
+
###### Hello, Computed!
|
253
528
|
|
254
529
|
Add the following require statement to `app/assets/javascripts/application.rb`
|
255
530
|
|
@@ -368,7 +643,7 @@ You should see "Hello, Computed!"
|
|
368
643
|
|
369
644
|
![Glimmer DSL for Opal Hello Computed](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-computed.png)
|
370
645
|
|
371
|
-
|
646
|
+
###### Glimmer Calculator
|
372
647
|
|
373
648
|
Add the [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) gem to `Gemfile` (without requiring):
|
374
649
|
|
@@ -385,6 +660,7 @@ require 'glimmer-cs-calculator/launch'
|
|
385
660
|
Sample GUI code (relies on custom widgets `command_button`, `operation_button`, and `number_button`):
|
386
661
|
|
387
662
|
```ruby
|
663
|
+
# ...
|
388
664
|
shell {
|
389
665
|
minimum_size (OS.mac? ? 320 : (OS.windows? ? 390 : 520)), 240
|
390
666
|
image File.join(APP_ROOT, 'package', 'windows', "Glimmer Calculator.ico") if OS.windows?
|
@@ -403,9 +679,9 @@ shell {
|
|
403
679
|
caret nil
|
404
680
|
}
|
405
681
|
command_button('AC')
|
406
|
-
operation_button('
|
407
|
-
operation_button('
|
408
|
-
operation_button('
|
682
|
+
operation_button('÷')
|
683
|
+
operation_button('×')
|
684
|
+
operation_button('−')
|
409
685
|
(7..9).each { |number|
|
410
686
|
number_button(number)
|
411
687
|
}
|
@@ -420,6 +696,7 @@ shell {
|
|
420
696
|
number_button(0, horizontal_span: 2)
|
421
697
|
operation_button('.')
|
422
698
|
}
|
699
|
+
# ...
|
423
700
|
```
|
424
701
|
|
425
702
|
Glimmer app on the desktop (using the [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
|
@@ -448,13 +725,13 @@ You should see "Apple Calculator Theme"
|
|
448
725
|
|
449
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)
|
450
727
|
|
451
|
-
|
728
|
+
#### Glimmer DSL for XML (& HTML)
|
452
729
|
|
453
730
|
[Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) provides Ruby syntax for building XML (eXtensible Markup Language) documents.
|
454
731
|
|
455
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).
|
456
733
|
|
457
|
-
|
734
|
+
##### XML DSL
|
458
735
|
|
459
736
|
Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
|
460
737
|
Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
|
@@ -486,13 +763,13 @@ Output:
|
|
486
763
|
<html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
|
487
764
|
```
|
488
765
|
|
489
|
-
|
766
|
+
#### Glimmer DSL for CSS
|
490
767
|
|
491
768
|
[Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) provides Ruby syntax for building CSS (Cascading Style Sheets).
|
492
769
|
|
493
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).
|
494
771
|
|
495
|
-
|
772
|
+
##### CSS DSL
|
496
773
|
|
497
774
|
Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
|
498
775
|
Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
|
@@ -525,7 +802,7 @@ Output:
|
|
525
802
|
body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2em}
|
526
803
|
```
|
527
804
|
|
528
|
-
|
805
|
+
#### Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
|
529
806
|
|
530
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.).
|
531
808
|
|
@@ -544,9 +821,9 @@ The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a pleth
|
|
544
821
|
|
545
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).
|
546
823
|
|
547
|
-
|
824
|
+
##### Glimmer DSL for Tk Samples
|
548
825
|
|
549
|
-
|
826
|
+
###### Hello, World!
|
550
827
|
|
551
828
|
Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
|
552
829
|
|
@@ -570,7 +847,7 @@ Glimmer app:
|
|
570
847
|
|
571
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)
|
572
849
|
|
573
|
-
|
850
|
+
###### Hello, Tab!
|
574
851
|
|
575
852
|
Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
|
576
853
|
|
@@ -607,7 +884,7 @@ Glimmer app:
|
|
607
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)
|
608
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)
|
609
886
|
|
610
|
-
|
887
|
+
###### Hello, Combo!
|
611
888
|
|
612
889
|
Glimmer code (from [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combo.rb)):
|
613
890
|
|
@@ -642,199 +919,6 @@ Glimmer app:
|
|
642
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)
|
643
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)
|
644
921
|
|
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
922
|
## Data-Binding Library
|
839
923
|
|
840
924
|
Data-Binding enables mapping GUI properties (like text and color) to Model attributes (like name and age).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.1.
|
1
|
+
1.1.2
|
data/glimmer.gemspec
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer 1.1.
|
5
|
+
# stub: glimmer 1.1.2 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer".freeze
|
9
|
-
s.version = "1.1.
|
9
|
+
s.version = "1.1.2"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["AndyMaleh".freeze]
|
14
|
-
s.date = "2021-02-
|
15
|
-
s.description = "Glimmer is a Ruby DSL Framework consisting of a DSL Engine and an Observable/Observer/Data-Binding Library. Used in the Glimmer DSL for SWT (JRuby Desktop Development GUI Framework), the Glimmer DSL for Tk (Ruby Desktop Development GUI Library), the Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps), the Glimmer DSL for XML (& HTML), and the Glimmer DSL for CSS.".freeze
|
14
|
+
s.date = "2021-02-20"
|
15
|
+
s.description = "Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of a DSL Engine and an Observable/Observer/Data-Binding Library. Used in the Glimmer DSL for SWT (JRuby Desktop Development GUI Framework), the Glimmer DSL for Tk (Ruby Desktop Development GUI Library), the Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps), the Glimmer DSL for XML (& HTML), and the Glimmer DSL for CSS.".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"CHANGELOG.md",
|
@@ -46,41 +46,27 @@ Gem::Specification.new do |s|
|
|
46
46
|
]
|
47
47
|
s.homepage = "http://github.com/AndyObtiva/glimmer".freeze
|
48
48
|
s.licenses = ["MIT".freeze]
|
49
|
-
s.rubygems_version = "3.
|
49
|
+
s.rubygems_version = "3.2.3".freeze
|
50
50
|
s.summary = "Glimmer - DSL Engine for Ruby GUI and More".freeze
|
51
51
|
|
52
52
|
if s.respond_to? :specification_version then
|
53
53
|
s.specification_version = 4
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
else
|
70
|
-
s.add_dependency(%q<array_include_methods>.freeze, [">= 1.0.4", "< 2.0.0"])
|
71
|
-
s.add_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
|
72
|
-
s.add_dependency(%q<concurrent-ruby>.freeze, [">= 1.1.7", "< 2.0.0"])
|
73
|
-
s.add_dependency(%q<rspec-mocks>.freeze, ["~> 3.5.0"])
|
74
|
-
s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
|
75
|
-
s.add_dependency(%q<puts_debuggerer>.freeze, ["~> 0.10.0"])
|
76
|
-
s.add_dependency(%q<rake>.freeze, [">= 10.1.0", "< 14.0.0"])
|
77
|
-
s.add_dependency(%q<jeweler>.freeze, [">= 2.0.0", "< 3.0.0"])
|
78
|
-
s.add_dependency(%q<rdoc>.freeze, [">= 6.2.1", "< 7.0.0"])
|
79
|
-
s.add_dependency(%q<coveralls>.freeze, [">= 0"])
|
80
|
-
s.add_dependency(%q<simplecov>.freeze, ["~> 0.16.1"])
|
81
|
-
s.add_dependency(%q<simplecov-lcov>.freeze, ["~> 0.7.0"])
|
82
|
-
s.add_dependency(%q<rake-tui>.freeze, [">= 0"])
|
83
|
-
end
|
56
|
+
if s.respond_to? :add_runtime_dependency then
|
57
|
+
s.add_runtime_dependency(%q<array_include_methods>.freeze, [">= 1.0.4", "< 2.0.0"])
|
58
|
+
s.add_runtime_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
|
59
|
+
s.add_runtime_dependency(%q<concurrent-ruby>.freeze, [">= 1.1.7", "< 2.0.0"])
|
60
|
+
s.add_development_dependency(%q<rspec-mocks>.freeze, ["~> 3.5.0"])
|
61
|
+
s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
|
62
|
+
s.add_development_dependency(%q<puts_debuggerer>.freeze, ["~> 0.10.0"])
|
63
|
+
s.add_development_dependency(%q<rake>.freeze, [">= 10.1.0", "< 14.0.0"])
|
64
|
+
s.add_development_dependency(%q<jeweler>.freeze, [">= 2.0.0", "< 3.0.0"])
|
65
|
+
s.add_development_dependency(%q<rdoc>.freeze, [">= 6.2.1", "< 7.0.0"])
|
66
|
+
s.add_development_dependency(%q<coveralls>.freeze, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<simplecov>.freeze, ["~> 0.16.1"])
|
68
|
+
s.add_development_dependency(%q<simplecov-lcov>.freeze, ["~> 0.7.0"])
|
69
|
+
s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"])
|
84
70
|
else
|
85
71
|
s.add_dependency(%q<array_include_methods>.freeze, [">= 1.0.4", "< 2.0.0"])
|
86
72
|
s.add_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
|
data/lib/glimmer.rb
CHANGED
@@ -81,6 +81,7 @@ module Glimmer
|
|
81
81
|
Glimmer.loop_reset!
|
82
82
|
end
|
83
83
|
Glimmer.loop_last_data = new_loop_data
|
84
|
+
Glimmer::Config.logger.info {">"*80}
|
84
85
|
Glimmer::Config.logger.info {"Interpreting keyword: #{method_symbol}"}
|
85
86
|
Glimmer::DSL::Engine.interpret(method_symbol, *args, &block)
|
86
87
|
end
|
data/lib/glimmer/config.rb
CHANGED
@@ -56,10 +56,10 @@ module Glimmer
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def reset_logger!
|
59
|
-
self.logger = Logger.new(
|
59
|
+
self.logger = Logger.new($stdout).tap do |logger|
|
60
60
|
logger.level = Logger::ERROR
|
61
61
|
begin
|
62
|
-
logger.level = ENV['GLIMMER_LOGGER_LEVEL']
|
62
|
+
logger.level = ENV['GLIMMER_LOGGER_LEVEL']&.strip&.downcase
|
63
63
|
rescue => e
|
64
64
|
puts e.message
|
65
65
|
end
|
data/lib/glimmer/dsl/engine.rb
CHANGED
@@ -37,7 +37,7 @@ module Glimmer
|
|
37
37
|
STATIC_EXPRESSION_METHOD_FACTORY = lambda do |keyword|
|
38
38
|
lambda do |*args, &block|
|
39
39
|
if Glimmer::DSL::Engine.no_dsls?
|
40
|
-
|
40
|
+
Glimmer::Config.logger.error {Glimmer::DSL::Engine::MESSAGE_NO_DSLS}
|
41
41
|
else
|
42
42
|
retrieved_static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
|
43
43
|
# TODO consider replacing Glimmer::DSL::Engine.static_expressions[keyword].keys - Glimmer::DSL::Engine.disabled_dsls with Glimmer::DSL::Engine.enabled_static_expression_dsls(keyword)
|
@@ -55,6 +55,7 @@ module Glimmer
|
|
55
55
|
else
|
56
56
|
raise Glimmer::Error, "Unsupported keyword: #{keyword}" unless static_expression_dsl || retrieved_static_expression
|
57
57
|
Glimmer::DSL::Engine.dsl_stack.push(static_expression_dsl || Glimmer::DSL::Engine.dsl)
|
58
|
+
Glimmer::Config.logger.info {"Assuming DSL: #{Glimmer::DSL::Engine.dsl_stack.last}"}
|
58
59
|
static_expression = Glimmer::DSL::Engine.static_expressions[keyword][Glimmer::DSL::Engine.dsl]
|
59
60
|
static_expression_can_interpret = nil
|
60
61
|
if static_expression.nil? || !(static_expression_can_interpret = static_expression.can_interpret?(Glimmer::DSL::Engine.parent, keyword, *args, &block))
|
@@ -169,6 +170,7 @@ module Glimmer
|
|
169
170
|
dynamic_expression_dsl = (dynamic_expression_chains_of_responsibility.keys - disabled_dsls).first if dsl.nil?
|
170
171
|
# TODO consider pushing this code into interpret_expresion to provide hooks that work around it regardless of static vs dynamic
|
171
172
|
dsl_stack.push(dynamic_expression_dsl || dsl)
|
173
|
+
Glimmer::Config.logger.info {"Assuming DSL: #{dsl_stack.last}"}
|
172
174
|
expression = dynamic_expression_chains_of_responsibility[dsl].handle(parent, keyword, *args, &block)
|
173
175
|
interpret_expression(expression, keyword, *args, &block)
|
174
176
|
end
|
metadata
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
+
name: array_include_methods
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
15
16
|
requirements:
|
16
17
|
- - ">="
|
@@ -19,7 +20,6 @@ dependencies:
|
|
19
20
|
- - "<"
|
20
21
|
- !ruby/object:Gem::Version
|
21
22
|
version: 2.0.0
|
22
|
-
name: array_include_methods
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -31,6 +31,7 @@ dependencies:
|
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 2.0.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
+
name: facets
|
34
35
|
requirement: !ruby/object:Gem::Requirement
|
35
36
|
requirements:
|
36
37
|
- - ">="
|
@@ -39,7 +40,6 @@ dependencies:
|
|
39
40
|
- - "<"
|
40
41
|
- !ruby/object:Gem::Version
|
41
42
|
version: 4.0.0
|
42
|
-
name: facets
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -51,6 +51,7 @@ dependencies:
|
|
51
51
|
- !ruby/object:Gem::Version
|
52
52
|
version: 4.0.0
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
|
+
name: concurrent-ruby
|
54
55
|
requirement: !ruby/object:Gem::Requirement
|
55
56
|
requirements:
|
56
57
|
- - ">="
|
@@ -59,7 +60,6 @@ dependencies:
|
|
59
60
|
- - "<"
|
60
61
|
- !ruby/object:Gem::Version
|
61
62
|
version: 2.0.0
|
62
|
-
name: concurrent-ruby
|
63
63
|
type: :runtime
|
64
64
|
prerelease: false
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -71,12 +71,12 @@ dependencies:
|
|
71
71
|
- !ruby/object:Gem::Version
|
72
72
|
version: 2.0.0
|
73
73
|
- !ruby/object:Gem::Dependency
|
74
|
+
name: rspec-mocks
|
74
75
|
requirement: !ruby/object:Gem::Requirement
|
75
76
|
requirements:
|
76
77
|
- - "~>"
|
77
78
|
- !ruby/object:Gem::Version
|
78
79
|
version: 3.5.0
|
79
|
-
name: rspec-mocks
|
80
80
|
type: :development
|
81
81
|
prerelease: false
|
82
82
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -85,12 +85,12 @@ dependencies:
|
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: 3.5.0
|
87
87
|
- !ruby/object:Gem::Dependency
|
88
|
+
name: rspec
|
88
89
|
requirement: !ruby/object:Gem::Requirement
|
89
90
|
requirements:
|
90
91
|
- - "~>"
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
version: 3.5.0
|
93
|
-
name: rspec
|
94
94
|
type: :development
|
95
95
|
prerelease: false
|
96
96
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -99,12 +99,12 @@ dependencies:
|
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: 3.5.0
|
101
101
|
- !ruby/object:Gem::Dependency
|
102
|
+
name: puts_debuggerer
|
102
103
|
requirement: !ruby/object:Gem::Requirement
|
103
104
|
requirements:
|
104
105
|
- - "~>"
|
105
106
|
- !ruby/object:Gem::Version
|
106
107
|
version: 0.10.0
|
107
|
-
name: puts_debuggerer
|
108
108
|
type: :development
|
109
109
|
prerelease: false
|
110
110
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -113,6 +113,7 @@ dependencies:
|
|
113
113
|
- !ruby/object:Gem::Version
|
114
114
|
version: 0.10.0
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
|
+
name: rake
|
116
117
|
requirement: !ruby/object:Gem::Requirement
|
117
118
|
requirements:
|
118
119
|
- - ">="
|
@@ -121,7 +122,6 @@ dependencies:
|
|
121
122
|
- - "<"
|
122
123
|
- !ruby/object:Gem::Version
|
123
124
|
version: 14.0.0
|
124
|
-
name: rake
|
125
125
|
type: :development
|
126
126
|
prerelease: false
|
127
127
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -133,6 +133,7 @@ dependencies:
|
|
133
133
|
- !ruby/object:Gem::Version
|
134
134
|
version: 14.0.0
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
|
+
name: jeweler
|
136
137
|
requirement: !ruby/object:Gem::Requirement
|
137
138
|
requirements:
|
138
139
|
- - ">="
|
@@ -141,7 +142,6 @@ dependencies:
|
|
141
142
|
- - "<"
|
142
143
|
- !ruby/object:Gem::Version
|
143
144
|
version: 3.0.0
|
144
|
-
name: jeweler
|
145
145
|
type: :development
|
146
146
|
prerelease: false
|
147
147
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -153,6 +153,7 @@ dependencies:
|
|
153
153
|
- !ruby/object:Gem::Version
|
154
154
|
version: 3.0.0
|
155
155
|
- !ruby/object:Gem::Dependency
|
156
|
+
name: rdoc
|
156
157
|
requirement: !ruby/object:Gem::Requirement
|
157
158
|
requirements:
|
158
159
|
- - ">="
|
@@ -161,7 +162,6 @@ dependencies:
|
|
161
162
|
- - "<"
|
162
163
|
- !ruby/object:Gem::Version
|
163
164
|
version: 7.0.0
|
164
|
-
name: rdoc
|
165
165
|
type: :development
|
166
166
|
prerelease: false
|
167
167
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -173,12 +173,12 @@ dependencies:
|
|
173
173
|
- !ruby/object:Gem::Version
|
174
174
|
version: 7.0.0
|
175
175
|
- !ruby/object:Gem::Dependency
|
176
|
+
name: coveralls
|
176
177
|
requirement: !ruby/object:Gem::Requirement
|
177
178
|
requirements:
|
178
179
|
- - ">="
|
179
180
|
- !ruby/object:Gem::Version
|
180
181
|
version: '0'
|
181
|
-
name: coveralls
|
182
182
|
type: :development
|
183
183
|
prerelease: false
|
184
184
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -187,12 +187,12 @@ dependencies:
|
|
187
187
|
- !ruby/object:Gem::Version
|
188
188
|
version: '0'
|
189
189
|
- !ruby/object:Gem::Dependency
|
190
|
+
name: simplecov
|
190
191
|
requirement: !ruby/object:Gem::Requirement
|
191
192
|
requirements:
|
192
193
|
- - "~>"
|
193
194
|
- !ruby/object:Gem::Version
|
194
195
|
version: 0.16.1
|
195
|
-
name: simplecov
|
196
196
|
type: :development
|
197
197
|
prerelease: false
|
198
198
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -201,12 +201,12 @@ dependencies:
|
|
201
201
|
- !ruby/object:Gem::Version
|
202
202
|
version: 0.16.1
|
203
203
|
- !ruby/object:Gem::Dependency
|
204
|
+
name: simplecov-lcov
|
204
205
|
requirement: !ruby/object:Gem::Requirement
|
205
206
|
requirements:
|
206
207
|
- - "~>"
|
207
208
|
- !ruby/object:Gem::Version
|
208
209
|
version: 0.7.0
|
209
|
-
name: simplecov-lcov
|
210
210
|
type: :development
|
211
211
|
prerelease: false
|
212
212
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -215,12 +215,12 @@ dependencies:
|
|
215
215
|
- !ruby/object:Gem::Version
|
216
216
|
version: 0.7.0
|
217
217
|
- !ruby/object:Gem::Dependency
|
218
|
+
name: rake-tui
|
218
219
|
requirement: !ruby/object:Gem::Requirement
|
219
220
|
requirements:
|
220
221
|
- - ">="
|
221
222
|
- !ruby/object:Gem::Version
|
222
223
|
version: '0'
|
223
|
-
name: rake-tui
|
224
224
|
type: :development
|
225
225
|
prerelease: false
|
226
226
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -228,11 +228,12 @@ dependencies:
|
|
228
228
|
- - ">="
|
229
229
|
- !ruby/object:Gem::Version
|
230
230
|
version: '0'
|
231
|
-
description: Glimmer is a Ruby DSL Framework
|
232
|
-
Library. Used in the Glimmer
|
233
|
-
|
234
|
-
|
235
|
-
XML (& HTML), and the Glimmer
|
231
|
+
description: Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of
|
232
|
+
a DSL Engine and an Observable/Observer/Data-Binding Library. Used in the Glimmer
|
233
|
+
DSL for SWT (JRuby Desktop Development GUI Framework), the Glimmer DSL for Tk (Ruby
|
234
|
+
Desktop Development GUI Library), the Glimmer DSL for Opal (Pure Ruby Web GUI and
|
235
|
+
Auto-Webifier of Desktop Apps), the Glimmer DSL for XML (& HTML), and the Glimmer
|
236
|
+
DSL for CSS.
|
236
237
|
email: andy.am@gmail.com
|
237
238
|
executables: []
|
238
239
|
extensions: []
|
@@ -283,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
283
284
|
- !ruby/object:Gem::Version
|
284
285
|
version: '0'
|
285
286
|
requirements: []
|
286
|
-
rubygems_version: 3.
|
287
|
+
rubygems_version: 3.2.3
|
287
288
|
signing_key:
|
288
289
|
specification_version: 4
|
289
290
|
summary: Glimmer - DSL Engine for Ruby GUI and More
|