glimmer 2.1.2 → 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: f7759feb8da21bb9e696870708163b9aebdecab3c15181a28e86fb8255c4fe4c
4
- data.tar.gz: ee5f7b55fbd6b4cf24ef28df0956b9fcbfa33b0544f81470d8bc2072c97479d2
3
+ metadata.gz: d61c8e3da07109cf0edf0df46e0937a862b3e80007a12da8050c2415481cbb6e
4
+ data.tar.gz: c618e4c0b024a9da219fbe78c263a442981d3d0a40f8a13bb7639edf50310e09
5
5
  SHA512:
6
- metadata.gz: 2c56a1347768269d1af90d1bd44a39f8bf5d1b3252da9140334a8a342e5855d49528e7b9b5c9a4f80464b91febcd2d59c6c350b148fbb7662167e32f9c818c55
7
- data.tar.gz: d8436f12a3c54baa44692ce5d1d07ea544cd59efedea4ec76b01737b68d90ba2ebae8fe4621960b6c7fb6d792444b3a8e6fab3fc57600b027da20419b14cc5e9
6
+ metadata.gz: c1b3b5d7aa04427f0e1188e0cd7e3855d517c0adfb6c561160c465f043685c48a651df8fde9eb05e31b84578a1986182422e49bb329fd35b9f0cd623fac4bc77
7
+ data.tar.gz: 5b4177d4ae038eefd1c210e8054f92ebe716b1a7edb2b40e94466ee2ba35aaafced1320128e75603f156ed3a7cdc56db200fd4369916dc0862f44fbd7ac12760
data/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
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
+
12
+ ### 2.1.5
13
+
14
+ - Upgrade array_include_methods gem to version 1.4.0 (and fix minor version)
15
+ - Update API usage of `Array#include_all?` in `Glimmer::DataBinding::ObservableArray` to splat array (now required)
16
+
17
+ ### 2.1.4
18
+
19
+ - Upgrade array_include_methods gem to version 1.2.0
20
+
21
+ ### 2.1.3
22
+
23
+ - Upgrade array_include_methods gem to version 1.1.0
24
+
6
25
  ### 2.1.2
7
26
 
8
27
  - Fix issue with irrelevent error message showing up for ENV['GLIMMER_LOGGER_LEVEL'] being empty (it is not a true error)
data/README.md CHANGED
@@ -35,6 +35,7 @@ Start by checking out Glimmer's original GUI DSL, which got extracted into its o
35
35
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
36
36
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
37
37
  - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
38
+ - [glimmer-dsl-libui](https://github.com/AndyObtiva/glimmer-dsl-libui): Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
38
39
 
39
40
  ## Table of Contents
40
41
 
@@ -49,6 +50,7 @@ Start by checking out Glimmer's original GUI DSL, which got extracted into its o
49
50
  - [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
50
51
  - [Glimmer DSL for CSS](#glimmer-dsl-for-css)
51
52
  - [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-mri-ruby-desktop-development-gui-library)
53
+ - [Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](#glimmer-dsl-for-libui-prerequisite-free-ruby-desktop-development-gui-library)
52
54
  - [Data-Binding Library](#data-binding-library)
53
55
  - [Glimmer Process](#glimmer-process)
54
56
  - [Resources](#resources)
@@ -208,7 +210,7 @@ end
208
210
  ### Setup
209
211
 
210
212
  Follow these steps to author a [Glimmer](https://rubygems.org/gems/glimmer) DSL:
211
- - Add `gem 'glimmer', '~> 2.1.2'` to `Gemfile` and run `bundle` or run `gem install glimmer -v2.1.2` 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'`
212
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)
213
215
  - Create `glimmer/dsl/[dsl_name]/[expresion_name]_expresion.rb` for every [expresion_name] expression needed, whether dynamic or static
214
216
 
@@ -926,6 +928,291 @@ Glimmer app:
926
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)
927
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
930
 
931
+ #### Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)
932
+
933
+ [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) is a prerequisite-free Ruby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!
934
+
935
+ [LibUI](https://github.com/kojix2/LibUI) is a thin [Ruby](https://www.ruby-lang.org/en/) wrapper around [libui](https://github.com/andlabs/libui), a relatively new C GUI library that renders native controls on every platform (similar to [SWT](https://www.eclipse.org/swt/), but without the heavy weight of the [Java Virtual Machine](https://www.java.com/en/)).
936
+
937
+ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) or [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) is the fact that [SWT](https://www.eclipse.org/swt/) and [Tk](https://www.tcl.tk/) are more mature than mid-alpha [libui](https://github.com/andlabs/libui) as GUI toolkits. Still, if there is only a need to build a small simple application, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) could be a good convenient choice due to having zero prerequisites beyond the dependencies included in the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui). Also, just like [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk), its apps start instantly and have a small memory footprint. [LibUI](https://github.com/kojix2/LibUI) is a promising new GUI toolkit that might prove quite worthy in the future.
938
+
939
+ [Glimmer DSL for LibUI](https://github.com/AndyObtiva/glimmer-dsl-libui) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
940
+ - Declarative DSL syntax that visually maps to the GUI widget hierarchy
941
+ - Convention over configuration via smart defaults and automation of low-level details
942
+ - Requiring the least amount of syntax possible to build GUI
943
+ - Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
944
+ - Custom Widget support
945
+ - Scaffolding for new custom widgets, apps, and gems
946
+ - Native-Executable packaging on Mac, Windows, and Linux
947
+
948
+ ##### Glimmer DSL for LibUI Samples
949
+
950
+ ###### Hello, World!
951
+
952
+ ```ruby
953
+ require 'glimmer-dsl-libui'
954
+
955
+ include Glimmer
956
+
957
+ window('hello world').show
958
+ ```
959
+
960
+ Mac
961
+
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)
963
+
964
+ Linux
965
+
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
969
+
970
+ ```ruby
971
+ require 'glimmer-dsl-libui'
972
+
973
+ include Glimmer
974
+
975
+ window('hello world', 300, 200) { |w|
976
+ button('Button') {
977
+ on_clicked do
978
+ msg_box(w, 'Information', 'You clicked the button')
979
+ end
980
+ }
981
+
982
+ on_closing do
983
+ puts 'Bye Bye'
984
+ end
985
+ }.show
986
+ ```
987
+
988
+ Mac
989
+
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)
992
+
993
+ Linux
994
+
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
999
+
1000
+ ```ruby
1001
+ require 'glimmer-dsl-libui'
1002
+
1003
+ include Glimmer
1004
+
1005
+ menu('File') {
1006
+ menu_item('Open') {
1007
+ on_clicked do
1008
+ file = open_file(MAIN_WINDOW)
1009
+ puts file unless file.nil?
1010
+ end
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
+ }
1042
+
1043
+ MAIN_WINDOW = window('Control Gallery', 600, 500) {
1044
+ margined true
1045
+
1046
+ on_closing do
1047
+ puts 'Bye Bye'
1048
+ end
1049
+
1050
+ vertical_box {
1051
+ horizontal_box {
1052
+ group('Basic Controls') {
1053
+ vertical_box {
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'
1145
+
1146
+ on_changed do |e|
1147
+ puts "Current textbox data: '#{e.text}'"
1148
+ end
1149
+ }
1150
+ }
1151
+ }
1152
+
1153
+ tab_item('Page 2') {
1154
+ horizontal_box
1155
+ }
1156
+
1157
+ tab_item('Page 3') {
1158
+ horizontal_box
1159
+ }
1160
+ }
1161
+ }
1162
+ }
1163
+ }
1164
+ }
1165
+
1166
+ MAIN_WINDOW.show
1167
+ ```
1168
+
1169
+ Mac
1170
+
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)
1172
+
1173
+ Linux
1174
+
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
1178
+
1179
+ ```ruby
1180
+ require 'glimmer-dsl-libui'
1181
+
1182
+ include Glimmer
1183
+
1184
+ data = [
1185
+ %w[cat meow],
1186
+ %w[dog woof],
1187
+ %w[chicken cock-a-doodle-doo],
1188
+ %w[hourse neigh],
1189
+ %w[cow moo]
1190
+ ]
1191
+
1192
+ window('Animal sounds', 300, 200) {
1193
+ horizontal_box {
1194
+ table {
1195
+ text_column('Animal')
1196
+ text_column('Description')
1197
+
1198
+ cell_rows data
1199
+ }
1200
+ }
1201
+
1202
+ on_closing do
1203
+ puts 'Bye Bye'
1204
+ end
1205
+ }.show
1206
+ ```
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
+
929
1216
  ## Data-Binding Library
930
1217
 
931
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.
@@ -935,12 +1222,13 @@ Glimmer enhances observed models automatically (including array operations like
935
1222
  This relies mainly on the Observer Design Pattern and the MVP (Model-View-Presenter) Architectural Pattern (a variation on MVC)
936
1223
 
937
1224
  These are the main classes concerning data-binding:
938
- - `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`)
939
- - `Observable`: General super-module for all observables. Main methods concerned are: `add_observer` and `remove_observer`
940
- - `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.
941
- - `ObservableArray`: Mixin module for any observable array collection that automatically handles notifying observers upon performing array mutation operations (e.g. `push` or `delete`)
942
- - `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
943
- - `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 `<=`
944
1232
 
945
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)
946
1234
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.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.1.2 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.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"
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",
@@ -58,7 +59,7 @@ Gem::Specification.new do |s|
58
59
  end
59
60
 
60
61
  if s.respond_to? :add_runtime_dependency then
61
- s.add_runtime_dependency(%q<array_include_methods>.freeze, [">= 1.0.4", "< 2.0.0"])
62
+ s.add_runtime_dependency(%q<array_include_methods>.freeze, ["~> 1.4.0"])
62
63
  s.add_runtime_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
63
64
  s.add_development_dependency(%q<rspec-mocks>.freeze, ["~> 3.5.0"])
64
65
  s.add_development_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
@@ -71,7 +72,7 @@ Gem::Specification.new do |s|
71
72
  s.add_development_dependency(%q<simplecov-lcov>.freeze, ["~> 0.7.0"])
72
73
  s.add_development_dependency(%q<rake-tui>.freeze, [">= 0"])
73
74
  else
74
- s.add_dependency(%q<array_include_methods>.freeze, [">= 1.0.4", "< 2.0.0"])
75
+ s.add_dependency(%q<array_include_methods>.freeze, ["~> 1.4.0"])
75
76
  s.add_dependency(%q<facets>.freeze, [">= 3.1.0", "< 4.0.0"])
76
77
  s.add_dependency(%q<rspec-mocks>.freeze, ["~> 3.5.0"])
77
78
  s.add_dependency(%q<rspec>.freeze, ["~> 3.5.0"])
@@ -198,7 +198,7 @@ module Glimmer
198
198
  end
199
199
  end
200
200
 
201
- def call(value)
201
+ def call(value, *extra_args)
202
202
  return if model.nil?
203
203
  converted_value = value
204
204
  invoke_property_writer(model, "#{property_name}=", converted_value) unless converted_value == evaluate_property
@@ -83,7 +83,7 @@ module Glimmer
83
83
  end
84
84
 
85
85
  def has_observer_element_properties?(observer, element_properties)
86
- element_properties_for(observer).to_a.include_all?(element_properties)
86
+ element_properties_for(observer).to_a.include_all?(*element_properties)
87
87
  end
88
88
 
89
89
  def property_observer_list
@@ -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,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.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-14 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
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.0.4
20
- - - "<"
17
+ - - "~>"
21
18
  - !ruby/object:Gem::Version
22
- version: 2.0.0
19
+ version: 1.4.0
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: 1.0.4
30
- - - "<"
24
+ - - "~>"
31
25
  - !ruby/object:Gem::Version
32
- version: 2.0.0
26
+ version: 1.4.0
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: facets
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -234,6 +228,7 @@ files:
234
228
  - lib/glimmer/data_binding/model_binding.rb
235
229
  - lib/glimmer/data_binding/observable.rb
236
230
  - lib/glimmer/data_binding/observable_array.rb
231
+ - lib/glimmer/data_binding/observable_hash.rb
237
232
  - lib/glimmer/data_binding/observable_model.rb
238
233
  - lib/glimmer/data_binding/observer.rb
239
234
  - lib/glimmer/data_binding/shine.rb