glimmer 2.1.2 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/README.md +295 -7
- data/VERSION +1 -1
- data/glimmer.gemspec +6 -5
- data/lib/glimmer/data_binding/model_binding.rb +1 -1
- data/lib/glimmer/data_binding/observable_array.rb +1 -1
- data/lib/glimmer/data_binding/observable_hash.rb +181 -0
- data/lib/glimmer/data_binding/observable_model.rb +21 -2
- data/lib/glimmer/data_binding/observer.rb +6 -3
- metadata +7 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d61c8e3da07109cf0edf0df46e0937a862b3e80007a12da8050c2415481cbb6e
|
4
|
+
data.tar.gz: c618e4c0b024a9da219fbe78c263a442981d3d0a40f8a13bb7639edf50310e09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1b3b5d7aa04427f0e1188e0cd7e3855d517c0adfb6c561160c465f043685c48a651df8fde9eb05e31b84578a1986182422e49bb329fd35b9f0cd623fac4bc77
|
7
|
+
data.tar.gz: 5b4177d4ae038eefd1c210e8054f92ebe716b1a7edb2b40e94466ee2ba35aaafced1320128e75603f156ed3a7cdc56db200fd4369916dc0862f44fbd7ac12760
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,25 @@
|
|
3
3
|
Related Change Logs:
|
4
4
|
- [glimmer-dsl-swt/CHANGELOG.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/CHANGELOG.md)
|
5
5
|
|
6
|
+
### 2.2.0
|
7
|
+
|
8
|
+
- New `Glimmer::DataBinding::ObservableHash` support for observing `hash[key]=value` mutations
|
9
|
+
- Ensure observed ObservableHash values are enhanced with ObservableHash if they were of type Hash
|
10
|
+
- Ensure observed ObservableModel values are enhanced with ObservableHash if they were of type Hash
|
11
|
+
|
12
|
+
### 2.1.5
|
13
|
+
|
14
|
+
- Upgrade array_include_methods gem to version 1.4.0 (and fix minor version)
|
15
|
+
- Update API usage of `Array#include_all?` in `Glimmer::DataBinding::ObservableArray` to splat array (now required)
|
16
|
+
|
17
|
+
### 2.1.4
|
18
|
+
|
19
|
+
- Upgrade array_include_methods gem to version 1.2.0
|
20
|
+
|
21
|
+
### 2.1.3
|
22
|
+
|
23
|
+
- Upgrade array_include_methods gem to version 1.1.0
|
24
|
+
|
6
25
|
### 2.1.2
|
7
26
|
|
8
27
|
- Fix issue with irrelevent error message showing up for ENV['GLIMMER_LOGGER_LEVEL'] being empty (it is not a true error)
|
data/README.md
CHANGED
@@ -35,6 +35,7 @@ Start by checking out Glimmer's original GUI DSL, which got extracted into its o
|
|
35
35
|
- [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
|
36
36
|
- [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
|
37
37
|
- [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
|
38
|
+
- [glimmer-dsl-libui](https://github.com/AndyObtiva/glimmer-dsl-libui): Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
|
38
39
|
|
39
40
|
## Table of Contents
|
40
41
|
|
@@ -49,6 +50,7 @@ Start by checking out Glimmer's original GUI DSL, which got extracted into its o
|
|
49
50
|
- [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
|
50
51
|
- [Glimmer DSL for CSS](#glimmer-dsl-for-css)
|
51
52
|
- [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
|
53
|
+
- [Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](#glimmer-dsl-for-libui-prerequisite-free-ruby-desktop-development-gui-library)
|
52
54
|
- [Data-Binding Library](#data-binding-library)
|
53
55
|
- [Glimmer Process](#glimmer-process)
|
54
56
|
- [Resources](#resources)
|
@@ -208,7 +210,7 @@ end
|
|
208
210
|
### Setup
|
209
211
|
|
210
212
|
Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL:
|
211
|
-
- Add `gem 'glimmer', '~> 2.
|
213
|
+
- Add `gem 'glimmer', '~> 2.2.0'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.2.0` and add `require 'glimmer'`
|
212
214
|
- 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)
|
213
215
|
- Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static
|
214
216
|
|
@@ -926,6 +928,291 @@ Glimmer app:
|
|
926
928
|
![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)
|
927
929
|
![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)
|
928
930
|
|
931
|
+
#### Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
|
932
|
+
|
933
|
+
[Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) is a prerequisite-free Ruby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!
|
934
|
+
|
935
|
+
[LibUI](https://github.com/kojix2/LibUI) is a thin [Ruby](https://www.ruby-lang.org/en/) wrapper around [libui](https://github.com/andlabs/libui), a relatively new C GUI library that renders native controls on every platform (similar to [SWT](https://www.eclipse.org/swt/), but without the heavy weight of the [Java Virtual Machine](https://www.java.com/en/)).
|
936
|
+
|
937
|
+
The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) or [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) is the fact that [SWT](https://www.eclipse.org/swt/) and [Tk](https://www.tcl.tk/) are more mature than mid-alpha [libui](https://github.com/andlabs/libui) as GUI toolkits. Still, if there is only a need to build a small simple application, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) could be a good convenient choice due to having zero prerequisites beyond the dependencies included in the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui). Also, just like [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk), its apps start instantly and have a small memory footprint. [LibUI](https://github.com/kojix2/LibUI) is a promising new GUI toolkit that might prove quite worthy in the future.
|
938
|
+
|
939
|
+
[Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) 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:
|
940
|
+
- Declarative DSL syntax that visually maps to the GUI widget hierarchy
|
941
|
+
- Convention over configuration via smart defaults and automation of low-level details
|
942
|
+
- Requiring the least amount of syntax possible to build GUI
|
943
|
+
- Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
944
|
+
- Custom Widget support
|
945
|
+
- Scaffolding for new custom widgets, apps, and gems
|
946
|
+
- Native-Executable packaging on Mac, Windows, and Linux
|
947
|
+
|
948
|
+
##### Glimmer DSL for LibUI Samples
|
949
|
+
|
950
|
+
###### Hello, World!
|
951
|
+
|
952
|
+
```ruby
|
953
|
+
require 'glimmer-dsl-libui'
|
954
|
+
|
955
|
+
include Glimmer
|
956
|
+
|
957
|
+
window('hello world').show
|
958
|
+
```
|
959
|
+
|
960
|
+
Mac
|
961
|
+
|
962
|
+
![glimmer-dsl-libui-mac-basic-window.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-window.png)
|
963
|
+
|
964
|
+
Linux
|
965
|
+
|
966
|
+
![glimmer-dsl-libui-linux-basic-window.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-window.png)
|
967
|
+
|
968
|
+
###### Basic Button
|
969
|
+
|
970
|
+
```ruby
|
971
|
+
require 'glimmer-dsl-libui'
|
972
|
+
|
973
|
+
include Glimmer
|
974
|
+
|
975
|
+
window('hello world', 300, 200) { |w|
|
976
|
+
button('Button') {
|
977
|
+
on_clicked do
|
978
|
+
msg_box(w, 'Information', 'You clicked the button')
|
979
|
+
end
|
980
|
+
}
|
981
|
+
|
982
|
+
on_closing do
|
983
|
+
puts 'Bye Bye'
|
984
|
+
end
|
985
|
+
}.show
|
986
|
+
```
|
987
|
+
|
988
|
+
Mac
|
989
|
+
|
990
|
+
![glimmer-dsl-libui-mac-basic-button.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-button.png)
|
991
|
+
![glimmer-dsl-libui-mac-basic-button-msg-box.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-button-msg-box.png)
|
992
|
+
|
993
|
+
Linux
|
994
|
+
|
995
|
+
![glimmer-dsl-libui-linux-basic-button.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-button.png)
|
996
|
+
![glimmer-dsl-libui-linux-basic-button-msg-box.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-button-msg-box.png)
|
997
|
+
|
998
|
+
###### Control Gallery
|
999
|
+
|
1000
|
+
```ruby
|
1001
|
+
require 'glimmer-dsl-libui'
|
1002
|
+
|
1003
|
+
include Glimmer
|
1004
|
+
|
1005
|
+
menu('File') {
|
1006
|
+
menu_item('Open') {
|
1007
|
+
on_clicked do
|
1008
|
+
file = open_file(MAIN_WINDOW)
|
1009
|
+
puts file unless file.nil?
|
1010
|
+
end
|
1011
|
+
}
|
1012
|
+
|
1013
|
+
menu_item('Save') {
|
1014
|
+
on_clicked do
|
1015
|
+
file = save_file(MAIN_WINDOW)
|
1016
|
+
puts file unless file.nil?
|
1017
|
+
end
|
1018
|
+
}
|
1019
|
+
|
1020
|
+
quit_menu_item {
|
1021
|
+
on_clicked do
|
1022
|
+
puts 'Bye Bye'
|
1023
|
+
end
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
preferences_menu_item # Can optionally contain an on_clicked listener
|
1027
|
+
}
|
1028
|
+
|
1029
|
+
menu('Edit') {
|
1030
|
+
check_menu_item('Checkable Item_')
|
1031
|
+
separator_menu_item
|
1032
|
+
menu_item('Disabled Item_') {
|
1033
|
+
enabled false
|
1034
|
+
}
|
1035
|
+
}
|
1036
|
+
|
1037
|
+
menu('Help') {
|
1038
|
+
menu_item('Help')
|
1039
|
+
|
1040
|
+
about_menu_item # Can optionally contain an on_clicked listener
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
MAIN_WINDOW = window('Control Gallery', 600, 500) {
|
1044
|
+
margined true
|
1045
|
+
|
1046
|
+
on_closing do
|
1047
|
+
puts 'Bye Bye'
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
vertical_box {
|
1051
|
+
horizontal_box {
|
1052
|
+
group('Basic Controls') {
|
1053
|
+
vertical_box {
|
1054
|
+
button('Button') {
|
1055
|
+
stretchy false
|
1056
|
+
|
1057
|
+
on_clicked do
|
1058
|
+
msg_box(MAIN_WINDOW, 'Information', 'You clicked the button')
|
1059
|
+
end
|
1060
|
+
}
|
1061
|
+
|
1062
|
+
checkbox('Checkbox') {
|
1063
|
+
stretchy false
|
1064
|
+
|
1065
|
+
on_toggled do |c|
|
1066
|
+
checked = c.checked == 1
|
1067
|
+
MAIN_WINDOW.title = "Checkbox is #{checked}"
|
1068
|
+
c.text = "I am the checkbox (#{checked})"
|
1069
|
+
end
|
1070
|
+
}
|
1071
|
+
|
1072
|
+
label('Label') { stretchy false }
|
1073
|
+
|
1074
|
+
horizontal_separator { stretchy false }
|
1075
|
+
|
1076
|
+
date_picker { stretchy false }
|
1077
|
+
|
1078
|
+
time_picker { stretchy false }
|
1079
|
+
|
1080
|
+
date_time_picker { stretchy false }
|
1081
|
+
|
1082
|
+
font_button { stretchy false }
|
1083
|
+
|
1084
|
+
color_button { stretchy false }
|
1085
|
+
}
|
1086
|
+
}
|
1087
|
+
|
1088
|
+
vertical_box {
|
1089
|
+
group('Numbers') {
|
1090
|
+
stretchy false
|
1091
|
+
|
1092
|
+
vertical_box {
|
1093
|
+
spinbox(0, 100) {
|
1094
|
+
stretchy false
|
1095
|
+
value 42
|
1096
|
+
|
1097
|
+
on_changed do |s|
|
1098
|
+
puts "New Spinbox value: #{s.value}"
|
1099
|
+
end
|
1100
|
+
}
|
1101
|
+
|
1102
|
+
slider(0, 100) {
|
1103
|
+
stretchy false
|
1104
|
+
|
1105
|
+
on_changed do |s|
|
1106
|
+
v = s.value
|
1107
|
+
puts "New Slider value: #{v}"
|
1108
|
+
@progress_bar.value = v
|
1109
|
+
end
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
@progress_bar = progress_bar { stretchy false }
|
1113
|
+
}
|
1114
|
+
}
|
1115
|
+
|
1116
|
+
group('Lists') {
|
1117
|
+
stretchy false
|
1118
|
+
|
1119
|
+
vertical_box {
|
1120
|
+
combobox {
|
1121
|
+
stretchy false
|
1122
|
+
items 'combobox Item 1', 'combobox Item 2', 'combobox Item 3' # also accepts a single array argument
|
1123
|
+
|
1124
|
+
on_selected do |c|
|
1125
|
+
puts "New combobox selection: #{c.selected}"
|
1126
|
+
end
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
editable_combobox {
|
1130
|
+
stretchy false
|
1131
|
+
items 'Editable Item 1', 'Editable Item 2', 'Editable Item 3' # also accepts a single array argument
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
radio_buttons {
|
1135
|
+
items 'Radio Button 1', 'Radio Button 2', 'Radio Button 3' # also accepts a single array argument
|
1136
|
+
}
|
1137
|
+
}
|
1138
|
+
}
|
1139
|
+
|
1140
|
+
tab {
|
1141
|
+
tab_item('Page 1') {
|
1142
|
+
horizontal_box {
|
1143
|
+
entry {
|
1144
|
+
text 'Please enter your feelings'
|
1145
|
+
|
1146
|
+
on_changed do |e|
|
1147
|
+
puts "Current textbox data: '#{e.text}'"
|
1148
|
+
end
|
1149
|
+
}
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
tab_item('Page 2') {
|
1154
|
+
horizontal_box
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
tab_item('Page 3') {
|
1158
|
+
horizontal_box
|
1159
|
+
}
|
1160
|
+
}
|
1161
|
+
}
|
1162
|
+
}
|
1163
|
+
}
|
1164
|
+
}
|
1165
|
+
|
1166
|
+
MAIN_WINDOW.show
|
1167
|
+
```
|
1168
|
+
|
1169
|
+
Mac
|
1170
|
+
|
1171
|
+
![glimmer-dsl-libui-mac-control-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-control-gallery.png)
|
1172
|
+
|
1173
|
+
Linux
|
1174
|
+
|
1175
|
+
![glimmer-dsl-libui-linux-control-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-control-gallery.png)
|
1176
|
+
|
1177
|
+
###### Basic Table
|
1178
|
+
|
1179
|
+
```ruby
|
1180
|
+
require 'glimmer-dsl-libui'
|
1181
|
+
|
1182
|
+
include Glimmer
|
1183
|
+
|
1184
|
+
data = [
|
1185
|
+
%w[cat meow],
|
1186
|
+
%w[dog woof],
|
1187
|
+
%w[chicken cock-a-doodle-doo],
|
1188
|
+
%w[hourse neigh],
|
1189
|
+
%w[cow moo]
|
1190
|
+
]
|
1191
|
+
|
1192
|
+
window('Animal sounds', 300, 200) {
|
1193
|
+
horizontal_box {
|
1194
|
+
table {
|
1195
|
+
text_column('Animal')
|
1196
|
+
text_column('Description')
|
1197
|
+
|
1198
|
+
cell_rows data
|
1199
|
+
}
|
1200
|
+
}
|
1201
|
+
|
1202
|
+
on_closing do
|
1203
|
+
puts 'Bye Bye'
|
1204
|
+
end
|
1205
|
+
}.show
|
1206
|
+
```
|
1207
|
+
|
1208
|
+
Mac
|
1209
|
+
|
1210
|
+
![glimmer-dsl-libui-mac-basic-table.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-table.png)
|
1211
|
+
|
1212
|
+
Linux
|
1213
|
+
|
1214
|
+
![glimmer-dsl-libui-linux-basic-table.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-table.png)
|
1215
|
+
|
929
1216
|
## Data-Binding Library
|
930
1217
|
|
931
1218
|
Data-Binding enables mapping GUI properties (like text and color) to Model attributes (like name and age) for bidirectional or unidirectional synchronization and conversion as needed.
|
@@ -935,12 +1222,13 @@ Glimmer enhances observed models automatically (including array operations like
|
|
935
1222
|
This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
|
936
1223
|
|
937
1224
|
These are the main classes concerning data-binding:
|
938
|
-
- `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`)
|
939
|
-
- `Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
940
|
-
- `ObservableModel`: Mixin module for any observable model with observable attributes. In addition to `Observable` methods, it has a `notify_observers` method to be called when changes occur. It automatically enhances all attribute setters (ending with `=`) to notify observers on changes. Also, it automatically handles observing array attributes using `ObservableArray` appropriately so they would notify observers upon array mutation changes.
|
941
|
-
- `ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push` or `delete`)
|
942
|
-
- `
|
943
|
-
- `
|
1225
|
+
- `Glimmer::DataBinding::Observer`: Provides general observer support including unique registration and deregistration for cleanup and prevention of memory leaks. Main methods concerned are: `call`, `register` (alias: `observe`), and `unregister` (alias: `unobserve` or `deregister`)
|
1226
|
+
- `Glimmer::DataBinding::Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
1227
|
+
- `Glimmer::DataBinding::ObservableModel`: Mixin module for any observable model with observable attributes. In addition to `Observable` methods, it has a `notify_observers` method to be called when changes occur. It automatically enhances all attribute setters (ending with `=`) to notify observers on changes. Also, it automatically handles observing array attributes using `ObservableArray` appropriately so they would notify observers upon array mutation changes.
|
1228
|
+
- `Glimmer::DataBinding::ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push`, `select!`, or `delete`)
|
1229
|
+
- `Glimmer::DataBinding::ObservableHash`: Mixin module for any observable hash that automatically handles notifying observers upon performing array mutation operation (e.g. `hash[key]=value`)
|
1230
|
+
- `Glimmer::DataBinding::ModelBinding`: a higher-level abstraction that relies on all the other observer/observable classes to support basic data-binding, nested data-binding, and computed data-binding
|
1231
|
+
- `Glimmer::DataBinding::Shine`: enables highly intuitive and visually expressive syntax to perform bidirectional (two-way) data-binding with `<=>` and unidirectional (one-way) data-binding with `<=`
|
944
1232
|
|
945
1233
|
You may learn more from [Data-Binding](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#data-binding) and [Observer](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#observer) usage in [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)
|
946
1234
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/glimmer.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer 2.
|
5
|
+
# stub: glimmer 2.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer".freeze
|
9
|
-
s.version = "2.
|
9
|
+
s.version = "2.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["AndyMaleh".freeze]
|
14
|
-
s.date = "2021-09-
|
14
|
+
s.date = "2021-09-28"
|
15
15
|
s.description = "Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of a DSL Engine and an Observable/Observer/Data-Binding Library. 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 = [
|
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/glimmer/data_binding/model_binding.rb",
|
33
33
|
"lib/glimmer/data_binding/observable.rb",
|
34
34
|
"lib/glimmer/data_binding/observable_array.rb",
|
35
|
+
"lib/glimmer/data_binding/observable_hash.rb",
|
35
36
|
"lib/glimmer/data_binding/observable_model.rb",
|
36
37
|
"lib/glimmer/data_binding/observer.rb",
|
37
38
|
"lib/glimmer/data_binding/shine.rb",
|
@@ -58,7 +59,7 @@ Gem::Specification.new do |s|
|
|
58
59
|
end
|
59
60
|
|
60
61
|
if s.respond_to? :add_runtime_dependency then
|
61
|
-
s.add_runtime_dependency(%q<array_include_methods>.freeze, ["
|
62
|
+
s.add_runtime_dependency(%q<array_include_methods>.freeze, ["~> 1.4.0"])
|
62
63
|
s.add_runtime_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
|
63
64
|
s.add_development_dependency(%q<rspec-mocks>.freeze, ["~> 3.5.0"])
|
64
65
|
s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
|
@@ -71,7 +72,7 @@ Gem::Specification.new do |s|
|
|
71
72
|
s.add_development_dependency(%q<simplecov-lcov>.freeze, ["~> 0.7.0"])
|
72
73
|
s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"])
|
73
74
|
else
|
74
|
-
s.add_dependency(%q<array_include_methods>.freeze, ["
|
75
|
+
s.add_dependency(%q<array_include_methods>.freeze, ["~> 1.4.0"])
|
75
76
|
s.add_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
|
76
77
|
s.add_dependency(%q<rspec-mocks>.freeze, ["~> 3.5.0"])
|
77
78
|
s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
|
@@ -198,7 +198,7 @@ module Glimmer
|
|
198
198
|
end
|
199
199
|
end
|
200
200
|
|
201
|
-
def call(value)
|
201
|
+
def call(value, *extra_args)
|
202
202
|
return if model.nil?
|
203
203
|
converted_value = value
|
204
204
|
invoke_property_writer(model, "#{property_name}=", converted_value) unless converted_value == evaluate_property
|
@@ -83,7 +83,7 @@ module Glimmer
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def has_observer_element_properties?(observer, element_properties)
|
86
|
-
element_properties_for(observer).to_a.include_all?(element_properties)
|
86
|
+
element_properties_for(observer).to_a.include_all?(*element_properties)
|
87
87
|
end
|
88
88
|
|
89
89
|
def property_observer_list
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'glimmer/data_binding/observable'
|
23
|
+
require 'glimmer/data_binding/observer'
|
24
|
+
|
25
|
+
module Glimmer
|
26
|
+
module DataBinding
|
27
|
+
module ObservableHash
|
28
|
+
include Observable
|
29
|
+
|
30
|
+
class Notifier
|
31
|
+
include Observer
|
32
|
+
|
33
|
+
def initialize(observable_model, key)
|
34
|
+
@observable_model = observable_model
|
35
|
+
@key = key
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(new_value=nil, *extra_args)
|
39
|
+
@observable_model.notify_observers(@key)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
OBSERVED_STORE_METHOD = lambda do |key, value|
|
44
|
+
if key_observer_list(key).empty?
|
45
|
+
if all_key_observer_list.empty?
|
46
|
+
self.send('__original__store', key, value)
|
47
|
+
else
|
48
|
+
old_value = self[key]
|
49
|
+
unregister_dependent_observers(nil, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
|
50
|
+
self.send('__original__store', key, value)
|
51
|
+
notify_observers(key)
|
52
|
+
ensure_array_object_observer(nil, value, old_value)
|
53
|
+
ensure_hash_object_observer(nil, value, old_value)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
old_value = self[key]
|
57
|
+
unregister_dependent_observers(key, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
|
58
|
+
self.send('__original__store', key, value)
|
59
|
+
notify_observers(key)
|
60
|
+
ensure_array_object_observer(key, value, old_value)
|
61
|
+
ensure_hash_object_observer(key, value, old_value)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_observer(observer, key = nil)
|
66
|
+
return observer if has_observer?(observer, key)
|
67
|
+
key_observer_list(key) << observer
|
68
|
+
add_key_writer_observer(key)
|
69
|
+
observer
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_observer(observer, key = nil)
|
73
|
+
if has_observer?(observer, key)
|
74
|
+
key_observer_list(key).delete(observer)
|
75
|
+
observer.unobserve(self, key)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_observers(key)
|
80
|
+
key_observer_hash[key].each do |observer|
|
81
|
+
remove_observer(observer, key)
|
82
|
+
end
|
83
|
+
key_observer_hash.delete(key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def remove_all_observers
|
87
|
+
all_observers = key_observer_hash.clone
|
88
|
+
key_observer_hash.keys.each do |key|
|
89
|
+
remove_observers(key)
|
90
|
+
end
|
91
|
+
key_observer_hash.clear
|
92
|
+
all_observers
|
93
|
+
end
|
94
|
+
|
95
|
+
def has_observer?(observer, key = nil)
|
96
|
+
key_observer_list(key).include?(observer)
|
97
|
+
end
|
98
|
+
|
99
|
+
def has_observer_for_any_key?(observer)
|
100
|
+
key_observer_hash.values.map(&:to_a).reduce(:+).include?(observer)
|
101
|
+
end
|
102
|
+
|
103
|
+
def key_observer_hash
|
104
|
+
@key_observers ||= Hash.new
|
105
|
+
end
|
106
|
+
|
107
|
+
def key_observer_list(key)
|
108
|
+
key_observer_hash[key] = Concurrent::Set.new unless key_observer_hash[key]
|
109
|
+
key_observer_hash[key]
|
110
|
+
end
|
111
|
+
|
112
|
+
def all_key_observer_list
|
113
|
+
key_observer_list(nil)
|
114
|
+
end
|
115
|
+
|
116
|
+
def notify_observers(key)
|
117
|
+
all_key_observer_list.to_a.each { |observer| observer.call(self[key], key) }
|
118
|
+
(key_observer_list(key).to_a - all_key_observer_list.to_a).each { |observer| observer.call(self[key]) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_key_writer_observer(key = nil)
|
122
|
+
ensure_array_object_observer(key, self[key])
|
123
|
+
ensure_hash_object_observer(key, self[key])
|
124
|
+
begin
|
125
|
+
method('__original__store')
|
126
|
+
rescue
|
127
|
+
define_singleton_method('__original__store', store_method)
|
128
|
+
define_singleton_method('[]=', &OBSERVED_STORE_METHOD)
|
129
|
+
end
|
130
|
+
rescue => e
|
131
|
+
#ignore writing if no key writer exists
|
132
|
+
Glimmer::Config.logger.debug {"No need to observe store method: '[]='\n#{e.message}\n#{e.backtrace.join("\n")}"}
|
133
|
+
end
|
134
|
+
|
135
|
+
def store_method
|
136
|
+
self.class.instance_method('[]=') rescue self.method('[]=')
|
137
|
+
end
|
138
|
+
|
139
|
+
def unregister_dependent_observers(key, old_value)
|
140
|
+
# TODO look into optimizing this
|
141
|
+
return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray) || old_value.is_a?(ObservableHash)
|
142
|
+
key_observer_list(key).each { |observer| observer.unregister_dependents_with_observable(observer.registration_for(self, key), old_value) }
|
143
|
+
end
|
144
|
+
alias deregister_dependent_observers unregister_dependent_observers
|
145
|
+
|
146
|
+
def ensure_array_object_observer(key, object, old_object = nil)
|
147
|
+
return unless object&.is_a?(Array)
|
148
|
+
array_object_observer = array_object_observer_for(key)
|
149
|
+
array_observer_registration = array_object_observer.observe(object)
|
150
|
+
key_observer_list(key).each do |observer|
|
151
|
+
my_registration = observer.registration_for(self, key) # TODO eliminate repetition
|
152
|
+
observer.add_dependent(my_registration => array_observer_registration)
|
153
|
+
end
|
154
|
+
array_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableArray)
|
155
|
+
end
|
156
|
+
|
157
|
+
def array_object_observer_for(key)
|
158
|
+
@array_object_observers ||= Concurrent::Hash.new
|
159
|
+
@array_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @array_object_observers.has_key?(key)
|
160
|
+
@array_object_observers[key]
|
161
|
+
end
|
162
|
+
|
163
|
+
def ensure_hash_object_observer(key, object, old_object = nil)
|
164
|
+
return unless object&.is_a?(Hash)
|
165
|
+
hash_object_observer = hash_object_observer_for(key)
|
166
|
+
hash_observer_registration = hash_object_observer.observe(object)
|
167
|
+
key_observer_list(key).each do |observer|
|
168
|
+
my_registration = observer.registration_for(self, key) # TODO eliminate repetition
|
169
|
+
observer.add_dependent(my_registration => hash_observer_registration)
|
170
|
+
end
|
171
|
+
hash_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableHash)
|
172
|
+
end
|
173
|
+
|
174
|
+
def hash_object_observer_for(key)
|
175
|
+
@hash_object_observers ||= Concurrent::Hash.new
|
176
|
+
@hash_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @hash_object_observers.has_key?(key)
|
177
|
+
@hash_object_observers[key]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -35,7 +35,7 @@ module Glimmer
|
|
35
35
|
@property_name = property_name
|
36
36
|
end
|
37
37
|
|
38
|
-
def call(new_value=nil)
|
38
|
+
def call(new_value=nil, *extra_args)
|
39
39
|
@observable_model.notify_observers(@property_name)
|
40
40
|
end
|
41
41
|
end
|
@@ -44,10 +44,11 @@ module Glimmer
|
|
44
44
|
property_writer_name = "#{property_name}="
|
45
45
|
lambda do |value|
|
46
46
|
old_value = self.send(property_name)
|
47
|
-
unregister_dependent_observers(property_name, old_value)
|
47
|
+
unregister_dependent_observers(property_name, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
|
48
48
|
self.send("__original__#{property_writer_name}", value)
|
49
49
|
notify_observers(property_name)
|
50
50
|
ensure_array_object_observer(property_name, value, old_value)
|
51
|
+
ensure_hash_object_observer(property_name, value, old_value)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
@@ -106,6 +107,7 @@ module Glimmer
|
|
106
107
|
property_writer_name = "#{property_name}="
|
107
108
|
method(property_writer_name)
|
108
109
|
ensure_array_object_observer(property_name, send(property_name))
|
110
|
+
ensure_hash_object_observer(property_name, send(property_name))
|
109
111
|
begin
|
110
112
|
method("__original__#{property_writer_name}")
|
111
113
|
rescue
|
@@ -144,6 +146,23 @@ module Glimmer
|
|
144
146
|
@array_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
|
145
147
|
@array_object_observers[property_name]
|
146
148
|
end
|
149
|
+
|
150
|
+
def ensure_hash_object_observer(property_name, object, old_object = nil)
|
151
|
+
return unless object&.is_a?(Hash)
|
152
|
+
hash_object_observer = hash_object_observer_for(property_name)
|
153
|
+
hash_observer_registration = hash_object_observer.observe(object)
|
154
|
+
property_observer_list(property_name).each do |observer|
|
155
|
+
my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
|
156
|
+
observer.add_dependent(my_registration => hash_observer_registration)
|
157
|
+
end
|
158
|
+
hash_object_observer_for(property_name).unregister(old_object) if old_object.is_a?(ObservableHash)
|
159
|
+
end
|
160
|
+
|
161
|
+
def hash_object_observer_for(property_name)
|
162
|
+
@hash_object_observers ||= Concurrent::Hash.new
|
163
|
+
@hash_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name) unless @hash_object_observers.has_key?(property_name)
|
164
|
+
@hash_object_observers[property_name]
|
165
|
+
end
|
147
166
|
end
|
148
167
|
end
|
149
168
|
end
|
@@ -41,8 +41,8 @@ module Glimmer
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Called by observables once updates occur sending in the new_value if any
|
44
|
-
def call(new_value=nil)
|
45
|
-
@observer_block.call(new_value)
|
44
|
+
def call(new_value=nil, *extra_args)
|
45
|
+
@observer_block.call(new_value, *extra_args)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -86,6 +86,8 @@ module Glimmer
|
|
86
86
|
# TODO refactor code to be more smart/polymorphic/automated and honor open/closed principle
|
87
87
|
if observable.is_a?(Array)
|
88
88
|
observable.extend(ObservableArray)
|
89
|
+
elsif observable.is_a?(Hash)
|
90
|
+
observable.extend(ObservableHash)
|
89
91
|
else
|
90
92
|
observable.extend(ObservableModel)
|
91
93
|
end
|
@@ -141,7 +143,7 @@ module Glimmer
|
|
141
143
|
dependents_for(registration).delete(dependent)
|
142
144
|
end
|
143
145
|
|
144
|
-
def call(new_value)
|
146
|
+
def call(new_value = nil, *extra_args)
|
145
147
|
raise Error, 'Not implemented!'
|
146
148
|
end
|
147
149
|
end
|
@@ -150,3 +152,4 @@ end
|
|
150
152
|
|
151
153
|
require 'glimmer/data_binding/observable_model'
|
152
154
|
require 'glimmer/data_binding/observable_array'
|
155
|
+
require 'glimmer/data_binding/observable_hash'
|
metadata
CHANGED
@@ -1,35 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: array_include_methods
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.0.4
|
20
|
-
- - "<"
|
17
|
+
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
19
|
+
version: 1.4.0
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 1.0.4
|
30
|
-
- - "<"
|
24
|
+
- - "~>"
|
31
25
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
26
|
+
version: 1.4.0
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: facets
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -234,6 +228,7 @@ files:
|
|
234
228
|
- lib/glimmer/data_binding/model_binding.rb
|
235
229
|
- lib/glimmer/data_binding/observable.rb
|
236
230
|
- lib/glimmer/data_binding/observable_array.rb
|
231
|
+
- lib/glimmer/data_binding/observable_hash.rb
|
237
232
|
- lib/glimmer/data_binding/observable_model.rb
|
238
233
|
- lib/glimmer/data_binding/observer.rb
|
239
234
|
- lib/glimmer/data_binding/shine.rb
|