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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c73b637dd451b7033852edb1490cf02f68ac49cdfbaa364ba14f4427a5d35253
4
- data.tar.gz: 3c6a2cd64bdf17087150a39427da632912c5c8f2d8a839bb8c6beda90efc1433
3
+ metadata.gz: d61c8e3da07109cf0edf0df46e0937a862b3e80007a12da8050c2415481cbb6e
4
+ data.tar.gz: c618e4c0b024a9da219fbe78c263a442981d3d0a40f8a13bb7639edf50310e09
5
5
  SHA512:
6
- metadata.gz: 2678b5f34362a5e63de8bfd6951966322b909de0a667e6bf6004d3c78792ea7a9f514cc1159eb84bc257d6152d83f184fb028a718cc6ea5dc8008a777525efdb
7
- data.tar.gz: 7b96445440558cf0a2c97ee2b5b2a55ee9d1a1466d54bc849537d7ca73568faf348f21dfba358a4b8d0722ad629e4462952499c58ded0a99b33eceda16542727
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
- 2.1.5mg 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
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.1.5'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.1.5` and add `require 'glimmer'`
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-button.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-button.png)
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-button.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-button.png)
978
- ![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)
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-control-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-control-gallery.png)
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-control-gallery.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-control-gallery.png)
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-basic-table.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-mac-basic-table.png)
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-basic-table.png](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-libui/master/images/glimmer-dsl-libui-linux-basic-table.png)
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
- - `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
1230
- - `Shine`: enables highly intuitive and visually expressive syntax to perform bidirectional (two-way) data-binding with `<=>` and unidirectional (one-way) data-binding with `<=`
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.5
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.1.5 ruby lib
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.1.5"
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-26"
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.1.5
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-26 00:00:00.000000000 Z
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