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.



153
154
155
# File 'lib/motion/util.rb', line 153

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



178
179
180
181
182
183
184
185
# File 'lib/motion/util.rb', line 178

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



201
202
203
204
205
206
207
# File 'lib/motion/util.rb', line 201

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



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/motion/util.rb', line 235

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



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/motion/util.rb', line 260

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



287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/motion/util.rb', line 287

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`



301
302
303
# File 'lib/motion/util.rb', line 301

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



189
190
191
192
193
194
195
# File 'lib/motion/util.rb', line 189

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



160
161
162
163
164
165
166
167
168
# File 'lib/motion/util.rb', line 160

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



170
171
172
173
174
175
176
# File 'lib/motion/util.rb', line 170

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

#rmext_object_descObject



147
148
149
# File 'lib/motion/util.rb', line 147

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