volt 0.3.7 → 0.3.8
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/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}"
|