glimmer 2.1.5 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +31 -30
- data/VERSION +1 -1
- data/glimmer.gemspec +4 -3
- data/lib/glimmer/data_binding/model_binding.rb +1 -1
- data/lib/glimmer/data_binding/observable_hash.rb +181 -0
- data/lib/glimmer/data_binding/observable_model.rb +21 -2
- data/lib/glimmer/data_binding/observer.rb +6 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d61c8e3da07109cf0edf0df46e0937a862b3e80007a12da8050c2415481cbb6e
|
4
|
+
data.tar.gz: c618e4c0b024a9da219fbe78c263a442981d3d0a40f8a13bb7639edf50310e09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1b3b5d7aa04427f0e1188e0cd7e3855d517c0adfb6c561160c465f043685c48a651df8fde9eb05e31b84578a1986182422e49bb329fd35b9f0cd623fac4bc77
|
7
|
+
data.tar.gz: 5b4177d4ae038eefd1c210e8054f92ebe716b1a7edb2b40e94466ee2ba35aaafced1320128e75603f156ed3a7cdc56db200fd4369916dc0862f44fbd7ac12760
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,12 @@
|
|
3
3
|
Related Change Logs:
|
4
4
|
- [glimmer-dsl-swt/CHANGELOG.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/CHANGELOG.md)
|
5
5
|
|
6
|
+
### 2.2.0
|
7
|
+
|
8
|
+
- New `Glimmer::DataBinding::ObservableHash` support for observing `hash[key]=value` mutations
|
9
|
+
- Ensure observed ObservableHash values are enhanced with ObservableHash if they were of type Hash
|
10
|
+
- Ensure observed ObservableModel values are enhanced with ObservableHash if they were of type Hash
|
11
|
+
|
6
12
|
### 2.1.5
|
7
13
|
|
8
14
|
- Upgrade array_include_methods gem to version 1.4.0 (and fix minor version)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 style="position: relative; top: 20px;" />](https://rubygems.org/gems/glimmer) Glimmer - DSL Framework for Ruby GUI and More
|
2
2
|
[![Gem Version](https://badge.fury.io/rb/glimmer.svg)](http://badge.fury.io/rb/glimmer)
|
3
3
|
[![rspec](https://github.com/AndyObtiva/glimmer/workflows/rspec/badge.svg)](https://github.com/AndyObtiva/glimmer/actions?query=workflow%3Arspec)
|
4
4
|
[![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
|
@@ -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.0'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.2.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
|
|
@@ -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,17 +957,15 @@ 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'
|
@@ -995,15 +985,17 @@ 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
|
+
###### Control Gallery
|
1007
999
|
|
1008
1000
|
```ruby
|
1009
1001
|
require 'glimmer-dsl-libui'
|
@@ -1174,15 +1166,15 @@ MAIN_WINDOW = window('Control Gallery', 600, 500) {
|
|
1174
1166
|
MAIN_WINDOW.show
|
1175
1167
|
```
|
1176
1168
|
|
1177
|
-
###### Basic Table
|
1178
|
-
|
1179
1169
|
Mac
|
1180
1170
|
|
1181
|
-
![glimmer-dsl-libui-mac-
|
1171
|
+
![glimmer-dsl-libui-mac-control-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-control-gallery.png)
|
1182
1172
|
|
1183
1173
|
Linux
|
1184
1174
|
|
1185
|
-
![glimmer-dsl-libui-linux-
|
1175
|
+
![glimmer-dsl-libui-linux-control-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-control-gallery.png)
|
1176
|
+
|
1177
|
+
###### Basic Table
|
1186
1178
|
|
1187
1179
|
```ruby
|
1188
1180
|
require 'glimmer-dsl-libui'
|
@@ -1213,6 +1205,14 @@ window('Animal sounds', 300, 200) {
|
|
1213
1205
|
}.show
|
1214
1206
|
```
|
1215
1207
|
|
1208
|
+
Mac
|
1209
|
+
|
1210
|
+
![glimmer-dsl-libui-mac-basic-table.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-table.png)
|
1211
|
+
|
1212
|
+
Linux
|
1213
|
+
|
1214
|
+
![glimmer-dsl-libui-linux-basic-table.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-table.png)
|
1215
|
+
|
1216
1216
|
## Data-Binding Library
|
1217
1217
|
|
1218
1218
|
Data-Binding enables mapping GUI properties (like text and color) to Model attributes (like name and age) for bidirectional or unidirectional synchronization and conversion as needed.
|
@@ -1222,12 +1222,13 @@ Glimmer enhances observed models automatically (including array operations like
|
|
1222
1222
|
This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
|
1223
1223
|
|
1224
1224
|
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
|
-
- `
|
1225
|
+
- `Glimmer::DataBinding::Observer`: Provides general observer support including unique registration and deregistration for cleanup and prevention of memory leaks. Main methods concerned are: `call`, `register` (alias: `observe`), and `unregister` (alias: `unobserve` or `deregister`)
|
1226
|
+
- `Glimmer::DataBinding::Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
|
1227
|
+
- `Glimmer::DataBinding::ObservableModel`: Mixin module for any observable model with observable attributes. In addition to `Observable` methods, it has a `notify_observers` method to be called when changes occur. It automatically enhances all attribute setters (ending with `=`) to notify observers on changes. Also, it automatically handles observing array attributes using `ObservableArray` appropriately so they would notify observers upon array mutation changes.
|
1228
|
+
- `Glimmer::DataBinding::ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push`, `select!`, or `delete`)
|
1229
|
+
- `Glimmer::DataBinding::ObservableHash`: Mixin module for any observable hash that automatically handles notifying observers upon performing array mutation operation (e.g. `hash[key]=value`)
|
1230
|
+
- `Glimmer::DataBinding::ModelBinding`: a higher-level abstraction that relies on all the other observer/observable classes to support basic data-binding, nested data-binding, and computed data-binding
|
1231
|
+
- `Glimmer::DataBinding::Shine`: enables highly intuitive and visually expressive syntax to perform bidirectional (two-way) data-binding with `<=>` and unidirectional (one-way) data-binding with `<=`
|
1231
1232
|
|
1232
1233
|
You may learn more from [Data-Binding](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#data-binding) and [Observer](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#observer) usage in [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)
|
1233
1234
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.2.0
|
data/glimmer.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: glimmer 2.
|
5
|
+
# stub: glimmer 2.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "glimmer".freeze
|
9
|
-
s.version = "2.
|
9
|
+
s.version = "2.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["AndyMaleh".freeze]
|
14
|
-
s.date = "2021-09-
|
14
|
+
s.date = "2021-09-28"
|
15
15
|
s.description = "Glimmer is a Ruby DSL Framework for Ruby GUI and More, consisting of a DSL Engine and an Observable/Observer/Data-Binding Library. Used in the Glimmer DSL for SWT (JRuby Desktop Development GUI Framework), the Glimmer DSL for Tk (Ruby Desktop Development GUI Library), the Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps), the Glimmer DSL for XML (& HTML), and the Glimmer DSL for CSS.".freeze
|
16
16
|
s.email = "andy.am@gmail.com".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
"lib/glimmer/data_binding/model_binding.rb",
|
33
33
|
"lib/glimmer/data_binding/observable.rb",
|
34
34
|
"lib/glimmer/data_binding/observable_array.rb",
|
35
|
+
"lib/glimmer/data_binding/observable_hash.rb",
|
35
36
|
"lib/glimmer/data_binding/observable_model.rb",
|
36
37
|
"lib/glimmer/data_binding/observer.rb",
|
37
38
|
"lib/glimmer/data_binding/shine.rb",
|
@@ -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
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# Copyright (c) 2007-2021 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'glimmer/data_binding/observable'
|
23
|
+
require 'glimmer/data_binding/observer'
|
24
|
+
|
25
|
+
module Glimmer
|
26
|
+
module DataBinding
|
27
|
+
module ObservableHash
|
28
|
+
include Observable
|
29
|
+
|
30
|
+
class Notifier
|
31
|
+
include Observer
|
32
|
+
|
33
|
+
def initialize(observable_model, key)
|
34
|
+
@observable_model = observable_model
|
35
|
+
@key = key
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(new_value=nil, *extra_args)
|
39
|
+
@observable_model.notify_observers(@key)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
OBSERVED_STORE_METHOD = lambda do |key, value|
|
44
|
+
if key_observer_list(key).empty?
|
45
|
+
if all_key_observer_list.empty?
|
46
|
+
self.send('__original__store', key, value)
|
47
|
+
else
|
48
|
+
old_value = self[key]
|
49
|
+
unregister_dependent_observers(nil, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
|
50
|
+
self.send('__original__store', key, value)
|
51
|
+
notify_observers(key)
|
52
|
+
ensure_array_object_observer(nil, value, old_value)
|
53
|
+
ensure_hash_object_observer(nil, value, old_value)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
old_value = self[key]
|
57
|
+
unregister_dependent_observers(key, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
|
58
|
+
self.send('__original__store', key, value)
|
59
|
+
notify_observers(key)
|
60
|
+
ensure_array_object_observer(key, value, old_value)
|
61
|
+
ensure_hash_object_observer(key, value, old_value)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_observer(observer, key = nil)
|
66
|
+
return observer if has_observer?(observer, key)
|
67
|
+
key_observer_list(key) << observer
|
68
|
+
add_key_writer_observer(key)
|
69
|
+
observer
|
70
|
+
end
|
71
|
+
|
72
|
+
def remove_observer(observer, key = nil)
|
73
|
+
if has_observer?(observer, key)
|
74
|
+
key_observer_list(key).delete(observer)
|
75
|
+
observer.unobserve(self, key)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_observers(key)
|
80
|
+
key_observer_hash[key].each do |observer|
|
81
|
+
remove_observer(observer, key)
|
82
|
+
end
|
83
|
+
key_observer_hash.delete(key)
|
84
|
+
end
|
85
|
+
|
86
|
+
def remove_all_observers
|
87
|
+
all_observers = key_observer_hash.clone
|
88
|
+
key_observer_hash.keys.each do |key|
|
89
|
+
remove_observers(key)
|
90
|
+
end
|
91
|
+
key_observer_hash.clear
|
92
|
+
all_observers
|
93
|
+
end
|
94
|
+
|
95
|
+
def has_observer?(observer, key = nil)
|
96
|
+
key_observer_list(key).include?(observer)
|
97
|
+
end
|
98
|
+
|
99
|
+
def has_observer_for_any_key?(observer)
|
100
|
+
key_observer_hash.values.map(&:to_a).reduce(:+).include?(observer)
|
101
|
+
end
|
102
|
+
|
103
|
+
def key_observer_hash
|
104
|
+
@key_observers ||= Hash.new
|
105
|
+
end
|
106
|
+
|
107
|
+
def key_observer_list(key)
|
108
|
+
key_observer_hash[key] = Concurrent::Set.new unless key_observer_hash[key]
|
109
|
+
key_observer_hash[key]
|
110
|
+
end
|
111
|
+
|
112
|
+
def all_key_observer_list
|
113
|
+
key_observer_list(nil)
|
114
|
+
end
|
115
|
+
|
116
|
+
def notify_observers(key)
|
117
|
+
all_key_observer_list.to_a.each { |observer| observer.call(self[key], key) }
|
118
|
+
(key_observer_list(key).to_a - all_key_observer_list.to_a).each { |observer| observer.call(self[key]) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_key_writer_observer(key = nil)
|
122
|
+
ensure_array_object_observer(key, self[key])
|
123
|
+
ensure_hash_object_observer(key, self[key])
|
124
|
+
begin
|
125
|
+
method('__original__store')
|
126
|
+
rescue
|
127
|
+
define_singleton_method('__original__store', store_method)
|
128
|
+
define_singleton_method('[]=', &OBSERVED_STORE_METHOD)
|
129
|
+
end
|
130
|
+
rescue => e
|
131
|
+
#ignore writing if no key writer exists
|
132
|
+
Glimmer::Config.logger.debug {"No need to observe store method: '[]='\n#{e.message}\n#{e.backtrace.join("\n")}"}
|
133
|
+
end
|
134
|
+
|
135
|
+
def store_method
|
136
|
+
self.class.instance_method('[]=') rescue self.method('[]=')
|
137
|
+
end
|
138
|
+
|
139
|
+
def unregister_dependent_observers(key, old_value)
|
140
|
+
# TODO look into optimizing this
|
141
|
+
return unless old_value.is_a?(ObservableModel) || old_value.is_a?(ObservableArray) || old_value.is_a?(ObservableHash)
|
142
|
+
key_observer_list(key).each { |observer| observer.unregister_dependents_with_observable(observer.registration_for(self, key), old_value) }
|
143
|
+
end
|
144
|
+
alias deregister_dependent_observers unregister_dependent_observers
|
145
|
+
|
146
|
+
def ensure_array_object_observer(key, object, old_object = nil)
|
147
|
+
return unless object&.is_a?(Array)
|
148
|
+
array_object_observer = array_object_observer_for(key)
|
149
|
+
array_observer_registration = array_object_observer.observe(object)
|
150
|
+
key_observer_list(key).each do |observer|
|
151
|
+
my_registration = observer.registration_for(self, key) # TODO eliminate repetition
|
152
|
+
observer.add_dependent(my_registration => array_observer_registration)
|
153
|
+
end
|
154
|
+
array_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableArray)
|
155
|
+
end
|
156
|
+
|
157
|
+
def array_object_observer_for(key)
|
158
|
+
@array_object_observers ||= Concurrent::Hash.new
|
159
|
+
@array_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @array_object_observers.has_key?(key)
|
160
|
+
@array_object_observers[key]
|
161
|
+
end
|
162
|
+
|
163
|
+
def ensure_hash_object_observer(key, object, old_object = nil)
|
164
|
+
return unless object&.is_a?(Hash)
|
165
|
+
hash_object_observer = hash_object_observer_for(key)
|
166
|
+
hash_observer_registration = hash_object_observer.observe(object)
|
167
|
+
key_observer_list(key).each do |observer|
|
168
|
+
my_registration = observer.registration_for(self, key) # TODO eliminate repetition
|
169
|
+
observer.add_dependent(my_registration => hash_observer_registration)
|
170
|
+
end
|
171
|
+
hash_object_observer_for(key).unregister(old_object) if old_object.is_a?(ObservableHash)
|
172
|
+
end
|
173
|
+
|
174
|
+
def hash_object_observer_for(key)
|
175
|
+
@hash_object_observers ||= Concurrent::Hash.new
|
176
|
+
@hash_object_observers[key] = ObservableModel::Notifier.new(self, key) unless @hash_object_observers.has_key?(key)
|
177
|
+
@hash_object_observers[key]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -35,7 +35,7 @@ module Glimmer
|
|
35
35
|
@property_name = property_name
|
36
36
|
end
|
37
37
|
|
38
|
-
def call(new_value=nil)
|
38
|
+
def call(new_value=nil, *extra_args)
|
39
39
|
@observable_model.notify_observers(@property_name)
|
40
40
|
end
|
41
41
|
end
|
@@ -44,10 +44,11 @@ module Glimmer
|
|
44
44
|
property_writer_name = "#{property_name}="
|
45
45
|
lambda do |value|
|
46
46
|
old_value = self.send(property_name)
|
47
|
-
unregister_dependent_observers(property_name, old_value)
|
47
|
+
unregister_dependent_observers(property_name, old_value) # remove dependent observers previously installed in ensure_array_object_observer and ensure_hash_object_observer
|
48
48
|
self.send("__original__#{property_writer_name}", value)
|
49
49
|
notify_observers(property_name)
|
50
50
|
ensure_array_object_observer(property_name, value, old_value)
|
51
|
+
ensure_hash_object_observer(property_name, value, old_value)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
@@ -106,6 +107,7 @@ module Glimmer
|
|
106
107
|
property_writer_name = "#{property_name}="
|
107
108
|
method(property_writer_name)
|
108
109
|
ensure_array_object_observer(property_name, send(property_name))
|
110
|
+
ensure_hash_object_observer(property_name, send(property_name))
|
109
111
|
begin
|
110
112
|
method("__original__#{property_writer_name}")
|
111
113
|
rescue
|
@@ -144,6 +146,23 @@ module Glimmer
|
|
144
146
|
@array_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name) unless @array_object_observers.has_key?(property_name)
|
145
147
|
@array_object_observers[property_name]
|
146
148
|
end
|
149
|
+
|
150
|
+
def ensure_hash_object_observer(property_name, object, old_object = nil)
|
151
|
+
return unless object&.is_a?(Hash)
|
152
|
+
hash_object_observer = hash_object_observer_for(property_name)
|
153
|
+
hash_observer_registration = hash_object_observer.observe(object)
|
154
|
+
property_observer_list(property_name).each do |observer|
|
155
|
+
my_registration = observer.registration_for(self, property_name) # TODO eliminate repetition
|
156
|
+
observer.add_dependent(my_registration => hash_observer_registration)
|
157
|
+
end
|
158
|
+
hash_object_observer_for(property_name).unregister(old_object) if old_object.is_a?(ObservableHash)
|
159
|
+
end
|
160
|
+
|
161
|
+
def hash_object_observer_for(property_name)
|
162
|
+
@hash_object_observers ||= Concurrent::Hash.new
|
163
|
+
@hash_object_observers[property_name] = ObservableModel::Notifier.new(self, property_name) unless @hash_object_observers.has_key?(property_name)
|
164
|
+
@hash_object_observers[property_name]
|
165
|
+
end
|
147
166
|
end
|
148
167
|
end
|
149
168
|
end
|
@@ -41,8 +41,8 @@ module Glimmer
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Called by observables once updates occur sending in the new_value if any
|
44
|
-
def call(new_value=nil)
|
45
|
-
@observer_block.call(new_value)
|
44
|
+
def call(new_value=nil, *extra_args)
|
45
|
+
@observer_block.call(new_value, *extra_args)
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -86,6 +86,8 @@ module Glimmer
|
|
86
86
|
# TODO refactor code to be more smart/polymorphic/automated and honor open/closed principle
|
87
87
|
if observable.is_a?(Array)
|
88
88
|
observable.extend(ObservableArray)
|
89
|
+
elsif observable.is_a?(Hash)
|
90
|
+
observable.extend(ObservableHash)
|
89
91
|
else
|
90
92
|
observable.extend(ObservableModel)
|
91
93
|
end
|
@@ -141,7 +143,7 @@ module Glimmer
|
|
141
143
|
dependents_for(registration).delete(dependent)
|
142
144
|
end
|
143
145
|
|
144
|
-
def call(new_value)
|
146
|
+
def call(new_value = nil, *extra_args)
|
145
147
|
raise Error, 'Not implemented!'
|
146
148
|
end
|
147
149
|
end
|
@@ -150,3 +152,4 @@ end
|
|
150
152
|
|
151
153
|
require 'glimmer/data_binding/observable_model'
|
152
154
|
require 'glimmer/data_binding/observable_array'
|
155
|
+
require 'glimmer/data_binding/observable_hash'
|
metadata
CHANGED
@@ -1,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.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: array_include_methods
|
@@ -228,6 +228,7 @@ files:
|
|
228
228
|
- lib/glimmer/data_binding/model_binding.rb
|
229
229
|
- lib/glimmer/data_binding/observable.rb
|
230
230
|
- lib/glimmer/data_binding/observable_array.rb
|
231
|
+
- lib/glimmer/data_binding/observable_hash.rb
|
231
232
|
- lib/glimmer/data_binding/observable_model.rb
|
232
233
|
- lib/glimmer/data_binding/observer.rb
|
233
234
|
- lib/glimmer/data_binding/shine.rb
|