volt 0.4.9 → 0.4.10
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/tasks/channel_tasks.rb +2 -2
- data/lib/volt/console.rb +0 -1
- data/lib/volt/controllers/model_controller.rb +20 -2
- data/lib/volt/models.rb +4 -2
- data/lib/volt/models/array_model.rb +19 -5
- data/lib/volt/models/model.rb +51 -9
- data/lib/volt/models/model_wrapper.rb +2 -2
- data/lib/volt/models/persistors/array_store.rb +79 -0
- data/lib/volt/models/persistors/base.rb +24 -0
- data/lib/volt/models/persistors/model_store.rb +140 -0
- data/lib/volt/models/persistors/params.rb +26 -0
- data/lib/volt/models/persistors/store.rb +44 -0
- data/lib/volt/models/persistors/store_factory.rb +15 -0
- data/lib/volt/models/url.rb +2 -1
- data/lib/volt/page/bindings/each_binding.rb +2 -2
- data/lib/volt/page/channel.rb +0 -2
- data/lib/volt/page/page.rb +2 -3
- data/lib/volt/page/tasks.rb +28 -7
- data/lib/volt/reactive/reactive_array.rb +14 -1
- data/lib/volt/tasks/dispatcher.rb +8 -2
- data/spec/models/model_spec.rb +32 -2
- data/spec/models/persistors/params_spec.rb +16 -0
- data/spec/models/persistors/store_spec.rb +29 -0
- data/spec/models/store_spec.rb +16 -16
- data/spec/router/routes_spec.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +2 -1
- metadata +12 -8
- data/lib/volt/models/params.rb +0 -72
- data/lib/volt/models/params_array.rb +0 -9
- data/lib/volt/models/store.rb +0 -183
- data/lib/volt/models/store_array.rb +0 -82
- data/spec/models/params_spec.rb +0 -16
data/lib/volt/models/params.rb
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
require 'volt/models/params_array'
|
2
|
-
|
3
|
-
# All url related data is stored in params. This includes the main uri
|
4
|
-
# in addition to any query parameters. The router is responsible for
|
5
|
-
# converting any uri sections into params. Sections in the uri will
|
6
|
-
# override any specified parameters.
|
7
|
-
#
|
8
|
-
# The params value can be updated the same way a model would be, only
|
9
|
-
# the updates will trigger an updated url via the browser history api.
|
10
|
-
# TODO: Support # for browsers without the history api.
|
11
|
-
class Params < Model
|
12
|
-
def initialize(*args)
|
13
|
-
super(*args)
|
14
|
-
end
|
15
|
-
|
16
|
-
def deep_clone
|
17
|
-
new_obj = clone
|
18
|
-
|
19
|
-
new_obj.attributes = new_obj.attributes.dup
|
20
|
-
|
21
|
-
new_obj
|
22
|
-
end
|
23
|
-
|
24
|
-
tag_method(:delete) do
|
25
|
-
destructive!
|
26
|
-
end
|
27
|
-
def delete(*args)
|
28
|
-
super
|
29
|
-
|
30
|
-
value_updated
|
31
|
-
end
|
32
|
-
|
33
|
-
def method_missing(method_name, *args, &block)
|
34
|
-
result = super
|
35
|
-
|
36
|
-
if method_name[0] == '_' && method_name[-1] == '='
|
37
|
-
# Trigger value updated after an assignment
|
38
|
-
self.value_updated
|
39
|
-
end
|
40
|
-
|
41
|
-
return result
|
42
|
-
end
|
43
|
-
|
44
|
-
def value_updated
|
45
|
-
# Once the initial url has been parsed and set into the attributes,
|
46
|
-
# start triggering updates on change events.
|
47
|
-
# TODO: This is a temp solution, we need to make it so value_updated
|
48
|
-
# is called after the reactive_value has been updated.
|
49
|
-
if RUBY_PLATFORM == 'opal'
|
50
|
-
%x{
|
51
|
-
if (window.setTimeout && this.$run_update.bind) {
|
52
|
-
if (window.paramsUpdateTimer) {
|
53
|
-
clearTimeout(window.paramsUpdateTimer);
|
54
|
-
}
|
55
|
-
window.paramsUpdateTimer = setTimeout(this.$run_update.bind(this), 0);
|
56
|
-
}
|
57
|
-
}
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def run_update
|
62
|
-
$page.params.trigger!('child_changed') if Volt.client?
|
63
|
-
end
|
64
|
-
|
65
|
-
def new_model(*args)
|
66
|
-
Params.new(*args)
|
67
|
-
end
|
68
|
-
|
69
|
-
def new_array_model(*args)
|
70
|
-
ParamsArray.new(*args)
|
71
|
-
end
|
72
|
-
end
|
data/lib/volt/models/store.rb
DELETED
@@ -1,183 +0,0 @@
|
|
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
|
-
attr_reader :state
|
9
|
-
|
10
|
-
def initialize(tasks=nil, *args)
|
11
|
-
@tasks = tasks
|
12
|
-
@state = :not_loaded
|
13
|
-
|
14
|
-
super(*args)
|
15
|
-
|
16
|
-
track_in_identity_map if attributes && attributes[:_id]
|
17
|
-
|
18
|
-
value_updated
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.from_id(id)
|
22
|
-
@@identity_map[id]
|
23
|
-
end
|
24
|
-
|
25
|
-
def event_added(event, scope_provider, first)
|
26
|
-
if first && event == :changed
|
27
|
-
# Start listening
|
28
|
-
ensure_id
|
29
|
-
change_channel_connection("add")
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def event_removed(event, no_more_events)
|
34
|
-
if no_more_events && event == :changed
|
35
|
-
# Stop listening
|
36
|
-
change_channel_connection("remove")
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def change_channel_connection(add_or_remove)
|
41
|
-
if attributes && path.size > 1
|
42
|
-
channel_name = "#{path[-2]}##{attributes[:_id]}"
|
43
|
-
puts "Event Added: #{channel_name} -- #{attributes.inspect}"
|
44
|
-
@tasks.call('ChannelTasks', "#{add_or_remove}_listener", channel_name)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.update(model_id, data)
|
49
|
-
model = @@identity_map[model_id]
|
50
|
-
|
51
|
-
if model
|
52
|
-
data.each_pair do |key, value|
|
53
|
-
if key != '_id'
|
54
|
-
model.send(:"#{key}=", value)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def generate_id
|
61
|
-
id = []
|
62
|
-
12.times { id << ID_CHARS.sample }
|
63
|
-
|
64
|
-
return id.join
|
65
|
-
end
|
66
|
-
|
67
|
-
def method_missing(method_name, *args, &block)
|
68
|
-
if method_name[-1] == ']'
|
69
|
-
# Load the model
|
70
|
-
self.load!
|
71
|
-
end
|
72
|
-
|
73
|
-
result = super
|
74
|
-
|
75
|
-
if method_name[0] == '_' && method_name[-1] == '='
|
76
|
-
# Trigger value updated after an assignment
|
77
|
-
self.value_updated
|
78
|
-
end
|
79
|
-
|
80
|
-
return result
|
81
|
-
end
|
82
|
-
|
83
|
-
def track_in_identity_map
|
84
|
-
@@identity_map[attributes[:_id]] = self
|
85
|
-
end
|
86
|
-
|
87
|
-
# When called, will setup an id if there is not one
|
88
|
-
def ensure_id
|
89
|
-
# No id yet, lets create one
|
90
|
-
if attributes && !attributes[:_id]
|
91
|
-
self.attributes[:_id] = generate_id
|
92
|
-
track_in_identity_map
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def value_updated
|
97
|
-
path_size = path.size
|
98
|
-
if !(defined?($loading_models) && $loading_models) && @tasks && path_size > 0 && !nil?
|
99
|
-
|
100
|
-
ensure_id
|
101
|
-
|
102
|
-
if path_size > 3 && parent && source = parent.parent
|
103
|
-
self.attributes[:"#{path[-4].singularize}_id"] = source._id
|
104
|
-
end
|
105
|
-
|
106
|
-
# puts "Save: #{collection} - #{attrs.inspect}"
|
107
|
-
@tasks.call('StoreTasks', 'save', collection, self_attributes)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Return the attributes that are only for this store, not any sub-associations.
|
112
|
-
def self_attributes
|
113
|
-
# Don't store any sub-stores, those will do their own saving.
|
114
|
-
attrs = attributes.reject {|k,v| v.is_a?(Model) || v.is_a?(ArrayModel) }
|
115
|
-
end
|
116
|
-
|
117
|
-
def collection(path=nil)
|
118
|
-
path ||= self.path
|
119
|
-
|
120
|
-
collection_name = path.last
|
121
|
-
collection_name = path[-2] if collection_name == :[]
|
122
|
-
|
123
|
-
return collection_name
|
124
|
-
end
|
125
|
-
|
126
|
-
# On stores, we store the model so we don't have to look it up
|
127
|
-
# every time we do a read.
|
128
|
-
def read_new_model(method_name)
|
129
|
-
# On stores, plural associations are automatically assumed to be
|
130
|
-
# collections.
|
131
|
-
if method_name.plural?
|
132
|
-
model = new_array_model([], self, path + [method_name])
|
133
|
-
else
|
134
|
-
model = new_model(nil, self, path + [method_name])
|
135
|
-
end
|
136
|
-
|
137
|
-
self.attributes ||= {}
|
138
|
-
attributes[method_name] = model
|
139
|
-
|
140
|
-
if model.is_a?(StoreArray)# && model.state == :not_loaded
|
141
|
-
model.load!
|
142
|
-
end
|
143
|
-
|
144
|
-
return model
|
145
|
-
end
|
146
|
-
|
147
|
-
|
148
|
-
# When called, this model is deleted from its current parent collection
|
149
|
-
# and from the database
|
150
|
-
def delete!
|
151
|
-
if path.size == 0
|
152
|
-
raise "Not in a collection"
|
153
|
-
end
|
154
|
-
|
155
|
-
# TEMP: Find this model in the parent's collection
|
156
|
-
parent.each_with_index do |child,index|
|
157
|
-
puts "CHECK #{child.inspect} vs #{self.inspect}"
|
158
|
-
if child._id == self._id
|
159
|
-
puts "FOUND AT: #{index}"
|
160
|
-
parent.delete_at(index)
|
161
|
-
break
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Send to the DB that we got deleted
|
166
|
-
unless $loading_models
|
167
|
-
puts "delete #{collection} - #{attributes[:_id]}"
|
168
|
-
@tasks.call('StoreTasks', 'delete', collection, attributes[:_id])
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def inspect
|
173
|
-
"<#{self.class.to_s}-#{@state} #{attributes.inspect}>"
|
174
|
-
end
|
175
|
-
|
176
|
-
def new_model(attributes={}, parent=nil, path=nil, class_paths=nil)
|
177
|
-
return Store.new(@tasks, attributes, parent, path, class_paths)
|
178
|
-
end
|
179
|
-
|
180
|
-
def new_array_model(*args)
|
181
|
-
StoreArray.new(@tasks, *args)
|
182
|
-
end
|
183
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
class StoreArray < ArrayModel
|
2
|
-
attr_reader :state
|
3
|
-
|
4
|
-
def initialize(tasks=nil, array=[], parent=nil, path=nil)
|
5
|
-
@tasks = tasks
|
6
|
-
@state = :not_loaded
|
7
|
-
|
8
|
-
super(array, parent, path)
|
9
|
-
|
10
|
-
# TEMP: TODO: Setup the listeners right away
|
11
|
-
change_channel_connection('add', 'added')
|
12
|
-
change_channel_connection('add', 'removed')
|
13
|
-
end
|
14
|
-
|
15
|
-
# def event_added(event, scope_provider, first)
|
16
|
-
# puts "New event1: #{event.inspect} - #{first}"
|
17
|
-
# if first && [:added, :removed].include?(event)
|
18
|
-
# # Start listening for added items on the collection
|
19
|
-
#
|
20
|
-
# change_channel_connection('add', event)
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# def event_removed(event, no_more_events)
|
25
|
-
# if no_more_events && [:added, :removed].include?(event)
|
26
|
-
# # Stop listening
|
27
|
-
# change_channel_connection("remove", event)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
|
31
|
-
|
32
|
-
def load!
|
33
|
-
if @state == :not_loaded
|
34
|
-
@state = :loading
|
35
|
-
|
36
|
-
if @tasks && path.last.plural?
|
37
|
-
# Check to see the parents scope so we can only lookup associated
|
38
|
-
# models.
|
39
|
-
scope = {}
|
40
|
-
|
41
|
-
# Scope to the parent
|
42
|
-
if path.size > 1 && attributes && attributes[:_id].true?
|
43
|
-
scope[:"#{path[-2].singularize}_id"] = _id
|
44
|
-
end
|
45
|
-
|
46
|
-
puts "Load At Scope: #{scope.inspect}"
|
47
|
-
|
48
|
-
load_child_models(scope)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
return self
|
53
|
-
end
|
54
|
-
|
55
|
-
def load_child_models(scope)
|
56
|
-
# puts "FIND: #{collection(path).inspect} at #{scope.inspect}"
|
57
|
-
@tasks.call('StoreTasks', 'find', path.last, scope) do |results|
|
58
|
-
# TODO: Globals evil, replace
|
59
|
-
$loading_models = true
|
60
|
-
results.each do |result|
|
61
|
-
self << Store.new(@tasks, result, self, path + [:[]], @class_paths)
|
62
|
-
end
|
63
|
-
$loading_models = false
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def change_channel_connection(add_or_remove, event)
|
68
|
-
if @tasks && parent.attributes && path.size != 0
|
69
|
-
channel_name = "#{path[-1]}-#{event}"
|
70
|
-
puts "Listen on #{channel_name} - #{add_or_remove}"
|
71
|
-
@tasks.call('ChannelTasks', "#{add_or_remove}_listener", channel_name)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def new_model(*args)
|
76
|
-
Store.new(@tasks, *args)
|
77
|
-
end
|
78
|
-
|
79
|
-
def new_array_model(*args)
|
80
|
-
StoreArray.new(@tasks, *args)
|
81
|
-
end
|
82
|
-
end
|
data/spec/models/params_spec.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'volt/models'
|
2
|
-
|
3
|
-
describe Params do
|
4
|
-
it "should stay as params classes when used" do
|
5
|
-
a = Params.new
|
6
|
-
expect(a._test.class).to eq(Params)
|
7
|
-
|
8
|
-
expect(a._test._cool.class).to eq(Params)
|
9
|
-
|
10
|
-
a._items << {_name: 'Test'}
|
11
|
-
|
12
|
-
expect(a._items.class).to eq(ParamsArray)
|
13
|
-
expect(a._items[0].class).to eq(Params)
|
14
|
-
expect(a._items[0]._name.class).to eq(String)
|
15
|
-
end
|
16
|
-
end
|