volt 0.7.23 → 0.8.0

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