glimmer 2.3.0 → 2.4.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: 04a3036ea66bf774c6c27cf8b248e5658df35ccc3a69998159e43a013d3489e0
4
- data.tar.gz: 4241bfc406e54c13bbc4357918bf2bfed5a353f63f5200eea6ed60e0f7c84281
3
+ metadata.gz: f6a8921b091e770dd1acb0528bdf8dad0a63e79290ffb457325abf261b4ac8ca
4
+ data.tar.gz: 4d530d648577640a25943fb55f61918d352f30e2007f7426b739b877cd6a44a0
5
5
  SHA512:
6
- metadata.gz: ff74a70f8137ce7967d36e47a191ebdf8efbd67e15724449dd9c5b3a9db04d526a3ab00f4ba484d89af71430ed0543217ac7a0722427b47d735baf3ca4adc524
7
- data.tar.gz: f854ebfc523130f308b289fcf575d782776057f4f2d212332d38eeb719444e0d8d7790d0f7e2824059a9bccea1f22be532e1bd55619ba2e66dd585930d4574f5
6
+ metadata.gz: 68f5e3c05e2381a6f4855cec0832cc983fc74d0dcf27649efaf9e92dd8cf7aa69f9e171494ee1ab17a6a8d68ce0d71cb9ef61521290d999eaa70ad3915b989f2
7
+ data.tar.gz: 6b3af9b3d411bed16cc9c9f2f227394675fd255098d64729424e31dfdbf1b7e75d8e3aac102016f31c8b729978e33b44fbca1b0dbecec1d1acffea3651dbc468
data/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@
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
+
6
13
  ### 2.3.0
7
14
 
8
15
  - Have observing `ObservableArray` automatically notice if any values are nested arrays and observe them for micro-changes (recursively all the way down)
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.3.0'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.3.0` and add `require 'glimmer'`
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, Tab!
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, Tab!'
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/hello_tab.rb'"
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 tab English](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-tab-english.png)
894
- ![glimmer dsl tk screenshot sample hello tab French](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-tab-french.png)
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, Combo!'
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/hello_combo.rb'"
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-combo.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-combo-dropdown.png)
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
 
@@ -1163,7 +1163,7 @@ These are the main classes concerning data-binding:
1163
1163
  - `Glimmer::DataBinding::Observer`: Provides general observer support including unique registration and deregistration for cleanup and prevention of memory leaks. Main methods concerned are: `call`, `register` (alias: `observe`), and `unregister` (alias: `unobserve` or `deregister`)
1164
1164
  - `Glimmer::DataBinding::Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
1165
1165
  - `Glimmer::DataBinding::ObservableModel`: Mixin module for any observable model with observable attributes. In addition to `Observable` methods, it has a `notify_observers` method to be called when changes occur. It automatically enhances all attribute setters (ending with `=`) to notify observers on changes. Also, it automatically handles observing array attributes using `ObservableArray` appropriately so they would notify observers upon array mutation changes.
1166
- - `Glimmer::DataBinding::ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push`, `select!`, or `delete`) recursively (meaning if an array contained arrays and they changed, observers are notified)
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
1167
  - `Glimmer::DataBinding::ObservableHash`: Mixin module for any observable hash that automatically handles notifying observers upon performing hash mutation operations (e.g. `hash[key]=value`, `select!`, `merge!`)
1168
1168
  - `Glimmer::DataBinding::ModelBinding`: a higher-level abstraction that relies on all the other observer/observable classes to support basic data-binding, nested data-binding, and computed data-binding
1169
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 `<=`
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.3.0
1
+ 2.4.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.3.0 ruby lib
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.3.0"
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-10-07"
14
+ s.date = "2021-10-15"
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 = [
@@ -51,7 +51,7 @@ Gem::Specification.new do |s|
51
51
  ]
52
52
  s.homepage = "http://github.com/AndyObtiva/glimmer".freeze
53
53
  s.licenses = ["MIT".freeze]
54
- s.rubygems_version = "3.2.28".freeze
54
+ s.rubygems_version = "3.2.22".freeze
55
55
  s.summary = "Glimmer - DSL Engine for Ruby GUI and More".freeze
56
56
 
57
57
  if s.respond_to? :specification_version then
@@ -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
@@ -45,32 +45,34 @@ module Glimmer
45
45
  end
46
46
  end
47
47
 
48
- def add_observer(observer, *element_properties)
48
+ def add_observer(observer, *args)
49
+ options = args.last.is_a?(Hash) ? args.pop : {}
50
+ element_properties = args
49
51
  element_properties = element_properties.flatten.compact.uniq
50
52
  return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
51
53
  property_observer_list << observer
52
54
  observer_element_properties[observer] = element_properties_for(observer) + Concurrent::Set.new(element_properties)
53
- each { |element| add_element_observer(element, observer) }
55
+ each { |element| add_element_observer(element, observer, options) }
54
56
  observer
55
57
  end
56
58
 
57
- def add_element_observers(element)
59
+ def add_element_observers(element, options = {})
58
60
  property_observer_list.each do |observer|
59
- add_element_observer(element, observer)
61
+ add_element_observer(element, observer, options)
60
62
  end
61
63
  end
62
64
 
63
- def add_element_observer(element, observer)
65
+ def add_element_observer(element, observer, options = {})
64
66
  element_properties_for(observer).each do |property|
65
- observer.observe(element, property)
67
+ observer.observe(element, property, options)
66
68
  end
67
- ensure_array_object_observer(element) if element.is_a?(Array)
69
+ ensure_array_object_observer(element, options) if options[:recursive] && element.is_a?(Array)
68
70
  end
69
71
 
70
- def ensure_array_object_observer(object)
72
+ def ensure_array_object_observer(object, options)
71
73
  return unless object&.is_a?(Array)
72
74
  array_object_observer = array_object_observer_for(object)
73
- array_observer_registration = array_object_observer.observe(object)
75
+ array_observer_registration = array_object_observer.observe(object, options)
74
76
  property_observer_list.each do |observer|
75
77
  my_registration = observer.registration_for(self)
76
78
  observer.add_dependent(my_registration => array_observer_registration)
@@ -83,7 +85,9 @@ module Glimmer
83
85
  @array_object_observers[object]
84
86
  end
85
87
 
86
- def remove_observer(observer, *element_properties)
88
+ def remove_observer(observer, *args)
89
+ options = args.last.is_a?(Hash) ? args.pop : {}
90
+ element_properties = args
87
91
  element_properties = element_properties.flatten.compact.uniq
88
92
  if !element_properties.empty?
89
93
  old_element_properties = element_properties_for(observer)
@@ -110,10 +114,8 @@ module Glimmer
110
114
  end
111
115
  if element.is_a?(ObservableArray)
112
116
  array_object_observer_for(element).unobserve(element)
113
- element.property_observer_list.select {|o| o.observable_array == self}.each do |o|
114
- o.registrations.each do |registration|
115
- registration.deregister
116
- end
117
+ element.property_observer_list.select {|o| o.respond_to?(:observable_array) && o.observable_array == self}.each do |o|
118
+ o.deregister_all_observables
117
119
  @array_object_observers.reject! {|k, v| v == o}
118
120
  end
119
121
  end
@@ -245,6 +247,7 @@ module Glimmer
245
247
  alias map! collect!
246
248
 
247
249
  def compact!
250
+ # TODO consider checking which exact indices changed and only notifying if there is a change
248
251
  super.tap { notify_observers }
249
252
  end
250
253
 
@@ -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)
@@ -118,9 +123,9 @@ module Glimmer
118
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)
@@ -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)
@@ -147,10 +148,11 @@ module Glimmer
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)
@@ -46,9 +46,9 @@ module Glimmer
46
46
  end
47
47
  end
48
48
 
49
- class Registration < Struct.new(:observer, :observable, :property, keyword_init: true)
49
+ class Registration < Struct.new(:observer, :observable, :args, keyword_init: true)
50
50
  def unregister
51
- observer.unobserve(observable, property)
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::Set.new
64
+ @registrations ||= Concurrent::Hash.new
65
65
  end
66
66
 
67
- def registration_for(observable, property = nil)
68
- Registration.new(observer: self, observable: observable, property: property)
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, property = nil)
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
- observable.add_observer(*[self, property].compact)
96
- registration_for(observable, property).tap do |registration|
97
- self.registrations << registration
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, property = nil)
103
+ def unregister(observable, *args)
103
104
  return unless observable.is_a?(Observable)
104
- # TODO optimize performance in the future via indexing and/or making a registration official object/class
105
- registration = registration_for(observable, property)
106
- dependents_for(registration).each do |dependent|
107
- dependent.unregister
108
- remove_dependent(registration => dependent)
109
- end
110
- registrations.delete(registration).tap do |registration|
111
- observable.remove_observer(*[self, property].compact)
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(&:unregister)
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(&:unregister)
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.3.0
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-10-07 00:00:00.000000000 Z
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
@@ -264,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
264
264
  - !ruby/object:Gem::Version
265
265
  version: '0'
266
266
  requirements: []
267
- rubygems_version: 3.2.28
267
+ rubygems_version: 3.2.22
268
268
  signing_key:
269
269
  specification_version: 4
270
270
  summary: Glimmer - DSL Engine for Ruby GUI and More