lib/motion_bindable/strategy.rb in motion_bindable-0.1.1 vs lib/motion_bindable/strategy.rb in motion_bindable-0.2.0

- old
+ new

@@ -1,12 +1,11 @@ module MotionBindable - # - # Represents a binding strategy. Designed to be as flexible as possible. - # class Strategy + WATCH_TICK = 0.2 + @strategies_map = [{ class: Strategy, candidates: [Object] }] def self.register_strategy(strategy, *objects) @strategies_map << { class: strategy, candidates: objects } end @@ -23,33 +22,86 @@ def initialize(object, attr_name) @attr_name = attr_name.to_sym self.object = object end + public # Methods to override + + # def start_observing_bound; end + # def start_observing_object; end + # def refresh_bound; end + # def on_object_change; end + # def on_bound_change; end + def bind(bound) self.bound = bound - on_bind - + initial_state + start_listen self end - def unbind + def refresh_object + attribute end - # You can either choose to just override `#refresh` for objects that can't - # be bound with callbacks. Or override `#on_bind` for objects that can be - # bound with a callback. - def refresh; end - def on_bind - refresh + def unbind + @watch_bound, @watch_object = nil end + private # Methods to leave alone + def attribute object.send(@attr_name) end def attribute=(value) object.send(:"#{@attr_name.to_s}=", value) + end + + def initial_state + if attribute.nil? + if respond_to?(:refresh_bound) then on_bound_change(refresh_bound) + else on_bound_change + end if respond_to?(:on_bound_change) + else + if respond_to?(:refresh_object) then on_object_change(refresh_object) + else on_object_change(attribute) + end if respond_to?(:on_object_change) + end + end + + def start_listen + if respond_to?(:start_observing_bound) then start_observing_bound + elsif respond_to?(:refresh_bound) && respond_to?(:on_bound_change) + watch_bound + end + + if respond_to?(:start_observing_object) then start_observing_object + elsif respond_to?(:refresh_object) && respond_to?(:on_object_change) + watch_object + end + end + + def watch_bound + @watch_bound = dispatcher.async do + result = refresh_bound + on_bound_change(result) if result + dispatcher.after(WATCH_TICK) { watch_bound } unless @watch_bound + end + end + + def watch_object + @watch_object = dispatcher.async do + result = refresh_object + on_object_change(result) if result + dispatcher.after(WATCH_TICK) { watch_object } unless @watch_object + end + end + + def dispatcher + @dispatcher ||= begin + Dispatch::Queue.concurrent 'motion.bindable' + end end end end