glimmer 2.1.4 → 2.2.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 +22 -0
- data/README.md +142 -203
- data/VERSION +1 -1
- data/glimmer.gemspec +8 -7
- data/lib/glimmer/data_binding/model_binding.rb +1 -1
- data/lib/glimmer/data_binding/observable_array.rb +28 -2
- data/lib/glimmer/data_binding/observable_hash.rb +375 -0
- data/lib/glimmer/data_binding/observable_model.rb +21 -2
- data/lib/glimmer/data_binding/observer.rb +6 -3
- metadata +13 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 672161d98054249c5d807ee86ba9209a471fdc7bf1ce6a2a861f2b363493cb13
|
4
|
+
data.tar.gz: 1d811ea1139eda01c8593416669b1088624def2df0fbc3ee9dc9deb992ab8215
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38e7efff7b054c7eb1ebbdc1c32fb6f9df7b19522e17124f6e63076295a0e46e22c8f12b061cfba2a789326c9a2ef1d24921ae2fdb999ab4f6ad9dec22865ee3
|
7
|
+
data.tar.gz: d149a78a300cc1f572d61fa894bb265948ed11fa0ee5b8ba8fcba8f2e3af096dfa492cb7f6b3e51d1dc0f37585491846bdbd17abee335902bc9ab36c40543bb1
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,28 @@
|
|
3
3
|
Related Change Logs:
|
4
4
|
- [glimmer-dsl-swt/CHANGELOG.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/CHANGELOG.md)
|
5
5
|
|
6
|
+
### 2.2.2
|
7
|
+
|
8
|
+
- Support auto-notifying on `ObservableArray#replace` observed changes
|
9
|
+
- Support auto-notifying on `ObservableArray#filter!` observed changes explicitly (even if `filter!` aliases `select!`)
|
10
|
+
- Support auto-notifying on `ObservableHash#filter!` observed changes explicitly (even if `filter!` aliases `select!`)
|
11
|
+
|
12
|
+
### 2.2.1
|
13
|
+
|
14
|
+
- Have `ObservableHash` call `notify_observers(key)` upon performing all mutation operations (as expected): `delete`, `delete_if`, `filter!`, `select!`, `keep_if`, `reject!`, `merge!`, `replace`, `shift`, `transform_keys!`, `transform_values!`
|
15
|
+
- Fix issue with `Glimmer::DataBinding::Observer.proc {|new_value| ...}.observe(array)` resulting in new_value coming across as `nil when the array is updated
|
16
|
+
|
17
|
+
### 2.2.0
|
18
|
+
|
19
|
+
- New `Glimmer::DataBinding::ObservableHash` support for observing `hash[key]=value` mutations
|
20
|
+
- Ensure observed ObservableHash values are enhanced with ObservableHash if they were of type Hash
|
21
|
+
- Ensure observed ObservableModel values are enhanced with ObservableHash if they were of type Hash
|
22
|
+
|
23
|
+
### 2.1.5
|
24
|
+
|
25
|
+
- Upgrade array_include_methods gem to version 1.4.0 (and fix minor version)
|
26
|
+
- Update API usage of `Array#include_all?` in `Glimmer::DataBinding::ObservableArray` to splat array (now required)
|
27
|
+
|
6
28
|
### 2.1.4
|
7
29
|
|
8
30
|
- Upgrade array_include_methods gem to version 1.2.0
|
data/README.md
CHANGED
@@ -210,7 +210,7 @@ end
|
|
210
210
|
### Setup
|
211
211
|
|
212
212
|
Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL:
|
213
|
-
- Add `gem 'glimmer', '~> 2.
|
213
|
+
- Add `gem 'glimmer', '~> 2.2.2'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.2.2` and add `require 'glimmer'`
|
214
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)
|
215
215
|
- Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static
|
216
216
|
|
@@ -949,14 +949,6 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
|
|
949
949
|
|
950
950
|
###### Hello, World!
|
951
951
|
|
952
|
-
Mac
|
953
|
-
|
954
|
-
![glimmer-dsl-libui-mac-basic-window.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-window.png)
|
955
|
-
|
956
|
-
Linux
|
957
|
-
|
958
|
-
![glimmer-dsl-libui-linux-basic-window.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-window.png)
|
959
|
-
|
960
952
|
```ruby
|
961
953
|
require 'glimmer-dsl-libui'
|
962
954
|
|
@@ -965,27 +957,25 @@ include Glimmer
|
|
965
957
|
window('hello world').show
|
966
958
|
```
|
967
959
|
|
968
|
-
###### Basic Button
|
969
|
-
|
970
960
|
Mac
|
971
961
|
|
972
|
-
![glimmer-dsl-libui-mac-basic-
|
973
|
-
![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)
|
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)
|
974
963
|
|
975
964
|
Linux
|
976
965
|
|
977
|
-
![glimmer-dsl-libui-linux-basic-
|
978
|
-
|
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
|
979
969
|
|
980
970
|
```ruby
|
981
971
|
require 'glimmer-dsl-libui'
|
982
972
|
|
983
973
|
include Glimmer
|
984
974
|
|
985
|
-
window('hello world', 300, 200) {
|
975
|
+
window('hello world', 300, 200) {
|
986
976
|
button('Button') {
|
987
977
|
on_clicked do
|
988
|
-
msg_box(
|
978
|
+
msg_box('Information', 'You clicked the button')
|
989
979
|
end
|
990
980
|
}
|
991
981
|
|
@@ -995,223 +985,171 @@ window('hello world', 300, 200) { |w|
|
|
995
985
|
}.show
|
996
986
|
```
|
997
987
|
|
998
|
-
###### Control Gallery
|
999
|
-
|
1000
988
|
Mac
|
1001
989
|
|
1002
|
-
![glimmer-dsl-libui-mac-
|
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)
|
1003
992
|
|
1004
993
|
Linux
|
1005
994
|
|
1006
|
-
![glimmer-dsl-libui-linux-
|
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
|
+
###### Basic Table Progress Bar
|
1007
999
|
|
1008
1000
|
```ruby
|
1009
1001
|
require 'glimmer-dsl-libui'
|
1010
1002
|
|
1011
1003
|
include Glimmer
|
1012
1004
|
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1005
|
+
data = [
|
1006
|
+
['task 1', 0],
|
1007
|
+
['task 2', 15],
|
1008
|
+
['task 3', 100],
|
1009
|
+
['task 4', 75],
|
1010
|
+
['task 5', -1],
|
1011
|
+
]
|
1020
1012
|
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
}
|
1027
|
-
|
1028
|
-
quit_menu_item {
|
1029
|
-
on_clicked do
|
1030
|
-
puts 'Bye Bye'
|
1031
|
-
end
|
1032
|
-
}
|
1033
|
-
|
1034
|
-
preferences_menu_item # Can optionally contain an on_clicked listener
|
1035
|
-
}
|
1013
|
+
window('Task Progress', 300, 200) {
|
1014
|
+
vertical_box {
|
1015
|
+
table {
|
1016
|
+
text_column('Task')
|
1017
|
+
progress_bar_column('Progress')
|
1036
1018
|
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1019
|
+
cell_rows data # implicit data-binding
|
1020
|
+
}
|
1021
|
+
|
1022
|
+
button('Mark All As Done') {
|
1023
|
+
stretchy false
|
1024
|
+
|
1025
|
+
on_clicked do
|
1026
|
+
data.each_with_index do |row_data, row|
|
1027
|
+
data[row] = [row_data[0], 100] # automatically updates table due to implicit data-binding
|
1028
|
+
end
|
1029
|
+
end
|
1030
|
+
}
|
1042
1031
|
}
|
1043
|
-
}
|
1044
|
-
|
1045
|
-
menu('Help') {
|
1046
|
-
menu_item('Help')
|
1047
|
-
|
1048
|
-
about_menu_item # Can optionally contain an on_clicked listener
|
1049
|
-
}
|
1050
|
-
|
1051
|
-
MAIN_WINDOW = window('Control Gallery', 600, 500) {
|
1052
|
-
margined true
|
1053
|
-
|
1054
|
-
on_closing do
|
1055
|
-
puts 'Bye Bye'
|
1056
|
-
end
|
1057
|
-
|
1058
|
-
vertical_box {
|
1059
|
-
horizontal_box {
|
1060
|
-
group('Basic Controls') {
|
1061
|
-
vertical_box {
|
1062
|
-
button('Button') {
|
1063
|
-
stretchy false
|
1064
|
-
|
1065
|
-
on_clicked do
|
1066
|
-
msg_box(MAIN_WINDOW, 'Information', 'You clicked the button')
|
1067
|
-
end
|
1068
|
-
}
|
1032
|
+
}.show
|
1033
|
+
```
|
1069
1034
|
|
1070
|
-
|
1071
|
-
stretchy false
|
1035
|
+
Mac
|
1072
1036
|
|
1073
|
-
|
1074
|
-
checked = c.checked == 1
|
1075
|
-
MAIN_WINDOW.title = "Checkbox is #{checked}"
|
1076
|
-
c.text = "I am the checkbox (#{checked})"
|
1077
|
-
end
|
1078
|
-
}
|
1037
|
+
![glimmer-dsl-libui-mac-basic-table-progress-bar.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-table-progress-bar.png)
|
1079
1038
|
|
1080
|
-
|
1039
|
+
Linux
|
1081
1040
|
|
1082
|
-
|
1041
|
+
![glimmer-dsl-libui-linux-basic-table-progress-bar.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-table-progress-bar.png)
|
1083
1042
|
|
1084
|
-
|
1043
|
+
###### Area Gallery
|
1085
1044
|
|
1086
|
-
|
1045
|
+
```ruby
|
1046
|
+
require 'glimmer-dsl-libui'
|
1087
1047
|
|
1088
|
-
|
1048
|
+
include Glimmer
|
1089
1049
|
|
1090
|
-
|
1050
|
+
window('Area Gallery', 400, 400) {
|
1051
|
+
area {
|
1052
|
+
path { # declarative stable path
|
1053
|
+
square(0, 0, 100)
|
1054
|
+
square(100, 100, 400)
|
1055
|
+
|
1056
|
+
fill r: 102, g: 102, b: 204
|
1057
|
+
}
|
1058
|
+
path { # declarative stable path
|
1059
|
+
rectangle(0, 100, 100, 400)
|
1060
|
+
rectangle(100, 0, 400, 100)
|
1061
|
+
|
1062
|
+
fill r: 204, g: 102, b: 204
|
1063
|
+
}
|
1064
|
+
path { # declarative stable path
|
1065
|
+
figure(100, 100) {
|
1066
|
+
line(100, 400)
|
1067
|
+
line(400, 100)
|
1068
|
+
line(400, 400)
|
1091
1069
|
|
1092
|
-
|
1093
|
-
}
|
1070
|
+
closed true
|
1094
1071
|
}
|
1095
1072
|
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
on_changed do |s|
|
1106
|
-
puts "New Spinbox value: #{s.value}"
|
1107
|
-
end
|
1108
|
-
}
|
1109
|
-
|
1110
|
-
slider(0, 100) {
|
1111
|
-
stretchy false
|
1112
|
-
|
1113
|
-
on_changed do |s|
|
1114
|
-
v = s.value
|
1115
|
-
puts "New Slider value: #{v}"
|
1116
|
-
@progress_bar.value = v
|
1117
|
-
end
|
1118
|
-
}
|
1119
|
-
|
1120
|
-
@progress_bar = progress_bar { stretchy false }
|
1121
|
-
}
|
1122
|
-
}
|
1123
|
-
|
1124
|
-
group('Lists') {
|
1125
|
-
stretchy false
|
1126
|
-
|
1127
|
-
vertical_box {
|
1128
|
-
combobox {
|
1129
|
-
stretchy false
|
1130
|
-
items 'combobox Item 1', 'combobox Item 2', 'combobox Item 3' # also accepts a single array argument
|
1131
|
-
|
1132
|
-
on_selected do |c|
|
1133
|
-
puts "New combobox selection: #{c.selected}"
|
1134
|
-
end
|
1135
|
-
}
|
1136
|
-
|
1137
|
-
editable_combobox {
|
1138
|
-
stretchy false
|
1139
|
-
items 'Editable Item 1', 'Editable Item 2', 'Editable Item 3' # also accepts a single array argument
|
1140
|
-
}
|
1073
|
+
fill r: 202, g: 102, b: 104, a: 0.5
|
1074
|
+
stroke r: 0, g: 0, b: 0
|
1075
|
+
}
|
1076
|
+
path { # declarative stable path
|
1077
|
+
figure(0, 0) {
|
1078
|
+
bezier(200, 100, 100, 200, 400, 100)
|
1079
|
+
bezier(300, 100, 100, 300, 100, 400)
|
1080
|
+
bezier(100, 300, 300, 100, 400, 400)
|
1141
1081
|
|
1142
|
-
|
1143
|
-
|
1144
|
-
}
|
1145
|
-
}
|
1146
|
-
}
|
1082
|
+
closed true
|
1083
|
+
}
|
1147
1084
|
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1085
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
1086
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
1087
|
+
}
|
1088
|
+
path { # declarative stable path
|
1089
|
+
arc(200, 200, 90, 0, 360, false)
|
1153
1090
|
|
1154
|
-
|
1155
|
-
|
1156
|
-
end
|
1157
|
-
}
|
1158
|
-
}
|
1159
|
-
}
|
1160
|
-
|
1161
|
-
tab_item('Page 2') {
|
1162
|
-
horizontal_box
|
1163
|
-
}
|
1164
|
-
|
1165
|
-
tab_item('Page 3') {
|
1166
|
-
horizontal_box
|
1167
|
-
}
|
1168
|
-
}
|
1169
|
-
}
|
1091
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
1092
|
+
stroke r: 0, g: 0, b: 0, thickness: 2
|
1170
1093
|
}
|
1094
|
+
|
1095
|
+
on_mouse_event do |area_mouse_event|
|
1096
|
+
p area_mouse_event
|
1097
|
+
end
|
1098
|
+
|
1099
|
+
on_mouse_moved do |area_mouse_event|
|
1100
|
+
puts 'moved'
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
on_mouse_down do |area_mouse_event|
|
1104
|
+
puts 'mouse down'
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
on_mouse_up do |area_mouse_event|
|
1108
|
+
puts 'mouse up'
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
on_mouse_drag_started do |area_mouse_event|
|
1112
|
+
puts 'drag started'
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
on_mouse_dragged do |area_mouse_event|
|
1116
|
+
puts 'dragged'
|
1117
|
+
end
|
1118
|
+
|
1119
|
+
on_mouse_dropped do |area_mouse_event|
|
1120
|
+
puts 'dropped'
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
on_mouse_entered do
|
1124
|
+
puts 'entered'
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
on_mouse_exited do
|
1128
|
+
puts 'exited'
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
on_key_event do |area_key_event|
|
1132
|
+
p area_key_event
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
on_key_up do |area_key_event|
|
1136
|
+
puts 'key up'
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
on_key_down do |area_key_event|
|
1140
|
+
puts 'key down'
|
1141
|
+
end
|
1171
1142
|
}
|
1172
|
-
}
|
1173
|
-
|
1174
|
-
MAIN_WINDOW.show
|
1143
|
+
}.show
|
1175
1144
|
```
|
1176
1145
|
|
1177
|
-
###### Basic Table
|
1178
|
-
|
1179
1146
|
Mac
|
1180
1147
|
|
1181
|
-
![glimmer-dsl-libui-mac-
|
1148
|
+
![glimmer-dsl-libui-mac-area-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-area-gallery.png)
|
1182
1149
|
|
1183
1150
|
Linux
|
1184
1151
|
|
1185
|
-
![glimmer-dsl-libui-linux-
|
1186
|
-
|
1187
|
-
```ruby
|
1188
|
-
require 'glimmer-dsl-libui'
|
1189
|
-
|
1190
|
-
include Glimmer
|
1191
|
-
|
1192
|
-
data = [
|
1193
|
-
%w[cat meow],
|
1194
|
-
%w[dog woof],
|
1195
|
-
%w[chicken cock-a-doodle-doo],
|
1196
|
-
%w[hourse neigh],
|
1197
|
-
%w[cow moo]
|
1198
|
-
]
|
1199
|
-
|
1200
|
-
window('Animal sounds', 300, 200) {
|
1201
|
-
horizontal_box {
|
1202
|
-
table {
|
1203
|
-
text_column('Animal')
|
1204
|
-
text_column('Description')
|
1205
|
-
|
1206
|
-
cell_rows data
|
1207
|
-
}
|
1208
|
-
}
|
1209
|
-
|
1210
|
-
on_closing do
|
1211
|
-
puts 'Bye Bye'
|
1212
|
-
end
|
1213
|
-
}.show
|
1214
|
-
```
|
1152
|
+
![glimmer-dsl-libui-linux-area-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-area-gallery.png)
|
1215
1153
|
|
1216
1154
|
## Data-Binding Library
|
1217
1155
|
|
@@ -1222,12 +1160,13 @@ Glimmer enhances observed models automatically (including array operations like
|
|
1222
1160
|
This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
|
1223
1161
|
|
1224
1162
|
These are the main classes concerning data-binding:
|
1225
|
-
- `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
|
-
- `Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
1227
|
-
- `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
|
-
- `ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push` or `delete`)
|
1229
|
-
- `
|
1230
|
-
- `
|
1163
|
+
- `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`)
|
1164
|
+
- `Glimmer::DataBinding::Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
1165
|
+
- `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.
|
1166
|
+
- `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`)
|
1167
|
+
- `Glimmer::DataBinding::ObservableHash`: Mixin module for any observable hash that automatically handles notifying observers upon performing hash mutation operations (e.g. `hash[key]=value`, `select!`, `merge!`)
|
1168
|
+
- `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
|
1169
|
+
- `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 `<=`
|
1231
1170
|
|
1232
1171
|
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)
|
1233
1172
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.2
|
data/glimmer.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
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.2 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.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-
|
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
|
14
|
+
s.date = "2021-10-03"
|
15
|
+
s.description = "Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of a DSL Engine and an Observable / Observer / Data-Binding Library (including ObservableModel, ObservableArray, and ObservableHash). 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",
|
@@ -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",
|
@@ -50,7 +51,7 @@ Gem::Specification.new do |s|
|
|
50
51
|
]
|
51
52
|
s.homepage = "http://github.com/AndyObtiva/glimmer".freeze
|
52
53
|
s.licenses = ["MIT".freeze]
|
53
|
-
s.rubygems_version = "3.2.
|
54
|
+
s.rubygems_version = "3.2.28".freeze
|
54
55
|
s.summary = "Glimmer - DSL Engine for Ruby GUI and More".freeze
|
55
56
|
|
56
57
|
if s.respond_to? :specification_version then
|
@@ -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
|
@@ -99,7 +99,7 @@ module Glimmer
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def notify_observers
|
102
|
-
property_observer_list.to_a.each(
|
102
|
+
property_observer_list.to_a.each { |o| o.call(self) }
|
103
103
|
end
|
104
104
|
|
105
105
|
def <<(element)
|
@@ -237,6 +237,21 @@ module Glimmer
|
|
237
237
|
end
|
238
238
|
end
|
239
239
|
|
240
|
+
def filter!(&block)
|
241
|
+
if block_given?
|
242
|
+
old_array = Array.new(self)
|
243
|
+
super(&block).tap do
|
244
|
+
(old_array - self).each do |old_value|
|
245
|
+
unregister_dependent_observers(old_value)
|
246
|
+
remove_element_observers(old_value)
|
247
|
+
end
|
248
|
+
notify_observers
|
249
|
+
end
|
250
|
+
else
|
251
|
+
super
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
240
255
|
def shuffle!(hash = nil)
|
241
256
|
(hash.nil? ? super() : super(random: hash[:random])).tap { notify_observers }
|
242
257
|
end
|
@@ -294,6 +309,17 @@ module Glimmer
|
|
294
309
|
end
|
295
310
|
end
|
296
311
|
|
312
|
+
def replace(other_array)
|
313
|
+
old_array = Array.new(self)
|
314
|
+
super(other_array).tap do
|
315
|
+
(old_array - self).each do |old_value|
|
316
|
+
unregister_dependent_observers(old_value)
|
317
|
+
remove_element_observers(old_value)
|
318
|
+
end
|
319
|
+
notify_observers
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
297
323
|
def unregister_dependent_observers(old_value)
|
298
324
|
return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
|
299
325
|
property_observer_list.each { |observer| observer.unregister_dependents_with_observable(observer.registration_for(self), old_value) }
|
@@ -0,0 +1,375 @@
|
|
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], 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
|
+
|
180
|
+
def delete(key, &block)
|
181
|
+
old_value = self[key]
|
182
|
+
unless old_value.nil?
|
183
|
+
unregister_dependent_observers(key, old_value)
|
184
|
+
unregister_dependent_observers(nil, old_value)
|
185
|
+
end
|
186
|
+
super(key, &block).tap do
|
187
|
+
notify_observers(key) unless old_value.nil?
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def delete_if(&block)
|
192
|
+
if block_given?
|
193
|
+
old_hash = self.dup
|
194
|
+
super(&block).tap do |new_hash|
|
195
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
196
|
+
deleted_keys.each do |deleted_key|
|
197
|
+
deleted_value = old_hash[deleted_key]
|
198
|
+
unless deleted_value.nil?
|
199
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
200
|
+
unregister_dependent_observers(nil, deleted_value)
|
201
|
+
notify_observers(deleted_key)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
else
|
206
|
+
super
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def select!(&block)
|
211
|
+
if block_given?
|
212
|
+
old_hash = self.dup
|
213
|
+
super(&block).tap do |new_hash|
|
214
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
215
|
+
deleted_keys.each do |deleted_key|
|
216
|
+
deleted_value = old_hash[deleted_key]
|
217
|
+
unless deleted_value.nil?
|
218
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
219
|
+
unregister_dependent_observers(nil, deleted_value)
|
220
|
+
notify_observers(deleted_key)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
else
|
225
|
+
super
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def filter!(&block)
|
230
|
+
if block_given?
|
231
|
+
old_hash = self.dup
|
232
|
+
super(&block).tap do |new_hash|
|
233
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
234
|
+
deleted_keys.each do |deleted_key|
|
235
|
+
deleted_value = old_hash[deleted_key]
|
236
|
+
unless deleted_value.nil?
|
237
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
238
|
+
unregister_dependent_observers(nil, deleted_value)
|
239
|
+
notify_observers(deleted_key)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
else
|
244
|
+
super
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def keep_if(&block)
|
249
|
+
if block_given?
|
250
|
+
old_hash = self.dup
|
251
|
+
super(&block).tap do |new_hash|
|
252
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
253
|
+
deleted_keys.each do |deleted_key|
|
254
|
+
deleted_value = old_hash[deleted_key]
|
255
|
+
unless deleted_value.nil?
|
256
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
257
|
+
unregister_dependent_observers(nil, deleted_value)
|
258
|
+
notify_observers(deleted_key)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
else
|
263
|
+
super
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def reject!(&block)
|
268
|
+
if block_given?
|
269
|
+
old_hash = self.dup
|
270
|
+
super(&block).tap do |new_hash|
|
271
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
272
|
+
deleted_keys.each do |deleted_key|
|
273
|
+
deleted_value = old_hash[deleted_key]
|
274
|
+
unless deleted_value.nil?
|
275
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
276
|
+
unregister_dependent_observers(nil, deleted_value)
|
277
|
+
notify_observers(deleted_key)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
else
|
282
|
+
super
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def shift
|
287
|
+
old_hash = self.dup
|
288
|
+
super.tap do
|
289
|
+
new_hash = self
|
290
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
291
|
+
deleted_keys.each do |deleted_key|
|
292
|
+
deleted_value = old_hash[deleted_key]
|
293
|
+
unless deleted_value.nil?
|
294
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
295
|
+
unregister_dependent_observers(nil, deleted_value)
|
296
|
+
notify_observers(deleted_key)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def merge!(*other_hashes, &block)
|
303
|
+
if other_hashes.empty?
|
304
|
+
super
|
305
|
+
else
|
306
|
+
old_hash = self.dup
|
307
|
+
super(*other_hashes, &block).tap do |new_hash|
|
308
|
+
changed_keys = other_hashes.map(&:keys).reduce(:+)
|
309
|
+
changed_keys.each do |changed_key|
|
310
|
+
old_value = old_hash[changed_key]
|
311
|
+
if new_hash[changed_key] != old_value
|
312
|
+
unregister_dependent_observers(changed_key, old_value)
|
313
|
+
unregister_dependent_observers(nil, old_value)
|
314
|
+
notify_observers(changed_key)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def replace(other_hash)
|
322
|
+
old_hash = self.dup
|
323
|
+
super(other_hash).tap do |new_hash|
|
324
|
+
changed_keys = old_hash.keys + new_hash.keys
|
325
|
+
changed_keys.each do |changed_key|
|
326
|
+
old_value = old_hash[changed_key]
|
327
|
+
if new_hash[changed_key] != old_value
|
328
|
+
unregister_dependent_observers(changed_key, old_value)
|
329
|
+
unregister_dependent_observers(nil, old_value)
|
330
|
+
notify_observers(changed_key)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def transform_keys!(hash2 = nil, &block)
|
337
|
+
if hash2.nil? && block.nil?
|
338
|
+
super
|
339
|
+
else
|
340
|
+
old_hash = self.dup
|
341
|
+
result = hash2.nil? ? super(&block) : super(hash2, &block)
|
342
|
+
result.tap do |new_hash|
|
343
|
+
changed_keys = old_hash.keys + new_hash.keys
|
344
|
+
changed_keys.each do |changed_key|
|
345
|
+
old_value = old_hash[changed_key]
|
346
|
+
if new_hash[changed_key] != old_value
|
347
|
+
unregister_dependent_observers(changed_key, old_value)
|
348
|
+
unregister_dependent_observers(nil, old_value)
|
349
|
+
notify_observers(changed_key)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def transform_values!(&block)
|
357
|
+
if block_given?
|
358
|
+
old_hash = self.dup
|
359
|
+
super(&block).tap do |new_hash|
|
360
|
+
new_hash.keys.each do |changed_key|
|
361
|
+
old_value = old_hash[changed_key]
|
362
|
+
if new_hash[changed_key] != old_value
|
363
|
+
unregister_dependent_observers(changed_key, old_value)
|
364
|
+
unregister_dependent_observers(nil, old_value)
|
365
|
+
notify_observers(changed_key)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
else
|
370
|
+
super
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
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.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-
|
11
|
+
date: 2021-10-03 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.2.0
|
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.2.0
|
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
|
@@ -209,11 +203,11 @@ dependencies:
|
|
209
203
|
- !ruby/object:Gem::Version
|
210
204
|
version: '0'
|
211
205
|
description: Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of
|
212
|
-
a DSL Engine and an Observable/Observer/Data-Binding Library
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
DSL for CSS.
|
206
|
+
a DSL Engine and an Observable / Observer / Data-Binding Library (including ObservableModel,
|
207
|
+
ObservableArray, and ObservableHash). Used in the Glimmer DSL for SWT (JRuby Desktop
|
208
|
+
Development GUI Framework), the Glimmer DSL for Tk (Ruby Desktop Development GUI
|
209
|
+
Library), the Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop
|
210
|
+
Apps), the Glimmer DSL for XML (& HTML), and the Glimmer DSL for CSS.
|
217
211
|
email: andy.am@gmail.com
|
218
212
|
executables: []
|
219
213
|
extensions: []
|
@@ -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
|
@@ -268,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
268
263
|
- !ruby/object:Gem::Version
|
269
264
|
version: '0'
|
270
265
|
requirements: []
|
271
|
-
rubygems_version: 3.2.
|
266
|
+
rubygems_version: 3.2.28
|
272
267
|
signing_key:
|
273
268
|
specification_version: 4
|
274
269
|
summary: Glimmer - DSL Engine for Ruby GUI and More
|