volt 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/volt/assets/css/notices.css.scss +8 -0
- data/app/volt/tasks/channel_tasks.rb +33 -0
- data/app/volt/tasks/store_tasks.rb +45 -0
- data/app/volt/views/notices/index.html +11 -0
- data/lib/volt/controllers/model_controller.rb +4 -0
- data/lib/volt/models/array_model.rb +11 -1
- data/lib/volt/models/model.rb +22 -14
- data/lib/volt/models/model_wrapper.rb +13 -6
- data/lib/volt/models/params.rb +6 -1
- data/lib/volt/models/params_array.rb +9 -0
- data/lib/volt/models/store.rb +164 -0
- data/lib/volt/models/store_array.rb +15 -0
- data/lib/volt/models/url.rb +0 -4
- data/lib/volt/models.rb +1 -0
- data/lib/volt/{templates → page/bindings}/attribute_binding.rb +8 -7
- data/lib/volt/{templates → page/bindings}/base_binding.rb +6 -1
- data/lib/volt/{templates → page/bindings}/content_binding.rb +1 -1
- data/lib/volt/{templates → page/bindings}/each_binding.rb +15 -6
- data/lib/volt/{templates → page/bindings}/event_binding.rb +1 -5
- data/lib/volt/{templates → page/bindings}/if_binding.rb +1 -1
- data/lib/volt/{templates → page/bindings}/template_binding.rb +16 -7
- data/lib/volt/page/channel.rb +105 -0
- data/lib/volt/{templates → page}/document_events.rb +0 -0
- data/lib/volt/{templates → page}/memory_test.rb +0 -0
- data/lib/volt/{templates → page}/page.rb +22 -21
- data/lib/volt/{templates → page}/reactive_template.rb +0 -0
- data/lib/volt/{templates → page}/render_queue.rb +0 -0
- data/lib/volt/{templates → page}/sub_context.rb +0 -0
- data/lib/volt/{templates → page}/targets/attribute_section.rb +1 -1
- data/lib/volt/{templates → page}/targets/attribute_target.rb +4 -4
- data/lib/volt/{templates → page}/targets/base_section.rb +0 -0
- data/lib/volt/{templates → page}/targets/binding_document/base_node.rb +0 -0
- data/lib/volt/{templates → page}/targets/binding_document/component_node.rb +1 -1
- data/lib/volt/{templates → page}/targets/binding_document/html_node.rb +1 -1
- data/lib/volt/{templates → page}/targets/dom_section.rb +1 -1
- data/lib/volt/{templates → page}/targets/dom_target.rb +2 -2
- data/lib/volt/page/tasks.rb +61 -0
- data/lib/volt/{templates → page}/template_renderer.rb +1 -1
- data/lib/volt/reactive/destructive_methods.rb +19 -0
- data/lib/volt/reactive/event_chain.rb +13 -19
- data/lib/volt/reactive/events.rb +30 -116
- data/lib/volt/reactive/object_tracker.rb +29 -22
- data/lib/volt/reactive/reactive_array.rb +2 -3
- data/lib/volt/reactive/reactive_tags.rb +7 -0
- data/lib/volt/reactive/reactive_value.rb +86 -81
- data/lib/volt/reactive/string_extensions.rb +0 -3
- data/lib/volt/router/routes.rb +2 -2
- data/lib/volt/server/channel_handler.rb +24 -4
- data/lib/volt/server/component_handler.rb +2 -1
- data/lib/volt/server/rack/component_files.rb +3 -2
- data/lib/volt/server/rack/component_paths.rb +11 -1
- data/lib/volt/server/rack/index_files.rb +1 -1
- data/lib/volt/server.rb +16 -0
- data/lib/volt/store/mongo.rb +1 -1
- data/lib/volt/tasks/dispatcher.rb +25 -0
- data/spec/models/model_spec.rb +90 -15
- data/spec/models/params_spec.rb +16 -0
- data/spec/models/reactive_array_spec.rb +17 -18
- data/spec/models/reactive_value_spec.rb +11 -0
- data/spec/models/store_spec.rb +16 -0
- data/spec/server/rack/component_files_spec.rb +18 -16
- data/spec/server/rack/component_paths_spec.rb +21 -19
- data/spec/templates/targets/binding_document/component_node_spec.rb +1 -1
- data/spec/templates/template_binding_spec.rb +1 -1
- data/templates/project/app/home/views/index/index.html +2 -0
- data/volt.gemspec +2 -0
- metadata +67 -25
- data/lib/volt/templates/channel.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d7bacdfeed9b6502911eb0eeea86c91ff909e58
|
4
|
+
data.tar.gz: f7a9dbc54254ba479f159054500e0d7be94383d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fc4b3f656d8b40a8160eb5f386d0e76e156eb282ea3eb0f4359a246b2147127fd632eefea78f78916238474ecc721f12e88c22d1e8abac95b632b45bb4947c8
|
7
|
+
data.tar.gz: 559444ac5f139576652669add6d7fe23b7532b4dd1a6b8fe849181e584fad67298597668f0d975bd25338791c398ac952b663f3b88208564ead76607d93ce02b
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.3.
|
1
|
+
0.3.8
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class ChannelTasks
|
2
|
+
@@listeners = {}
|
3
|
+
|
4
|
+
def initialize(channel, dispatcher)
|
5
|
+
@channel = channel
|
6
|
+
@dispatcher = dispatcher
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_listener(channel_name)
|
10
|
+
@@listeners[channel_name] ||= []
|
11
|
+
@@listeners[channel_name] << @channel
|
12
|
+
end
|
13
|
+
|
14
|
+
def remove_listener(channel_name)
|
15
|
+
if @@listeners[channel_name]
|
16
|
+
@@listeners[channel_name].delete(@channel)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.send_message_to_channel(channel_name, message, skip_channel)
|
21
|
+
listeners = @@listeners[channel_name]
|
22
|
+
|
23
|
+
if listeners
|
24
|
+
listeners.each do |listener|
|
25
|
+
# We might need to skip a channel if the update came in on this
|
26
|
+
# channel.
|
27
|
+
next if listener == skip_channel
|
28
|
+
|
29
|
+
listener.send_message(*message)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mongo'
|
2
|
+
require_relative 'channel_tasks'
|
3
|
+
|
4
|
+
class StoreTasks
|
5
|
+
def initialize(channel=nil, dispatcher=nil)
|
6
|
+
@@mongo_db ||= Mongo::MongoClient.new("localhost", 27017)
|
7
|
+
@@db ||= @@mongo_db.db("development")
|
8
|
+
|
9
|
+
@channel = channel
|
10
|
+
@dispatcher = dispatcher
|
11
|
+
end
|
12
|
+
|
13
|
+
def db
|
14
|
+
@@db
|
15
|
+
end
|
16
|
+
|
17
|
+
def save(collection, data)
|
18
|
+
puts "Insert: #{data.inspect} on #{collection.inspect}"
|
19
|
+
# Try to create
|
20
|
+
# TODO: Seems mongo is dumb and doesn't let you upsert with custom id's
|
21
|
+
begin
|
22
|
+
@@db[collection].insert(data)
|
23
|
+
id = {'_id' => data.delete('_id')}
|
24
|
+
rescue Mongo::OperationFailure => error
|
25
|
+
# Really mongo client?
|
26
|
+
if error.message[/^11000[:]/]
|
27
|
+
# Update because the id already exists
|
28
|
+
id = {'_id' => data.delete('_id')}
|
29
|
+
@@db[collection].update(id, data)
|
30
|
+
else
|
31
|
+
raise
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
id = id['_id']
|
36
|
+
# ChannelHandler.send_message_all(@channel, 'update', nil, id, data.merge('_id' => id))
|
37
|
+
|
38
|
+
ChannelTasks.send_message_to_channel("#{collection}##{id}", ['update', nil, id, data.merge('_id' => id)], @channel)
|
39
|
+
end
|
40
|
+
|
41
|
+
def find(collection, scope, query=nil)
|
42
|
+
puts "FIND: #{collection.inspect} - #{scope}"
|
43
|
+
return @@db[collection].find(scope).to_a
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<:body>
|
2
|
+
{#if page._reloading}
|
3
|
+
<div class="notices alert alert-info">Reloading...</div>
|
4
|
+
{/}
|
5
|
+
{#if channel.state == :closed}
|
6
|
+
<div class="notices alert alert-info">
|
7
|
+
Connection Lost... {channel.error}...
|
8
|
+
{#if channel.reconnect_interval} Reconnecting in {(channel.reconnect_interval / 1000.0).round} sec{/}
|
9
|
+
</div>
|
10
|
+
{/}
|
11
|
+
</:body>
|
@@ -2,10 +2,12 @@ require 'volt/models/model_wrapper'
|
|
2
2
|
|
3
3
|
class ArrayModel < ReactiveArray
|
4
4
|
include ModelWrapper
|
5
|
+
|
6
|
+
attr_reader :parent, :path
|
5
7
|
|
6
8
|
def initialize(array=[], parent=nil, path=nil)
|
7
9
|
@parent = parent
|
8
|
-
@path = path
|
10
|
+
@path = path || []
|
9
11
|
|
10
12
|
array = wrap_values(array)
|
11
13
|
|
@@ -34,4 +36,12 @@ class ArrayModel < ReactiveArray
|
|
34
36
|
args = wrap_values(args)
|
35
37
|
super(*args)
|
36
38
|
end
|
39
|
+
|
40
|
+
def new_model(*args)
|
41
|
+
Model.new(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_array_model(*args)
|
45
|
+
ArrayModel.new(*args)
|
46
|
+
end
|
37
47
|
end
|
data/lib/volt/models/model.rb
CHANGED
@@ -33,9 +33,9 @@ class Model
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def initialize(attributes={}, parent=nil, path=nil, class_paths=nil)
|
36
|
-
self.attributes = wrap_values(attributes)
|
37
36
|
@parent = parent
|
38
|
-
@path = path
|
37
|
+
@path = path || []
|
38
|
+
self.attributes = wrap_values(attributes)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Pass the comparison through
|
@@ -58,9 +58,6 @@ class Model
|
|
58
58
|
end
|
59
59
|
|
60
60
|
tag_all_methods do
|
61
|
-
destructive! do |method_name|
|
62
|
-
method_name[0] == '_' && method_name[-1] == '='
|
63
|
-
end
|
64
61
|
pass_reactive! do |method_name|
|
65
62
|
method_name[0] == '_' && method_name[-1] == '='
|
66
63
|
end
|
@@ -89,7 +86,7 @@ class Model
|
|
89
86
|
value = args[0]
|
90
87
|
__assign_element(attribute_name, value)
|
91
88
|
|
92
|
-
attributes[attribute_name] = wrap_value(value)
|
89
|
+
attributes[attribute_name] = wrap_value(value, [attribute_name])
|
93
90
|
trigger_by_attribute!('changed', attribute_name)
|
94
91
|
end
|
95
92
|
|
@@ -101,7 +98,7 @@ class Model
|
|
101
98
|
# Reading an attribute, we may get back a nil model.
|
102
99
|
method_name = method_name.to_sym
|
103
100
|
|
104
|
-
if attributes == nil
|
101
|
+
if method_name[0] != '_' && attributes == nil
|
105
102
|
# The method we are calling is on a nil model, return a wrapped
|
106
103
|
# exception.
|
107
104
|
return return_undefined_method(method_name)
|
@@ -109,9 +106,14 @@ class Model
|
|
109
106
|
# Method has the key, look it up directly
|
110
107
|
return attributes[method_name]
|
111
108
|
else
|
112
|
-
return
|
109
|
+
return read_new_model(method_name)
|
113
110
|
end
|
114
111
|
end
|
112
|
+
|
113
|
+
# Get a new model, make it easy to override
|
114
|
+
def read_new_model(method_name)
|
115
|
+
return new_model(nil, self, path + [method_name])
|
116
|
+
end
|
115
117
|
|
116
118
|
def return_undefined_method(method_name)
|
117
119
|
# Methods called on nil capture an error so the user can know where
|
@@ -131,6 +133,10 @@ class Model
|
|
131
133
|
Model.new(*args)
|
132
134
|
end
|
133
135
|
|
136
|
+
def new_array_model(*args)
|
137
|
+
ArrayModel.new(*args)
|
138
|
+
end
|
139
|
+
|
134
140
|
def trigger_by_attribute!(event_name, attribute, *passed_args)
|
135
141
|
trigger_by_scope!(event_name, *passed_args) do |scope|
|
136
142
|
method_name, *args, block = scope
|
@@ -152,24 +158,26 @@ class Model
|
|
152
158
|
if @parent
|
153
159
|
@parent.expand!
|
154
160
|
|
155
|
-
@parent.attributes[@path] = self
|
161
|
+
@parent.attributes[@path.last] = self
|
156
162
|
end
|
157
163
|
end
|
158
164
|
end
|
159
165
|
|
160
166
|
tag_method(:<<) do
|
161
|
-
destructive!
|
162
167
|
pass_reactive!
|
163
168
|
end
|
164
169
|
# Initialize an empty array and append to it
|
165
170
|
def <<(value)
|
166
171
|
@parent.expand!
|
167
|
-
|
172
|
+
|
173
|
+
# Grab the last section of the path, so we can do the assign on the parent
|
174
|
+
path = @path.last
|
175
|
+
result = @parent.send(path)
|
168
176
|
|
169
177
|
if result.nil?
|
170
178
|
# If this isn't a model yet, instantiate it
|
171
|
-
@parent.send(:"#{
|
172
|
-
result = @parent.send(
|
179
|
+
@parent.send(:"#{path}=", new_array_model([], @parent, @path))
|
180
|
+
result = @parent.send(path)
|
173
181
|
|
174
182
|
# Add the new item
|
175
183
|
result << value
|
@@ -179,7 +187,7 @@ class Model
|
|
179
187
|
end
|
180
188
|
|
181
189
|
def inspect
|
182
|
-
"<#{self.class.to_s}
|
190
|
+
"<#{self.class.to_s} #{attributes.inspect}>"
|
183
191
|
end
|
184
192
|
|
185
193
|
|
@@ -1,21 +1,28 @@
|
|
1
1
|
module ModelWrapper
|
2
2
|
# For cretain values, we wrap them to make the behave as a
|
3
3
|
# model.
|
4
|
-
def wrap_value(value)
|
4
|
+
def wrap_value(value, lookup)
|
5
5
|
if value.cur.is_a?(Array)
|
6
|
-
value =
|
6
|
+
value = new_array_model(value, self, path + lookup)
|
7
7
|
elsif value.cur.is_a?(Hash)
|
8
|
-
value =
|
8
|
+
value = new_model(value, self, path + lookup)
|
9
9
|
end
|
10
10
|
|
11
11
|
return value
|
12
12
|
end
|
13
13
|
|
14
|
-
def wrap_values(values)
|
14
|
+
def wrap_values(values, lookup=[])
|
15
15
|
if values.cur.is_a?(Array)
|
16
|
-
|
16
|
+
# Coming from an array
|
17
|
+
values = values.map {|v| wrap_value(v,lookup + [:[]]) }
|
17
18
|
elsif values.cur.is_a?(Hash)
|
18
|
-
|
19
|
+
pairs = values.map do |k,v|
|
20
|
+
path = lookup + [k]
|
21
|
+
|
22
|
+
[k, wrap_value(v,path)]
|
23
|
+
end
|
24
|
+
|
25
|
+
values = Hash[pairs]
|
19
26
|
end
|
20
27
|
|
21
28
|
return values
|
data/lib/volt/models/params.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'volt/models/params_array'
|
2
|
+
|
1
3
|
# All url related data is stored in params. This includes the main uri
|
2
4
|
# in addition to any query parameters. The router is responsible for
|
3
5
|
# converting any uri sections into params. Sections in the uri will
|
@@ -6,7 +8,6 @@
|
|
6
8
|
# The params value can be updated the same way a model would be, only
|
7
9
|
# the updates will trigger an updated url via the browser history api.
|
8
10
|
# TODO: Support # for browsers without the history api.
|
9
|
-
|
10
11
|
class Params < Model
|
11
12
|
def initialize(*args)
|
12
13
|
super(*args)
|
@@ -64,4 +65,8 @@ class Params < Model
|
|
64
65
|
def new_model(*args)
|
65
66
|
Params.new(*args)
|
66
67
|
end
|
68
|
+
|
69
|
+
def new_array_model(*args)
|
70
|
+
ParamsArray.new(*args)
|
71
|
+
end
|
67
72
|
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'volt/models/store_array'
|
2
|
+
|
3
|
+
class Store < Model
|
4
|
+
ID_CHARS = [('a'..'z'), ('A'..'Z'), ('0'..'9')].map {|v| v.to_a }.flatten
|
5
|
+
|
6
|
+
@@identity_map = {}
|
7
|
+
|
8
|
+
def initialize(tasks=nil, *args)
|
9
|
+
@tasks = tasks
|
10
|
+
|
11
|
+
super(*args)
|
12
|
+
|
13
|
+
track_in_identity_map if attributes && attributes['_id']
|
14
|
+
|
15
|
+
value_updated
|
16
|
+
end
|
17
|
+
|
18
|
+
# def _id
|
19
|
+
# return attributes && attributes['_id']
|
20
|
+
# end
|
21
|
+
|
22
|
+
def event_added(event, scope_provider, first)
|
23
|
+
if first && event == :changed
|
24
|
+
# Start listening
|
25
|
+
ensure_id
|
26
|
+
if self.attributes && self.path.size > 1
|
27
|
+
channel_name = "#{self.path[-2]}##{self.attributes['_id']}"
|
28
|
+
puts "LISTENER ON: #{channel_name.inspect} -- #{self.path.inspect}"
|
29
|
+
$page.tasks.call('ChannelTasks', 'add_listener', channel_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def event_removed(event, no_more_events)
|
35
|
+
if no_more_events && event == :changed
|
36
|
+
# Stop listening
|
37
|
+
if self.attributes && self.path.size > 1
|
38
|
+
channel_name = "#{self.path[-2]}##{self.attributes['_id']}"
|
39
|
+
puts "REMOVE LISTENER ON: #{channel_name}"
|
40
|
+
$page.tasks.call('ChannelTasks', 'remove_listener', channel_name)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.update(model_id, data)
|
46
|
+
model = @@identity_map[model_id]
|
47
|
+
|
48
|
+
if model
|
49
|
+
data.each_pair do |key, value|
|
50
|
+
if key != '_id'
|
51
|
+
puts "update #{key} with #{value.inspect}"
|
52
|
+
model.send(:"#{key}=", value)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def generate_id
|
59
|
+
id = []
|
60
|
+
12.times { id << ID_CHARS.sample }
|
61
|
+
|
62
|
+
return id.join
|
63
|
+
end
|
64
|
+
|
65
|
+
def method_missing(method_name, *args, &block)
|
66
|
+
result = super
|
67
|
+
|
68
|
+
if method_name[0] == '_' && method_name[-1] == '='
|
69
|
+
# Trigger value updated after an assignment
|
70
|
+
self.value_updated
|
71
|
+
end
|
72
|
+
|
73
|
+
return result
|
74
|
+
end
|
75
|
+
|
76
|
+
def track_in_identity_map
|
77
|
+
@@identity_map[attributes['_id']] = self
|
78
|
+
end
|
79
|
+
|
80
|
+
# When called, will setup an id if there is not one
|
81
|
+
def ensure_id
|
82
|
+
# No id yet, lets create one
|
83
|
+
if attributes && !attributes['_id']
|
84
|
+
self.attributes['_id'] = generate_id
|
85
|
+
track_in_identity_map
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def value_updated
|
90
|
+
# puts "VU: #{@tasks.inspect} = #{path.inspect} - #{attributes.inspect}"
|
91
|
+
if (!defined?($loading_models) || !$loading_models) && @tasks && path.size > 0 && !self.nil?
|
92
|
+
|
93
|
+
ensure_id
|
94
|
+
|
95
|
+
if path.size > 2 && parent && source = parent.parent
|
96
|
+
self.attributes[path[-2].to_s.singularize+'_id'] = source._id
|
97
|
+
end
|
98
|
+
|
99
|
+
# Don't store any sub-stores, those will do their own saving.
|
100
|
+
attrs = attributes.reject {|k,v| v.is_a?(Model) || v.is_a?(ArrayModel) }
|
101
|
+
|
102
|
+
puts "Save: #{collection} - #{attrs.inspect}"
|
103
|
+
@tasks.call('StoreTasks', 'save', collection, attrs)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def collection(path=nil)
|
108
|
+
path ||= self.path
|
109
|
+
|
110
|
+
collection_name = path.last
|
111
|
+
collection_name = path[-2] if collection_name == :[]
|
112
|
+
|
113
|
+
return collection_name
|
114
|
+
end
|
115
|
+
|
116
|
+
# On stores, we store the model so we don't have to look it up
|
117
|
+
# every time we do a read.
|
118
|
+
def read_new_model(method_name)
|
119
|
+
model = new_model(nil, self, path + [method_name])
|
120
|
+
|
121
|
+
self.attributes ||= {}
|
122
|
+
attributes[method_name] = model
|
123
|
+
|
124
|
+
return model
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
|
129
|
+
def new_model(attributes={}, parent=nil, path=nil, class_paths=nil)
|
130
|
+
model = Store.new(@tasks, attributes, parent, path, class_paths)
|
131
|
+
|
132
|
+
if @tasks && path.last[-1] == 's'
|
133
|
+
# puts "FIND NEW MODEL: #{path.inspect} - #{attributes.inspect}"
|
134
|
+
|
135
|
+
# Check to see the parents scope so we can only lookup associated
|
136
|
+
# models.
|
137
|
+
scope = {}
|
138
|
+
|
139
|
+
if parent.attributes && parent.attributes['_id'].true?
|
140
|
+
scope[path[-1].singularize + '_id'] = parent._id
|
141
|
+
end
|
142
|
+
|
143
|
+
puts "FIND: #{collection(path).inspect} at #{scope.inspect}"
|
144
|
+
@tasks.call('StoreTasks', 'find', collection(path), scope) do |results|
|
145
|
+
# TODO: Globals evil, replace
|
146
|
+
$loading_models = true
|
147
|
+
results.each do |result|
|
148
|
+
# Get model again, we need to fetch it each time so it gets the
|
149
|
+
# updated model when it switches from nil.
|
150
|
+
# TODO: Strange that this is needed
|
151
|
+
model = self.send(path.last)
|
152
|
+
model << Store.new(@tasks, result, model, path + [:[]], class_paths)
|
153
|
+
end
|
154
|
+
$loading_models = false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
return model
|
159
|
+
end
|
160
|
+
|
161
|
+
def new_array_model(*args)
|
162
|
+
StoreArray.new(@tasks, *args)
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class StoreArray < ArrayModel
|
2
|
+
def initialize(tasks=nil, array=[], parent=nil, path=nil)
|
3
|
+
@tasks = tasks
|
4
|
+
|
5
|
+
super(array, parent, path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def new_model(*args)
|
9
|
+
Store.new(@tasks, *args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_array_model(*args)
|
13
|
+
StoreArray.new(@tasks, *args)
|
14
|
+
end
|
15
|
+
end
|
data/lib/volt/models/url.rb
CHANGED
@@ -60,10 +60,6 @@ class URL
|
|
60
60
|
# Called when the state has changed and the url in the
|
61
61
|
# browser should be updated
|
62
62
|
# Called when an attribute changes to update the url
|
63
|
-
tag_method(:update!) do
|
64
|
-
destructive!
|
65
|
-
# TODO: ! methods should default to destructive
|
66
|
-
end
|
67
63
|
def update!
|
68
64
|
new_url = full_url()
|
69
65
|
|
data/lib/volt/models.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'volt/
|
2
|
-
require 'volt/
|
1
|
+
require 'volt/page/bindings/base_binding'
|
2
|
+
require 'volt/page/targets/attribute_target'
|
3
3
|
|
4
4
|
class AttributeBinding < BaseBinding
|
5
5
|
def initialize(target, context, binding_name, attribute_name, getter)
|
@@ -82,11 +82,7 @@ class AttributeBinding < BaseBinding
|
|
82
82
|
value = false
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
element['checked'] = 'checked'
|
87
|
-
else
|
88
|
-
element.remove_attr('checked')
|
89
|
-
end
|
85
|
+
element.prop('checked', value)
|
90
86
|
|
91
87
|
end
|
92
88
|
|
@@ -104,6 +100,11 @@ class AttributeBinding < BaseBinding
|
|
104
100
|
@update_listener.remove
|
105
101
|
@update_listener = nil
|
106
102
|
end
|
103
|
+
|
104
|
+
# Clear any references
|
105
|
+
@target = nil
|
106
|
+
@context = nil
|
107
|
+
@section = nil
|
107
108
|
end
|
108
109
|
|
109
110
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'volt/
|
1
|
+
require 'volt/page/bindings/base_binding'
|
2
2
|
|
3
3
|
class EachBinding < BaseBinding
|
4
4
|
def initialize(target, context, binding_name, getter, variable_name, template_name)
|
@@ -17,20 +17,23 @@ class EachBinding < BaseBinding
|
|
17
17
|
# Run the initial render
|
18
18
|
update
|
19
19
|
|
20
|
-
@added_listener = @value.on('added') { |position, item| puts "ADDED" ; item_added(position) }
|
20
|
+
@added_listener = @value.on('added') { |_, position, item| puts "ADDED" ; item_added(position) }
|
21
21
|
@changed_listener = @value.on('changed') { puts "CHANGED" ; reload }
|
22
|
-
@removed_listener = @value.on('removed') { |position|
|
22
|
+
@removed_listener = @value.on('removed') { |_, position| item_removed(position) }
|
23
23
|
end
|
24
24
|
|
25
25
|
# When a change event comes through, its most likely upstream, so the whole
|
26
26
|
# array might have changed. In this case, just reload the whole thing
|
27
27
|
# TODO: Track to make sure the changed event isn't being called too often (it is currently)
|
28
28
|
def reload
|
29
|
+
# ObjectTracker.enable_cache
|
29
30
|
# Remove all of the current templates
|
30
31
|
if @templates
|
31
32
|
@templates.each do |template|
|
32
|
-
template.remove
|
33
33
|
template.remove_anchors
|
34
|
+
|
35
|
+
# TODO: Make sure this is being removed since we already removed the anchors
|
36
|
+
template.remove
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
@@ -38,12 +41,14 @@ class EachBinding < BaseBinding
|
|
38
41
|
|
39
42
|
# Run update again to rebuild
|
40
43
|
update
|
44
|
+
|
45
|
+
# ObjectTracker.disable_cache
|
41
46
|
end
|
42
47
|
|
43
48
|
def item_removed(position)
|
44
49
|
position = position.cur
|
45
|
-
@templates[position].remove
|
46
50
|
@templates[position].remove_anchors
|
51
|
+
@templates[position].remove
|
47
52
|
@templates.delete_at(position)
|
48
53
|
|
49
54
|
value_obj = @value.cur
|
@@ -63,7 +68,8 @@ class EachBinding < BaseBinding
|
|
63
68
|
end
|
64
69
|
|
65
70
|
def item_added(position)
|
66
|
-
#
|
71
|
+
# ObjectTracker.enable_cache
|
72
|
+
# puts "ADDED 1"
|
67
73
|
binding_name = @@binding_number
|
68
74
|
@@binding_number += 1
|
69
75
|
|
@@ -75,7 +81,10 @@ class EachBinding < BaseBinding
|
|
75
81
|
|
76
82
|
item_context = SubContext.new({@item_name => value, :index => index, :parent => @value}, @context)
|
77
83
|
|
84
|
+
# ObjectTracker.enable_cache
|
78
85
|
@templates << TemplateRenderer.new(@target, item_context, binding_name, @template_name)
|
86
|
+
# puts "ADDED 2"
|
87
|
+
# ObjectTracker.disable_cache
|
79
88
|
end
|
80
89
|
|
81
90
|
def update(item=nil)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'volt/
|
1
|
+
require 'volt/page/bindings/base_binding'
|
2
2
|
|
3
3
|
# TODO: We need to figure out how we want to wrap JS events
|
4
4
|
class JSEvent
|
@@ -39,10 +39,6 @@ class EventBinding < BaseBinding
|
|
39
39
|
@listener = $page.events.add(event_name, self, handler)
|
40
40
|
end
|
41
41
|
|
42
|
-
def element
|
43
|
-
Element.find('#' + binding_name)
|
44
|
-
end
|
45
|
-
|
46
42
|
# Remove the event binding
|
47
43
|
def remove
|
48
44
|
# puts "REMOVE EL FOR #{@event}"
|