volt 0.7.23 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile +8 -0
  5. data/Guardfile +2 -2
  6. data/Readme.md +139 -136
  7. data/VERSION +1 -1
  8. data/app/volt/assets/js/setImmediate.js +175 -0
  9. data/app/volt/tasks/live_query/data_store.rb +0 -2
  10. data/app/volt/tasks/live_query/live_query.rb +4 -4
  11. data/docs/GETTING_STARTED.md +24 -3
  12. data/docs/WHY.md +1 -22
  13. data/lib/volt.rb +20 -1
  14. data/lib/volt/console.rb +20 -0
  15. data/lib/volt/controllers/model_controller.rb +25 -11
  16. data/lib/volt/extra_core/object.rb +2 -14
  17. data/lib/volt/extra_core/string.rb +4 -0
  18. data/lib/volt/models.rb +0 -1
  19. data/lib/volt/models/array_model.rb +8 -16
  20. data/lib/volt/models/cursor.rb +1 -1
  21. data/lib/volt/models/model.rb +40 -60
  22. data/lib/volt/models/model_hash_behaviour.rb +10 -24
  23. data/lib/volt/models/model_helpers.rb +2 -2
  24. data/lib/volt/models/model_state.rb +1 -1
  25. data/lib/volt/models/model_wrapper.rb +4 -4
  26. data/lib/volt/models/persistors/array_store.rb +44 -28
  27. data/lib/volt/models/persistors/base.rb +1 -1
  28. data/lib/volt/models/persistors/model_store.rb +1 -1
  29. data/lib/volt/models/persistors/params.rb +5 -1
  30. data/lib/volt/models/persistors/query/query_listener.rb +2 -0
  31. data/lib/volt/models/persistors/store.rb +3 -2
  32. data/lib/volt/models/persistors/store_state.rb +7 -2
  33. data/lib/volt/models/url.rb +35 -29
  34. data/lib/volt/models/validations.rb +7 -17
  35. data/lib/volt/page/bindings/attribute_binding.rb +57 -39
  36. data/lib/volt/page/bindings/base_binding.rb +0 -14
  37. data/lib/volt/page/bindings/content_binding.rb +15 -18
  38. data/lib/volt/page/bindings/each_binding.rb +67 -34
  39. data/lib/volt/page/bindings/if_binding.rb +15 -12
  40. data/lib/volt/page/bindings/template_binding.rb +77 -59
  41. data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
  42. data/lib/volt/page/channel.rb +22 -38
  43. data/lib/volt/page/channel_stub.rb +3 -6
  44. data/lib/volt/page/page.rb +24 -26
  45. data/lib/volt/page/string_template_renderer.rb +46 -0
  46. data/lib/volt/page/sub_context.rb +7 -1
  47. data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
  48. data/lib/volt/page/tasks.rb +3 -2
  49. data/lib/volt/page/url_tracker.rb +4 -3
  50. data/lib/volt/reactive/computation.rb +131 -0
  51. data/lib/volt/reactive/dependency.rb +71 -0
  52. data/lib/volt/reactive/eventable.rb +82 -0
  53. data/lib/volt/reactive/hash_dependency.rb +36 -0
  54. data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
  55. data/lib/volt/reactive/reactive_array.rb +100 -193
  56. data/lib/volt/reactive/reactive_hash.rb +49 -0
  57. data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
  58. data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
  59. data/lib/volt/server/html_parser/view_scope.rb +31 -1
  60. data/spec/apps/kitchen_sink/Gemfile +4 -8
  61. data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
  62. data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
  63. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
  64. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
  65. data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
  66. data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
  67. data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
  68. data/spec/controllers/reactive_accessors_spec.rb +13 -15
  69. data/spec/integration/bindings_spec.rb +159 -0
  70. data/spec/integration/templates_spec.rb +15 -0
  71. data/spec/models/model_spec.rb +130 -228
  72. data/spec/reactive/computation_spec.rb +63 -0
  73. data/spec/reactive/dependency_spec.rb +5 -0
  74. data/spec/reactive/eventable_spec.rb +48 -0
  75. data/spec/reactive/reactive_array_spec.rb +97 -0
  76. data/spec/router/routes_spec.rb +26 -27
  77. data/spec/server/html_parser/view_parser_spec.rb +3 -21
  78. data/spec/server/rack/asset_files_spec.rb +1 -1
  79. data/templates/project/app/main/views/main/main.html +2 -2
  80. metadata +29 -41
  81. data/lib/volt/extra_core/time.rb +0 -16
  82. data/lib/volt/page/draw_cycle.rb +0 -31
  83. data/lib/volt/page/memory_test.rb +0 -26
  84. data/lib/volt/page/reactive_template.rb +0 -32
  85. data/lib/volt/reactive/array_extensions.rb +0 -12
  86. data/lib/volt/reactive/destructive_methods.rb +0 -19
  87. data/lib/volt/reactive/event_chain.rb +0 -125
  88. data/lib/volt/reactive/events.rb +0 -216
  89. data/lib/volt/reactive/object_tracking.rb +0 -14
  90. data/lib/volt/reactive/reactive_block.rb +0 -88
  91. data/lib/volt/reactive/reactive_generator.rb +0 -44
  92. data/lib/volt/reactive/reactive_tags.rb +0 -71
  93. data/lib/volt/reactive/reactive_value.rb +0 -427
  94. data/lib/volt/reactive/string_extensions.rb +0 -31
  95. data/spec/integration/test_integration_spec.rb +0 -14
  96. data/spec/models/event_chain_spec.rb +0 -150
  97. data/spec/models/model_buffers_spec.rb +0 -9
  98. data/spec/models/old_model_spec.rb +0 -67
  99. data/spec/models/reactive_array_spec.rb +0 -364
  100. data/spec/models/reactive_block_spec.rb +0 -13
  101. data/spec/models/reactive_call_times_spec.rb +0 -28
  102. data/spec/models/reactive_generator_spec.rb +0 -58
  103. data/spec/models/reactive_tags_spec.rb +0 -35
  104. data/spec/models/reactive_value_spec.rb +0 -370
  105. data/spec/models/store_spec.rb +0 -16
  106. data/spec/models/string_extensions_spec.rb +0 -57
@@ -0,0 +1,131 @@
1
+ class Computation
2
+ @@current = nil
3
+ @@flush_queue = []
4
+
5
+
6
+ def self.current=(val)
7
+ @@current = val
8
+ end
9
+
10
+ def self.current
11
+ @@current
12
+ end
13
+
14
+ def initialize(computation)
15
+ @computation = computation
16
+ @invalidations = []
17
+ end
18
+
19
+ # Runs the computation
20
+ def compute!
21
+ @invalidated = false
22
+
23
+ unless @stopped
24
+
25
+ @computing = true
26
+ run_in do
27
+ @computation.call
28
+ end
29
+ @computing = false
30
+ end
31
+ end
32
+
33
+ def on_invalidate(&callback)
34
+ if @invalidated
35
+ # Call invalidate now, since its already invalidated
36
+ Computation.run_without_tracking do
37
+ callback.call
38
+ end
39
+ else
40
+ # Store the invalidation
41
+ @invalidations << callback
42
+ end
43
+ end
44
+
45
+ # Calling invalidate removes the computation from all of
46
+ # its dependencies. This keeps its dependencies from
47
+ # invalidating it again.
48
+ def invalidate!
49
+ unless @invalidated
50
+ @invalidated = true
51
+
52
+ if !@stopped && !@computing
53
+ @@flush_queue << self
54
+
55
+ # If we are in the browser, we queue a flush for the next tick
56
+ if Volt.in_browser?
57
+ self.class.queue_flush!
58
+ end
59
+ end
60
+
61
+ invalidations = @invalidations
62
+ @invalidations = []
63
+
64
+ invalidations.each do |invalidation|
65
+ invalidation.call
66
+ end
67
+ end
68
+ end
69
+
70
+ # Stop re-run of the computations
71
+ def stop
72
+ unless @stopped
73
+ @stopped = true
74
+ invalidate!
75
+ end
76
+ end
77
+
78
+ # Runs in this computation as the current computation, returns the computation
79
+ def run_in
80
+ previous = Computation.current
81
+ Computation.current = self
82
+ yield
83
+ Computation.current = previous
84
+
85
+ return self
86
+ end
87
+
88
+ def self.run_without_tracking
89
+ previous = Computation.current
90
+ Computation.current = nil
91
+ return_value = yield
92
+ Computation.current = previous
93
+
94
+ return return_value
95
+ end
96
+
97
+
98
+ def self.flush!
99
+ raise "Can't flush while in a flush" if @flushing
100
+
101
+ @flushing = true
102
+ # clear any timers
103
+ @timer = nil
104
+
105
+ computations = @@flush_queue
106
+ @@flush_queue = []
107
+
108
+ computations.each do |computation|
109
+ computation.compute!
110
+ end
111
+
112
+ @flushing = false
113
+ end
114
+
115
+ def self.queue_flush!
116
+ if !@timer
117
+ # Flush once everything else has finished running
118
+ @timer = `setImmediate(function() { self['$flush!'](); });`
119
+ end
120
+ end
121
+ end
122
+
123
+
124
+ class Proc
125
+ def watch!
126
+ return Computation.new(self).run_in do
127
+ # run self
128
+ self.call
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,71 @@
1
+ # Temp until https://github.com/opal/opal/pull/596
2
+ require 'set'
3
+
4
+ class Set
5
+ def delete(o)
6
+ @hash.delete(o)
7
+ end
8
+
9
+ def delete?(o)
10
+ if include?(o)
11
+ delete(o)
12
+ else
13
+ nil
14
+ end
15
+ end
16
+
17
+ def delete_if
18
+ block_given? or return enum_for(__method__)
19
+ # @hash.delete_if should be faster, but using it breaks the order
20
+ # of enumeration in subclasses.
21
+ select { |o| yield o }.each { |o| @hash.delete(o) }
22
+ self
23
+ end
24
+
25
+ def to_a
26
+ @hash.keys
27
+ end
28
+ end
29
+
30
+ class Dependency
31
+ def initialize
32
+ @dependencies = Set.new
33
+ end
34
+
35
+ def depend
36
+ # If there is no @dependencies, don't depend because it has been removed
37
+ return unless @dependencies
38
+
39
+ current = Computation.current
40
+ if current
41
+ added = @dependencies.add?(current)
42
+
43
+ if added
44
+ # puts "Added #{self.inspect} to #{current.inspect}"
45
+ current.on_invalidate do
46
+ # If @dependencies is nil, this Dependency has been removed
47
+ @dependencies.delete(current) if @dependencies
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ def changed!
54
+ deps = @dependencies
55
+
56
+ # If no deps, dependency has been removed
57
+ return unless deps
58
+
59
+ @dependencies = Set.new
60
+
61
+ deps.each do |dep|
62
+ dep.invalidate!
63
+ end
64
+ end
65
+
66
+ # Called when a dependency is no longer needed
67
+ def remove
68
+ changed!
69
+ @dependencies = nil
70
+ end
71
+ end
@@ -0,0 +1,82 @@
1
+ class Listener
2
+ def initialize(klass, event, callback)
3
+ @klass = klass
4
+ @event = event
5
+ @callback = callback
6
+ end
7
+
8
+ def call(*args)
9
+ @callback.call(*args) unless @removed
10
+ end
11
+
12
+ def remove
13
+ @removed = true
14
+
15
+ @klass.remove_listener(@event, self)
16
+
17
+ # Make things easier on the GC
18
+ @klass = nil
19
+ @callback = nil
20
+ end
21
+
22
+ def inspect
23
+ "<Listener:#{object_id} event=#{@event}>"
24
+ end
25
+ end
26
+
27
+ module Eventable
28
+ def on(event, &callback)
29
+ event = event.to_sym
30
+ listener = Listener.new(self, event, callback)
31
+ @listeners ||= {}
32
+ @listeners[event] ||= []
33
+ @listeners[event] << listener
34
+
35
+ first_for_event = @listeners[event].size == 1
36
+ first = first_for_event && @listeners.size == 1
37
+
38
+ # Let the included class know that an event was registered. (if it cares)
39
+ if self.respond_to?(:event_added)
40
+ # call event added passing the event, the scope, and a boolean if it
41
+ # is the first time this event has been added.
42
+ self.event_added(event, first, first_for_event)
43
+ end
44
+
45
+ return listener
46
+ end
47
+
48
+ def trigger!(event, *args)
49
+ event = event.to_sym
50
+
51
+ return unless @listeners && @listeners[event]
52
+
53
+ # TODO: We have to dup here because one trigger might remove another
54
+ @listeners[event].dup.each do |listener|
55
+ # Call the event on each listener
56
+ listener.call(*args)
57
+ end
58
+ end
59
+
60
+ def remove_listener(event, listener)
61
+ event = event.to_sym
62
+
63
+ raise "Unable to delete #{event} from #{self.inspect}" unless @listeners && @listeners[event]
64
+
65
+ @listeners[event].delete(listener)
66
+
67
+ last_for_event = @listeners[event].size == 0
68
+
69
+ if last_for_event
70
+ # No registered listeners now on this event
71
+ @listeners.delete(event)
72
+ end
73
+
74
+ last = last_for_event && @listeners.size == 0
75
+
76
+ # Let the class we're included on know that we removed a listener (if it cares)
77
+ if self.respond_to?(:event_removed)
78
+ # Pass in the event and a boolean indicating if it is the last event
79
+ self.event_removed(event, last, last_for_event)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ class HashDependency
2
+ def initialize
3
+ @hash_depedencies = {}
4
+ end
5
+
6
+ def depend(key)
7
+ ensure_key(key).depend
8
+ end
9
+
10
+ def changed!(key)
11
+ ensure_key(key).changed!
12
+ end
13
+
14
+ def delete(key)
15
+ # TODORW: should this .remove
16
+ dep = @hash_depedencies[key]
17
+
18
+ if dep
19
+ dep.changed!
20
+ dep.remove
21
+ end
22
+
23
+ @hash_depedencies.delete(key)
24
+ end
25
+
26
+ def changed_all!
27
+ @hash_depedencies.each_pair do |key,value|
28
+ value.changed!
29
+ end
30
+ end
31
+
32
+ private
33
+ def ensure_key(key)
34
+ @hash_depedencies[key] ||= Dependency.new
35
+ end
36
+ end
@@ -4,17 +4,18 @@ module ReactiveAccessors
4
4
  # Create a method to read a reactive value from an instance value. If it
5
5
  # is not setup, create it so it can be updated through the reactive value
6
6
  # at a later point.
7
+ def __reactive_dependency_get(var_name)
8
+ value_dep = instance_variable_get(:"@__#{var_name}_dependency")
9
+ value_dep ||= instance_variable_set(:"@__#{var_name}_dependency", Dependency.new)
10
+ end
11
+
7
12
  def reactive_reader(*names)
8
13
  names.each do |name|
9
14
  var_name = :"@#{name}"
10
15
  define_method(name.to_sym) do
11
16
  value = instance_variable_get(var_name)
12
17
 
13
- unless value
14
- value = ReactiveValue.new(nil)
15
-
16
- instance_variable_set(var_name, value)
17
- end
18
+ self.class.__reactive_dependency_get(name).depend
18
19
 
19
20
  value
20
21
  end
@@ -25,13 +26,9 @@ module ReactiveAccessors
25
26
  names.each do |name|
26
27
  var_name = :"@#{name}"
27
28
  define_method(:"#{name}=") do |new_value|
28
- value = instance_variable_get(var_name)
29
+ instance_variable_set(var_name, new_value)
29
30
 
30
- if value
31
- value.cur = new_value
32
- else
33
- instance_variable_set(var_name, ReactiveValue.new(value))
34
- end
31
+ self.class.__reactive_dependency_get(name).changed!
35
32
  end
36
33
  end
37
34
  end
@@ -1,12 +1,13 @@
1
- require 'volt/reactive/object_tracking'
2
- require 'volt/reactive/reactive_block'
1
+ require 'volt/reactive/eventable'
3
2
 
4
3
  class ReactiveArray# < Array
5
- include ReactiveTags
6
- include ObjectTracking
4
+ include Eventable
7
5
 
8
6
  def initialize(array=[])
9
7
  @array = array
8
+ @array_deps = []
9
+ @size_dep = Dependency.new
10
+ @old_size = 0
10
11
  end
11
12
 
12
13
  # Forward any missing methods to the array
@@ -18,64 +19,74 @@ class ReactiveArray# < Array
18
19
  @array.==(*args)
19
20
  end
20
21
 
21
- tag_method(:each) do
22
- destructive!
23
- end
24
22
  # At the moment, each just passes through.
25
23
  def each(&block)
26
24
  @array.each(&block)
27
25
  end
28
26
 
29
- tag_method(:[]=) do
30
- pass_reactive!
27
+ def count(&block)
28
+ if block
29
+ count = 0
30
+ size.times do |index|
31
+ if block.call(self[index]).true?
32
+ count += 1
33
+ end
34
+ end
35
+
36
+ return count
37
+ else
38
+ return size
39
+ end
31
40
  end
32
41
 
33
- # alias :__old_assign :[]=
34
- def []=(index, value)
35
- index_val = index.cur
42
+ # TODO: Handle a range
43
+ def [](index)
44
+ # Handle a negative index
45
+ index = size + index if index < 0
36
46
 
37
- if index_val < 0
38
- # Handle a negative index
39
- index_val = size + index_val
40
- end
47
+ # Get or create the dependency
48
+ dep = (@array_deps[index] ||= Dependency.new)
41
49
 
42
- # Clean old value
43
- __clear_element(index)
50
+ # Track the dependency
51
+ dep.depend
44
52
 
45
- @array[index.cur] = value
53
+ # Return the index
54
+ return @array[index]
55
+ end
46
56
 
47
- # Track new value
48
- __track_element(index, value)
57
+ def []=(index, value)
49
58
 
50
- # Also track the index if its reactive
51
- if index.reactive?
52
- # TODO: Need to clean this up when the index changes
53
- event_chain.add_object(index.reactive_manager) do |event, *args|
54
- trigger_for_index!(event, index.cur)
55
- end
56
- end
59
+ # Assign the new value
60
+ @array[index] = value
57
61
 
58
- # Trigger changed
59
- trigger_for_index!('changed', index_val)
62
+ trigger_for_index!(index)
63
+
64
+ trigger_size_change!
60
65
  end
61
66
 
62
- tag_method(:delete_at) do
63
- destructive!
67
+ def size
68
+ @size_dep.depend
69
+
70
+ return @array.size
64
71
  end
65
- # alias :__old_delete_at :delete_at
72
+ alias :length :size
73
+
66
74
  def delete_at(index)
67
- index_val = index.cur
75
+ # Handle a negative index
76
+ index = size + index if index < 0
68
77
 
69
- __clear_element(index)
78
+ model = @array.delete_at(index)
70
79
 
71
- model = @array.delete_at(index_val)
80
+ # Remove the dependency for that cell, and #remove it
81
+ index_deps = @array_deps.delete_at(index)
82
+ index_deps.remove if index_deps
72
83
 
73
- trigger_on_direct_listeners!('removed', index_val)
84
+ trigger_removed!(index)
74
85
 
75
86
  # Trigger a changed event for each element in the zone where the
76
- # lookup would change
87
+ # delete would change
77
88
  index.upto(self.size+1) do |position|
78
- trigger_for_index!('changed', position)
89
+ trigger_for_index!(position)
79
90
  end
80
91
 
81
92
  trigger_size_change!
@@ -86,35 +97,45 @@ class ReactiveArray# < Array
86
97
  end
87
98
 
88
99
 
89
- # Delete is implemented as part of delete_at
90
- tag_method(:delete) do
91
- destructive!
92
- end
93
100
  def delete(val)
94
- self.delete_at(@array.index(val))
101
+ index = @array.index(val)
102
+ if index
103
+ self.delete_at(index)
104
+ else
105
+ # Sometimes the model isn't loaded at the right state yet, so we
106
+ # just remove it from the persistor
107
+ @persistor.removed(val) if @persistor
108
+ end
95
109
  end
96
110
 
97
- # Removes all items in the array model.
98
- tag_method(:clear) do
99
- destructive!
100
- end
101
111
  def clear
112
+ old_size = @array.size
113
+
114
+ deps = @array_deps
115
+ @array_deps = []
116
+
117
+ # Trigger remove for each cell
118
+ old_size.times do |index|
119
+ trigger_removed!(old_size - index - 1)
120
+ end
121
+
122
+ # Trigger on each cell since we are clearing out the array
123
+ if deps
124
+ deps.each do |dep|
125
+ dep.changed! if dep
126
+ end
127
+ end
128
+
129
+ # clear the array
102
130
  @array = []
103
- trigger!('changed')
104
131
  end
105
132
 
106
- tag_method(:<<) do
107
- pass_reactive!
108
- end
109
133
  # alias :__old_append :<<
110
134
  def <<(value)
111
135
  result = (@array << value)
112
136
 
113
- # Track new value
114
- __track_element(self.size-1, value)
115
-
116
- trigger_for_index!('changed', self.size-1)
117
- trigger_on_direct_listeners!('added', self.size-1)
137
+ trigger_for_index!(self.size-1)
138
+ trigger_added!(self.size-1)
118
139
  trigger_size_change!
119
140
 
120
141
  return result
@@ -122,6 +143,7 @@ class ReactiveArray# < Array
122
143
 
123
144
 
124
145
  def +(array)
146
+ raise "not implemented yet"
125
147
  old_size = self.size
126
148
 
127
149
  # TODO: += is funky here, might need to make a .plus! method
@@ -129,7 +151,7 @@ class ReactiveArray# < Array
129
151
 
130
152
  old_size.upto(result.size-1) do |index|
131
153
  trigger_for_index!('changed', index)
132
- trigger_on_direct_listeners!('added', old_size + index)
154
+ trigger_added!(old_size + index)
133
155
  end
134
156
 
135
157
  trigger_size_change!
@@ -137,19 +159,16 @@ class ReactiveArray# < Array
137
159
  return result
138
160
  end
139
161
 
140
- tag_method(:insert) do
141
- destructive!
142
- end
143
162
  def insert(index, *objects)
144
163
  result = @array.insert(index, *objects)
145
164
 
146
165
  # All objects from index to the end have "changed"
147
- index.upto(result.size-1) do |idx|
148
- trigger_for_index!('changed', idx)
166
+ index.upto(result.size) do |index|
167
+ trigger_for_index!(index)
149
168
  end
150
169
 
151
170
  objects.size.times do |count|
152
- trigger_on_direct_listeners!('added', index+count)
171
+ trigger_added!(index+count)
153
172
  end
154
173
 
155
174
  trigger_size_change!
@@ -157,150 +176,38 @@ class ReactiveArray# < Array
157
176
  return result
158
177
  end
159
178
 
160
- def trigger_on_direct_listeners!(event, *args)
161
- trigger_by_scope!(event, *args) do |scope|
162
- # Only if it is bound directly to us. Don't pass
163
- # down the chain
164
- !scope || scope[0] == nil
165
- end
166
-
167
- end
168
-
169
- def trigger_size_change!
170
- trigger_by_scope!('changed') do |scope|
171
- # method_name, *args, block = scope
172
- method_name, args, block = split_scope(scope)
173
-
174
- result = case method_name && method_name.to_sym
175
- when :size, :length
176
- true
177
- else
178
- false
179
- end
180
-
181
- result
182
- end
183
- end
184
-
185
- # TODO: This is an opal work around. Currently there is a bug with destructuring
186
- # method_name, *args, block = scope
187
- def split_scope(scope)
188
- if scope
189
- scope = scope.dup
190
- method_name = scope.shift
191
- block = scope.pop
192
-
193
- return method_name, scope, block
194
- else
195
- return nil,[],nil
196
- end
197
- end
198
-
199
- # Trigger the changed event to any values fetched either through the
200
- # lookup ([]), #last, or any fetched through the array its self. (sum, max, etc...)
201
- # On an array, when an element is added or removed, we need to trigger change
202
- # events on each method that does the following:
203
- # 1. uses the whole array (max, sum, etc...)
204
- # 2. accesses this specific element - array[index]
205
- # 3. accesses an element via a method (first, last)
206
- def trigger_for_index!(event_name, index, *passed_args)
207
- self.trigger_by_scope!(event_name, *passed_args) do |scope|
208
- # method_name, *args, block = scope
209
- method_name, args, block = split_scope(scope)
210
-
211
- result = case method_name
212
- when nil
213
- # no method name means the event was bound directly, we don't
214
- # want to trigger changed on the array its self.
215
- false
216
- when :[]
217
- # Extract the current index if its reactive
218
- arg_index = args[0].cur
219
-
220
- # TODO: we could handle negative indicies better
221
- arg_index == index.cur || arg_index < 0
222
- when :last
223
- index.cur == self.size-1
224
- when :first
225
- index.cur == 0
226
- when :size, :length
227
- # Size does not depend on the contents of the cells
228
- false
229
- else
230
- true
231
- end
232
-
233
- result = false if method_name == :reject
234
-
235
- result
236
- end
237
- end
238
179
 
239
180
  def inspect
240
181
  "#<#{self.class.to_s}:#{object_id} #{@array.inspect}>"
241
182
  end
242
183
 
243
- # tag_method(:count) do
244
- # destructive!
245
- # end
246
- def count(*args, &block)
247
- # puts "GET COUNT"
248
- if block
249
- run_block = Proc.new do |source|
250
- count = 0
251
- source.cur.size.times do |index|
252
- val = source[index]
253
- result = block.call(val).cur
254
- if result == true
255
- count += 1
256
- end
257
- end
258
184
 
259
- count
185
+ private
186
+ # Check to see if the size has changed, trigger a change on size if it has
187
+ def trigger_size_change!
188
+ new_size = @array.size
189
+ if new_size != @old_size
190
+ @old_size = new_size
191
+ @size_dep.changed!
260
192
  end
261
-
262
- return ReactiveBlock.new(self, block, run_block)
263
- else
264
- @array.count(*args)
265
193
  end
266
- end
267
194
 
268
- def reject(*args, &block)
269
- if block
270
- run_block = Proc.new do |source|
271
- puts "RUN REJECT"
272
- new_array = []
273
- source.cur.size.times do |index|
274
- val = source[index]
275
- result = block.call(val).cur
276
- if result != true
277
- new_array << val.cur
278
- end
279
- end
195
+ def trigger_for_index!(index)
196
+ # Trigger a change for the cell
197
+ dep = @array_deps[index]
280
198
 
281
- ReactiveArray.new(new_array)
282
- end
283
-
284
- return ReactiveBlock.new(self, block, run_block)
285
- else
286
- @array.count
199
+ dep.changed! if dep
287
200
  end
288
- end
289
201
 
290
- private
291
202
 
292
- def __clear_element(index)
293
- # Cleanup any tracking on an index
294
- if @reactive_element_listeners && self[index].reactive?
295
- @reactive_element_listeners[index].remove
296
- @reactive_element_listeners.delete(index)
297
- end
203
+ def trigger_added!(index)
204
+ trigger!('added', index)
298
205
  end
299
206
 
300
- def __track_element(index, value)
301
- __setup_tracking(index, value) do |event, index, args|
302
- trigger_for_index!(event, index, *args)
303
- end
207
+ def trigger_removed!(index)
208
+ trigger!('removed', index)
304
209
  end
305
210
 
211
+
212
+
306
213
  end