volt 0.7.13 → 0.7.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6b0050f5ac129588d9ec24b02a9b8aaa19ff2003
4
- data.tar.gz: 05b63b91265dc30256fa8db98114542aad699987
3
+ metadata.gz: 1fc433aea390d72ea67bc96a667a68184162990c
4
+ data.tar.gz: d75d3644a9633f81c9ccb20da751586f67463c73
5
5
  SHA512:
6
- metadata.gz: 354f2e09f8e0827a693171b73fbfcd08e66dece3725a788047d40e72a71890f046030e0bf8240f6270755c7f36b8e668fbf257d20346849feb93c19147b0923f
7
- data.tar.gz: a33eb668641e8a7d2c6ab1176f025719486e4dc3fd9c1d4c26dcd4ff8e1c1e3e7c837178c7f1d2f8ccf651e84542102668d1e6b89f7be27d0615e07b9c3a7b7d
6
+ metadata.gz: 6a9a01e11d069071794c7d279a49bbbafb54b16a9a9f7a4f4d1210291914390c26383908884428d79ff5fa139608d1df270d8e83a742d15ec10e5e0de3db4487
7
+ data.tar.gz: a72b58ada2bda7c71d5be6ba48328b8c7390052f6a4ba479c948f1c1923d07bf7f7cea60736f97b7e4964652108187b19de9fd065b3db6fe96149ecdbf85b0dd
data/Readme.md CHANGED
@@ -14,7 +14,15 @@ Instead of syncing data between the client and server via HTTP, volt uses a pers
14
14
 
15
15
  Pages HTML is written in a handlebars like template language. Volt uses data flow/reactive programming to automatically and intelligently propagate changes to the DOM (or anything other code wanting to know when a value updates) When something in the DOM changes, Volt intelligently updates only the nodes that need to be changed.
16
16
 
17
- See a quick demo video here: [https://www.youtube.com/watch?v=6ZIvs0oKnYs](https://www.youtube.com/watch?v=6ZIvs0oKnYs) and [https://www.youtube.com/watch?v=c478sMlhx1o](https://www.youtube.com/watch?v=c478sMlhx1o)
17
+ See some demo videos here:
18
+ - [https://www.youtube.com/watch?v=6ZIvs0oKnYs](https://www.youtube.com/watch?v=6ZIvs0oKnYs)
19
+ - [https://www.youtube.com/watch?v=c478sMlhx1o](https://www.youtube.com/watch?v=c478sMlhx1o)
20
+ - [https://www.youtube.com/watch?v=yZIQ-2irY-Q](https://www.youtube.com/watch?v=yZIQ-2irY-Q)
21
+
22
+ Check out demo apps:
23
+ - https://github.com/voltrb/todos
24
+ - https://github.com/voltrb/blog
25
+ - https://github.com/voltrb/contactsdemo
18
26
 
19
27
 
20
28
  ## Goals
@@ -544,6 +552,18 @@ Controllers in the app/home component do not need to be namespaced, all other co
544
552
 
545
553
  Here "auth" would be the component name.
546
554
 
555
+ ## Reactive Accessors
556
+
557
+ The default ModelController proxies any missing methods to its model. Since models are wrapped in ReactiveValue's, they return ReactiveValue's by default. Sometimes you need to store additional data reactively in the controller outside of the model. (Though often you may want to condier doing another control/controller) In this case, you can add a ```reactive_accessor```. These behave just like ```attr_accessor``` except the values assigned and returned are wrapped in a ReactiveValue. Updates update the existing ReactiveValue.
558
+
559
+ ```ruby
560
+ class Contacts < ModelController
561
+ reactive_accessor :_query
562
+ end
563
+ ```
564
+
565
+ Now from the view we can bind to _query while also changing in and out the model. You can also use ```reactive_reader``` and ```reactive_writer```
566
+
547
567
  # Components
548
568
 
549
569
  Apps are made up of Components. Each folder under app/ is a component. When you visit a route, it loads all of the files in the component on the front end, so new pages within the component can be rendered without a new http request. If a URL is visited that routes to a different component, the request will be loaded as a normal page load and all of that components files will be loaded. You can think of components as the "reload boundary" between sections of your app.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.13
1
+ 0.7.14
@@ -1,4 +1,8 @@
1
+ require 'volt/controllers/reactive_accessors'
2
+
1
3
  class ModelController
4
+ include ReactiveAccessors
5
+
2
6
  def self.model(val)
3
7
  @default_model = val
4
8
  end
@@ -0,0 +1,48 @@
1
+ module ReactiveAccessors
2
+
3
+ module ClassMethods
4
+ # Create a method to read a reactive value from an instance value. If it
5
+ # is not setup, create it so it can be updated through the reactive value
6
+ # at a later point.
7
+ def reactive_reader(*names)
8
+ names.each do |name|
9
+ var_name = :"@#{name}"
10
+ define_method(name.to_sym) do
11
+ value = instance_variable_get(var_name)
12
+
13
+ unless value
14
+ value = ReactiveValue.new(nil)
15
+
16
+ instance_variable_set(var_name, value)
17
+ end
18
+
19
+ value
20
+ end
21
+ end
22
+ end
23
+
24
+ def reactive_writer(*names)
25
+ names.each do |name|
26
+ var_name = :"@#{name}"
27
+ define_method(:"#{name}=") do |new_value|
28
+ value = instance_variable_get(var_name)
29
+
30
+ if value
31
+ value.cur = new_value
32
+ else
33
+ instance_variable_set(var_name, ReactiveValue.new(value))
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def reactive_accessor(*names)
40
+ reactive_reader(*names)
41
+ reactive_writer(*names)
42
+ end
43
+ end
44
+
45
+ def self.included(base)
46
+ base.send :extend, ClassMethods
47
+ end
48
+ end
@@ -130,7 +130,13 @@ class URL
130
130
  query_hash = self.query_hash
131
131
 
132
132
  # Get the params that are in the route
133
- query_hash.merge!(@router.url_to_params(@path))
133
+ new_params = @router.url_to_params(@path)
134
+
135
+ if new_params == false
136
+ raise "no routes match path: #{@path}"
137
+ end
138
+
139
+ query_hash.merge!(new_params)
134
140
 
135
141
  # Loop through the .params we already have assigned.
136
142
  assign_from_old(@params, query_hash)
@@ -20,21 +20,21 @@ class BaseBinding
20
20
  @@binding_number ||= 10000
21
21
  end
22
22
 
23
- def section
24
- @section ||= target.section(@binding_name)
23
+ def dom_section
24
+ @dom_section ||= target.dom_section(@binding_name)
25
25
  end
26
26
 
27
27
  def remove
28
- section.remove if @section
28
+ @dom_section.remove if @dom_section
29
29
 
30
30
  # Clear any references
31
31
  @target = nil
32
32
  @context = nil
33
- @section = nil
33
+ @dom_section = nil
34
34
  end
35
35
 
36
36
  def remove_anchors
37
- section.remove_anchors
37
+ @dom_section.remove_anchors if @dom_section
38
38
  end
39
39
 
40
40
  def queue_update
@@ -17,13 +17,16 @@ class ContentBinding < BaseBinding
17
17
 
18
18
  def update
19
19
  value = @value.cur.or('')
20
+ if value.reactive?
21
+ puts "GOT CUR: #{value.inspect}"
22
+ end
20
23
 
21
24
  # Exception values display the exception as a string
22
25
  value = value.to_s
23
26
 
24
27
  # Update the html in this section
25
28
  # TODO: Move the formatter into another class.
26
- section.html = value.gsub("\n", "<br />\n")
29
+ dom_section.html = value.gsub("\n", "<br />\n")
27
30
  end
28
31
 
29
32
  def remove
@@ -56,10 +56,10 @@ class EachBinding < BaseBinding
56
56
 
57
57
  if position >= @templates.size
58
58
  # Setup new bindings in the spot we want to insert the item
59
- section.insert_anchor_before_end(binding_name)
59
+ dom_section.insert_anchor_before_end(binding_name)
60
60
  else
61
61
  # Insert the item before an existing item
62
- section.insert_anchor_before(binding_name, @templates[position].binding_name)
62
+ dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
63
63
  end
64
64
 
65
65
  index = ReactiveValue.new(position)
@@ -174,7 +174,7 @@ class TemplateBinding < BaseBinding
174
174
  # Set the current section on the controller if it wants so it can manipulate
175
175
  # the dom if needed
176
176
  if @controller.respond_to?(:section=)
177
- @controller.section = @current_template.section
177
+ @controller.section = @current_template.dom_section
178
178
  end
179
179
 
180
180
  if @controller.respond_to?(:dom_ready)
@@ -8,7 +8,7 @@ require 'volt/page/targets/binding_document/html_node'
8
8
 
9
9
  class AttributeTarget < ComponentNode
10
10
 
11
- def section(*args)
11
+ def dom_section(*args)
12
12
  return AttributeSection.new(self, *args)
13
13
  end
14
14
  end
@@ -5,7 +5,7 @@ require 'volt/page/targets/dom_section'
5
5
  # the dom. Currently only one "dom" is supported, but multiple
6
6
  # may be allowed in the future (iframes?)
7
7
  class DomTarget < BaseSection
8
- def section(*args)
8
+ def dom_section(*args)
9
9
  return DomSection.new(*args)
10
10
  end
11
11
  end
@@ -7,7 +7,7 @@ class TemplateRenderer < BaseBinding
7
7
 
8
8
  @sub_bindings = []
9
9
 
10
- bindings = self.section.set_content_to_template(page, template_name)
10
+ bindings = self.dom_section.set_content_to_template(page, template_name)
11
11
 
12
12
  bindings.each_pair do |id,bindings_for_id|
13
13
  bindings_for_id.each do |binding|
@@ -17,6 +17,8 @@ class ReactiveCount
17
17
  # After events are bound, we keep a cache of each cell's count
18
18
  # value, and base the results
19
19
  def cached_count
20
+ @cached_results = []
21
+
20
22
 
21
23
  end
22
24
 
@@ -24,7 +26,7 @@ class ReactiveCount
24
26
  # run the count on the source object.
25
27
  def direct_count
26
28
  count = 0
27
- @source.size.cur.times do |index|
29
+ @source.cur.size.times do |index|
28
30
  val = @source[index]
29
31
  result = @block.call(val).cur
30
32
  if result == true
@@ -37,16 +39,17 @@ class ReactiveCount
37
39
 
38
40
  def setup_listeners
39
41
  @cell_trackers = []
42
+ @setup = false
40
43
  @added_tracker = @source.on('added') do |_, index|
41
44
  change_cell_count(@source.size.cur)
42
- trigger!('changed')
43
45
  end
44
46
 
45
47
  @removed_tracker = @source.on('removed') do |_, index|
46
48
  change_cell_count(@source.size.cur)
47
- trigger!('changed')
48
49
  end
49
50
 
51
+ @setup = true
52
+
50
53
  # Initial cell tracking
51
54
  change_cell_count(@source.size.cur)
52
55
  end
@@ -54,6 +57,7 @@ class ReactiveCount
54
57
  # We need to make sure we're listening on the result from each cell,
55
58
  # that way we can trigger when the value changes.
56
59
  def change_cell_count(size)
60
+ # puts "CHANGE SIZE: #{size}"
57
61
  current_size = @cell_trackers.size
58
62
 
59
63
  if current_size < size
@@ -66,6 +70,7 @@ class ReactiveCount
66
70
  result = @block.call(val)
67
71
 
68
72
  @cell_trackers << result.on('changed') do
73
+ # puts "RESULT CHANGED: #{index}"
69
74
  trigger!('changed')
70
75
  end
71
76
  end
@@ -239,21 +239,23 @@ class ReactiveManager
239
239
 
240
240
  # Fetch the current value
241
241
  def cur(shallow=false, ignore_cache=false)
242
- # Return from cache if it is cached
242
+ # Use cache if it is cached
243
243
  if @cur_cache && !shallow && !ignore_cache
244
- return @cur_cache
245
- end
246
-
247
- if @getter.class == ::Proc
248
- # Get the current value, capture any errors
249
- begin
250
- result = @getter.call
251
- rescue => e
252
- result = e
253
- end
244
+ # We might be caching another reactive value, so we just set
245
+ # it as the result and let it get unwrapped.
246
+ result = @cur_cache
254
247
  else
255
- # getter is just an object, return it
256
- result = @getter
248
+ if @getter.class == ::Proc
249
+ # Get the current value, capture any errors
250
+ begin
251
+ result = @getter.call
252
+ rescue => e
253
+ result = e
254
+ end
255
+ else
256
+ # getter is just an object, return it
257
+ result = @getter
258
+ end
257
259
  end
258
260
 
259
261
  if !shallow && result.reactive?
@@ -268,7 +270,7 @@ class ReactiveManager
268
270
  def update_followers
269
271
  return if @setting_up
270
272
  if has_listeners?
271
- current_obj = cur(false, true)
273
+ current_obj = cur(true, true)
272
274
  should_attach = current_obj.respond_to?(:on)
273
275
 
274
276
  if should_attach
@@ -0,0 +1,41 @@
1
+ require 'volt/controllers/reactive_accessors'
2
+
3
+ class TestReactiveAccessors
4
+ include ReactiveAccessors
5
+
6
+ reactive_accessor :_name
7
+ end
8
+
9
+ describe ReactiveAccessors do
10
+ it "should return the same reactive value after each read" do
11
+ inst = TestReactiveAccessors.new
12
+
13
+ expect(inst._name.reactive_manager.object_id).to eq(inst._name.reactive_manager.object_id)
14
+ end
15
+
16
+ it "should assign a reactive value" do
17
+ inst = TestReactiveAccessors.new
18
+
19
+ inst._name = 'Ryan'
20
+ expect(inst._name).to eq('Ryan')
21
+ end
22
+
23
+ it "should start nil" do
24
+ inst = TestReactiveAccessors.new
25
+
26
+ expect(inst._name.cur).to eq(nil)
27
+ end
28
+
29
+ it "should keep the same reactive value when reassigning" do
30
+ inst = TestReactiveAccessors.new
31
+
32
+ inst._name = 'Ryan'
33
+ rv1_id = inst._name.reactive_manager.object_id
34
+
35
+ inst._name = 'Jim'
36
+ rv2_id = inst._name.reactive_manager.object_id
37
+
38
+ expect(rv1_id).to eq(rv2_id)
39
+ end
40
+
41
+ end
@@ -0,0 +1,13 @@
1
+ require 'volt/models'
2
+
3
+ describe ReactiveCount do
4
+ it "should call cur through the reactive count to the number" do
5
+ model = ReactiveValue.new(Model.new)
6
+
7
+ model._items << {_name: 'ok'}
8
+
9
+ count = model._items.count {|m| m._name == 'ok' }
10
+
11
+ expect(count.cur).to eq(1)
12
+ end
13
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.13
4
+ version: 0.7.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Stout
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-19 00:00:00.000000000 Z
11
+ date: 2014-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -361,6 +361,7 @@ files:
361
361
  - lib/volt/cli/new_gem.rb
362
362
  - lib/volt/console.rb
363
363
  - lib/volt/controllers/model_controller.rb
364
+ - lib/volt/controllers/reactive_accessors.rb
364
365
  - lib/volt/extra_core/array.rb
365
366
  - lib/volt/extra_core/blank.rb
366
367
  - lib/volt/extra_core/extra_core.rb
@@ -485,6 +486,7 @@ files:
485
486
  - spec/apps/kitchen_sink/app/main/views/main/main.html
486
487
  - spec/apps/kitchen_sink/config.ru
487
488
  - spec/apps/kitchen_sink/public/index.html
489
+ - spec/controllers/reactive_accessors_spec.rb
488
490
  - spec/extra_core/inflector_spec.rb
489
491
  - spec/integration/test_integration_spec.rb
490
492
  - spec/models/event_chain_spec.rb
@@ -495,6 +497,7 @@ files:
495
497
  - spec/models/persistors/store_spec.rb
496
498
  - spec/models/reactive_array_spec.rb
497
499
  - spec/models/reactive_call_times_spec.rb
500
+ - spec/models/reactive_count_spec.rb
498
501
  - spec/models/reactive_generator_spec.rb
499
502
  - spec/models/reactive_tags_spec.rb
500
503
  - spec/models/reactive_value_spec.rb
@@ -596,6 +599,7 @@ test_files:
596
599
  - spec/apps/kitchen_sink/app/main/views/main/main.html
597
600
  - spec/apps/kitchen_sink/config.ru
598
601
  - spec/apps/kitchen_sink/public/index.html
602
+ - spec/controllers/reactive_accessors_spec.rb
599
603
  - spec/extra_core/inflector_spec.rb
600
604
  - spec/integration/test_integration_spec.rb
601
605
  - spec/models/event_chain_spec.rb
@@ -606,6 +610,7 @@ test_files:
606
610
  - spec/models/persistors/store_spec.rb
607
611
  - spec/models/reactive_array_spec.rb
608
612
  - spec/models/reactive_call_times_spec.rb
613
+ - spec/models/reactive_count_spec.rb
609
614
  - spec/models/reactive_generator_spec.rb
610
615
  - spec/models/reactive_tags_spec.rb
611
616
  - spec/models/reactive_value_spec.rb