volt 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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.