Module: RMExtensions::ObjectExtensions::Util

Defined in:
lib/motion/util.rb

Instance Method Summary collapse

Instance Method Details

#rmext_assert_main_thread!Object

Raises an exception when called from a thread other than the main thread. Good for development and experimenting.



81
82
83
# File 'lib/motion/util.rb', line 81

def rmext_assert_main_thread!
  raise "This method must be called on the main thread." unless NSThread.currentThread.isMainThread
end

#rmext_assign_debug_labels_to_ivars!Object



106
107
108
109
110
111
112
113
# File 'lib/motion/util.rb', line 106

def rmext_assign_debug_labels_to_ivars!
  ivars = [] + instance_variables
  while ivar = ivars.pop
    val = instance_variable_get(ivar)
    val.rmext_ivar(:debug_label, ivar)
  end
  true
end

#rmext_block_on_main_q(block, *args) ⇒ Object

call the block immediately if called on the main thread with the given args, otherwise call it async on the main queue. silently ignores nil blocks to avoid if !block.nil? checks, useful for async callbacks that optionally take a callback



129
130
131
132
133
134
135
# File 'lib/motion/util.rb', line 129

def rmext_block_on_main_q(block, *args)
  unless block.nil?
    rmext_inline_or_on_main_q do
      block.call(*args)
    end
  end
end

#rmext_debounce_on_next_runloop(unique_id, run_immediately, &block) ⇒ Object

takes a unique_id, run_immediately bool, and block if run_immediately is true, the block is executed immediately and not counted on the next run loop, the block will be called IF it has been counted at least once. examples:

# CALLED will be printed twice. Onces immediately, and once on the next runloop: 10.times do

rmext_debounce_on_next_runloop(:my_unique_id, true) do
  p "CALLED"
end

end

# CALLED will be printed once, on the next runloop: 10.times do

rmext_debounce_on_next_runloop(:my_unique_id, false) do
  p "CALLED"
end

end

useful for queuing up something that should happen on the next runloop, but not every time its called. for example, reloadData. the goal was to get a similar behavior to how setNeedsDisplay/setNeedsLayout scheduled display/layout rendering on the next UI runloop pass



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/motion/util.rb', line 163

def rmext_debounce_on_next_runloop(unique_id, run_immediately, &block)
  Thread.current["rmext_debounce_on_next_runloop"] ||= {}
  lookup = Thread.current["rmext_debounce_on_next_runloop"]
  if lookup.key?(unique_id)
    lookup[unique_id][0] += 1
  else
    lookup[unique_id] = [ 0, lambda do
      if (debounced_times = lookup[unique_id][0]) > 0
        # p "we have", debounced_times, "debounced_times queued for unique_id", unique_id
        block.call
      else
        # p "no debounced_times queued for unique_id", unique_id
      end
      lookup.delete(unique_id)
    end ]
    if run_immediately
      block.call
    else
      lookup[unique_id][0] += 1
    end
    # p NSRunLoop.currentRunLoop, NSRunLoop.currentRunLoop.currentMode, lookup[unique_id][1]
    NSRunLoop.currentRunLoop.performSelector('call', target:lookup[unique_id][1], argument:nil, order:0, modes:[NSRunLoop.currentRunLoop.currentMode])
  end
end

#rmext_debounce_selector_on_next_runloop(selector, run_immediately) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/motion/util.rb', line 188

def rmext_debounce_selector_on_next_runloop(selector, run_immediately)
  Thread.current["rmext_debounce_selector_on_next_runloop"] ||= {}
  lookup = Thread.current["rmext_debounce_selector_on_next_runloop"]
  if lookup.key?(selector)
    lookup[selector] += 1
  else
    lookup[selector] = 0
    if run_immediately
      send(selector)
    else
      lookup[selector] += 1
    end
    # p NSRunLoop.currentRunLoop, NSRunLoop.currentRunLoop.currentMode, self, selector, lookup[selector]
    block = lambda do
      if (debounced_times = lookup[selector]) > 0
        # p "we have", debounced_times, "debounced_times queued for", self, selector
        send(selector)
      else
        # p "no debounced_times queued for", self, selector
      end
      lookup.delete(selector)
    end
    NSRunLoop.currentRunLoop.performSelector('call', target:block, argument:nil, order:0, modes:[NSRunLoop.currentRunLoop.currentMode])
  end
end

#rmext_debounced(method_name, seconds, *args) ⇒ Object

more typical debouncing behavior



215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/motion/util.rb', line 215

def rmext_debounced(method_name, seconds, *args)
  new_method_name = "#{method_name}_#{seconds}"
  unless respond_to?(new_method_name)
    self.class.send(:define_method, new_method_name) do |*xargs|
      xargs.unshift(method_name)
      send(*xargs)
    end
  end
  args.unshift(new_method_name)
  NSObject.cancelPreviousPerformRequestsWithTarget(self, selector:"rmext_dispatch__send__", object:args)
  performSelector("rmext_dispatch__send__", withObject:args, afterDelay:seconds)
end

#rmext_dispatch__send__(*args) ⇒ Object

used internally by ‘rmext_debounced`



229
230
231
# File 'lib/motion/util.rb', line 229

def rmext_dispatch__send__(*args)
  send(*args)
end

#rmext_inline_or_on_main_q(&block) ⇒ Object

call the block immediately if called on the main thread, otherwise call it async on the main queue



117
118
119
120
121
122
123
# File 'lib/motion/util.rb', line 117

def rmext_inline_or_on_main_q(&block)
  if NSThread.currentThread.isMainThread
    block.call
  else
    rmext_on_main_q(&block)
  end
end

#rmext_ivar(*args) ⇒ Object

Shortcut to instance_variable_get and instance_variable_get: 1 arg for instance_variable_get 2 args for instance_variable_set



88
89
90
91
92
93
94
95
96
# File 'lib/motion/util.rb', line 88

def rmext_ivar(*args)
  if args.size == 1
    instance_variable_get("@#{args[0]}")
  elsif args.size == 2
    instance_variable_set("@#{args[0]}", args[1])
  else
    raise "rmext_ivar called with invalid arguments: #{args.inspect}"
  end
end

#rmext_nil_instance_variables!Object



98
99
100
101
102
103
104
# File 'lib/motion/util.rb', line 98

def rmext_nil_instance_variables!
  ivars = [] + instance_variables
  while ivar = ivars.pop
    instance_variable_set(ivar, nil)
  end
  true
end

#rmext_object_descObject



75
76
77
# File 'lib/motion/util.rb', line 75

def rmext_object_desc
  "#<#{self.className}:0x#{'%x' % (self.object_id)}(#{self.object_id})>"
end