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.


184
185
186
# File 'lib/volt/reactive/reactive_value.rb', line 184

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



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

def !
  method_missing(:!)
end

#==(val) ⇒ Object

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



154
155
156
# File 'lib/volt/reactive/reactive_value.rb', line 154

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



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

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

#inspectObject



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

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

#pretty_inspectObject



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

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

Returns:



140
141
142
# File 'lib/volt/reactive/reactive_value.rb', line 140

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



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

def to_s
  cur.to_s
end