volt 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/Readme.md +97 -56
- data/VERSION +1 -1
- data/app/volt/assets/js/sockjs-0.3.4.min.js +27 -0
- data/app/volt/assets/js/vertxbus.js +216 -0
- data/app/volt/tasks/live_query/live_query.rb +5 -5
- data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
- data/app/volt/tasks/query_tasks.rb +5 -0
- data/app/volt/tasks/store_tasks.rb +44 -18
- data/docs/WHY.md +10 -0
- data/lib/volt/cli.rb +18 -7
- data/lib/volt/controllers/model_controller.rb +30 -8
- data/lib/volt/extra_core/inflections.rb +63 -0
- data/lib/volt/extra_core/inflector/inflections.rb +203 -0
- data/lib/volt/extra_core/inflector/methods.rb +63 -0
- data/lib/volt/extra_core/inflector.rb +4 -0
- data/lib/volt/extra_core/object.rb +9 -0
- data/lib/volt/extra_core/string.rb +10 -14
- data/lib/volt/models/array_model.rb +45 -27
- data/lib/volt/models/cursor.rb +6 -0
- data/lib/volt/models/model.rb +127 -12
- data/lib/volt/models/model_hash_behaviour.rb +8 -5
- data/lib/volt/models/model_helpers.rb +4 -4
- data/lib/volt/models/model_state.rb +22 -0
- data/lib/volt/models/persistors/array_store.rb +49 -35
- data/lib/volt/models/persistors/base.rb +3 -3
- data/lib/volt/models/persistors/model_store.rb +17 -6
- data/lib/volt/models/persistors/query/query_listener.rb +0 -2
- data/lib/volt/models/persistors/store.rb +0 -4
- data/lib/volt/models/persistors/store_state.rb +27 -0
- data/lib/volt/models/url.rb +2 -2
- data/lib/volt/models/validations/errors.rb +0 -0
- data/lib/volt/models/validations/length.rb +13 -0
- data/lib/volt/models/validations/validations.rb +82 -0
- data/lib/volt/models.rb +1 -1
- data/lib/volt/page/bindings/attribute_binding.rb +29 -14
- data/lib/volt/page/bindings/base_binding.rb +2 -2
- data/lib/volt/page/bindings/component_binding.rb +29 -25
- data/lib/volt/page/bindings/content_binding.rb +1 -0
- data/lib/volt/page/bindings/each_binding.rb +25 -33
- data/lib/volt/page/bindings/event_binding.rb +0 -1
- data/lib/volt/page/bindings/if_binding.rb +3 -1
- data/lib/volt/page/bindings/template_binding.rb +61 -28
- data/lib/volt/page/document_events.rb +3 -1
- data/lib/volt/page/draw_cycle.rb +22 -0
- data/lib/volt/page/page.rb +10 -1
- data/lib/volt/page/reactive_template.rb +23 -16
- data/lib/volt/page/sub_context.rb +1 -1
- data/lib/volt/page/targets/attribute_section.rb +3 -2
- data/lib/volt/page/targets/attribute_target.rb +0 -4
- data/lib/volt/page/targets/base_section.rb +25 -0
- data/lib/volt/page/targets/binding_document/component_node.rb +13 -14
- data/lib/volt/page/targets/binding_document/html_node.rb +4 -0
- data/lib/volt/page/targets/dom_section.rb +16 -67
- data/lib/volt/page/targets/dom_template.rb +99 -0
- data/lib/volt/page/targets/helpers/comment_searchers.rb +29 -0
- data/lib/volt/page/template_renderer.rb +2 -14
- data/lib/volt/reactive/array_extensions.rb +0 -1
- data/lib/volt/reactive/event_chain.rb +9 -2
- data/lib/volt/reactive/events.rb +44 -37
- data/lib/volt/reactive/object_tracking.rb +1 -1
- data/lib/volt/reactive/reactive_array.rb +18 -0
- data/lib/volt/reactive/reactive_count.rb +108 -0
- data/lib/volt/reactive/reactive_generator.rb +44 -0
- data/lib/volt/reactive/reactive_value.rb +73 -73
- data/lib/volt/reactive/string_extensions.rb +1 -1
- data/lib/volt/router/routes.rb +205 -88
- data/lib/volt/server/component_handler.rb +3 -1
- data/lib/volt/server/html_parser/view_parser.rb +20 -4
- data/lib/volt/server/rack/component_paths.rb +13 -10
- data/lib/volt/server/rack/index_files.rb +4 -4
- data/lib/volt/server/socket_connection_handler.rb +5 -1
- data/lib/volt/server.rb +10 -3
- data/spec/apps/kitchen_sink/.gitignore +8 -0
- data/spec/apps/kitchen_sink/Gemfile +32 -0
- data/spec/apps/kitchen_sink/app/home/views/index/index.html +3 -5
- data/spec/apps/kitchen_sink/config.ru +4 -0
- data/spec/apps/kitchen_sink/public/index.html +2 -2
- data/spec/extra_core/inflector_spec.rb +8 -0
- data/spec/models/event_chain_spec.rb +18 -0
- data/spec/models/model_buffers_spec.rb +9 -0
- data/spec/models/model_spec.rb +22 -9
- data/spec/models/reactive_array_spec.rb +26 -1
- data/spec/models/reactive_call_times_spec.rb +28 -0
- data/spec/models/reactive_value_spec.rb +19 -0
- data/spec/models/validations_spec.rb +39 -0
- data/spec/page/bindings/content_binding_spec.rb +1 -0
- data/spec/{templates → page/bindings}/template_binding_spec.rb +54 -0
- data/spec/router/routes_spec.rb +156 -8
- data/spec/server/html_parser/sandlebars_parser_spec.rb +55 -47
- data/spec/server/html_parser/view_parser_spec.rb +3 -0
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/spec/spec_helper.rb +25 -11
- data/spec/templates/targets/binding_document/component_node_spec.rb +12 -0
- data/templates/project/Gemfile.tt +11 -0
- data/templates/project/app/home/config/routes.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +5 -5
- data/templates/project/app/home/views/index/index.html +6 -6
- data/volt.gemspec +5 -6
- metadata +34 -76
- data/app/volt/assets/js/sockjs-0.2.1.min.js +0 -27
- data/lib/volt/reactive/object_tracker.rb +0 -107
@@ -27,12 +27,15 @@ module ModelHashBehaviour
|
|
27
27
|
attributes.true?
|
28
28
|
end
|
29
29
|
|
30
|
-
def delete(
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
def delete(name)
|
31
|
+
name = name.to_sym
|
32
|
+
__clear_element(name)
|
33
|
+
value = attributes.delete(name)
|
34
|
+
trigger_by_attribute!('changed', name)
|
34
35
|
|
35
|
-
@persistor.removed(
|
36
|
+
@persistor.removed(name) if @persistor
|
37
|
+
|
38
|
+
return value
|
36
39
|
end
|
37
40
|
|
38
41
|
|
@@ -12,13 +12,13 @@ module ModelHelpers
|
|
12
12
|
end
|
13
13
|
|
14
14
|
# Pass to the persisotr
|
15
|
-
def event_added(event, scope_provider, first)
|
16
|
-
@persistor.event_added(event, scope_provider, first) if @persistor
|
15
|
+
def event_added(event, scope_provider, first, first_for_event)
|
16
|
+
@persistor.event_added(event, scope_provider, first, first_for_event) if @persistor
|
17
17
|
end
|
18
18
|
|
19
19
|
# Pass to the persistor
|
20
|
-
def event_removed(event,
|
21
|
-
@persistor.event_removed(event,
|
20
|
+
def event_removed(event, last, last_for_event)
|
21
|
+
@persistor.event_removed(event, last, last_for_event) if @persistor
|
22
22
|
end
|
23
23
|
|
24
24
|
# Gets the class for a model at the specified path.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# All models have a state that has to do with it being loaded, in process of
|
2
|
+
# being loaded, or not yet loading.
|
3
|
+
module ModelState
|
4
|
+
|
5
|
+
def state
|
6
|
+
if @persistor && @persistor.respond_to?(:state)
|
7
|
+
@persistor.state
|
8
|
+
else
|
9
|
+
@state || :loaded
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def change_state_to(state)
|
14
|
+
@state = state
|
15
|
+
end
|
16
|
+
|
17
|
+
def loaded?
|
18
|
+
state == :loaded
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
@@ -1,73 +1,70 @@
|
|
1
1
|
require 'volt/models/persistors/store'
|
2
2
|
require 'volt/models/persistors/query/query_listener_pool'
|
3
|
+
require 'volt/models/persistors/store_state'
|
3
4
|
|
4
5
|
module Persistors
|
5
6
|
class ArrayStore < Store
|
7
|
+
include StoreState
|
8
|
+
|
6
9
|
@@query_pool = QueryListenerPool.new
|
7
10
|
|
8
11
|
attr_reader :model
|
9
|
-
attr_accessor :state
|
10
12
|
|
11
13
|
def self.query_pool
|
12
14
|
@@query_pool
|
13
15
|
end
|
14
16
|
|
15
17
|
def initialize(model, tasks=nil)
|
18
|
+
# puts "NEW ARRAY STORE FOR #{model.inspect}"
|
16
19
|
super
|
17
20
|
|
18
|
-
|
21
|
+
query = @model.options[:query]
|
19
22
|
|
23
|
+
@query = ReactiveValue.from_hash(query || {})
|
20
24
|
end
|
21
25
|
|
22
|
-
|
23
|
-
def loaded
|
24
|
-
change_state_to :not_loaded
|
25
|
-
end
|
26
|
-
|
27
|
-
def event_added(event, scope_provider, first)
|
28
|
-
puts "ADD EV: #{event} - #{first}"
|
26
|
+
def event_added(event, scope_provider, first, first_for_event)
|
29
27
|
# First event, we load the data.
|
30
28
|
load_data if first
|
31
29
|
end
|
32
30
|
|
33
|
-
def event_removed(event,
|
31
|
+
def event_removed(event, last, last_for_event)
|
34
32
|
# Remove listener where there are no more events on this model
|
35
|
-
if
|
36
|
-
stop_listening
|
37
|
-
end
|
33
|
+
stop_listening if last
|
38
34
|
end
|
39
35
|
|
40
36
|
# Called when an event is removed and we no longer want to keep in
|
41
37
|
# sync with the database.
|
42
38
|
def stop_listening
|
43
|
-
@
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
39
|
+
if @query_changed_listener
|
40
|
+
@query_changed_listener.remove
|
41
|
+
@query_changed_listener = nil
|
42
|
+
end
|
48
43
|
|
49
|
-
|
50
|
-
|
51
|
-
|
44
|
+
if @query_listener
|
45
|
+
@query_listener.remove_store(self)
|
46
|
+
@query_listener = nil
|
47
|
+
end
|
52
48
|
|
53
|
-
|
54
|
-
@model.trigger_for_methods!('changed', :state, :loaded?)
|
49
|
+
@state = :dirty
|
55
50
|
end
|
56
51
|
|
57
52
|
# Called the first time data is requested from this collection
|
58
53
|
def load_data
|
59
54
|
# Don't load data from any queried
|
60
55
|
if @state == :not_loaded || @state == :dirty
|
61
|
-
puts "Load Data"
|
56
|
+
puts "Load Data at #{@model.path.inspect} - query: #{@query.inspect}"# on #{@model.inspect}"
|
62
57
|
change_state_to :loading
|
63
58
|
|
64
59
|
@query_changed_listener.remove if @query_changed_listener
|
65
60
|
if @query.reactive?
|
61
|
+
# puts "SETUP REACTIVE QUERY LISTENER: #{@query.inspect}"
|
66
62
|
# Query might change, change the query when it does
|
67
63
|
@query_changed_listener = @query.on('changed') do
|
68
64
|
stop_listening
|
69
65
|
|
70
|
-
|
66
|
+
# Don't load again if all of the listeners are gone
|
67
|
+
load_data if @model.has_listeners?
|
71
68
|
end
|
72
69
|
end
|
73
70
|
|
@@ -94,29 +91,50 @@ module Persistors
|
|
94
91
|
end
|
95
92
|
end
|
96
93
|
|
94
|
+
# puts "IN QUERY: #{query.inspect} - #{self.inspect}"
|
97
95
|
@query_listener = @@query_pool.lookup(collection, query) do
|
98
96
|
# Create if it does not exist
|
99
97
|
QueryListener.new(@@query_pool, @tasks, collection, query)
|
100
98
|
end
|
101
|
-
|
99
|
+
|
100
|
+
@query_listener.add_store(self)
|
102
101
|
end
|
103
102
|
|
104
103
|
def find(query={})
|
105
|
-
model =
|
104
|
+
model = Cursor.new([], @model.options.merge(:query => query))
|
106
105
|
|
107
106
|
return ReactiveValue.new(model)
|
108
107
|
end
|
109
108
|
|
109
|
+
# Fetch does a one time load of the data on an unloaded model and returns
|
110
|
+
# the result.
|
111
|
+
def fetch(&block)
|
112
|
+
# puts "FETCH: #{@state.inspect}"
|
113
|
+
if @state == :loaded
|
114
|
+
block.call(@model)
|
115
|
+
else
|
116
|
+
@fetch_callbacks ||= []
|
117
|
+
@fetch_callbacks << block
|
118
|
+
|
119
|
+
load_data
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
110
123
|
# Called from backend
|
111
124
|
def add(index, data)
|
112
125
|
$loading_models = true
|
126
|
+
# puts "INSERT: #{data.inspect} into #{self.inspect}"
|
113
127
|
|
114
128
|
new_options = @model.options.merge(path: @model.path + [:[]], parent: @model)
|
115
129
|
|
116
|
-
#
|
117
|
-
|
130
|
+
# Don't add if the model is already in the ArrayModel
|
131
|
+
if !@model.cur.array.find {|v| v['_id'] == data['_id'] }
|
132
|
+
# Find the existing model, or create one
|
133
|
+
new_model = @@identity_map.find(data['_id']) { @model.new_model(data.symbolize_keys, new_options, :loaded) }
|
118
134
|
|
119
|
-
|
135
|
+
# puts "ADD: #{new_model.attributes.inspect}"
|
136
|
+
@model.insert(index, new_model)
|
137
|
+
end
|
120
138
|
|
121
139
|
$loading_models = false
|
122
140
|
end
|
@@ -148,10 +166,6 @@ module Persistors
|
|
148
166
|
# When a model is added to this collection, we call its "changed"
|
149
167
|
# method. This should trigger a save.
|
150
168
|
def added(model, index)
|
151
|
-
unless defined?($loading_models) && $loading_models
|
152
|
-
model.persistor.changed
|
153
|
-
end
|
154
|
-
|
155
169
|
if model.persistor
|
156
170
|
# Tell the persistor it was added
|
157
171
|
model.persistor.add_to_collection
|
@@ -164,7 +178,7 @@ module Persistors
|
|
164
178
|
model.persistor.remove_from_collection
|
165
179
|
end
|
166
180
|
|
167
|
-
if $loading_models
|
181
|
+
if defined?($loading_models) && $loading_models
|
168
182
|
return
|
169
183
|
else
|
170
184
|
puts "delete #{channel_name} - #{model.attributes[:_id]}"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Persistors
|
2
2
|
# Implements the base persistor functionality.
|
3
3
|
class Base
|
4
|
-
def loaded
|
4
|
+
def loaded(initial_state=nil)
|
5
5
|
end
|
6
6
|
|
7
7
|
def changed(attribute_name)
|
@@ -15,10 +15,10 @@ module Persistors
|
|
15
15
|
changed(attribute_name)
|
16
16
|
end
|
17
17
|
|
18
|
-
def event_added(event, scope_provider, first)
|
18
|
+
def event_added(event, scope_provider, first, first_for_event)
|
19
19
|
end
|
20
20
|
|
21
|
-
def event_removed(event,
|
21
|
+
def event_removed(event, last, last_for_event)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'volt/models/persistors/store'
|
2
|
-
|
2
|
+
require 'volt/models/persistors/store_state'
|
3
3
|
|
4
4
|
module Persistors
|
5
5
|
class ModelStore < Store
|
6
|
+
include StoreState
|
7
|
+
|
6
8
|
ID_CHARS = [('a'..'z'), ('A'..'Z'), ('0'..'9')].map {|v| v.to_a }.flatten
|
7
9
|
|
8
10
|
attr_reader :model
|
@@ -51,7 +53,8 @@ module Persistors
|
|
51
53
|
|
52
54
|
# Called when the model changes
|
53
55
|
def changed(attribute_name=nil)
|
54
|
-
|
56
|
+
promise = Promise.new
|
57
|
+
|
55
58
|
ensure_setup
|
56
59
|
|
57
60
|
path_size = @model.path.size
|
@@ -60,13 +63,21 @@ module Persistors
|
|
60
63
|
@model.attributes[:"#{@model.path[-4].singularize}_id"] = source._id
|
61
64
|
end
|
62
65
|
|
63
|
-
|
64
|
-
|
66
|
+
@tasks.call('StoreTasks', 'save', collection, self_attributes) do |errors|
|
67
|
+
puts "SAVE GOT: #{errors.inspect}"
|
68
|
+
if errors.size == 0
|
69
|
+
promise.resolve
|
70
|
+
else
|
71
|
+
promise.reject(errors)
|
72
|
+
end
|
73
|
+
end
|
65
74
|
end
|
75
|
+
|
76
|
+
return promise
|
66
77
|
end
|
67
78
|
|
68
|
-
def event_added(event, scope_provider, first)
|
69
|
-
if
|
79
|
+
def event_added(event, scope_provider, first, first_for_event)
|
80
|
+
if first_for_event && event == :changed
|
70
81
|
ensure_setup
|
71
82
|
end
|
72
83
|
end
|
@@ -65,7 +65,6 @@ class QueryListener
|
|
65
65
|
@stores.each do |store|
|
66
66
|
store.add(index, data)
|
67
67
|
end
|
68
|
-
puts "Added: #{index} - #{data.inspect}"
|
69
68
|
end
|
70
69
|
|
71
70
|
def removed(ids)
|
@@ -76,7 +75,6 @@ class QueryListener
|
|
76
75
|
|
77
76
|
def changed(model_id, data)
|
78
77
|
$loading_models = true
|
79
|
-
puts "From Backend: UPDATE: #{model_id} with #{data.inspect}"
|
80
78
|
Persistors::ModelStore.changed(model_id, data)
|
81
79
|
$loading_models = false
|
82
80
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# StoreState provides method for a store to track its loading state.
|
2
|
+
module StoreState
|
3
|
+
|
4
|
+
# Called when a collection loads
|
5
|
+
def loaded(initial_state=nil)
|
6
|
+
change_state_to(initial_state || :not_loaded)
|
7
|
+
end
|
8
|
+
|
9
|
+
def state
|
10
|
+
@state
|
11
|
+
end
|
12
|
+
|
13
|
+
# Called from the QueryListener when the data is loaded
|
14
|
+
def change_state_to(new_state)
|
15
|
+
@state = new_state
|
16
|
+
|
17
|
+
# Trigger changed on the 'state' method
|
18
|
+
@model.trigger_for_methods!('changed', :state, :loaded?)
|
19
|
+
|
20
|
+
if @state == :loaded && @fetch_callbacks
|
21
|
+
# Trigger each waiting fetch
|
22
|
+
@fetch_callbacks.compact.each {|fc| fc.call(@model) }
|
23
|
+
@fetch_callbacks = nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/volt/models/url.rb
CHANGED
@@ -60,7 +60,7 @@ class URL
|
|
60
60
|
host_with_port = @host
|
61
61
|
end
|
62
62
|
|
63
|
-
path, params = @router.
|
63
|
+
path, params = @router.params_to_url(@params.deep_cur.to_h)
|
64
64
|
|
65
65
|
new_url = "#{@scheme}://#{host_with_port}#{(path || @path).chomp('/')}"
|
66
66
|
|
@@ -127,7 +127,7 @@ class URL
|
|
127
127
|
query_hash = self.query_hash
|
128
128
|
|
129
129
|
# Get the params that are in the route
|
130
|
-
query_hash.merge!(@router.
|
130
|
+
query_hash.merge!(@router.url_to_params(@path))
|
131
131
|
|
132
132
|
# Loop through the .params we already have assigned.
|
133
133
|
assign_from_old(@params, query_hash)
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Validations
|
2
|
+
class Length
|
3
|
+
def self.validate(model, field_name, args)
|
4
|
+
errors = {}
|
5
|
+
value = model.send(field_name)
|
6
|
+
if !value || value.size < args
|
7
|
+
errors[field_name] = ["must be at least #{args} chars"]
|
8
|
+
end
|
9
|
+
|
10
|
+
return errors
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# require 'volt/models/validations/errors'
|
2
|
+
require 'volt/models/validations/length'
|
3
|
+
|
4
|
+
# Include in any class to get validation logic
|
5
|
+
module Validations
|
6
|
+
module ClassMethods
|
7
|
+
def validate(field_name, options)
|
8
|
+
@validations ||= {}
|
9
|
+
@validations[field_name] = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def validations
|
13
|
+
@validations
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
base.send :extend, ClassMethods
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sometimes we want to skip checking a field until some event
|
22
|
+
# has happened (usually a field has been typed in or blurred)
|
23
|
+
def exclude_from_errors!(field_name)
|
24
|
+
@exclude_from_errors ||= {}
|
25
|
+
@exclude_from_errors[field_name] = true
|
26
|
+
|
27
|
+
@include_in_errors.delete(field_name) if @include_in_errors
|
28
|
+
|
29
|
+
trigger_for_methods!('changed', :errors, :marked_errors)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Once a field is ready, we can use include_in_errors! to start
|
33
|
+
# showing its errors.
|
34
|
+
def mark_field!(field_name, trigger_changed=true)
|
35
|
+
@marked_fields ||= {}
|
36
|
+
@marked_fields[field_name] = true
|
37
|
+
|
38
|
+
if trigger_changed
|
39
|
+
trigger_for_methods!('changed', :errors, :marked_errors)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def marked_errors
|
44
|
+
errors(true)
|
45
|
+
end
|
46
|
+
|
47
|
+
def errors(marked_only=false)
|
48
|
+
errors = {}
|
49
|
+
|
50
|
+
validations = self.class.validations
|
51
|
+
|
52
|
+
if validations
|
53
|
+
# Merge into errors, combining any error arrays
|
54
|
+
merge = Proc.new do |new_errors|
|
55
|
+
errors.merge!(new_errors) do |key, new_val, old_val|
|
56
|
+
new_val + old_val
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Run through each validation
|
61
|
+
validations.each_pair do |field_name, options|
|
62
|
+
if marked_only
|
63
|
+
# When marked only, skip any validations on non-marked fields
|
64
|
+
next unless @marked_fields && @marked_fields[field_name]
|
65
|
+
end
|
66
|
+
|
67
|
+
options.each_pair do |validation, args|
|
68
|
+
# Call the specific validator, then merge the results back
|
69
|
+
# into one large errors hash.
|
70
|
+
case validation
|
71
|
+
when :length
|
72
|
+
merge.call(Length.validate(self, field_name, args))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# puts "ERROR: #{errors.inspect}"
|
79
|
+
|
80
|
+
return errors
|
81
|
+
end
|
82
|
+
end
|
data/lib/volt/models.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'volt/extra_core/extra_core'
|
2
2
|
require 'volt/reactive/reactive_value'
|
3
3
|
require 'volt/models/model'
|
4
|
+
require 'volt/models/cursor'
|
4
5
|
require 'volt/models/persistors/store_factory'
|
5
6
|
require 'volt/models/persistors/array_store'
|
6
7
|
require 'volt/models/persistors/model_store'
|
7
8
|
require 'volt/models/persistors/params'
|
8
9
|
require 'volt/models/persistors/flash'
|
9
10
|
require 'volt/models/persistors/local_store'
|
10
|
-
|
@@ -20,14 +20,16 @@ class AttributeBinding < BaseBinding
|
|
20
20
|
# Run the initial update (render)
|
21
21
|
update
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
if @value.reactive?
|
24
|
+
@update_listener = @value.on('changed') { update }
|
25
|
+
|
26
|
+
# Bind so when this value updates, we update
|
27
|
+
case @attribute_name
|
28
|
+
when 'value'
|
29
|
+
element.on('input.attrbind') { changed }
|
30
|
+
when 'checked'
|
31
|
+
element.on('change.attrbind') {|event| changed(event) }
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
33
35
|
|
@@ -40,7 +42,9 @@ class AttributeBinding < BaseBinding
|
|
40
42
|
current_value = element.is(':checked')
|
41
43
|
end
|
42
44
|
|
45
|
+
# puts "ASSIGN #{current_value}"
|
43
46
|
@value.cur = current_value
|
47
|
+
# puts "ASSIGNED"
|
44
48
|
end
|
45
49
|
|
46
50
|
def element
|
@@ -48,11 +52,10 @@ class AttributeBinding < BaseBinding
|
|
48
52
|
end
|
49
53
|
|
50
54
|
def update
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
55
|
+
# puts "UPDATE GET VAL"
|
56
|
+
value = @value.cur
|
57
|
+
# puts "UPDATE GOT"
|
58
|
+
# puts "UPDATE1 to #{value.inspect}"
|
56
59
|
|
57
60
|
if @attribute_name == 'checked'
|
58
61
|
update_checked
|
@@ -91,6 +94,7 @@ class AttributeBinding < BaseBinding
|
|
91
94
|
end
|
92
95
|
|
93
96
|
def remove
|
97
|
+
# puts "REMOVE #{self.inspect}"
|
94
98
|
# Unbind events, leave the element there since attribute bindings
|
95
99
|
# aren't responsible for it being there.
|
96
100
|
case @attribute_name
|
@@ -100,6 +104,12 @@ class AttributeBinding < BaseBinding
|
|
100
104
|
element.off('change.attrbind', nil)
|
101
105
|
end
|
102
106
|
|
107
|
+
# Value is a reactive template, remove it
|
108
|
+
if @value && @value.reactive?
|
109
|
+
@value.remove
|
110
|
+
end
|
111
|
+
|
112
|
+
|
103
113
|
if @update_listener
|
104
114
|
@update_listener.remove
|
105
115
|
@update_listener = nil
|
@@ -108,7 +118,12 @@ class AttributeBinding < BaseBinding
|
|
108
118
|
# Clear any references
|
109
119
|
@target = nil
|
110
120
|
@context = nil
|
111
|
-
@
|
121
|
+
@getter = nil
|
122
|
+
@value = nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def remove_anchors
|
126
|
+
raise "attribute bindings do not have anchors, can not remove them"
|
112
127
|
end
|
113
128
|
|
114
129
|
|
@@ -26,7 +26,7 @@ class BaseBinding
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def remove
|
29
|
-
section.remove
|
29
|
+
section.remove if @section
|
30
30
|
|
31
31
|
# Clear any references
|
32
32
|
@target = nil
|
@@ -43,7 +43,7 @@ class BaseBinding
|
|
43
43
|
# Run right away
|
44
44
|
update
|
45
45
|
else
|
46
|
-
|
46
|
+
@page.draw_cycle.queue(self)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -4,30 +4,34 @@ require 'volt/page/bindings/template_binding'
|
|
4
4
|
# and do not pass their context through
|
5
5
|
class ComponentBinding < TemplateBinding
|
6
6
|
# The context for a component binding can be either the controller, or the
|
7
|
-
# component arguments (@
|
7
|
+
# component arguments (@arguments), with the $page as the context. This gives
|
8
8
|
# components access to the page collections.
|
9
|
-
def render_template(full_path,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
9
|
+
# def render_template(full_path, controller_path)
|
10
|
+
# # TODO: at the moment a :body section and a :title will both initialize different
|
11
|
+
# # controllers. Maybe we should have a way to tie them together?
|
12
|
+
# controller_class, action = get_controller(controller_path)
|
13
|
+
#
|
14
|
+
# model_with_parent = {parent: @context}.merge(@arguments || {})
|
15
|
+
#
|
16
|
+
# if controller_class
|
17
|
+
# # The user provided a controller, pass in the model as an argument (in a
|
18
|
+
# # sub-context)
|
19
|
+
# args = []
|
20
|
+
# args << SubContext.new(model_with_parent) if @arguments
|
21
|
+
#
|
22
|
+
# current_context = controller_class.new(*args)
|
23
|
+
# @controller = current_context
|
24
|
+
#
|
25
|
+
# # Trigger the action
|
26
|
+
# @controller.send(action) if @controller.respond_to?(action)
|
27
|
+
# else
|
28
|
+
# # There is not a controller
|
29
|
+
# current_context = SubContext.new(model_with_parent, @page)
|
30
|
+
# @controller = nil
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @current_template = TemplateRenderer.new(@page, @target, current_context, @binding_name, full_path)
|
34
|
+
#
|
35
|
+
# call_ready
|
36
|
+
# end
|
33
37
|
end
|
@@ -21,6 +21,7 @@ class ContentBinding < BaseBinding
|
|
21
21
|
|
22
22
|
# Exception values display the exception as a string
|
23
23
|
value = value.to_s
|
24
|
+
# puts "UPDATE C: #{value.inspect} on #{self.inspect}"
|
24
25
|
|
25
26
|
# Update the html in this section
|
26
27
|
# TODO: Move the formatter into another class.
|