Class: ReactiveValue

Inherits:
BasicObject
Defined in:
lib/volt/reactive/reactive_value.rb

Constant Summary collapse

SKIP_METHODS =

Methods we should skip wrapping the results in We skip .hash because in uniq it has .to_int called on it, which needs to return a Fixnum instance. :hash - needs something where .to_int can be called on it and it will

return an int

:methods- needs to return a straight up array to work with irb tab completion :eql? - needed for .uniq to work correctly :to_ary - in some places ruby expects to get an array back from this method

[:hash, :methods, :eql?, :respond_to?, :respond_to_missing?, :to_ary, :to_int]

Instance Method Summary collapse

Constructor Details

#initialize(getter, setter = nil, scope = nil) ⇒ ReactiveValue

Returns a new instance of ReactiveValue.



33
34
35
# File 'lib/volt/reactive/reactive_value.rb', line 33

def initialize(getter, setter=nil, scope=nil)
  @reactive_manager = ::ReactiveManager.new(getter, setter, scope)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &block) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/volt/reactive/reactive_value.rb', line 79

def method_missing(method_name, *args, &block)
  # Unroll send into a direct call
  if method_name == :send
    method_name, *args = args
  end
  
  # Check to see if the method we're calling wants to receive reactive values.
  pass_reactive = check_tag(method_name, :pass_reactive)

  # For some methods, we pass directly to the current object.  This
  # helps ReactiveValue's be well behaved ruby citizens.
  # Also skip if this is a destructive method
  if SKIP_METHODS.include?(method_name) || check_tag(method_name, :destructive)# || (method_name[0] =~ /[a-zA-Z]/ && !cur.is_a?(::Exception))
    pass_args = pass_reactive ? args : args.map{|v| v.cur }
    return cur.__send__(method_name, *pass_args, &block)
  end
  
  @block_reactives = []
  result = @reactive_manager.with_and_options(args, pass_reactive) do |val, in_args|
    # When a method is called with a block, we pass in our own block that wraps the
    # block passed in.  This way we can pass in any arguments as reactive and track
    # the return values.
    new_block = block
    # index_cache = []
    # index = 0
    # 
    # if false && new_block
    #   new_block = ::Proc.new do |*block_args|
    #     res = block.call(*block_args.map {|v| ::ReactiveValue.new(v) })
    #     
    #     result.rm.remove_parent!(index_cache[index]) if index_cache[index]
    #     puts "index: #{index}"
    #     index_cache[index] = res
    # 
    #     # @block_reactives << res
    #     result.rm.add_parent!(res)
    #     # puts "Parent Size: #{result.rm.parents.size}"
    #     
    #     index += 1
    #     
    #     res.cur
    #   end
    # end
    
    val.__send__(method_name, *in_args, &new_block)
  end
  
  manager = result.reactive_manager
  
  setup_setter(manager, method_name, args)
  
  manager.set_scope!([method_name, *args, block])
  
  # result = result.with(block_reactives) if block
  
  return result
end

Instance Method Details

#!Object

TODO: this is broke in opal



182
183
184
# File 'lib/volt/reactive/reactive_value.rb', line 182

def !
  method_missing(:!)
end

#==(val) ⇒ Object

Not 100% sure why, but we need to define this directly, it doesn’t call on method missing



177
178
179
# File 'lib/volt/reactive/reactive_value.rb', line 177

def ==(val)
  method_missing(:==, val)
end

#check_tag(method_name, tag_name) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/volt/reactive/reactive_value.rb', line 55

def check_tag(method_name, tag_name)
  current_obj = cur # TODO: should be cached somehow
  
  if current_obj.respond_to?(:reactive_method_tag)
    tag = current_obj.reactive_method_tag(method_name, tag_name)
    
    unless tag
      # Get the tag from the all methods if its not directly specified
      tag = current_obj.reactive_method_tag(:__all_methods, tag_name)
    end
    
    # Evaluate now if its a proc
    tag = tag.call(method_name) if tag.class == ::Proc
    
    return tag
  end
  
  return nil
end

#coerce(other) ⇒ Object



190
191
192
193
194
195
196
197
# File 'lib/volt/reactive/reactive_value.rb', line 190

def coerce(other)
  if other.reactive?
    return [other, self]
  else
    wrapped_object = ::ReactiveValue.new(other, [])
    return [wrapped_object, self]
  end
end

#inspectObject



167
168
169
# File 'lib/volt/reactive/reactive_value.rb', line 167

def inspect
  "@#{cur.inspect}"
end

#pretty_inspectObject



171
172
173
# File 'lib/volt/reactive/reactive_value.rb', line 171

def pretty_inspect
  inspect
end

#puts(*args) ⇒ Object



75
76
77
# File 'lib/volt/reactive/reactive_value.rb', line 75

def puts(*args)
  ::Object.send(:puts, *args)
end

#reactive?Boolean

Returns:



37
38
39
# File 'lib/volt/reactive/reactive_value.rb', line 37

def reactive?
  true
end

#reactive_managerObject Also known as: rm



50
51
52
# File 'lib/volt/reactive/reactive_value.rb', line 50

def reactive_manager
  @reactive_manager
end

#respond_to_missing?(name, include_private = false) ⇒ Boolean

def respond_to?(name, include_private=false)

[:event_added, :event_removed].include?(name) || super

end

Returns:



159
160
161
# File 'lib/volt/reactive/reactive_value.rb', line 159

def respond_to_missing?(name, include_private=false)
  cur.respond_to?(name)
end

#setup_setter(manager, method_name, args) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/volt/reactive/reactive_value.rb', line 137

def setup_setter(manager, method_name, args)
  # See if we can automatically create a setter.  If we are fetching a
  # value via a read, we can probably reassign it with .name=
  if args.size == 0
    # TODO: At the moment we are defining a setter on all "reads", this
    # probably has some performance implications
    manager.setter! do |val|
      # Call setter
      self.cur.send(:"#{method_name}=", val)
    end
  elsif args.size == 1 && method_name == :[]
    manager.setter! do |val|
      # Call an array setter
      self.cur.send(:"#{method_name}=", args[0], val)
    end      
  end
end

#to_sObject



186
187
188
# File 'lib/volt/reactive/reactive_value.rb', line 186

def to_s
  cur.to_s
end

#with(*args, &block) ⇒ Object



163
164
165
# File 'lib/volt/reactive/reactive_value.rb', line 163

def with(*args, &block)
  return @reactive_manager.with(*args, &block)
end