volt 0.7.1 → 0.7.2

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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -2
  3. data/Readme.md +97 -56
  4. data/VERSION +1 -1
  5. data/app/volt/assets/js/sockjs-0.3.4.min.js +27 -0
  6. data/app/volt/assets/js/vertxbus.js +216 -0
  7. data/app/volt/tasks/live_query/live_query.rb +5 -5
  8. data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
  9. data/app/volt/tasks/query_tasks.rb +5 -0
  10. data/app/volt/tasks/store_tasks.rb +44 -18
  11. data/docs/WHY.md +10 -0
  12. data/lib/volt/cli.rb +18 -7
  13. data/lib/volt/controllers/model_controller.rb +30 -8
  14. data/lib/volt/extra_core/inflections.rb +63 -0
  15. data/lib/volt/extra_core/inflector/inflections.rb +203 -0
  16. data/lib/volt/extra_core/inflector/methods.rb +63 -0
  17. data/lib/volt/extra_core/inflector.rb +4 -0
  18. data/lib/volt/extra_core/object.rb +9 -0
  19. data/lib/volt/extra_core/string.rb +10 -14
  20. data/lib/volt/models/array_model.rb +45 -27
  21. data/lib/volt/models/cursor.rb +6 -0
  22. data/lib/volt/models/model.rb +127 -12
  23. data/lib/volt/models/model_hash_behaviour.rb +8 -5
  24. data/lib/volt/models/model_helpers.rb +4 -4
  25. data/lib/volt/models/model_state.rb +22 -0
  26. data/lib/volt/models/persistors/array_store.rb +49 -35
  27. data/lib/volt/models/persistors/base.rb +3 -3
  28. data/lib/volt/models/persistors/model_store.rb +17 -6
  29. data/lib/volt/models/persistors/query/query_listener.rb +0 -2
  30. data/lib/volt/models/persistors/store.rb +0 -4
  31. data/lib/volt/models/persistors/store_state.rb +27 -0
  32. data/lib/volt/models/url.rb +2 -2
  33. data/lib/volt/models/validations/errors.rb +0 -0
  34. data/lib/volt/models/validations/length.rb +13 -0
  35. data/lib/volt/models/validations/validations.rb +82 -0
  36. data/lib/volt/models.rb +1 -1
  37. data/lib/volt/page/bindings/attribute_binding.rb +29 -14
  38. data/lib/volt/page/bindings/base_binding.rb +2 -2
  39. data/lib/volt/page/bindings/component_binding.rb +29 -25
  40. data/lib/volt/page/bindings/content_binding.rb +1 -0
  41. data/lib/volt/page/bindings/each_binding.rb +25 -33
  42. data/lib/volt/page/bindings/event_binding.rb +0 -1
  43. data/lib/volt/page/bindings/if_binding.rb +3 -1
  44. data/lib/volt/page/bindings/template_binding.rb +61 -28
  45. data/lib/volt/page/document_events.rb +3 -1
  46. data/lib/volt/page/draw_cycle.rb +22 -0
  47. data/lib/volt/page/page.rb +10 -1
  48. data/lib/volt/page/reactive_template.rb +23 -16
  49. data/lib/volt/page/sub_context.rb +1 -1
  50. data/lib/volt/page/targets/attribute_section.rb +3 -2
  51. data/lib/volt/page/targets/attribute_target.rb +0 -4
  52. data/lib/volt/page/targets/base_section.rb +25 -0
  53. data/lib/volt/page/targets/binding_document/component_node.rb +13 -14
  54. data/lib/volt/page/targets/binding_document/html_node.rb +4 -0
  55. data/lib/volt/page/targets/dom_section.rb +16 -67
  56. data/lib/volt/page/targets/dom_template.rb +99 -0
  57. data/lib/volt/page/targets/helpers/comment_searchers.rb +29 -0
  58. data/lib/volt/page/template_renderer.rb +2 -14
  59. data/lib/volt/reactive/array_extensions.rb +0 -1
  60. data/lib/volt/reactive/event_chain.rb +9 -2
  61. data/lib/volt/reactive/events.rb +44 -37
  62. data/lib/volt/reactive/object_tracking.rb +1 -1
  63. data/lib/volt/reactive/reactive_array.rb +18 -0
  64. data/lib/volt/reactive/reactive_count.rb +108 -0
  65. data/lib/volt/reactive/reactive_generator.rb +44 -0
  66. data/lib/volt/reactive/reactive_value.rb +73 -73
  67. data/lib/volt/reactive/string_extensions.rb +1 -1
  68. data/lib/volt/router/routes.rb +205 -88
  69. data/lib/volt/server/component_handler.rb +3 -1
  70. data/lib/volt/server/html_parser/view_parser.rb +20 -4
  71. data/lib/volt/server/rack/component_paths.rb +13 -10
  72. data/lib/volt/server/rack/index_files.rb +4 -4
  73. data/lib/volt/server/socket_connection_handler.rb +5 -1
  74. data/lib/volt/server.rb +10 -3
  75. data/spec/apps/kitchen_sink/.gitignore +8 -0
  76. data/spec/apps/kitchen_sink/Gemfile +32 -0
  77. data/spec/apps/kitchen_sink/app/home/views/index/index.html +3 -5
  78. data/spec/apps/kitchen_sink/config.ru +4 -0
  79. data/spec/apps/kitchen_sink/public/index.html +2 -2
  80. data/spec/extra_core/inflector_spec.rb +8 -0
  81. data/spec/models/event_chain_spec.rb +18 -0
  82. data/spec/models/model_buffers_spec.rb +9 -0
  83. data/spec/models/model_spec.rb +22 -9
  84. data/spec/models/reactive_array_spec.rb +26 -1
  85. data/spec/models/reactive_call_times_spec.rb +28 -0
  86. data/spec/models/reactive_value_spec.rb +19 -0
  87. data/spec/models/validations_spec.rb +39 -0
  88. data/spec/page/bindings/content_binding_spec.rb +1 -0
  89. data/spec/{templates → page/bindings}/template_binding_spec.rb +54 -0
  90. data/spec/router/routes_spec.rb +156 -8
  91. data/spec/server/html_parser/sandlebars_parser_spec.rb +55 -47
  92. data/spec/server/html_parser/view_parser_spec.rb +3 -0
  93. data/spec/server/rack/asset_files_spec.rb +1 -1
  94. data/spec/spec_helper.rb +25 -11
  95. data/spec/templates/targets/binding_document/component_node_spec.rb +12 -0
  96. data/templates/project/Gemfile.tt +11 -0
  97. data/templates/project/app/home/config/routes.rb +1 -1
  98. data/templates/project/app/home/controllers/index_controller.rb +5 -5
  99. data/templates/project/app/home/views/index/index.html +6 -6
  100. data/volt.gemspec +5 -6
  101. metadata +34 -76
  102. data/app/volt/assets/js/sockjs-0.2.1.min.js +0 -27
  103. 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(*args)
31
- __clear_element(args[0])
32
- attributes.delete(*args)
33
- trigger_by_attribute!('changed', args[0])
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(args[0]) if @persistor
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, no_more_events)
21
- @persistor.event_removed(event, no_more_events) if @persistor
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
- @query = ReactiveValue.from_hash(@model.options[:query] || {})
21
+ query = @model.options[:query]
19
22
 
23
+ @query = ReactiveValue.from_hash(query || {})
20
24
  end
21
25
 
22
- # Called when a collection loads
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, no_more_events)
31
+ def event_removed(event, last, last_for_event)
34
32
  # Remove listener where there are no more events on this model
35
- if no_more_events && @query_listener && @model.listeners.size == 0
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
- @query_listener.remove_store(self)
44
- @query_listener = nil
45
-
46
- change_state_to :dirty
47
- end
39
+ if @query_changed_listener
40
+ @query_changed_listener.remove
41
+ @query_changed_listener = nil
42
+ end
48
43
 
49
- # Called from the QueryListener when the data is loaded
50
- def change_state_to(new_state)
51
- @state = new_state
44
+ if @query_listener
45
+ @query_listener.remove_store(self)
46
+ @query_listener = nil
47
+ end
52
48
 
53
- # Trigger changed on the 'state' method
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
- load_data
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
- @query_listener.add_store(model.persistor)
99
+
100
+ @query_listener.add_store(self)
102
101
  end
103
102
 
104
103
  def find(query={})
105
- model = ArrayModel.new([], @model.options.merge(:query => query))
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
- # Find the existing model, or create one
117
- new_model = @@identity_map.find(data['_id']) { Model.new(data.symbolize_keys, new_options) }
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
- @model.insert(index, new_model)
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, no_more_events)
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
- # puts "CHANGED: #{attribute_name.inspect} - #{@model.inspect}"
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
- puts "Save: #{collection} - #{self_attributes.inspect} - #{@model.path.inspect}"
64
- @tasks.call('StoreTasks', 'save', collection, self_attributes)
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 first && event == :changed
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
@@ -32,10 +32,6 @@ module Persistors
32
32
  @model.attributes ||= {}
33
33
  @model.attributes[method_name] = model
34
34
 
35
- # if model.is_a?(StoreArray)# && model.state == :not_loaded
36
- # model.load!
37
- # end
38
-
39
35
  return model
40
36
  end
41
37
  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
@@ -60,7 +60,7 @@ class URL
60
60
  host_with_port = @host
61
61
  end
62
62
 
63
- path, params = @router.url_for_params(@params)
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.params_for_path(@path))
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
- @update_listener = @value.on('changed') { update }
24
-
25
- # Bind so when this value updates, we update
26
- case @attribute_name
27
- when 'value'
28
- element.on('input.attrbind') { changed }
29
- when 'checked'
30
- element.on('change.attrbind') {|event| changed(event) }
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
- if @attribute_target
52
- value = @attribute_target.to_html
53
- else
54
- value = @value.cur
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
- @section = nil
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 (@model), with the $page as the context. This gives
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, controller_name)
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 = get_controller(controller_name)
13
- model_with_parent = {parent: @context}.merge(@model || {})
14
-
15
- if controller_class
16
- # The user provided a controller, pass in the model as an argument (in a
17
- # sub-context)
18
- args = []
19
- args << SubContext.new(model_with_parent) if @model
20
-
21
- current_context = controller_class.new(*args)
22
- @controller = current_context
23
- else
24
- # There is not a controller
25
- current_context = SubContext.new(model_with_parent, $page)
26
- @controller = nil
27
- end
28
-
29
- @current_template = TemplateRenderer.new(@page, @target, current_context, @binding_name, full_path)
30
-
31
- call_ready
32
- end
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.