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

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

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of ReactiveValue.



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

def initialize(getter, setter=nil, scope=nil)
  @reactive_manager = ::ReactiveManager.new(self, getter, setter, scope)
  # @reactive_cache = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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



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
115
116
117
118
119
120
# File 'lib/volt/reactive/reactive_value.rb', line 78

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

  # result = @reactive_cache[[method_name, args.map(&:object_id)]]
  # return result if result

  # 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

  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)

    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

  # if args.size == 0 || method_name == :[]
    # @reactive_cache[[method_name, args.map(&:object_id)]] = result
  # end

  return result
end

Class Method Details

.from_hash(hash, skip_if_no_reactives = false) ⇒ Object

Return a new reactive value that listens for changes on any ReactiveValues inside of its children (hash values, array items, etc..) This is useful if someone is passing in a set of options, but the main hash isn’t a ReactiveValue, but you want to listen for changes inside of the hash.

skip_if_no_reactives lets you get back a non-reactive value in the event

that there are no child reactive values.


188
189
190
# File 'lib/volt/reactive/reactive_value.rb', line 188

def self.from_hash(hash, skip_if_no_reactives=false)
  ::ReactiveGenerator.from_hash(hash)
end

Instance Method Details

#!Object

TODO: this is broke in opal



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

def !
  method_missing(:!)
end

#==(val) ⇒ Object

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



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

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

#__is_destructive?(method_name) ⇒ Boolean

Returns:



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

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 method_name.size > 1 && 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



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

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

#inspectObject



148
149
150
# File 'lib/volt/reactive/reactive_value.rb', line 148

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

#pretty_inspectObject



152
153
154
# File 'lib/volt/reactive/reactive_value.rb', line 152

def pretty_inspect
  inspect
end

#puts(*args) ⇒ Object



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

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

#reactive?Boolean

Returns:



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

def reactive?
  true
end

#reactive_managerObject Also known as: rm



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

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:



144
145
146
# File 'lib/volt/reactive/reactive_value.rb', line 144

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

#setup_setter(manager, method_name, args) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/volt/reactive/reactive_value.rb', line 122

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



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

def to_s
  cur.to_s
end