volt 0.7.23 → 0.8.0

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile +8 -0
  5. data/Guardfile +2 -2
  6. data/Readme.md +139 -136
  7. data/VERSION +1 -1
  8. data/app/volt/assets/js/setImmediate.js +175 -0
  9. data/app/volt/tasks/live_query/data_store.rb +0 -2
  10. data/app/volt/tasks/live_query/live_query.rb +4 -4
  11. data/docs/GETTING_STARTED.md +24 -3
  12. data/docs/WHY.md +1 -22
  13. data/lib/volt.rb +20 -1
  14. data/lib/volt/console.rb +20 -0
  15. data/lib/volt/controllers/model_controller.rb +25 -11
  16. data/lib/volt/extra_core/object.rb +2 -14
  17. data/lib/volt/extra_core/string.rb +4 -0
  18. data/lib/volt/models.rb +0 -1
  19. data/lib/volt/models/array_model.rb +8 -16
  20. data/lib/volt/models/cursor.rb +1 -1
  21. data/lib/volt/models/model.rb +40 -60
  22. data/lib/volt/models/model_hash_behaviour.rb +10 -24
  23. data/lib/volt/models/model_helpers.rb +2 -2
  24. data/lib/volt/models/model_state.rb +1 -1
  25. data/lib/volt/models/model_wrapper.rb +4 -4
  26. data/lib/volt/models/persistors/array_store.rb +44 -28
  27. data/lib/volt/models/persistors/base.rb +1 -1
  28. data/lib/volt/models/persistors/model_store.rb +1 -1
  29. data/lib/volt/models/persistors/params.rb +5 -1
  30. data/lib/volt/models/persistors/query/query_listener.rb +2 -0
  31. data/lib/volt/models/persistors/store.rb +3 -2
  32. data/lib/volt/models/persistors/store_state.rb +7 -2
  33. data/lib/volt/models/url.rb +35 -29
  34. data/lib/volt/models/validations.rb +7 -17
  35. data/lib/volt/page/bindings/attribute_binding.rb +57 -39
  36. data/lib/volt/page/bindings/base_binding.rb +0 -14
  37. data/lib/volt/page/bindings/content_binding.rb +15 -18
  38. data/lib/volt/page/bindings/each_binding.rb +67 -34
  39. data/lib/volt/page/bindings/if_binding.rb +15 -12
  40. data/lib/volt/page/bindings/template_binding.rb +77 -59
  41. data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
  42. data/lib/volt/page/channel.rb +22 -38
  43. data/lib/volt/page/channel_stub.rb +3 -6
  44. data/lib/volt/page/page.rb +24 -26
  45. data/lib/volt/page/string_template_renderer.rb +46 -0
  46. data/lib/volt/page/sub_context.rb +7 -1
  47. data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
  48. data/lib/volt/page/tasks.rb +3 -2
  49. data/lib/volt/page/url_tracker.rb +4 -3
  50. data/lib/volt/reactive/computation.rb +131 -0
  51. data/lib/volt/reactive/dependency.rb +71 -0
  52. data/lib/volt/reactive/eventable.rb +82 -0
  53. data/lib/volt/reactive/hash_dependency.rb +36 -0
  54. data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
  55. data/lib/volt/reactive/reactive_array.rb +100 -193
  56. data/lib/volt/reactive/reactive_hash.rb +49 -0
  57. data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
  58. data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
  59. data/lib/volt/server/html_parser/view_scope.rb +31 -1
  60. data/spec/apps/kitchen_sink/Gemfile +4 -8
  61. data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
  62. data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
  63. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
  64. data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
  65. data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
  66. data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
  67. data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
  68. data/spec/controllers/reactive_accessors_spec.rb +13 -15
  69. data/spec/integration/bindings_spec.rb +159 -0
  70. data/spec/integration/templates_spec.rb +15 -0
  71. data/spec/models/model_spec.rb +130 -228
  72. data/spec/reactive/computation_spec.rb +63 -0
  73. data/spec/reactive/dependency_spec.rb +5 -0
  74. data/spec/reactive/eventable_spec.rb +48 -0
  75. data/spec/reactive/reactive_array_spec.rb +97 -0
  76. data/spec/router/routes_spec.rb +26 -27
  77. data/spec/server/html_parser/view_parser_spec.rb +3 -21
  78. data/spec/server/rack/asset_files_spec.rb +1 -1
  79. data/templates/project/app/main/views/main/main.html +2 -2
  80. metadata +29 -41
  81. data/lib/volt/extra_core/time.rb +0 -16
  82. data/lib/volt/page/draw_cycle.rb +0 -31
  83. data/lib/volt/page/memory_test.rb +0 -26
  84. data/lib/volt/page/reactive_template.rb +0 -32
  85. data/lib/volt/reactive/array_extensions.rb +0 -12
  86. data/lib/volt/reactive/destructive_methods.rb +0 -19
  87. data/lib/volt/reactive/event_chain.rb +0 -125
  88. data/lib/volt/reactive/events.rb +0 -216
  89. data/lib/volt/reactive/object_tracking.rb +0 -14
  90. data/lib/volt/reactive/reactive_block.rb +0 -88
  91. data/lib/volt/reactive/reactive_generator.rb +0 -44
  92. data/lib/volt/reactive/reactive_tags.rb +0 -71
  93. data/lib/volt/reactive/reactive_value.rb +0 -427
  94. data/lib/volt/reactive/string_extensions.rb +0 -31
  95. data/spec/integration/test_integration_spec.rb +0 -14
  96. data/spec/models/event_chain_spec.rb +0 -150
  97. data/spec/models/model_buffers_spec.rb +0 -9
  98. data/spec/models/old_model_spec.rb +0 -67
  99. data/spec/models/reactive_array_spec.rb +0 -364
  100. data/spec/models/reactive_block_spec.rb +0 -13
  101. data/spec/models/reactive_call_times_spec.rb +0 -28
  102. data/spec/models/reactive_generator_spec.rb +0 -58
  103. data/spec/models/reactive_tags_spec.rb +0 -35
  104. data/spec/models/reactive_value_spec.rb +0 -370
  105. data/spec/models/store_spec.rb +0 -16
  106. data/spec/models/string_extensions_spec.rb +0 -57
@@ -1,14 +0,0 @@
1
- # Provides methods for objects that store reactive value's to trigger
2
- module ObjectTracking
3
- def __setup_tracking(key, value)
4
- if value.reactive?
5
- # TODO: We should build this in so it fires just for the current index.
6
- # Currently this is a big performance hit.
7
- chain_listener = event_chain.add_object(value.reactive_manager) do |event, filter, *args|
8
- yield(event, key, args)
9
- end
10
- @reactive_element_listeners ||= {}
11
- @reactive_element_listeners[key] = chain_listener
12
- end
13
- end
14
- end
@@ -1,88 +0,0 @@
1
- class ReactiveBlock
2
- include ReactiveTags
3
-
4
- def reactive?
5
- true
6
- end
7
-
8
- def initialize(source, check_block, run_block)
9
- @source = ReactiveValue.new(source)
10
- @check_block = check_block
11
- @run_block = run_block
12
- end
13
-
14
- def cur
15
- val = @run_block.call(@source)
16
-
17
- return val
18
- end
19
-
20
- def setup_listeners
21
- @cell_trackers = []
22
- @setup = false
23
- @added_tracker = @source.on('added') do |_, index|
24
- change_cell_count(@source.size.cur)
25
- end
26
-
27
- @removed_tracker = @source.on('removed') do |_, index|
28
- change_cell_count(@source.size.cur)
29
- end
30
-
31
- @setup = true
32
-
33
- # Initial cell tracking
34
- change_cell_count(@source.size.cur)
35
- end
36
-
37
- # We need to make sure we're listening on the result from each cell,
38
- # that way we can trigger when the value changes.
39
- def change_cell_count(size)
40
- current_size = @cell_trackers.size
41
-
42
- if current_size < size
43
- # Add trackers
44
-
45
- current_size.upto(size-1) do |index|
46
- # Get the reactive value for the index
47
- val = @source[index]
48
-
49
- result = @check_block.call(val)
50
-
51
- @cell_trackers << result.on('changed') do
52
- puts "RESULT CHANGED: #{index} - #{self.object_id}"
53
- trigger!('changed')
54
- end
55
- end
56
- elsif current_size > size
57
- (current_size-1).downto(size) do |index|
58
- @cell_trackers[index].remove
59
- @cell_trackers.delete_at(index)
60
- end
61
- end
62
- end
63
-
64
-
65
- def teardown_listeners
66
- @added_tracker.remove
67
- @added_tracker = nil
68
-
69
- @removed_tracker.remove
70
- @removed_tracker = nil
71
-
72
- change_cell_count(0)
73
-
74
- @cell_trackers = nil
75
- end
76
-
77
- def event_added(event, scope_provider, first, first_for_event)
78
- setup_listeners if first
79
- end
80
-
81
- def event_removed(event, last, last_for_event)
82
- teardown_listeners if last
83
- end
84
-
85
- def inspect
86
- "@#{cur}"
87
- end
88
- end
@@ -1,44 +0,0 @@
1
- class ReactiveGenerator
2
- # Takes a hash and returns a ReactiveValue that depends on
3
- # any ReactiveValue's inside of the hash (or children).
4
- def self.from_hash(hash, skip_if_no_reactives=false)
5
- reactives = find_reactives(hash)
6
-
7
- if skip_if_no_reactives && reactives.size == 0
8
- # There weren't any reactives, we can just use the hash
9
- return hash
10
- else
11
- # Create a new reactive value that listens on all of its
12
- # child reactive values.
13
- value = ReactiveValue.new(hash)
14
-
15
- reactives.each do |child|
16
- value.reactive_manager.add_parent!(child)
17
- end
18
-
19
- return value
20
- end
21
- end
22
-
23
- # Recursively loop through the data, returning a list of all
24
- # reactive values in the hash, array, etc..
25
- def self.find_reactives(object)
26
- found = []
27
- if object.reactive?
28
- found << object
29
-
30
- found += find_reactives(object.cur)
31
- elsif object.is_a?(Array)
32
- object.each do |item|
33
- found += find_reactives(item)
34
- end
35
- elsif object.is_a?(Hash)
36
- object.each_pair do |key, value|
37
- found += find_reactives(key)
38
- found += find_reactives(value)
39
- end
40
- end
41
-
42
- return found.flatten
43
- end
44
- end
@@ -1,71 +0,0 @@
1
- require 'volt/reactive/destructive_methods'
2
-
3
- # ReactiveTags provide an easy way to specify how a class deals with
4
- # reactive events and method calls.als
5
- module ReactiveTags
6
- class MethodTags
7
- attr_accessor :destructive, :pass_reactive, :reacts_with
8
- end
9
-
10
- class MethodTagger
11
- attr_reader :method_tags
12
-
13
- def initialize
14
- @method_tags = MethodTags.new
15
- end
16
-
17
- def destructive!(&block)
18
- @method_tags.destructive = block || true
19
- end
20
-
21
- def pass_reactive!
22
- @method_tags.pass_reactive = true
23
- end
24
- end
25
-
26
- module ClassMethods
27
- def tag_method(method_name, &block)
28
- tagger = MethodTagger.new
29
-
30
- tagger.instance_eval(&block)
31
-
32
- @reactive_method_tags ||= {}
33
- @reactive_method_tags[method_name.to_sym] = tagger.method_tags
34
-
35
- # Track a destructive method
36
- if tagger.method_tags.destructive
37
- DestructiveMethods.add_method(method_name)
38
- end
39
- end
40
-
41
- def tag_all_methods(&block)
42
- tagger = MethodTagger.new
43
-
44
- tagger.instance_eval(&block)
45
-
46
- @reactive_method_tags ||= {}
47
- @reactive_method_tags[:__all_methods] = tagger.method_tags
48
- end
49
- end
50
-
51
- # Returns a reference to the tags on a method
52
- def reactive_method_tag(method_name, tag_name, klass=self.class)
53
- # Check to make sure we haven't gone above a class that has included
54
- # ReactiveTags
55
- return nil if !klass || !klass.method_defined?(:reactive_method_tag)
56
-
57
- tags = klass.instance_variable_get('@reactive_method_tags')
58
-
59
- if tags && (tag = tags[method_name.to_sym]) && (tag = tag.send(tag_name))
60
- return tag
61
- end
62
-
63
- return self.reactive_method_tag(method_name, tag_name, klass.superclass)
64
- end
65
-
66
-
67
- def self.included(base)
68
- base.send(:extend, ClassMethods)
69
- base.send(:include, Events)
70
- end
71
- end
@@ -1,427 +0,0 @@
1
- require 'volt/reactive/events'
2
- require 'volt/reactive/reactive_tags'
3
- require 'volt/reactive/string_extensions'
4
- require 'volt/reactive/array_extensions'
5
- require 'volt/reactive/reactive_array'
6
- require 'volt/reactive/destructive_methods'
7
- require 'volt/reactive/reactive_generator'
8
-
9
- class Object
10
- def cur
11
- self
12
- end
13
-
14
- def reactive?
15
- false
16
- end
17
- end
18
-
19
- class ReactiveValue < BasicObject
20
- # methods on ReactiveValues:
21
- # reactive?, cur, with, on, data, trigger!
22
- # - everything else is forwarded to the ReactiveManager
23
-
24
- # Methods we should skip wrapping the results in
25
- # We skip .hash because in uniq it has .to_int called on it, which needs to
26
- # return a Fixnum instance.
27
- # :hash - needs something where .to_int can be called on it and it will
28
- # return an int
29
- # :methods- needs to return a straight up array to work with irb tab completion
30
- # :eql? - needed for .uniq to work correctly
31
- # :to_ary - in some places ruby expects to get an array back from this method
32
- SKIP_METHODS = [:object_id, :hash, :methods, :eql?, :respond_to?, :respond_to_missing?, :to_ary, :to_int]#, :instance_of?, :kind_of?, :to_s, :to_str]
33
-
34
- def initialize(getter, setter=nil, scope=nil)
35
- @reactive_manager = ::ReactiveManager.new(self, getter, setter, scope)
36
- # @reactive_cache = {}
37
- end
38
-
39
- def reactive?
40
- true
41
- end
42
-
43
- # Proxy methods to the ReactiveManager. We want to have as few
44
- # as possible methods on reactive values, so all other methods
45
- # are forwarded to the object the reactive value points to.
46
- [:cur, :cur=, :deep_cur, :on, :trigger!, :trigger_by_scope!, :with].each do |method_name|
47
- define_method(method_name) do |*args, &block|
48
- @reactive_manager.send(method_name, *args, &block)
49
- end
50
- end
51
-
52
- def reactive_manager
53
- @reactive_manager
54
- end
55
- alias_method :rm, :reactive_manager
56
-
57
- def puts(*args)
58
- ::Object.send(:puts, *args)
59
- end
60
-
61
- def __is_destructive?(method_name)
62
- last_char = method_name[-1]
63
- if last_char == '=' && method_name[-2] != '='
64
- # Method is an assignment (and not a comparator ==)
65
- return true
66
- elsif method_name.size > 1 && last_char == '!' || last_char == '<'
67
- # Method is tagged as destructive, or is a push ( << )
68
- return true
69
- elsif ::DestructiveMethods.might_be_destructive?(method_name)
70
- # Method may be destructive, check if it actually is on the current value
71
- # TODO: involves a call to cur
72
- return reactive_manager.check_tag(method_name, :destructive, self.cur)
73
- else
74
- return false
75
- end
76
- end
77
-
78
- def method_missing(method_name, *args, &block)
79
- # Unroll send into a direct call
80
- if method_name == :send
81
- method_name, *args = args
82
- end
83
-
84
- # result = @reactive_cache[[method_name, args.map(&:object_id)]]
85
- # return result if result
86
-
87
- # For some methods, we pass directly to the current object. This
88
- # helps ReactiveValue's be well behaved ruby citizens.
89
- # Also skip if this is a destructive method
90
- if SKIP_METHODS.include?(method_name) || __is_destructive?(method_name)
91
- current_obj = self.cur
92
-
93
- # Unwrap arguments if the method doesn't want reactive values
94
- pass_args = reactive_manager.unwrap_if_pass_reactive(args, method_name, current_obj)
95
-
96
- return current_obj.__send__(method_name, *pass_args, &block)
97
- end
98
-
99
- result = @reactive_manager.with_and_options(args) do |val, in_args|
100
- # Unwrap arguments if the method doesn't want reactive values
101
- # TODO: Should cache the lookup on pass_reactive
102
- pass_args = reactive_manager.unwrap_if_pass_reactive(in_args, method_name, val)
103
-
104
- val.__send__(method_name, *pass_args, &block)
105
- end
106
-
107
- manager = result.reactive_manager
108
-
109
- setup_setter(manager, method_name, args)
110
-
111
- manager.set_scope!([method_name, *args, block])
112
-
113
- # result = result.with(block_reactives) if block
114
-
115
- # if args.size == 0 || method_name == :[]
116
- # @reactive_cache[[method_name, args.map(&:object_id)]] = result
117
- # end
118
-
119
- return result
120
- end
121
-
122
- def setup_setter(manager, method_name, args)
123
- # See if we can automatically create a setter. If we are fetching a
124
- # value via a read, we can probably reassign it with .name=
125
- if args.size == 0
126
- # TODO: At the moment we are defining a setter on all "reads", this
127
- # probably has some performance implications
128
- manager.setter! do |val|
129
- # Call setter
130
- self.cur.send(:"#{method_name}=", val)
131
- end
132
- elsif args.size == 1 && method_name == :[]
133
- manager.setter! do |val|
134
- # Call an array setter
135
- self.cur.send(:"#{method_name}=", args[0], val)
136
- end
137
- end
138
- end
139
-
140
- def respond_to_missing?(name, include_private=false)
141
- cur.respond_to?(name)
142
- end
143
-
144
- def inspect
145
- "@#{cur.inspect}"
146
- end
147
-
148
- def pretty_inspect
149
- inspect
150
- end
151
-
152
- # Not 100% sure why, but we need to define this directly, it doesn't call
153
- # on method missing
154
- def ==(val)
155
- method_missing(:==, val)
156
- end
157
-
158
- # TODO: this is broke in opal
159
- def !
160
- method_missing(:!)
161
- end
162
-
163
- def to_s
164
- cur.to_s
165
- end
166
-
167
- def coerce(other)
168
- if other.reactive?
169
- return [other, self]
170
- else
171
- wrapped_object = ::ReactiveValue.new(other, [])
172
- return [wrapped_object, self]
173
- end
174
- end
175
-
176
- # Return a new reactive value that listens for changes on any
177
- # ReactiveValues inside of its children (hash values, array items, etc..)
178
- # This is useful if someone is passing in a set of options, but the main
179
- # hash isn't a ReactiveValue, but you want to listen for changes inside
180
- # of the hash.
181
- #
182
- # skip_if_no_reactives lets you get back a non-reactive value in the event
183
- # that there are no child reactive values.
184
- def self.from_hash(hash, skip_if_no_reactives=false)
185
- ::ReactiveGenerator.from_hash(hash)
186
- end
187
- end
188
-
189
- class ReactiveManager
190
- include ::Events
191
-
192
- attr_reader :scope, :parents
193
-
194
- # When created, ReactiveValue's get a getter (a proc)
195
- def initialize(reactive_value, getter, setter=nil, scope=nil)
196
- @reactive_value = reactive_value
197
- @getter = getter
198
- @setter = setter
199
- @scope = scope
200
-
201
- @parents = []
202
- end
203
-
204
- def reactive_value
205
- @reactive_value
206
- end
207
-
208
- def reactive?
209
- true
210
- end
211
-
212
- def inspect
213
- "@<#{self.class.to_s}:#{object_id} #{cur.inspect}>"
214
- end
215
-
216
- def reactive_object_id
217
- @reactive_object_id ||= rand(100000)
218
- end
219
-
220
-
221
- def event_added(event, scope, first, first_for_event)
222
- # When the first event is registered, we need to start listening on our current object
223
- # for it to publish events.
224
-
225
- update_followers if first
226
- end
227
-
228
- def event_removed(event, last, last_for_event)
229
- # If no one is listening on the reactive value, then we don't need to listen on our
230
- # current object for events, because no one cares.
231
-
232
- remove_followers if last
233
- end
234
-
235
-
236
- # Fetch the current value
237
- def cur(shallow=false, ignore_cache=false)
238
- # Use cache if it is cached
239
- if @cur_cache && !shallow && !ignore_cache
240
- # We might be caching another reactive value, so we just set
241
- # it as the result and let it get unwrapped.
242
- result = @cur_cache
243
- else
244
- if @getter.class == ::Proc
245
- # Get the current value, capture any errors
246
- begin
247
- result = @getter.call
248
- rescue => e
249
- result = e
250
- end
251
- else
252
- # getter is just an object, return it
253
- result = @getter
254
- end
255
- end
256
-
257
- if !shallow && result.reactive?
258
- # Unwrap any stored reactive values
259
- result = result.cur
260
- end
261
-
262
- return result
263
- end
264
-
265
-
266
- def update_followers
267
- return if @setting_up
268
- if has_listeners?
269
- current_obj = cur(true, true)
270
- should_attach = current_obj.respond_to?(:on)
271
-
272
- if should_attach
273
- if !@cur_cache || current_obj.object_id != @cur_cache.object_id
274
- remove_followers
275
-
276
- @setting_up = true
277
- @cur_cache_chain_listener = self.event_chain.add_object(current_obj)
278
- @setting_up = nil
279
- end
280
- else
281
- remove_followers
282
- end
283
-
284
- # Store current if we have listeners
285
- @cur_cache = current_obj
286
- end
287
-
288
- end
289
-
290
- def remove_followers
291
- # Remove from previous
292
- if @cur_cache
293
- @cur_cache = nil
294
- end
295
-
296
- if @cur_cache_chain_listener
297
- @cur_cache_chain_listener.remove
298
- @cur_cache_chain_listener = nil
299
- end
300
- end
301
-
302
- def cur=(val)
303
- if @setter
304
- @setter.call(val)
305
- # update_followers
306
- elsif @scope == nil
307
- @getter = val
308
- @setter = nil
309
-
310
- # update_followers
311
- trigger!('changed')
312
- else
313
- raise "Value can not be updated"
314
- end
315
-
316
- end
317
-
318
- # Returns a copy of the object with where all ReactiveValue's are replaced
319
- # with their current value.
320
- # NOTE: Classes need to implement their own deep_cur method for this to work,
321
- # it works out of the box with arrays and hashes.
322
- def deep_cur
323
- self.cur.deep_cur
324
- end
325
-
326
- # Method calls can be tagged so the reactive value knows
327
- # how to handle them. This lets you check the state of
328
- # the tags.
329
- def check_tag(method_name, tag_name, current_obj)
330
- if current_obj.respond_to?(:reactive_method_tag)
331
- tag = current_obj.reactive_method_tag(method_name, tag_name)
332
-
333
- unless tag
334
- # Get the tag from the all methods if its not directly specified
335
- tag = current_obj.reactive_method_tag(:__all_methods, tag_name)
336
- end
337
-
338
- # Evaluate now if its a proc
339
- tag = tag.call(method_name) if tag.class == ::Proc
340
-
341
- return tag
342
- end
343
-
344
- return nil
345
- end
346
-
347
- def unwrap_if_pass_reactive(args, method_name, current_obj)
348
- # Check to see if the method we're calling wants to receive reactive values.
349
- pass_reactive = check_tag(method_name, :pass_reactive, current_obj)
350
-
351
- # Unwrap arguments if the method doesn't want reactive values
352
- return pass_reactive ? args : args.map{|v| v.cur }
353
- end
354
-
355
- # With returns a new reactive value dependent on any arguments passed in.
356
- # If a block is passed in, the getter is the block its self, which will
357
- # be passed the .cur and the .cur of any reactive arguments.
358
- def with(*args, &block)
359
- return with_and_options(args, &block)
360
- end
361
-
362
- def with_and_options(args, &block)
363
- getter = @getter
364
- setter = @setter
365
- scope = @scope
366
-
367
- if block
368
- # If a block was passed in, the getter now becomes a proc that calls
369
- # the passed in block with the right arguments.
370
- getter = ::Proc.new do
371
- # TODO: Calling cur every time
372
- current_val = self.cur
373
-
374
- if current_val.is_a?(Exception)
375
- current_val
376
- else
377
- block.call(current_val, args)
378
- end
379
- end
380
-
381
- # TODO: Make this work with custom setters
382
- setter = nil
383
-
384
- # Scope also gets set to nil, because now we should always retrigger this
385
- # method because we don't know enough about what methods its calling.
386
- scope = nil
387
- end
388
-
389
- new_val = ReactiveValue.new(getter, setter, scope)
390
-
391
- # Add the ReactiveValue we're building from
392
- new_val.reactive_manager.add_parent!(self)
393
-
394
- # Add any reactive arguments as parents
395
- args.select(&:reactive?).each do |arg|
396
- new_val.reactive_manager.add_parent!(arg.reactive_manager)
397
- end
398
-
399
- return new_val
400
- end
401
-
402
- def add_parent!(parent)
403
- @parents << parent
404
- event_chain.add_object(parent)
405
- end
406
-
407
- def remove_parent!(parent)
408
- @parents.delete(parent)
409
- event_chain.remove_object(parent)
410
- end
411
-
412
-
413
- def set_scope!(new_scope)
414
- @scope = new_scope
415
-
416
- self
417
- end
418
-
419
- def set_scope(new_scope)
420
- dup.scope!(new_scope)
421
- end
422
-
423
- # Sets the setter
424
- def setter!(setter=nil, &block)
425
- @setter = setter || block
426
- end
427
- end