volt 0.7.23 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +8 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +8 -0
- data/Guardfile +2 -2
- data/Readme.md +139 -136
- data/VERSION +1 -1
- data/app/volt/assets/js/setImmediate.js +175 -0
- data/app/volt/tasks/live_query/data_store.rb +0 -2
- data/app/volt/tasks/live_query/live_query.rb +4 -4
- data/docs/GETTING_STARTED.md +24 -3
- data/docs/WHY.md +1 -22
- data/lib/volt.rb +20 -1
- data/lib/volt/console.rb +20 -0
- data/lib/volt/controllers/model_controller.rb +25 -11
- data/lib/volt/extra_core/object.rb +2 -14
- data/lib/volt/extra_core/string.rb +4 -0
- data/lib/volt/models.rb +0 -1
- data/lib/volt/models/array_model.rb +8 -16
- data/lib/volt/models/cursor.rb +1 -1
- data/lib/volt/models/model.rb +40 -60
- data/lib/volt/models/model_hash_behaviour.rb +10 -24
- data/lib/volt/models/model_helpers.rb +2 -2
- data/lib/volt/models/model_state.rb +1 -1
- data/lib/volt/models/model_wrapper.rb +4 -4
- data/lib/volt/models/persistors/array_store.rb +44 -28
- data/lib/volt/models/persistors/base.rb +1 -1
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/params.rb +5 -1
- data/lib/volt/models/persistors/query/query_listener.rb +2 -0
- data/lib/volt/models/persistors/store.rb +3 -2
- data/lib/volt/models/persistors/store_state.rb +7 -2
- data/lib/volt/models/url.rb +35 -29
- data/lib/volt/models/validations.rb +7 -17
- data/lib/volt/page/bindings/attribute_binding.rb +57 -39
- data/lib/volt/page/bindings/base_binding.rb +0 -14
- data/lib/volt/page/bindings/content_binding.rb +15 -18
- data/lib/volt/page/bindings/each_binding.rb +67 -34
- data/lib/volt/page/bindings/if_binding.rb +15 -12
- data/lib/volt/page/bindings/template_binding.rb +77 -59
- data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
- data/lib/volt/page/channel.rb +22 -38
- data/lib/volt/page/channel_stub.rb +3 -6
- data/lib/volt/page/page.rb +24 -26
- data/lib/volt/page/string_template_renderer.rb +46 -0
- data/lib/volt/page/sub_context.rb +7 -1
- data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
- data/lib/volt/page/tasks.rb +3 -2
- data/lib/volt/page/url_tracker.rb +4 -3
- data/lib/volt/reactive/computation.rb +131 -0
- data/lib/volt/reactive/dependency.rb +71 -0
- data/lib/volt/reactive/eventable.rb +82 -0
- data/lib/volt/reactive/hash_dependency.rb +36 -0
- data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
- data/lib/volt/reactive/reactive_array.rb +100 -193
- data/lib/volt/reactive/reactive_hash.rb +49 -0
- data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
- data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
- data/lib/volt/server/html_parser/view_scope.rb +31 -1
- data/spec/apps/kitchen_sink/Gemfile +4 -8
- data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
- data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
- data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
- data/spec/controllers/reactive_accessors_spec.rb +13 -15
- data/spec/integration/bindings_spec.rb +159 -0
- data/spec/integration/templates_spec.rb +15 -0
- data/spec/models/model_spec.rb +130 -228
- data/spec/reactive/computation_spec.rb +63 -0
- data/spec/reactive/dependency_spec.rb +5 -0
- data/spec/reactive/eventable_spec.rb +48 -0
- data/spec/reactive/reactive_array_spec.rb +97 -0
- data/spec/router/routes_spec.rb +26 -27
- data/spec/server/html_parser/view_parser_spec.rb +3 -21
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/templates/project/app/main/views/main/main.html +2 -2
- metadata +29 -41
- data/lib/volt/extra_core/time.rb +0 -16
- data/lib/volt/page/draw_cycle.rb +0 -31
- data/lib/volt/page/memory_test.rb +0 -26
- data/lib/volt/page/reactive_template.rb +0 -32
- data/lib/volt/reactive/array_extensions.rb +0 -12
- data/lib/volt/reactive/destructive_methods.rb +0 -19
- data/lib/volt/reactive/event_chain.rb +0 -125
- data/lib/volt/reactive/events.rb +0 -216
- data/lib/volt/reactive/object_tracking.rb +0 -14
- data/lib/volt/reactive/reactive_block.rb +0 -88
- data/lib/volt/reactive/reactive_generator.rb +0 -44
- data/lib/volt/reactive/reactive_tags.rb +0 -71
- data/lib/volt/reactive/reactive_value.rb +0 -427
- data/lib/volt/reactive/string_extensions.rb +0 -31
- data/spec/integration/test_integration_spec.rb +0 -14
- data/spec/models/event_chain_spec.rb +0 -150
- data/spec/models/model_buffers_spec.rb +0 -9
- data/spec/models/old_model_spec.rb +0 -67
- data/spec/models/reactive_array_spec.rb +0 -364
- data/spec/models/reactive_block_spec.rb +0 -13
- data/spec/models/reactive_call_times_spec.rb +0 -28
- data/spec/models/reactive_generator_spec.rb +0 -58
- data/spec/models/reactive_tags_spec.rb +0 -35
- data/spec/models/reactive_value_spec.rb +0 -370
- data/spec/models/store_spec.rb +0 -16
- 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
|
-
|
|
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
|
-
|
|
29
|
+
instance_variable_set(var_name, new_value)
|
|
29
30
|
|
|
30
|
-
|
|
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/
|
|
2
|
-
require 'volt/reactive/reactive_block'
|
|
1
|
+
require 'volt/reactive/eventable'
|
|
3
2
|
|
|
4
3
|
class ReactiveArray# < Array
|
|
5
|
-
include
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
#
|
|
34
|
-
def []
|
|
35
|
-
|
|
42
|
+
# TODO: Handle a range
|
|
43
|
+
def [](index)
|
|
44
|
+
# Handle a negative index
|
|
45
|
+
index = size + index if index < 0
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
index_val = size + index_val
|
|
40
|
-
end
|
|
47
|
+
# Get or create the dependency
|
|
48
|
+
dep = (@array_deps[index] ||= Dependency.new)
|
|
41
49
|
|
|
42
|
-
#
|
|
43
|
-
|
|
50
|
+
# Track the dependency
|
|
51
|
+
dep.depend
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
# Return the index
|
|
54
|
+
return @array[index]
|
|
55
|
+
end
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
__track_element(index, value)
|
|
57
|
+
def []=(index, value)
|
|
49
58
|
|
|
50
|
-
#
|
|
51
|
-
|
|
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
|
-
|
|
59
|
-
|
|
62
|
+
trigger_for_index!(index)
|
|
63
|
+
|
|
64
|
+
trigger_size_change!
|
|
60
65
|
end
|
|
61
66
|
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
def size
|
|
68
|
+
@size_dep.depend
|
|
69
|
+
|
|
70
|
+
return @array.size
|
|
64
71
|
end
|
|
65
|
-
|
|
72
|
+
alias :length :size
|
|
73
|
+
|
|
66
74
|
def delete_at(index)
|
|
67
|
-
|
|
75
|
+
# Handle a negative index
|
|
76
|
+
index = size + index if index < 0
|
|
68
77
|
|
|
69
|
-
|
|
78
|
+
model = @array.delete_at(index)
|
|
70
79
|
|
|
71
|
-
|
|
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
|
-
|
|
84
|
+
trigger_removed!(index)
|
|
74
85
|
|
|
75
86
|
# Trigger a changed event for each element in the zone where the
|
|
76
|
-
#
|
|
87
|
+
# delete would change
|
|
77
88
|
index.upto(self.size+1) do |position|
|
|
78
|
-
trigger_for_index!(
|
|
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
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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
|
|
148
|
-
trigger_for_index!(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
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
|
|
293
|
-
|
|
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
|
|
301
|
-
|
|
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
|