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]

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
# 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
# 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

  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

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.


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

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



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

def !
  method_missing(:!)
end

#==(val) ⇒ Object

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



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

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 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



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

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

#inspectObject



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

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

#pretty_inspectObject



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

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:



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

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

#setup_setter(manager, method_name, args) ⇒ Object



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

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



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

def to_s
  cur.to_s
end

#with(*args, &block) ⇒ Object



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

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