glimmer 2.2.0 → 2.4.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 +22 -0
- data/README.md +132 -194
- data/VERSION +1 -1
- data/glimmer.gemspec +4 -4
- data/lib/glimmer/data_binding/model_binding.rb +12 -7
- data/lib/glimmer/data_binding/observable_array.rb +78 -8
- data/lib/glimmer/data_binding/observable_hash.rb +212 -11
- data/lib/glimmer/data_binding/observable_model.rb +14 -12
- data/lib/glimmer/data_binding/observer.rb +39 -26
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6a8921b091e770dd1acb0528bdf8dad0a63e79290ffb457325abf261b4ac8ca
|
4
|
+
data.tar.gz: 4d530d648577640a25943fb55f61918d352f30e2007f7426b739b877cd6a44a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68f5e3c05e2381a6f4855cec0832cc983fc74d0dcf27649efaf9e92dd8cf7aa69f9e171494ee1ab17a6a8d68ce0d71cb9ef61521290d999eaa70ad3915b989f2
|
7
|
+
data.tar.gz: 6b3af9b3d411bed16cc9c9f2f227394675fd255098d64729424e31dfdbf1b7e75d8e3aac102016f31c8b729978e33b44fbca1b0dbecec1d1acffea3651dbc468
|
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.4.0
|
7
|
+
|
8
|
+
- Support passing arbitrary options to `Observer` `#observe` (`#register`) method (not just properties, like `recursive: true` for example)
|
9
|
+
- Make observing nested arrays within an array optional with `recursive: true` option
|
10
|
+
- Make `Observer` `dependents` collection rely on Concurrent::Hash when available
|
11
|
+
- Fix `Observer` cleanup of registrations and dependents on `#deregister` (was not cleaning up everything because observables were changing `#hash` value on content change and slipping through the cracks as a result)
|
12
|
+
|
13
|
+
### 2.3.0
|
14
|
+
|
15
|
+
- Have observing `ObservableArray` automatically notice if any values are nested arrays and observe them for micro-changes (recursively all the way down)
|
16
|
+
|
17
|
+
### 2.2.2
|
18
|
+
|
19
|
+
- Support auto-notifying on `ObservableArray#replace` observed changes
|
20
|
+
- Support auto-notifying on `ObservableArray#filter!` observed changes explicitly (even if `filter!` aliases `select!`)
|
21
|
+
- Support auto-notifying on `ObservableHash#filter!` observed changes explicitly (even if `filter!` aliases `select!`)
|
22
|
+
|
23
|
+
### 2.2.1
|
24
|
+
|
25
|
+
- 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!`
|
26
|
+
- Fix issue with `Glimmer::DataBinding::Observer.proc {|new_value| ...}.observe(array)` resulting in new_value coming across as `nil when the array is updated
|
27
|
+
|
6
28
|
### 2.2.0
|
7
29
|
|
8
30
|
- New `Glimmer::DataBinding::ObservableHash` support for observing `hash[key]=value` mutations
|
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.4.0'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.4.0` 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
|
|
@@ -856,7 +856,7 @@ Glimmer app:
|
|
856
856
|
|
857
857
|
![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)
|
858
858
|
|
859
|
-
###### Hello,
|
859
|
+
###### Hello, Notebook!
|
860
860
|
|
861
861
|
Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
|
862
862
|
|
@@ -864,7 +864,7 @@ Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/gl
|
|
864
864
|
include Glimmer
|
865
865
|
|
866
866
|
root {
|
867
|
-
title 'Hello,
|
867
|
+
title 'Hello, Notebook!'
|
868
868
|
|
869
869
|
notebook {
|
870
870
|
frame(text: 'English') {
|
@@ -885,13 +885,13 @@ root {
|
|
885
885
|
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
886
886
|
|
887
887
|
```
|
888
|
-
ruby -r glimmer-dsl-tk -e "require '../samples/hello/
|
888
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_notebook.rb'"
|
889
889
|
```
|
890
890
|
|
891
891
|
Glimmer app:
|
892
892
|
|
893
|
-
![glimmer dsl tk screenshot sample hello
|
894
|
-
![glimmer dsl tk screenshot sample hello
|
893
|
+
![glimmer dsl tk screenshot sample hello notebook English](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-notebook-english.png)
|
894
|
+
![glimmer dsl tk screenshot sample hello notebook French](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-notebook-french.png)
|
895
895
|
|
896
896
|
###### Hello, Combo!
|
897
897
|
|
@@ -900,7 +900,7 @@ Glimmer code (from [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/
|
|
900
900
|
```ruby
|
901
901
|
# ... more code precedes
|
902
902
|
root {
|
903
|
-
title 'Hello,
|
903
|
+
title 'Hello, Combobox!'
|
904
904
|
|
905
905
|
combobox { |proxy|
|
906
906
|
state 'readonly'
|
@@ -920,13 +920,13 @@ root {
|
|
920
920
|
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
921
921
|
|
922
922
|
```
|
923
|
-
ruby -r glimmer-dsl-tk -e "require '../samples/hello/
|
923
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combobox.rb'"
|
924
924
|
```
|
925
925
|
|
926
926
|
Glimmer app:
|
927
927
|
|
928
|
-
![glimmer dsl tk screenshot sample hello combo](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-
|
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-
|
928
|
+
![glimmer dsl tk screenshot sample hello combo](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combobox.png)
|
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-combobox-dropdown.png)
|
930
930
|
|
931
931
|
#### Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
|
932
932
|
|
@@ -972,10 +972,10 @@ require 'glimmer-dsl-libui'
|
|
972
972
|
|
973
973
|
include Glimmer
|
974
974
|
|
975
|
-
window('hello world', 300, 200) {
|
975
|
+
window('hello world', 300, 200) {
|
976
976
|
button('Button') {
|
977
977
|
on_clicked do
|
978
|
-
msg_box(
|
978
|
+
msg_box('Information', 'You clicked the button')
|
979
979
|
end
|
980
980
|
}
|
981
981
|
|
@@ -995,223 +995,161 @@ Linux
|
|
995
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
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
997
|
|
998
|
-
######
|
998
|
+
###### Basic Table Progress Bar
|
999
999
|
|
1000
1000
|
```ruby
|
1001
1001
|
require 'glimmer-dsl-libui'
|
1002
1002
|
|
1003
1003
|
include Glimmer
|
1004
1004
|
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
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
|
-
}
|
1005
|
+
data = [
|
1006
|
+
['task 1', 0],
|
1007
|
+
['task 2', 15],
|
1008
|
+
['task 3', 100],
|
1009
|
+
['task 4', 75],
|
1010
|
+
['task 5', -1],
|
1011
|
+
]
|
1042
1012
|
|
1043
|
-
|
1044
|
-
margined true
|
1045
|
-
|
1046
|
-
on_closing do
|
1047
|
-
puts 'Bye Bye'
|
1048
|
-
end
|
1049
|
-
|
1013
|
+
window('Task Progress', 300, 200) {
|
1050
1014
|
vertical_box {
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
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'
|
1015
|
+
table {
|
1016
|
+
text_column('Task')
|
1017
|
+
progress_bar_column('Progress')
|
1145
1018
|
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
tab_item('Page 3') {
|
1158
|
-
horizontal_box
|
1159
|
-
}
|
1160
|
-
}
|
1161
|
-
}
|
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
|
1162
1030
|
}
|
1163
1031
|
}
|
1164
|
-
}
|
1165
|
-
|
1166
|
-
MAIN_WINDOW.show
|
1032
|
+
}.show
|
1167
1033
|
```
|
1168
1034
|
|
1169
1035
|
Mac
|
1170
1036
|
|
1171
|
-
![glimmer-dsl-libui-mac-
|
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)
|
1172
1038
|
|
1173
1039
|
Linux
|
1174
1040
|
|
1175
|
-
![glimmer-dsl-libui-linux-
|
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)
|
1176
1042
|
|
1177
|
-
######
|
1043
|
+
###### Area Gallery
|
1178
1044
|
|
1179
1045
|
```ruby
|
1180
1046
|
require 'glimmer-dsl-libui'
|
1181
1047
|
|
1182
1048
|
include Glimmer
|
1183
1049
|
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
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)
|
1191
1069
|
|
1192
|
-
|
1193
|
-
|
1194
|
-
table {
|
1195
|
-
text_column('Animal')
|
1196
|
-
text_column('Description')
|
1070
|
+
closed true
|
1071
|
+
}
|
1197
1072
|
|
1198
|
-
|
1073
|
+
fill r: 202, g: 102, b: 104, a: 0.5
|
1074
|
+
stroke r: 0, g: 0, b: 0
|
1199
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)
|
1081
|
+
|
1082
|
+
closed true
|
1083
|
+
}
|
1084
|
+
|
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)
|
1090
|
+
|
1091
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
1092
|
+
stroke r: 0, g: 0, b: 0, thickness: 2
|
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
|
1200
1142
|
}
|
1201
|
-
|
1202
|
-
on_closing do
|
1203
|
-
puts 'Bye Bye'
|
1204
|
-
end
|
1205
1143
|
}.show
|
1206
1144
|
```
|
1207
1145
|
|
1208
1146
|
Mac
|
1209
1147
|
|
1210
|
-
![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)
|
1211
1149
|
|
1212
1150
|
Linux
|
1213
1151
|
|
1214
|
-
![glimmer-dsl-libui-linux-
|
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
|
|
@@ -1225,8 +1163,8 @@ These are the main classes concerning data-binding:
|
|
1225
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`)
|
1226
1164
|
- `Glimmer::DataBinding::Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
1227
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.
|
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
|
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`) recursively (meaning if an array contained arrays and they changed, observers are notified). Accepts `recursive: true` option in `add_observer` method to recursively observe nested arrays all the way down.
|
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!`)
|
1230
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
|
1231
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 `<=`
|
1232
1170
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.0
|
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.4.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.4.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-
|
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
|
14
|
+
s.date = "2021-10-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 (including Observable Model, Observable Array, and Observable Hash). Used in Glimmer DSL for SWT (JRuby Desktop Development GUI Framework), Glimmer DSL for Tk (Ruby Desktop Development GUI Library), Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library), Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps), Glimmer DSL for XML (& HTML), and Glimmer DSL for CSS.".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"CHANGELOG.md",
|
@@ -101,6 +101,10 @@ module Glimmer
|
|
101
101
|
def computed_by
|
102
102
|
Concurrent::Array.new([@binding_options[:computed_by]].flatten.compact)
|
103
103
|
end
|
104
|
+
|
105
|
+
def observation_options
|
106
|
+
@binding_options.slice(:recursive)
|
107
|
+
end
|
104
108
|
|
105
109
|
def nested_property_observers_for(observer)
|
106
110
|
@nested_property_observers_collection ||= Concurrent::Hash.new
|
@@ -121,7 +125,7 @@ module Glimmer
|
|
121
125
|
@nested_property_observers_collection[observer]
|
122
126
|
end
|
123
127
|
|
124
|
-
def add_observer(observer)
|
128
|
+
def add_observer(observer, extra_options = {})
|
125
129
|
if computed?
|
126
130
|
add_computed_observers(observer)
|
127
131
|
elsif nested_property?
|
@@ -133,19 +137,20 @@ module Glimmer
|
|
133
137
|
apply_processor(@binding_options[:after_read], converted_value)
|
134
138
|
end
|
135
139
|
end
|
136
|
-
observer_registration = model_binding_observer.observe(model, property_name)
|
140
|
+
observer_registration = model_binding_observer.observe(model, property_name, observation_options)
|
137
141
|
my_registration = observer.registration_for(self)
|
138
142
|
observer.add_dependent(my_registration => observer_registration)
|
139
143
|
end
|
140
144
|
end
|
141
145
|
|
142
|
-
def remove_observer(observer)
|
146
|
+
def remove_observer(observer, extra_options = {})
|
143
147
|
if computed?
|
144
148
|
@computed_model_bindings.each do |computed_model_binding|
|
145
149
|
computed_observer_for(observer).unobserve(computed_model_binding)
|
146
150
|
end
|
147
151
|
@computed_observer_collection.delete(observer)
|
148
152
|
elsif nested_property?
|
153
|
+
# No need to call remove_nested_observers(observer) (cleanup happens automatically indirectly when invoked through observer.unobserve(model_binding))
|
149
154
|
nested_property_observers_for(observer).clear
|
150
155
|
else
|
151
156
|
observer.unobserve(model, property_name)
|
@@ -167,7 +172,7 @@ module Glimmer
|
|
167
172
|
|
168
173
|
def add_computed_observers(observer)
|
169
174
|
@computed_model_bindings.each do |computed_model_binding|
|
170
|
-
observer_registration = computed_observer_for(observer).observe(computed_model_binding)
|
175
|
+
observer_registration = computed_observer_for(observer).observe(computed_model_binding, observation_options)
|
171
176
|
my_registration = observer.registration_for(self)
|
172
177
|
observer.add_dependent(my_registration => observer_registration)
|
173
178
|
end
|
@@ -191,13 +196,13 @@ module Glimmer
|
|
191
196
|
parent_property_name = nil if parent_property_name.to_s.start_with?('[')
|
192
197
|
unless model.nil?
|
193
198
|
# TODO figure out a way to deal with this more uniformly
|
194
|
-
observer_registration = property_indexed?(property_name) ? nested_property_observer.observe(model) : nested_property_observer.observe(model, property_name)
|
195
|
-
parent_registration = parent_observer.registration_for(parent_model, parent_property_name)
|
199
|
+
observer_registration = property_indexed?(property_name) ? nested_property_observer.observe(model, observation_options) : nested_property_observer.observe(model, property_name, observation_options)
|
200
|
+
parent_registration = parent_observer.registration_for(parent_model, *[parent_property_name].compact)
|
196
201
|
parent_observer.add_dependent(parent_registration => observer_registration)
|
197
202
|
end
|
198
203
|
end
|
199
204
|
end
|
200
|
-
|
205
|
+
|
201
206
|
def call(value, *extra_args)
|
202
207
|
return if model.nil?
|
203
208
|
converted_value = value
|
@@ -21,6 +21,7 @@
|
|
21
21
|
|
22
22
|
require 'set'
|
23
23
|
require 'glimmer/data_binding/observable'
|
24
|
+
require 'glimmer/data_binding/observer'
|
24
25
|
require 'array_include_methods'
|
25
26
|
|
26
27
|
using ArrayIncludeMethods
|
@@ -29,29 +30,64 @@ module Glimmer
|
|
29
30
|
module DataBinding
|
30
31
|
module ObservableArray
|
31
32
|
include Observable
|
33
|
+
|
34
|
+
class Notifier
|
35
|
+
include Observer
|
36
|
+
|
37
|
+
attr_reader :observable_array
|
38
|
+
|
39
|
+
def initialize(observable_array)
|
40
|
+
@observable_array = observable_array
|
41
|
+
end
|
42
|
+
|
43
|
+
def call(new_value=nil, *extra_args)
|
44
|
+
@observable_array.notify_observers
|
45
|
+
end
|
46
|
+
end
|
32
47
|
|
33
|
-
def add_observer(observer, *
|
48
|
+
def add_observer(observer, *args)
|
49
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
50
|
+
element_properties = args
|
34
51
|
element_properties = element_properties.flatten.compact.uniq
|
35
52
|
return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
|
36
53
|
property_observer_list << observer
|
37
54
|
observer_element_properties[observer] = element_properties_for(observer) + Concurrent::Set.new(element_properties)
|
38
|
-
each { |element| add_element_observer(element, observer) }
|
55
|
+
each { |element| add_element_observer(element, observer, options) }
|
39
56
|
observer
|
40
57
|
end
|
41
58
|
|
42
|
-
def add_element_observers(element)
|
59
|
+
def add_element_observers(element, options = {})
|
43
60
|
property_observer_list.each do |observer|
|
44
|
-
add_element_observer(element, observer)
|
61
|
+
add_element_observer(element, observer, options)
|
45
62
|
end
|
46
63
|
end
|
47
64
|
|
48
|
-
def add_element_observer(element, observer)
|
65
|
+
def add_element_observer(element, observer, options = {})
|
49
66
|
element_properties_for(observer).each do |property|
|
50
|
-
observer.observe(element, property)
|
67
|
+
observer.observe(element, property, options)
|
51
68
|
end
|
69
|
+
ensure_array_object_observer(element, options) if options[:recursive] && element.is_a?(Array)
|
70
|
+
end
|
71
|
+
|
72
|
+
def ensure_array_object_observer(object, options)
|
73
|
+
return unless object&.is_a?(Array)
|
74
|
+
array_object_observer = array_object_observer_for(object)
|
75
|
+
array_observer_registration = array_object_observer.observe(object, options)
|
76
|
+
property_observer_list.each do |observer|
|
77
|
+
my_registration = observer.registration_for(self)
|
78
|
+
observer.add_dependent(my_registration => array_observer_registration)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def array_object_observer_for(object)
|
83
|
+
@array_object_observers ||= Concurrent::Hash.new
|
84
|
+
@array_object_observers[object] = Notifier.new(self) unless @array_object_observers.has_key?(object)
|
85
|
+
@array_object_observers[object]
|
52
86
|
end
|
53
87
|
|
54
|
-
def remove_observer(observer, *
|
88
|
+
def remove_observer(observer, *args)
|
89
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
90
|
+
element_properties = args
|
55
91
|
element_properties = element_properties.flatten.compact.uniq
|
56
92
|
if !element_properties.empty?
|
57
93
|
old_element_properties = element_properties_for(observer)
|
@@ -76,6 +112,13 @@ module Glimmer
|
|
76
112
|
element_properties_for(observer).each do |property|
|
77
113
|
observer.unobserve(element, property)
|
78
114
|
end
|
115
|
+
if element.is_a?(ObservableArray)
|
116
|
+
array_object_observer_for(element).unobserve(element)
|
117
|
+
element.property_observer_list.select {|o| o.respond_to?(:observable_array) && o.observable_array == self}.each do |o|
|
118
|
+
o.deregister_all_observables
|
119
|
+
@array_object_observers.reject! {|k, v| v == o}
|
120
|
+
end
|
121
|
+
end
|
79
122
|
end
|
80
123
|
|
81
124
|
def has_observer?(observer)
|
@@ -99,7 +142,7 @@ module Glimmer
|
|
99
142
|
end
|
100
143
|
|
101
144
|
def notify_observers
|
102
|
-
property_observer_list.to_a.each(
|
145
|
+
property_observer_list.to_a.each { |o| o.call(self) }
|
103
146
|
end
|
104
147
|
|
105
148
|
def <<(element)
|
@@ -204,6 +247,7 @@ module Glimmer
|
|
204
247
|
alias map! collect!
|
205
248
|
|
206
249
|
def compact!
|
250
|
+
# TODO consider checking which exact indices changed and only notifying if there is a change
|
207
251
|
super.tap { notify_observers }
|
208
252
|
end
|
209
253
|
|
@@ -237,6 +281,21 @@ module Glimmer
|
|
237
281
|
end
|
238
282
|
end
|
239
283
|
|
284
|
+
def filter!(&block)
|
285
|
+
if block_given?
|
286
|
+
old_array = Array.new(self)
|
287
|
+
super(&block).tap do
|
288
|
+
(old_array - self).each do |old_value|
|
289
|
+
unregister_dependent_observers(old_value)
|
290
|
+
remove_element_observers(old_value)
|
291
|
+
end
|
292
|
+
notify_observers
|
293
|
+
end
|
294
|
+
else
|
295
|
+
super
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
240
299
|
def shuffle!(hash = nil)
|
241
300
|
(hash.nil? ? super() : super(random: hash[:random])).tap { notify_observers }
|
242
301
|
end
|
@@ -294,6 +353,17 @@ module Glimmer
|
|
294
353
|
end
|
295
354
|
end
|
296
355
|
|
356
|
+
def replace(other_array)
|
357
|
+
old_array = Array.new(self)
|
358
|
+
super(other_array).tap do
|
359
|
+
(old_array - self).each do |old_value|
|
360
|
+
unregister_dependent_observers(old_value)
|
361
|
+
remove_element_observers(old_value)
|
362
|
+
end
|
363
|
+
notify_observers
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
297
367
|
def unregister_dependent_observers(old_value)
|
298
368
|
return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray)
|
299
369
|
property_observer_list.each { |observer| observer.unregister_dependents_with_observable(observer.registration_for(self), old_value) }
|
@@ -62,14 +62,19 @@ module Glimmer
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
def add_observer(observer, key = nil)
|
65
|
+
def add_observer(observer, key = nil, options = {})
|
66
|
+
if key.is_a?(Hash)
|
67
|
+
options = key
|
68
|
+
key = nil
|
69
|
+
end
|
66
70
|
return observer if has_observer?(observer, key)
|
67
71
|
key_observer_list(key) << observer
|
68
|
-
add_key_writer_observer(key)
|
72
|
+
add_key_writer_observer(key, options)
|
69
73
|
observer
|
70
74
|
end
|
71
75
|
|
72
|
-
def remove_observer(observer, key = nil)
|
76
|
+
def remove_observer(observer, key = nil, options = {})
|
77
|
+
old_value = self[key]
|
73
78
|
if has_observer?(observer, key)
|
74
79
|
key_observer_list(key).delete(observer)
|
75
80
|
observer.unobserve(self, key)
|
@@ -115,12 +120,12 @@ module Glimmer
|
|
115
120
|
|
116
121
|
def notify_observers(key)
|
117
122
|
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]) }
|
123
|
+
(key_observer_list(key).to_a - all_key_observer_list.to_a).each { |observer| observer.call(self[key], key) }
|
119
124
|
end
|
120
125
|
|
121
|
-
def add_key_writer_observer(key = nil)
|
122
|
-
ensure_array_object_observer(key, self[key])
|
123
|
-
ensure_hash_object_observer(key, self[key])
|
126
|
+
def add_key_writer_observer(key = nil, options)
|
127
|
+
ensure_array_object_observer(key, self[key], nil, options)
|
128
|
+
ensure_hash_object_observer(key, self[key], nil, options)
|
124
129
|
begin
|
125
130
|
method('__original__store')
|
126
131
|
rescue
|
@@ -143,10 +148,11 @@ module Glimmer
|
|
143
148
|
end
|
144
149
|
alias deregister_dependent_observers unregister_dependent_observers
|
145
150
|
|
146
|
-
def ensure_array_object_observer(key, object, old_object = nil)
|
151
|
+
def ensure_array_object_observer(key, object, old_object = nil, options = {})
|
152
|
+
options ||= {}
|
147
153
|
return unless object&.is_a?(Array)
|
148
154
|
array_object_observer = array_object_observer_for(key)
|
149
|
-
array_observer_registration = array_object_observer.observe(object)
|
155
|
+
array_observer_registration = array_object_observer.observe(object, options)
|
150
156
|
key_observer_list(key).each do |observer|
|
151
157
|
my_registration = observer.registration_for(self, key) # TODO eliminate repetition
|
152
158
|
observer.add_dependent(my_registration => array_observer_registration)
|
@@ -160,10 +166,11 @@ module Glimmer
|
|
160
166
|
@array_object_observers[key]
|
161
167
|
end
|
162
168
|
|
163
|
-
def ensure_hash_object_observer(key, object, old_object = nil)
|
169
|
+
def ensure_hash_object_observer(key, object, old_object = nil, options = {})
|
170
|
+
options ||= {}
|
164
171
|
return unless object&.is_a?(Hash)
|
165
172
|
hash_object_observer = hash_object_observer_for(key)
|
166
|
-
hash_observer_registration = hash_object_observer.observe(object)
|
173
|
+
hash_observer_registration = hash_object_observer.observe(object, options)
|
167
174
|
key_observer_list(key).each do |observer|
|
168
175
|
my_registration = observer.registration_for(self, key) # TODO eliminate repetition
|
169
176
|
observer.add_dependent(my_registration => hash_observer_registration)
|
@@ -176,6 +183,200 @@ module Glimmer
|
|
176
183
|
@hash_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @hash_object_observers.has_key?(key)
|
177
184
|
@hash_object_observers[key]
|
178
185
|
end
|
186
|
+
|
187
|
+
def delete(key, &block)
|
188
|
+
old_value = self[key]
|
189
|
+
unless old_value.nil?
|
190
|
+
unregister_dependent_observers(key, old_value)
|
191
|
+
unregister_dependent_observers(nil, old_value)
|
192
|
+
end
|
193
|
+
super(key, &block).tap do
|
194
|
+
notify_observers(key) unless old_value.nil?
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def delete_if(&block)
|
199
|
+
if block_given?
|
200
|
+
old_hash = self.dup
|
201
|
+
super(&block).tap do |new_hash|
|
202
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
203
|
+
deleted_keys.each do |deleted_key|
|
204
|
+
deleted_value = old_hash[deleted_key]
|
205
|
+
unless deleted_value.nil?
|
206
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
207
|
+
unregister_dependent_observers(nil, deleted_value)
|
208
|
+
notify_observers(deleted_key)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
else
|
213
|
+
super
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def select!(&block)
|
218
|
+
if block_given?
|
219
|
+
old_hash = self.dup
|
220
|
+
super(&block).tap do |new_hash|
|
221
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
222
|
+
deleted_keys.each do |deleted_key|
|
223
|
+
deleted_value = old_hash[deleted_key]
|
224
|
+
unless deleted_value.nil?
|
225
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
226
|
+
unregister_dependent_observers(nil, deleted_value)
|
227
|
+
notify_observers(deleted_key)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
else
|
232
|
+
super
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def filter!(&block)
|
237
|
+
if block_given?
|
238
|
+
old_hash = self.dup
|
239
|
+
super(&block).tap do |new_hash|
|
240
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
241
|
+
deleted_keys.each do |deleted_key|
|
242
|
+
deleted_value = old_hash[deleted_key]
|
243
|
+
unless deleted_value.nil?
|
244
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
245
|
+
unregister_dependent_observers(nil, deleted_value)
|
246
|
+
notify_observers(deleted_key)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
else
|
251
|
+
super
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def keep_if(&block)
|
256
|
+
if block_given?
|
257
|
+
old_hash = self.dup
|
258
|
+
super(&block).tap do |new_hash|
|
259
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
260
|
+
deleted_keys.each do |deleted_key|
|
261
|
+
deleted_value = old_hash[deleted_key]
|
262
|
+
unless deleted_value.nil?
|
263
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
264
|
+
unregister_dependent_observers(nil, deleted_value)
|
265
|
+
notify_observers(deleted_key)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
else
|
270
|
+
super
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def reject!(&block)
|
275
|
+
if block_given?
|
276
|
+
old_hash = self.dup
|
277
|
+
super(&block).tap do |new_hash|
|
278
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
279
|
+
deleted_keys.each do |deleted_key|
|
280
|
+
deleted_value = old_hash[deleted_key]
|
281
|
+
unless deleted_value.nil?
|
282
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
283
|
+
unregister_dependent_observers(nil, deleted_value)
|
284
|
+
notify_observers(deleted_key)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
else
|
289
|
+
super
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def shift
|
294
|
+
old_hash = self.dup
|
295
|
+
super.tap do
|
296
|
+
new_hash = self
|
297
|
+
deleted_keys = old_hash.keys - new_hash.keys
|
298
|
+
deleted_keys.each do |deleted_key|
|
299
|
+
deleted_value = old_hash[deleted_key]
|
300
|
+
unless deleted_value.nil?
|
301
|
+
unregister_dependent_observers(deleted_key, deleted_value)
|
302
|
+
unregister_dependent_observers(nil, deleted_value)
|
303
|
+
notify_observers(deleted_key)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def merge!(*other_hashes, &block)
|
310
|
+
if other_hashes.empty?
|
311
|
+
super
|
312
|
+
else
|
313
|
+
old_hash = self.dup
|
314
|
+
super(*other_hashes, &block).tap do |new_hash|
|
315
|
+
changed_keys = other_hashes.map(&:keys).reduce(:+)
|
316
|
+
changed_keys.each do |changed_key|
|
317
|
+
old_value = old_hash[changed_key]
|
318
|
+
if new_hash[changed_key] != old_value
|
319
|
+
unregister_dependent_observers(changed_key, old_value)
|
320
|
+
unregister_dependent_observers(nil, old_value)
|
321
|
+
notify_observers(changed_key)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
def replace(other_hash)
|
329
|
+
old_hash = self.dup
|
330
|
+
super(other_hash).tap do |new_hash|
|
331
|
+
changed_keys = old_hash.keys + new_hash.keys
|
332
|
+
changed_keys.each do |changed_key|
|
333
|
+
old_value = old_hash[changed_key]
|
334
|
+
if new_hash[changed_key] != old_value
|
335
|
+
unregister_dependent_observers(changed_key, old_value)
|
336
|
+
unregister_dependent_observers(nil, old_value)
|
337
|
+
notify_observers(changed_key)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def transform_keys!(hash2 = nil, &block)
|
344
|
+
if hash2.nil? && block.nil?
|
345
|
+
super
|
346
|
+
else
|
347
|
+
old_hash = self.dup
|
348
|
+
result = hash2.nil? ? super(&block) : super(hash2, &block)
|
349
|
+
result.tap do |new_hash|
|
350
|
+
changed_keys = old_hash.keys + new_hash.keys
|
351
|
+
changed_keys.each do |changed_key|
|
352
|
+
old_value = old_hash[changed_key]
|
353
|
+
if new_hash[changed_key] != old_value
|
354
|
+
unregister_dependent_observers(changed_key, old_value)
|
355
|
+
unregister_dependent_observers(nil, old_value)
|
356
|
+
notify_observers(changed_key)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def transform_values!(&block)
|
364
|
+
if block_given?
|
365
|
+
old_hash = self.dup
|
366
|
+
super(&block).tap do |new_hash|
|
367
|
+
new_hash.keys.each do |changed_key|
|
368
|
+
old_value = old_hash[changed_key]
|
369
|
+
if new_hash[changed_key] != old_value
|
370
|
+
unregister_dependent_observers(changed_key, old_value)
|
371
|
+
unregister_dependent_observers(nil, old_value)
|
372
|
+
notify_observers(changed_key)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
else
|
377
|
+
super
|
378
|
+
end
|
379
|
+
end
|
179
380
|
end
|
180
381
|
end
|
181
382
|
end
|
@@ -52,14 +52,14 @@ module Glimmer
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
-
def add_observer(observer, property_name)
|
55
|
+
def add_observer(observer, property_name, options = {})
|
56
56
|
return observer if has_observer?(observer, property_name)
|
57
57
|
property_observer_list(property_name) << observer
|
58
|
-
add_property_writer_observers(property_name)
|
58
|
+
add_property_writer_observers(property_name, options)
|
59
59
|
observer
|
60
60
|
end
|
61
61
|
|
62
|
-
def remove_observer(observer, property_name)
|
62
|
+
def remove_observer(observer, property_name, options = {})
|
63
63
|
if has_observer?(observer, property_name)
|
64
64
|
property_observer_list(property_name).delete(observer)
|
65
65
|
observer.unobserve(self, property_name)
|
@@ -103,11 +103,11 @@ module Glimmer
|
|
103
103
|
property_observer_list(property_name).to_a.each { |observer| observer.call(send(property_name)) }
|
104
104
|
end
|
105
105
|
|
106
|
-
def add_property_writer_observers(property_name)
|
106
|
+
def add_property_writer_observers(property_name, options)
|
107
107
|
property_writer_name = "#{property_name}="
|
108
108
|
method(property_writer_name)
|
109
|
-
ensure_array_object_observer(property_name, send(property_name))
|
110
|
-
ensure_hash_object_observer(property_name, send(property_name))
|
109
|
+
ensure_array_object_observer(property_name, send(property_name), nil, options)
|
110
|
+
ensure_hash_object_observer(property_name, send(property_name), nil, options)
|
111
111
|
begin
|
112
112
|
method("__original__#{property_writer_name}")
|
113
113
|
rescue
|
@@ -130,10 +130,11 @@ module Glimmer
|
|
130
130
|
end
|
131
131
|
alias deregister_dependent_observers unregister_dependent_observers
|
132
132
|
|
133
|
-
def ensure_array_object_observer(property_name, object, old_object = nil)
|
133
|
+
def ensure_array_object_observer(property_name, object, old_object = nil, options = {})
|
134
|
+
options ||= {}
|
134
135
|
return unless object&.is_a?(Array)
|
135
136
|
array_object_observer = array_object_observer_for(property_name)
|
136
|
-
array_observer_registration = array_object_observer.observe(object)
|
137
|
+
array_observer_registration = array_object_observer.observe(object, options)
|
137
138
|
property_observer_list(property_name).each do |observer|
|
138
139
|
my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
|
139
140
|
observer.add_dependent(my_registration => array_observer_registration)
|
@@ -143,14 +144,15 @@ module Glimmer
|
|
143
144
|
|
144
145
|
def array_object_observer_for(property_name)
|
145
146
|
@array_object_observers ||= Concurrent::Hash.new
|
146
|
-
@array_object_observers[property_name] =
|
147
|
+
@array_object_observers[property_name] = Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
|
147
148
|
@array_object_observers[property_name]
|
148
149
|
end
|
149
150
|
|
150
|
-
def ensure_hash_object_observer(property_name, object, old_object = nil)
|
151
|
+
def ensure_hash_object_observer(property_name, object, old_object = nil, options)
|
152
|
+
options ||= {}
|
151
153
|
return unless object&.is_a?(Hash)
|
152
154
|
hash_object_observer = hash_object_observer_for(property_name)
|
153
|
-
hash_observer_registration = hash_object_observer.observe(object)
|
155
|
+
hash_observer_registration = hash_object_observer.observe(object, options)
|
154
156
|
property_observer_list(property_name).each do |observer|
|
155
157
|
my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
|
156
158
|
observer.add_dependent(my_registration => hash_observer_registration)
|
@@ -160,7 +162,7 @@ module Glimmer
|
|
160
162
|
|
161
163
|
def hash_object_observer_for(property_name)
|
162
164
|
@hash_object_observers ||= Concurrent::Hash.new
|
163
|
-
@hash_object_observers[property_name] =
|
165
|
+
@hash_object_observers[property_name] = Notifier.new(self, property_name) unless @hash_object_observers.has_key?(property_name)
|
164
166
|
@hash_object_observers[property_name]
|
165
167
|
end
|
166
168
|
end
|
@@ -46,9 +46,9 @@ module Glimmer
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
class Registration < Struct.new(:observer, :observable, :
|
49
|
+
class Registration < Struct.new(:observer, :observable, :args, keyword_init: true)
|
50
50
|
def unregister
|
51
|
-
observer.unobserve(observable,
|
51
|
+
observer.unobserve(observable, *args)
|
52
52
|
end
|
53
53
|
alias unobserve unregister
|
54
54
|
alias deregister unregister
|
@@ -61,29 +61,31 @@ module Glimmer
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def registrations
|
64
|
-
@registrations ||= Concurrent::
|
64
|
+
@registrations ||= Concurrent::Hash.new
|
65
65
|
end
|
66
66
|
|
67
|
-
def registration_for(observable,
|
68
|
-
|
67
|
+
def registration_for(observable, *args)
|
68
|
+
args = compact_args(args)
|
69
|
+
registrations[[observable.object_id, args]] ||= Registration.new(observer: self, observable: observable, args: args)
|
69
70
|
end
|
71
|
+
alias ensure_registration_for! registration_for
|
70
72
|
|
71
73
|
# mapping of registrations to dependents
|
72
74
|
# {[observable, property] => [[dependent, dependent_observable, dependent_property], ...]}
|
73
75
|
def dependents
|
74
|
-
@dependents ||= Hash.new
|
76
|
+
@dependents ||= Concurrent::Hash.new
|
75
77
|
end
|
76
78
|
|
77
79
|
def dependents_for(registration)
|
78
|
-
dependents[registration] ||= Concurrent::Set.new
|
80
|
+
dependents[registration.object_id] ||= Concurrent::Set.new
|
79
81
|
end
|
80
82
|
|
81
|
-
# registers observer in an observable on a property (optional)
|
83
|
+
# registers observer in an observable on args usually containing a property and options (optional)
|
82
84
|
# observer maintains registration list to unregister later
|
83
|
-
def register(observable,
|
85
|
+
def register(observable, *args)
|
84
86
|
return if observable.nil?
|
85
87
|
unless observable.is_a?(Observable)
|
86
|
-
# TODO refactor code to be more smart/polymorphic/automated and honor open/closed principle
|
88
|
+
# TODO refactor code to be more smart/polymorphic/automated and honor open/closed principle (e.g. for SomeClass, search if there is ObservableSomeClass)
|
87
89
|
if observable.is_a?(Array)
|
88
90
|
observable.extend(ObservableArray)
|
89
91
|
elsif observable.is_a?(Hash)
|
@@ -92,23 +94,23 @@ module Glimmer
|
|
92
94
|
observable.extend(ObservableModel)
|
93
95
|
end
|
94
96
|
end
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
97
|
+
args = compact_args(args)
|
98
|
+
observable.add_observer(self, *args)
|
99
|
+
ensure_registration_for!(observable, *args)
|
99
100
|
end
|
100
101
|
alias observe register
|
101
102
|
|
102
|
-
def unregister(observable,
|
103
|
+
def unregister(observable, *args)
|
103
104
|
return unless observable.is_a?(Observable)
|
104
|
-
|
105
|
-
registration = registration_for(observable,
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
105
|
+
args = compact_args(args)
|
106
|
+
registration = registration_for(observable, *args)
|
107
|
+
registrations.delete([observable.object_id, args])
|
108
|
+
registration.tap do |registration|
|
109
|
+
dependents_for(registration).each do |dependent|
|
110
|
+
remove_dependent(registration => dependent)
|
111
|
+
dependent.deregister if dependent != registration
|
112
|
+
end
|
113
|
+
observable.remove_observer(self, *args)
|
112
114
|
end
|
113
115
|
end
|
114
116
|
alias unobserve unregister
|
@@ -118,14 +120,17 @@ module Glimmer
|
|
118
120
|
thedependents = dependents_for(registration).select do |thedependent|
|
119
121
|
thedependent.observable == dependent_observable
|
120
122
|
end
|
121
|
-
thedependents.each(&:
|
123
|
+
thedependents.each(&:deregister)
|
122
124
|
end
|
123
125
|
alias unobserve_dependents_with_observable unregister_dependents_with_observable
|
124
126
|
alias deregister_dependents_with_observable unregister_dependents_with_observable
|
125
127
|
|
126
128
|
# cleans up all registrations in observables
|
127
129
|
def unregister_all_observables
|
128
|
-
registrations.each
|
130
|
+
registrations.values.dup.each do |registration|
|
131
|
+
registration.deregister
|
132
|
+
registrations.delete([registration.observable.object_id, registration.args])
|
133
|
+
end
|
129
134
|
end
|
130
135
|
alias unobserve_all_observables unregister_all_observables
|
131
136
|
alias deregister_all_observables unregister_all_observables
|
@@ -140,12 +145,20 @@ module Glimmer
|
|
140
145
|
def remove_dependent(parent_to_dependent_hash)
|
141
146
|
registration = parent_to_dependent_hash.keys.first
|
142
147
|
dependent = parent_to_dependent_hash.values.first
|
143
|
-
dependents_for(registration).delete(dependent)
|
148
|
+
dependents_for(registration).delete(dependent).tap do
|
149
|
+
dependents.delete([registration.object_id]) if dependents_for(registration).empty?
|
150
|
+
end
|
144
151
|
end
|
145
152
|
|
146
153
|
def call(new_value = nil, *extra_args)
|
147
154
|
raise Error, 'Not implemented!'
|
148
155
|
end
|
156
|
+
|
157
|
+
def compact_args(args)
|
158
|
+
args = args[0...-1] if args.last == {}
|
159
|
+
args = args[0...-1] if args.last == []
|
160
|
+
args.compact
|
161
|
+
end
|
149
162
|
end
|
150
163
|
end
|
151
164
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.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-
|
11
|
+
date: 2021-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: array_include_methods
|
@@ -203,11 +203,12 @@ dependencies:
|
|
203
203
|
- !ruby/object:Gem::Version
|
204
204
|
version: '0'
|
205
205
|
description: Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of
|
206
|
-
a DSL Engine and an Observable/Observer/Data-Binding Library
|
207
|
-
|
208
|
-
Desktop Development GUI
|
209
|
-
|
210
|
-
DSL for
|
206
|
+
a DSL Engine and an Observable / Observer / Data-Binding Library (including Observable
|
207
|
+
Model, Observable Array, and Observable Hash). Used in Glimmer DSL for SWT (JRuby
|
208
|
+
Desktop Development GUI Framework), Glimmer DSL for Tk (Ruby Desktop Development
|
209
|
+
GUI Library), Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development
|
210
|
+
GUI Library), Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop
|
211
|
+
Apps), Glimmer DSL for XML (& HTML), and Glimmer DSL for CSS.
|
211
212
|
email: andy.am@gmail.com
|
212
213
|
executables: []
|
213
214
|
extensions: []
|