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.



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

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



77
78
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
# File 'lib/volt/reactive/reactive_value.rb', line 77

def method_missing(method_name, *args, &block)
  # Unroll send into a direct call
  if method_name == :send
    method_name, *args = args
  end

  # 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) || __is_destructive?(method_name)
    current_obj = self.cur
    
    # Unwrap arguments if the method doesn't want reactive values
    pass_args = reactive_manager.unwrap_if_pass_reactive(args, method_name, current_obj)
    
    return current_obj.__send__(method_name, *pass_args, &block)
  end
  
  @block_reactives = []
  result = @reactive_manager.with_and_options(args) do |val, in_args|
    # Unwrap arguments if the method doesn't want reactive values
    # TODO: Should cache the lookup on pass_reactive
    pass_args = reactive_manager.unwrap_if_pass_reactive(in_args, method_name, val)

    # puts "GET #{method_name.inspect}"
    val.__send__(method_name, *pass_args, &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



161
162
163
# File 'lib/volt/reactive/reactive_value.rb', line 161

def !
  method_missing(:!)
end

#==(val) ⇒ Object

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



156
157
158
# File 'lib/volt/reactive/reactive_value.rb', line 156

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

#__is_destructive?(method_name) ⇒ Boolean

Returns:



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/volt/reactive/reactive_value.rb', line 60

def __is_destructive?(method_name)
  last_char = method_name[-1]
  if last_char == '=' && method_name[-2] != '='
    # Method is an assignment (and not a comparator ==)
    return true
  elsif last_char == '!' || last_char == '<'
    # Method is tagged as destructive, or is a push ( << )
    return true
  elsif ::DestructiveMethods.might_be_destructive?(method_name)
    # Method may be destructive, check if it actually is on the current value
    # TODO: involves a call to cur
    return reactive_manager.check_tag(method_name, :destructive, self.cur)
  else
    return false
  end
end

#coerce(other) ⇒ Object



169
170
171
172
173
174
175
176
# File 'lib/volt/reactive/reactive_value.rb', line 169

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

#inspectObject



146
147
148
# File 'lib/volt/reactive/reactive_value.rb', line 146

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

#pretty_inspectObject



150
151
152
# File 'lib/volt/reactive/reactive_value.rb', line 150

def pretty_inspect
  inspect
end

#puts(*args) ⇒ Object



56
57
58
# File 'lib/volt/reactive/reactive_value.rb', line 56

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

#reactive?Boolean

Returns:



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

def reactive?
  true
end

#reactive_managerObject Also known as: rm



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

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:



138
139
140
# File 'lib/volt/reactive/reactive_value.rb', line 138

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

#setup_setter(manager, method_name, args) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/volt/reactive/reactive_value.rb', line 116

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



165
166
167
# File 'lib/volt/reactive/reactive_value.rb', line 165

def to_s
  cur.to_s
end

#with(*args, &block) ⇒ Object



142
143
144
# File 'lib/volt/reactive/reactive_value.rb', line 142

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