volt 0.5.18 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +14 -0
- data/VERSION +1 -1
- data/app/volt/controllers/notices_controller.rb +9 -0
- data/app/volt/tasks/live_query/data_store.rb +12 -0
- data/app/volt/tasks/live_query/live_query.rb +86 -0
- data/app/volt/tasks/live_query/live_query_pool.rb +36 -0
- data/app/volt/tasks/live_query/query_tracker.rb +95 -0
- data/app/volt/tasks/query_tasks.rb +57 -0
- data/app/volt/tasks/store_tasks.rb +4 -17
- data/lib/volt.rb +2 -0
- data/lib/volt/console.rb +1 -1
- data/lib/volt/controllers/model_controller.rb +4 -0
- data/lib/volt/extra_core/array.rb +9 -0
- data/lib/volt/extra_core/extra_core.rb +1 -0
- data/lib/volt/extra_core/hash.rb +11 -0
- data/lib/volt/extra_core/object.rb +4 -0
- data/lib/volt/models/array_model.rb +56 -0
- data/lib/volt/models/model.rb +6 -11
- data/lib/volt/models/model_helpers.rb +12 -0
- data/lib/volt/models/persistors/array_store.rb +120 -21
- data/lib/volt/models/persistors/model_identity_map.rb +12 -0
- data/lib/volt/models/persistors/model_store.rb +20 -60
- data/lib/volt/models/persistors/query/query_listener.rb +87 -0
- data/lib/volt/models/persistors/query/query_listener_pool.rb +9 -0
- data/lib/volt/models/persistors/store.rb +11 -13
- data/lib/volt/models/url.rb +1 -1
- data/lib/volt/page/bindings/attribute_binding.rb +2 -2
- data/lib/volt/page/bindings/base_binding.rb +13 -1
- data/lib/volt/page/bindings/component_binding.rb +1 -1
- data/lib/volt/page/bindings/content_binding.rb +2 -2
- data/lib/volt/page/bindings/each_binding.rb +25 -21
- data/lib/volt/page/bindings/event_binding.rb +4 -6
- data/lib/volt/page/bindings/if_binding.rb +4 -5
- data/lib/volt/page/bindings/template_binding.rb +4 -4
- data/lib/volt/page/channel.rb +0 -1
- data/lib/volt/page/document.rb +7 -0
- data/lib/volt/page/page.rb +4 -4
- data/lib/volt/page/reactive_template.rb +2 -2
- data/lib/volt/page/targets/dom_section.rb +5 -0
- data/lib/volt/page/tasks.rb +10 -40
- data/lib/volt/page/template_renderer.rb +4 -4
- data/lib/volt/reactive/events.rb +14 -0
- data/lib/volt/reactive/reactive_array.rb +17 -7
- data/lib/volt/reactive/reactive_value.rb +65 -1
- data/lib/volt/server.rb +1 -1
- data/lib/volt/server/if_binding_setup.rb +3 -1
- data/lib/volt/server/socket_connection_handler.rb +7 -5
- data/lib/volt/server/template_parser.rb +7 -7
- data/lib/volt/tasks/dispatcher.rb +3 -0
- data/lib/volt/utils/ejson.rb +9 -0
- data/lib/volt/utils/generic_counting_pool.rb +44 -0
- data/lib/volt/utils/generic_pool.rb +88 -0
- data/spec/models/reactive_array_spec.rb +43 -0
- data/spec/models/reactive_generator_spec.rb +58 -0
- data/spec/models/reactive_value_spec.rb +6 -0
- data/spec/page/bindings/content_binding_spec.rb +36 -0
- data/spec/spec_helper.rb +13 -12
- data/spec/tasks/live_query_spec.rb +20 -0
- data/spec/tasks/query_tasks.rb +10 -0
- data/spec/tasks/query_tracker_spec.rb +120 -0
- data/spec/templates/template_binding_spec.rb +16 -10
- data/spec/utils/generic_counting_pool_spec.rb +36 -0
- data/spec/utils/generic_pool_spec.rb +50 -0
- metadata +29 -5
- data/app/volt/tasks/channel_tasks.rb +0 -55
- data/spec/tasks/channel_tasks_spec.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b53ad931d8386ec350af55adf688c865a96021aa
|
4
|
+
data.tar.gz: db65b8addb0e41e518cc1494a0a75fdee4afc84a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b32dffb18e5e0e0052c71c15f7d3540fd1d7bcabb0d3c04d6b8187d4ec40044b4746fdb87a53a271c91bffad9a25cf49fc8bd1e3928bf4916ce839823b34594
|
7
|
+
data.tar.gz: 97d1d0c43d453482904cff905109192dccdfb18a388fddc5fa285e1958241802da9b64aeb6ed652d801d1d5aac6c4f1ad2d1c2ec6e932eb7dfd0de7117d61373
|
data/Readme.md
CHANGED
@@ -196,6 +196,8 @@ If you want these to be used reactively, see the section on [with](#with)
|
|
196
196
|
|
197
197
|
Also, due to a small limitation in ruby, ReactiveValue's always are truthy. See the [truthy checks](#truthy-checks-true-false-or-and-and) section on how to check for truth.
|
198
198
|
|
199
|
+
When passing something that may contain reactive values to a JS function, you can call ```.deep_cur``` on any object to get back a copy that will have all reactive value's turned into their current value.
|
200
|
+
|
199
201
|
### Current Status
|
200
202
|
|
201
203
|
NOTE: currently ReactiveValue's are not complete. At the moment, they do not handle methods that are passed blocks (or procs, lambda's). This is planned, but not complete. At the moment you can use [with](#with) to accomplish similar things.
|
@@ -689,3 +691,15 @@ Controllers provide a .channel method, that you can use to get the status of the
|
|
689
691
|
## Accessing DOM section in a controller
|
690
692
|
|
691
693
|
TODO
|
694
|
+
|
695
|
+
|
696
|
+
# Data Store
|
697
|
+
|
698
|
+
**Work in process**
|
699
|
+
|
700
|
+
| state | events bound | description |
|
701
|
+
|-------------|--------------|--------------------------------------------------------------|
|
702
|
+
| not_loaded | no | no events and no one has accessed the data in the model |
|
703
|
+
| loading | maybe | someone either accessed the data or bound an event |
|
704
|
+
| loaded | yes | data is loaded and there is an event bound |
|
705
|
+
| dirty | no | data was either accessed without binding an event, or an event was bound, but later unbound. |
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'query_tracker'
|
2
|
+
|
3
|
+
# Tracks a channel and a query on a collection. Alerts
|
4
|
+
# the listener when the data in the query changes.
|
5
|
+
class LiveQuery
|
6
|
+
attr_reader :current_ids, :collection, :query
|
7
|
+
|
8
|
+
def initialize(pool, data_store, collection, query)
|
9
|
+
@pool = pool
|
10
|
+
@collection = collection
|
11
|
+
@query = query
|
12
|
+
|
13
|
+
@channels = []
|
14
|
+
@data_store = data_store
|
15
|
+
|
16
|
+
@query_tracker = QueryTracker.new(self, @data_store)
|
17
|
+
|
18
|
+
run
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(skip_channel=nil)
|
22
|
+
@query_tracker.run(skip_channel)
|
23
|
+
end
|
24
|
+
|
25
|
+
def notify_removed(ids, skip_channel)
|
26
|
+
notify! do |channel|
|
27
|
+
# puts "Removed: #{ids.inspect} to #{channel.inspect}"
|
28
|
+
channel.send_message("removed", nil, @collection, @query, ids)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def notify_added(index, data, skip_channel)
|
33
|
+
notify! do |channel|
|
34
|
+
# puts "Added: #{index} - #{data.inspect} to #{channel.inspect}"
|
35
|
+
channel.send_message("added", nil, @collection, @query, index, data)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify_moved(id, new_position, skip_channel)
|
40
|
+
notify! do |channel|
|
41
|
+
# puts "Moved: #{id}, #{new_position} to #{channel.inspect}"
|
42
|
+
channel.send_message("moved", nil, @collection, @query, id, new_position)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def notify_changed(id, data, skip_channel)
|
47
|
+
notify!(skip_channel) do |channel|
|
48
|
+
# puts "Changed: #{id}, #{data} to #{channel.inspect}"
|
49
|
+
channel.send_message("changed", nil, @collection, @query, id, data)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# return the query results the first time a channel connects
|
55
|
+
def initial_data
|
56
|
+
@query_tracker.results.map.with_index {|data, index| [index, data] }
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_channel(channel)
|
60
|
+
@channels << channel
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_channel(channel)
|
64
|
+
@channels.delete(channel)
|
65
|
+
|
66
|
+
if @channels.size == 0
|
67
|
+
# remove this query, no one is listening anymore
|
68
|
+
@pool.remove(@collection, @query)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def notify!(skip_channel=nil, only_channel=nil)
|
73
|
+
if only_channel
|
74
|
+
channels = [only_channel]
|
75
|
+
else
|
76
|
+
channels = @channels
|
77
|
+
end
|
78
|
+
|
79
|
+
channels = channels.reject {|c| c == skip_channel }
|
80
|
+
|
81
|
+
channels.each do |channel|
|
82
|
+
yield(channel)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'live_query'
|
2
|
+
require 'volt/utils/generic_pool'
|
3
|
+
|
4
|
+
class LiveQueryPool < GenericPool
|
5
|
+
def initialize(data_store)
|
6
|
+
super()
|
7
|
+
@data_store = data_store
|
8
|
+
end
|
9
|
+
|
10
|
+
def lookup(collection, query)
|
11
|
+
query = normalize_query(query)
|
12
|
+
|
13
|
+
return super(collection, query)
|
14
|
+
end
|
15
|
+
|
16
|
+
def updated_collection(collection, skip_channel)
|
17
|
+
lookup_all(collection).each do |live_query|
|
18
|
+
# puts "RUN ON: #{live_query} with #{live_query.instance_variable_get('@channels').inspect}"
|
19
|
+
live_query.run(skip_channel)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# Creates the live query if it doesn't exist, and stores it so it
|
25
|
+
# can be found later.
|
26
|
+
def create(collection, query)
|
27
|
+
# If not already setup, create a new one for this collection/query
|
28
|
+
return LiveQuery.new(self, @data_store, collection, query)
|
29
|
+
end
|
30
|
+
|
31
|
+
def normalize_query(query)
|
32
|
+
# TODO: add something to sort query properties so the queries are
|
33
|
+
# always compared the same.
|
34
|
+
return query
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# The query tracker runs queries and then tracks the changes
|
2
|
+
# that take place.
|
3
|
+
class QueryTracker
|
4
|
+
attr_accessor :results
|
5
|
+
def initialize(live_query, data_store)
|
6
|
+
@live_query = live_query
|
7
|
+
@data_store = data_store
|
8
|
+
|
9
|
+
# Stores the list of id's currently associated with this query
|
10
|
+
@current_ids = []
|
11
|
+
@results = []
|
12
|
+
@results_hash = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Runs the query, stores the results and updates the current_ids
|
16
|
+
def run(skip_channel=nil)
|
17
|
+
@previous_results = @results
|
18
|
+
@previous_results_hash = @results_hash
|
19
|
+
@previous_ids = @current_ids
|
20
|
+
|
21
|
+
# Run the query again
|
22
|
+
@results = @data_store.query(@live_query.collection, @live_query.query)
|
23
|
+
|
24
|
+
# Update the current_ids
|
25
|
+
@current_ids = @results.map {|r| r['_id'] }
|
26
|
+
@results_hash = Hash[@results.map {|r| [r['_id'], r] }]
|
27
|
+
|
28
|
+
process_changes(skip_channel)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Looks at the changes in the last run and sends out notices
|
32
|
+
# all changes.
|
33
|
+
def process_changes(skip_channel)
|
34
|
+
if @previous_ids
|
35
|
+
detect_removed(skip_channel)
|
36
|
+
|
37
|
+
detect_added_and_moved(skip_channel)
|
38
|
+
|
39
|
+
detect_changed(skip_channel)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def detect_removed(skip_channel)
|
44
|
+
# Removed models
|
45
|
+
removed_ids = @previous_ids - @current_ids
|
46
|
+
if removed_ids.size > 0
|
47
|
+
@live_query.notify_removed(removed_ids, skip_channel)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Update @previous_ids to relect the removed
|
51
|
+
@previous_ids = @previous_ids & @current_ids
|
52
|
+
end
|
53
|
+
|
54
|
+
# Loop through the new list, tracking in the old, notifies of any that
|
55
|
+
# have been added or moved.
|
56
|
+
def detect_added_and_moved(skip_channel)
|
57
|
+
previous_index = 0
|
58
|
+
@current_ids.each_with_index do |id,index|
|
59
|
+
if (cur_previous = @previous_ids[previous_index]) && cur_previous == id
|
60
|
+
# Same in both previous and new
|
61
|
+
previous_index += 1
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
# We have an item that didn't match the current position's previous
|
66
|
+
# TODO: make a hash so we don't have to do include?
|
67
|
+
if @previous_ids.include?(id)
|
68
|
+
# The location from the previous has changed, move to correct location.
|
69
|
+
|
70
|
+
# Remove from previous_ids, since it will be moved and we will be past it.
|
71
|
+
@previous_ids.delete(id)
|
72
|
+
|
73
|
+
@live_query.notify_moved(id, index, skip_channel)
|
74
|
+
else
|
75
|
+
# TODO: Faster lookup
|
76
|
+
data = @results_hash[id]
|
77
|
+
@live_query.notify_added(index, data, skip_channel)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Finds all items in the previous results that have new values, and alerts
|
83
|
+
# of changes.
|
84
|
+
def detect_changed(skip_channel)
|
85
|
+
not_added_or_removed = @previous_ids & @current_ids
|
86
|
+
|
87
|
+
not_added_or_removed.each do |id|
|
88
|
+
if @previous_results_hash[id] != (data = @results_hash[id])
|
89
|
+
# Data hash changed
|
90
|
+
@live_query.notify_changed(id, data, skip_channel)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative 'live_query/data_store'
|
2
|
+
require_relative 'live_query/live_query_pool'
|
3
|
+
|
4
|
+
class QueryTasks
|
5
|
+
@@live_query_pool = LiveQueryPool.new(DataStore.new)
|
6
|
+
@@channel_live_queries = {}
|
7
|
+
|
8
|
+
def self.live_query_pool
|
9
|
+
@@live_query_pool
|
10
|
+
end
|
11
|
+
|
12
|
+
# The dispatcher passes its self in
|
13
|
+
def initialize(channel, dispatcher=nil)
|
14
|
+
@channel = channel
|
15
|
+
@dispatcher = dispatcher
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_listener(collection, query)
|
19
|
+
live_query = @@live_query_pool.lookup(collection, query)
|
20
|
+
track_channel_in_live_query(live_query)
|
21
|
+
|
22
|
+
live_query.add_channel(@channel)
|
23
|
+
|
24
|
+
# Return the initial data
|
25
|
+
return live_query.initial_data
|
26
|
+
end
|
27
|
+
|
28
|
+
# Remove a listening channel, the LiveQuery will automatically remove
|
29
|
+
# itsself from the pool when there are no channels.
|
30
|
+
def remove_listener(collection, query)
|
31
|
+
live_query = @@live_query_pool.lookup(collection, query)
|
32
|
+
live_query.remove_channel(@channel)
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
# Removes a channel from all associated live queries
|
37
|
+
def close!
|
38
|
+
live_queries = @@channel_live_queries[@channel]
|
39
|
+
|
40
|
+
if live_queries
|
41
|
+
live_queries.each do |live_query|
|
42
|
+
live_query.remove_channel(@channel)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
@@channel_live_queries.delete(@channel)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
# Tracks that this channel will be notified from the live query.
|
51
|
+
def track_channel_in_live_query(live_query)
|
52
|
+
@@channel_live_queries[@channel] ||= []
|
53
|
+
@@channel_live_queries[@channel] << live_query
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'mongo'
|
2
|
-
require_relative 'channel_tasks'
|
3
2
|
|
4
3
|
class StoreTasks
|
5
4
|
def initialize(channel=nil, dispatcher=nil)
|
@@ -15,7 +14,7 @@ class StoreTasks
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def save(collection, data)
|
18
|
-
puts "Insert: #{data.inspect} on #{collection.inspect}"
|
17
|
+
# puts "Insert: #{data.inspect} on #{collection.inspect}"
|
19
18
|
|
20
19
|
data = data.symbolize_keys
|
21
20
|
id = data[:_id]
|
@@ -24,10 +23,6 @@ class StoreTasks
|
|
24
23
|
# TODO: Seems mongo is dumb and doesn't let you upsert with custom id's
|
25
24
|
begin
|
26
25
|
@@db[collection].insert(data)
|
27
|
-
|
28
|
-
# Message that we inserted a new item
|
29
|
-
puts "SENDING DATA: #{data.inspect}"
|
30
|
-
ChannelTasks.send_message_to_channel("#{collection}-added", ['added', nil, collection, data], @channel)
|
31
26
|
rescue Mongo::OperationFailure => error
|
32
27
|
# Really mongo client?
|
33
28
|
if error.message[/^11000[:]/]
|
@@ -40,21 +35,13 @@ class StoreTasks
|
|
40
35
|
end
|
41
36
|
end
|
42
37
|
|
43
|
-
|
44
|
-
ChannelTasks.send_message_to_channel("#{collection}##{id}", ['changed', nil, id, data], @channel)
|
45
|
-
end
|
46
|
-
|
47
|
-
def find(collection, scope, query=nil)
|
48
|
-
results = @@db[collection].find(scope).to_a.map {|item| item.symbolize_keys }
|
49
|
-
puts "FIND: #{collection.inspect} - #{scope} - #{results.inspect}"
|
50
|
-
|
51
|
-
return results
|
38
|
+
QueryTasks.live_query_pool.updated_collection(collection, @channel)
|
52
39
|
end
|
53
|
-
|
40
|
+
|
54
41
|
def delete(collection, id)
|
55
42
|
puts "DELETE: #{collection.inspect} - #{id.inspect}"
|
56
43
|
@@db[collection].remove('_id' => id)
|
57
44
|
|
58
|
-
|
45
|
+
QueryTasks.live_query_pool.updated_collection(collection, @channel)
|
59
46
|
end
|
60
47
|
end
|
data/lib/volt.rb
CHANGED
data/lib/volt/console.rb
CHANGED