glimmer 0.9.5 → 0.10.4

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.5
1
+ 0.10.4
@@ -4,6 +4,7 @@
4
4
  # UI with domain models.
5
5
  require 'logger'
6
6
  require 'set'
7
+ require 'array_include_methods'
7
8
 
8
9
  $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
9
10
 
@@ -17,7 +18,6 @@ require 'glimmer/config'
17
18
  # Glimmer DSL dynamic keywords (e.g. label, combo, etc...) are available via method_missing
18
19
  module Glimmer
19
20
  #TODO make it configurable to include or not include perhaps reverting to using included
20
- REGEX_METHODS_EXCLUDED = /^(to_|\[)/
21
21
 
22
22
  # TODO add loop detection support to avoid infinite loops (perhaps breaks after 3 repetitions and provides an option to allow it if intentional)
23
23
  class << self
@@ -41,27 +41,22 @@ module Glimmer
41
41
  new_loop_data = [method_symbol, args, block]
42
42
  if new_loop_data == Glimmer.loop_last_data
43
43
  Glimmer.loop_increment!
44
- if Glimmer.loop == Config.loop_max_count
45
- raise "Glimmer looped #{Config.loop_max_count} times with keyword '#{new_loop_data[0]}'! Check code for errors."
46
- end
44
+ raise "Glimmer looped #{Config.loop_max_count} times with keyword '#{new_loop_data[0]}'! Check code for errors." if Glimmer.loop == Config.loop_max_count
47
45
  else
48
46
  Glimmer.loop_reset!
49
47
  end
50
48
  Glimmer.loop_last_data = new_loop_data
51
49
  # This if statement speeds up Glimmer in girb or whenever directly including on main object
52
- if method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
53
- raise ExcludedKeywordError, "Glimmer excluded keyword: #{method_symbol}"
50
+ is_excluded = Config.excluded_keyword_checkers.reduce(false) {|result, checker| result || instance_exec(method_symbol, *args, &checker) }
51
+ if is_excluded
52
+ Glimmer::Config.logger.debug "Glimmer excluded keyword: #{method_symbol}" if Glimmer::Config.log_excluded_keywords?
53
+ super(method_symbol, *args, &block)
54
54
  end
55
- Glimmer::Config.logger&.debug "Interpreting keyword: #{method_symbol}"
55
+ Glimmer::Config.logger.info {"Interpreting keyword: #{method_symbol}"}
56
56
  Glimmer::DSL::Engine.interpret(method_symbol, *args, &block)
57
- rescue ExcludedKeywordError => e
58
- # TODO add a feature to show excluded keywords optionally for debugging purposes
59
- super(method_symbol, *args, &block)
60
57
  rescue InvalidKeywordError => e
61
- if !method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
62
- Glimmer::Config.logger&.error e.message
63
- end
64
- Glimmer::Config.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
58
+ Glimmer::Config.logger.error {"Encountered an invalid keyword at this object: #{self}"}
59
+ Glimmer::Config.logger.error {e.full_message}
65
60
  super(method_symbol, *args, &block)
66
61
  end
67
62
  end
@@ -2,29 +2,48 @@ module Glimmer
2
2
  module Config
3
3
  class << self
4
4
  LOOP_MAX_COUNT_DEFAULT = 100
5
+ REGEX_METHODS_EXCLUDED = /^(to_|\[)/
5
6
 
6
7
  attr_writer :loop_max_count
8
+ attr_accessor :log_excluded_keywords
9
+ alias log_excluded_keywords? log_excluded_keywords
10
+
11
+ def excluded_keyword_checkers
12
+ @excluded_keyword_checkers ||= reset_excluded_keyword_checkers!
13
+ end
14
+
15
+ def excluded_keyword_checkers=(checkers)
16
+ @excluded_keyword_checkers = checkers
17
+ end
18
+
19
+ def reset_excluded_keyword_checkers!
20
+ @excluded_keyword_checkers = [ lambda { |method_symbol, *args| method_symbol.to_s.match(REGEX_METHODS_EXCLUDED) } ]
21
+ end
7
22
 
8
23
  def loop_max_count
9
24
  @loop_max_count ||= LOOP_MAX_COUNT_DEFAULT
10
25
  end
11
26
 
12
27
  # Returns Glimmer logger (standard Ruby logger)
13
- def logger
14
- # unless defined? @@logger
15
- # @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
16
- # end
17
- @@logger if defined? @@logger
28
+ def logger
29
+ reset_logger! unless defined? @@logger
30
+ @@logger
31
+ end
32
+
33
+ def logger=(custom_logger)
34
+ @@logger = custom_logger
18
35
  end
19
36
 
20
- def enable_logging
21
- @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
37
+ def reset_logger!
38
+ self.logger = Logger.new(STDOUT).tap do |logger|
39
+ logger.level = Logger::ERROR
40
+ begin
41
+ logger.level = ENV['GLIMMER_LOGGER_LEVEL'].strip.downcase if ENV['GLIMMER_LOGGER_LEVEL']
42
+ rescue => e
43
+ puts e.message
44
+ end
45
+ end
22
46
  end
23
47
  end
24
48
  end
25
49
  end
26
-
27
- if ENV['GLIMMER_LOGGER_LEVEL']
28
- Glimmer::Config.enable_logging
29
- Glimmer::Config.logger.level = ENV['GLIMMER_LOGGER_LEVEL'].downcase
30
- end
@@ -1,77 +1,280 @@
1
1
  require 'set'
2
+ require 'array_include_methods'
2
3
 
3
4
  require 'glimmer/data_binding/observable'
4
5
 
6
+ using ArrayIncludeMethods
7
+
5
8
  module Glimmer
6
9
  module DataBinding
7
10
  # TODO prefix utility methods with double-underscore
8
11
  module ObservableArray
9
12
  include Observable
10
13
 
11
- def add_observer(observer, element_properties=nil)
12
- return observer if has_observer?(observer) && element_properties.nil?
14
+ def add_observer(observer, *element_properties)
15
+ element_properties = element_properties.flatten.compact.uniq
16
+ return observer if has_observer?(observer) && has_observer_element_properties?(observer, element_properties)
13
17
  property_observer_list << observer
14
- [element_properties].flatten.compact.each do |property|
15
- each do |element|
16
- observer.observe(element, property)
17
- end
18
- end
18
+ observer_element_properties[observer] = element_properties_for(observer) + Set.new(element_properties)
19
+ each { |element| add_element_observer(element, observer) }
19
20
  observer
20
21
  end
22
+
23
+ def add_element_observers(element)
24
+ property_observer_list.each do |observer|
25
+ add_element_observer(element, observer)
26
+ end
27
+ end
21
28
 
22
- def remove_observer(observer, element_properties=nil)
23
- property_observer_list.delete(observer)
24
- [element_properties].flatten.compact.each do |property|
29
+ def add_element_observer(element, observer)
30
+ element_properties_for(observer).each do |property|
31
+ observer.observe(element, property)
32
+ end
33
+ end
34
+
35
+ def remove_observer(observer, *element_properties)
36
+ element_properties = element_properties.flatten.compact.uniq
37
+ if !element_properties.empty?
38
+ old_element_properties = element_properties_for(observer)
39
+ observer_element_properties[observer] = element_properties_for(observer) - Set.new(element_properties)
25
40
  each do |element|
26
- observer.unobserve(element, property)
41
+ element_properties.each do |property|
42
+ observer.unobserve(element, property)
43
+ end
27
44
  end
28
45
  end
46
+ if element_properties_for(observer).empty?
47
+ property_observer_list.delete(observer)
48
+ observer_element_properties.delete(observer)
49
+ each { |element| remove_element_observer(element, observer) }
50
+ end
29
51
  observer
30
52
  end
31
53
 
54
+ def remove_element_observers(element)
55
+ property_observer_list.each do |observer|
56
+ remove_element_observer(element, observer)
57
+ end
58
+ end
59
+
60
+ def remove_element_observer(element, observer)
61
+ element_properties_for(observer).each do |property|
62
+ observer.unobserve(element, property)
63
+ end
64
+ end
65
+
32
66
  def has_observer?(observer)
33
67
  property_observer_list.include?(observer)
34
68
  end
69
+
70
+ def has_observer_element_properties?(observer, element_properties)
71
+ element_properties_for(observer).to_a.include_all?(element_properties)
72
+ end
35
73
 
36
74
  def property_observer_list
37
75
  @property_observer_list ||= Set.new
38
76
  end
39
77
 
78
+ def observer_element_properties
79
+ @observer_element_properties ||= {}
80
+ end
81
+
82
+ def element_properties_for(observer)
83
+ observer_element_properties[observer] ||= Set.new
84
+ end
85
+
40
86
  def notify_observers
41
- property_observer_list.each {|observer| observer.call}
87
+ property_observer_list.to_a.each(&:call)
42
88
  end
43
89
 
44
90
  def <<(element)
45
- super(element)
46
- notify_observers
91
+ super(element).tap do
92
+ add_element_observers(element)
93
+ notify_observers
94
+ end
47
95
  end
96
+ alias push <<
48
97
 
49
98
  def []=(index, value)
50
99
  old_value = self[index]
51
100
  unregister_dependent_observers(old_value)
52
- super(index, value)
53
- notify_observers
101
+ remove_element_observers(old_value)
102
+ add_element_observers(value)
103
+ super(index, value).tap do
104
+ notify_observers
105
+ end
106
+ end
107
+
108
+ def pop
109
+ popped_element = last
110
+ unregister_dependent_observers(popped_element)
111
+ remove_element_observers(popped_element)
112
+ super.tap do
113
+ notify_observers
114
+ end
54
115
  end
55
116
 
56
117
  def delete(element)
57
118
  unregister_dependent_observers(element)
58
- super(element)
59
- notify_observers
119
+ remove_element_observers(element)
120
+ super(element).tap do
121
+ notify_observers
122
+ end
60
123
  end
61
124
 
62
125
  def delete_at(index)
63
126
  old_value = self[index]
64
127
  unregister_dependent_observers(old_value)
65
- super(index)
66
- notify_observers
128
+ remove_element_observers(old_value)
129
+ super(index).tap do
130
+ notify_observers
131
+ end
132
+ end
133
+
134
+ def delete_if(&block)
135
+ if block_given?
136
+ old_array = Array.new(self)
137
+ super(&block).tap do |new_array|
138
+ (old_array - new_array).each do |element|
139
+ unregister_dependent_observers(element)
140
+ remove_element_observers(element)
141
+ end
142
+ notify_observers
143
+ end
144
+ else
145
+ super
146
+ end
67
147
  end
68
148
 
69
149
  def clear
70
150
  each do |old_value|
71
151
  unregister_dependent_observers(old_value)
152
+ remove_element_observers(old_value)
153
+ end
154
+ super.tap do
155
+ notify_observers
156
+ end
157
+ end
158
+
159
+ def reverse!
160
+ super.tap do
161
+ notify_observers
162
+ end
163
+ end
164
+
165
+ def collect!(&block)
166
+ if block_given?
167
+ each do |old_value|
168
+ unregister_dependent_observers(old_value)
169
+ remove_element_observers(old_value)
170
+ end
171
+ super(&block).tap do
172
+ each do |element|
173
+ add_element_observers(element)
174
+ end
175
+ notify_observers
176
+ end
177
+ else
178
+ super
179
+ end
180
+ end
181
+ alias map! collect!
182
+
183
+ def compact!
184
+ super.tap do
185
+ notify_observers
186
+ end
187
+ end
188
+
189
+ def flatten!(level=nil)
190
+ each do |old_value|
191
+ unregister_dependent_observers(old_value)
192
+ remove_element_observers(old_value)
193
+ end
194
+ (level.nil? ? super() : super(level)).tap do
195
+ each do |element|
196
+ add_element_observers(element)
197
+ end
198
+ notify_observers
199
+ end
200
+ end
201
+
202
+ def rotate!(count=1)
203
+ super(count).tap do
204
+ notify_observers
205
+ end
206
+ end
207
+
208
+ def select!(&block)
209
+ if block_given?
210
+ old_array = Array.new(self)
211
+ super(&block).tap do
212
+ (old_array - self).each do |old_value|
213
+ unregister_dependent_observers(old_value)
214
+ remove_element_observers(old_value)
215
+ end
216
+ notify_observers
217
+ end
218
+ else
219
+ super
220
+ end
221
+ end
222
+
223
+ def shuffle!(hash = nil)
224
+ (hash.nil? ? super() : super(random: hash[:random])).tap do
225
+ notify_observers
226
+ end
227
+ end
228
+
229
+ def slice!(arg1, arg2=nil)
230
+ old_array = Array.new(self)
231
+ (arg2.nil? ? super(arg1) : super(arg1, arg2)).tap do
232
+ (old_array - self).each do |old_value|
233
+ unregister_dependent_observers(old_value)
234
+ remove_element_observers(old_value)
235
+ end
236
+ notify_observers
237
+ end
238
+ end
239
+
240
+ def sort!(&block)
241
+ (block.nil? ? super() : super(&block)).tap do
242
+ notify_observers
243
+ end
244
+ end
245
+
246
+ def sort_by!(&block)
247
+ (block.nil? ? super() : super(&block)).tap do
248
+ notify_observers
249
+ end
250
+ end
251
+
252
+ def uniq!(&block)
253
+ each do |old_value|
254
+ unregister_dependent_observers(old_value)
255
+ remove_element_observers(old_value)
256
+ end
257
+ (block.nil? ? super() : super(&block)).tap do
258
+ each do |element|
259
+ add_element_observers(element)
260
+ end
261
+ notify_observers
262
+ end
263
+ end
264
+
265
+ def reject!(&block)
266
+ if block_given?
267
+ old_array = Array.new(self)
268
+ super(&block).tap do
269
+ (old_array - self).each do |old_value|
270
+ unregister_dependent_observers(old_value)
271
+ remove_element_observers(old_value)
272
+ end
273
+ notify_observers
274
+ end
275
+ else
276
+ super
72
277
  end
73
- super()
74
- notify_observers
75
278
  end
76
279
 
77
280
  def unregister_dependent_observers(old_value)
@@ -34,7 +34,7 @@ module Glimmer
34
34
  end
35
35
 
36
36
  def has_observer_for_any_property?(observer)
37
- property_observer_hash.values.map(&:to_a).sum.include?(observer)
37
+ property_observer_hash.values.map(&:to_a).reduce(:+).include?(observer)
38
38
  end
39
39
 
40
40
  def property_observer_hash
@@ -47,7 +47,9 @@ module Glimmer
47
47
  end
48
48
 
49
49
  def notify_observers(property_name)
50
- property_observer_list(property_name).each {|observer| observer.call(send(property_name))}
50
+ property_observer_list(property_name).to_a.each do |observer|
51
+ observer.call(send(property_name))
52
+ end
51
53
  end
52
54
  #TODO upon updating values, make sure dependent observers are cleared (not added as dependents here)
53
55
 
@@ -70,7 +72,7 @@ module Glimmer
70
72
  end
71
73
  rescue => e
72
74
  #ignore writing if no property writer exists
73
- Glimmer::Config.logger&.debug "No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"
75
+ Glimmer::Config.logger.debug {"No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"}
74
76
  end
75
77
 
76
78
  def unregister_dependent_observers(property_name, old_value)
@@ -59,6 +59,7 @@ module Glimmer
59
59
  # registers observer in an observable on a property (optional)
60
60
  # observer maintains registration list to unregister later
61
61
  def register(observable, property = nil)
62
+ return if observable.nil?
62
63
  unless observable.is_a?(Observable)
63
64
  # TODO refactor code to be more smart/polymorphic/automated and honor open/closed principle
64
65
  if observable.is_a?(Array)
@@ -75,6 +76,7 @@ module Glimmer
75
76
  alias observe register
76
77
 
77
78
  def unregister(observable, property = nil)
79
+ return unless observable.is_a?(Observable)
78
80
  # TODO optimize performance in the future via indexing and/or making a registration official object/class
79
81
  observable.remove_observer(*[self, property].compact)
80
82
  registration = registration_for(observable, property)
@@ -90,16 +92,12 @@ module Glimmer
90
92
  thedependents = dependents_for(registration).select do |thedependent|
91
93
  thedependent.observable == dependent_observable
92
94
  end
93
- thedependents.each do |thedependent|
94
- thedependent.unregister
95
- end
95
+ thedependents.each(&:unregister)
96
96
  end
97
97
 
98
98
  # cleans up all registrations in observables
99
99
  def unregister_all_observables
100
- registrations.each do |registration|
101
- registration.unregister
102
- end
100
+ registrations.each(&:unregister)
103
101
  end
104
102
  alias unobserve_all_observables unregister_all_observables
105
103