lib/qfill/manager.rb in qfill-0.0.4 vs lib/qfill/manager.rb in qfill-0.1.0
- old
+ new
@@ -1,166 +1,120 @@
-#Qfill::Manager.new(
+# frozen_string_literal: true
+
+require 'forwardable'
+
+# A Qfill::Manager builds a set of result data (as `result`) from the source data in Qfill::Popper,
+# according to the Qfill::Result definitions in the Qfill::Pusher, and the selected strategy.
+#
+# Qfill::Manager.new(
# :all_list_max => 40,
# :popper => popper,
# :pusher => pusher,
-#)
+# )
module Qfill
class Manager
- attr_accessor :all_list_max, :popper, :pusher, :fill_count, :strategy, :result
+ extend Forwardable
+ def_delegators :@strategy,
+ :popper,
+ :pusher,
+ :result,
+ :remaining_to_fill
+ attr_accessor :all_list_max,
+ :primary_list_total,
+ :popper,
+ :pusher,
+ :fill_count,
+ :result,
+ :strategy_options
- STRATEGY_OPTIONS = [:drain_to_limit, :drain_to_empty, :sample]
+ STRATEGY_OPTIONS = %i[drain_to_limit drain_to_empty sample time_slice].freeze
def initialize(options = {})
- unless options[:popper] && options[:pusher]
- raise ArgumentError, "#{self.class}: popper and pusher are required options for #{self.class}.new(options)"
- end
unless options[:strategy].nil? || STRATEGY_OPTIONS.include?(options[:strategy])
- if options[:strategy] == :drain
- warn "Qfill strategy :drain has been renamed :drain_to_limit, please update your code."
- options[:strategy] = :drain_to_limit
- else
- raise ArgumentError, "#{self.class}: strategy is optional, but must be one of #{STRATEGY_OPTIONS.inspect} if provided"
- end
+ raise ArgumentError,
+ "#{self.class}: strategy is optional, but must be one of #{STRATEGY_OPTIONS.inspect} if provided"
end
+
+ @fill_count = 0
+
@popper = options[:popper]
@pusher = options[:pusher]
- # Provided by user, or defaults to the total number of primary elements in popper list set
- @all_list_max = options[:all_list_max] ? [options[:all_list_max], self.popper.count_primary_elements].min : self.popper.count_primary_elements
- @fill_count = 0
- @strategy = options[:strategy] || :drain_to_limit # or :drain_to_empty or :sample
+ @strategy_name = options[:strategy] || :drain_to_limit # or :drain_to_empty or :sample
+ @strategy_options = options[:strategy_options]
+
+ # Allow the strategy to define the pusher when not defined by user
+ @pusher ||= strategy.default_pusher
+ unless @popper && @pusher
+ raise ArgumentError, "#{self.class}: popper and pusher (except where defined by the strategy) are required options for #{self.class}.new(options)"
+ end
+
+ # Provided by user, or defaults to the total number of elements in popper list set
+ @all_list_max = if options[:all_list_max]
+ [options[:all_list_max],
+ popper.count_all_elements].min
+ else
+ popper.count_all_elements
+ end
+ @primary_list_total = popper.count_primary_elements
end
+ def strategy
+ @strategy ||= case @strategy_name
+ when :drain_to_empty
+ Qfill::Strategy::DrainToEmpty.new(self)
+ when :drain_to_limit
+ Qfill::Strategy::DrainToLimit.new(self)
+ when :sample
+ Qfill::Strategy::Sample.new(self)
+ when :time_slice
+ Qfill::Strategy::TimeSlice.new(self)
+ end
+ end
+
def fill!
- while !is_full? && !self.popper.primary_empty? && (self.result = self.pusher.current_list)
- if self.current_strategy == :drain_to_empty
- preferred_potential_ratio = 0
- preferred_potential = 0
- self.result.list_ratios.each do |list_name, list_ratio|
- poppy = self.result.preferred.select {|x| x == list_name}
- if poppy
- preferred_potential_ratio += list_ratio
- num = self.popper[list_name].elements.length
- preferred_potential += num
- self.result.max_tracker[list_name] = num
- end
- end
- self.result.preferred_potential = preferred_potential
- self.result.preferred_potential_ratio = preferred_potential_ratio
- end
- self.fill_to_ratio!
- self.pusher.set_next_as_current!
- self.result.elements.shuffle! if self.result.shuffle
+ while !is_full? && !popper.primary_empty? && (self.result = pusher.current_list)
+ strategy.on_fill!
+ fill_to_ratio!
+ pusher.set_next_as_current!
+ result.elements.shuffle! if result.shuffle
end
end
def fill_to_ratio!
- case self.current_strategy
- when :drain_to_empty then
- result.max = self.result.preferred_potential_ratio > 0 ? [(self.result.preferred_potential / self.result.preferred_potential_ratio), self.remaining_to_fill].min : self.remaining_to_fill
- when :drain_to_limit, :sample then
- result.max = Qfill::Result.get_limit_from_max_and_ratio(self.remaining_to_fill, result.ratio)
- end
- #result.max = Qfill::Result.get_limit_from_max_and_ratio(self.all_list_max, result.ratio)
- if !result.list_ratios.empty?
- self.fill_according_to_list_ratios!
+ strategy.result_max!
+ if result.list_ratios.empty?
+ fill_up_to_ratio!
else
- self.fill_up_to_ratio!
+ fill_according_to_list_ratios!
end
end
def remaining_to_fill
- self.all_list_max - self.fill_count
+ primary_list_total - fill_count
end
# Go through the queues this result should be filled from and push elements from them onto the current result list.
def fill_according_to_list_ratios!
- added = 0
- tally = 0
- ratio_modifier = 1
- case self.current_strategy
- when :drain_to_empty then
- # Are there any elements in preferred queues that we should add?
- if self.result.preferred_potential > 0
- # Setup a ratio modifier for the non-preferred queues
- result.list_ratios.each do |list_name, list_ratio|
- max_from_list = self.result.max_tracker[list_name] || Qfill::Result.get_limit_from_max_and_ratio(result.max, list_ratio)
- array_to_push = self.popper.next_objects!(list_name, max_from_list)
- self.popper.current_index = self.popper.index_of(list_name)
- added = result.push(array_to_push, list_name)
- puts "[fill_according_to_list_ratios!]#{self}[#{list_name}][added:#{added}]" if Qfill::VERBOSE
- tally += added
- end
- self.fill_count += tally
- end
- when :drain_to_limit
- result.list_ratios.each do |list_name, list_ratio|
- max_from_list = Qfill::Result.get_limit_from_max_and_ratio(result.max, list_ratio)
- array_to_push = self.popper.next_objects!(list_name, max_from_list)
- self.popper.current_index = self.popper.index_of(list_name)
- added = result.push(array_to_push, list_name)
- puts "[fill_according_to_list_ratios!]#{self}[#{list_name}][added:#{added}]" if Qfill::VERBOSE
- tally += added
- end
- self.fill_count += tally
- when :sample then
- #puts "#{!is_full?} && #{result.fill_count} >= #{result.max} && #{!self.popper.totally_empty?} && #{(list_ratio_tuple = result.current_list_ratio)}"
- while !is_full? && !result.is_full? && !self.popper.totally_empty? && (list_ratio_tuple = result.current_list_ratio)
- max_from_list = Qfill::Result.get_limit_from_max_and_ratio(result.max, list_ratio_tuple[1])
- array_to_push = self.popper.next_objects!(list_ratio_tuple[0], max_from_list)
- added = result.push(array_to_push, list_ratio_tuple[0])
- self.fill_count += added
- puts "[fill_according_to_list_ratios!]#{self}[#{list_ratio_tuple[0]}][added:#{added}]" if Qfill::VERBOSE
- result.set_next_as_current!
- end
- end
+ strategy.fill_according_to_list_ratios!
end
# Go through the primary (non backfill) queues in the popper and push elements from them onto the current result list.
def fill_up_to_ratio!
- added = 0
- tally = 0
- if self.current_strategy == :drain_to_empty
- self.popper.primary.each do |queue|
- array_to_push = self.popper.next_objects!(queue.name, result.max)
- added = result.push(array_to_push, queue.name)
- self.popper.current_index = self.popper.index_of(queue.name)
- puts "[fill_up_to_ratio!]#{self}[Q:#{queue.name}][added:#{added}]" if Qfill::VERBOSE
- tally += added
- end
- self.fill_count += added
- else
- ratio = 1.0 / self.popper.primary.length # 1 divided by the number of queues
- max_from_list = Qfill::Result.get_limit_from_max_and_ratio(result.max, ratio)
- if self.current_strategy == :drain_to_limit
- self.popper.primary.each do |queue|
- array_to_push = self.popper.next_objects!(queue.name, max_from_list)
- added = result.push(array_to_push, queue.name)
- self.popper.current_index = self.popper.index_of(queue.name)
- puts "[fill_up_to_ratio!]#{self}[Q:#{queue.name}][added:#{added}]" if Qfill::VERBOSE
- tally += added
- end
- self.fill_count += tally
- elsif self.current_strategy == :sample
- while !is_full? && !result.is_full? && !self.popper.totally_empty? && (origin_list = self.popper.current_list)
- array_to_push = self.popper.next_objects!(origin_list.name, max_from_list)
- added = result.push(array_to_push, origin_list.name)
- self.fill_count += added
- puts "[fill_up_to_ratio!]#{self}[Added:#{added}][Max List:#{max_from_list}][ratio:#{ratio}][added:#{added}]" if Qfill::VERBOSE
- self.popper.set_next_as_current!
- end
- end
- end
+ strategy.fill_up_to_ratio!
end
- def current_strategy
- (result.strategy || self.strategy)
+ def is_full?
+ fill_count >= all_list_max
end
- def is_full?
- self.fill_count >= self.all_list_max
+ def each(&block)
+ # NOTE: on magic: http://blog.arkency.com/2014/01/ruby-to-enum-for-enumerator/
+ return enum_for(:each) unless block # Sparkling magic!
+
+ pusher.each(&block)
end
def to_s
- "[#{self.current_strategy}][Result Max:#{result.max}][All Max:#{self.all_list_max}][Current Max:#{self.result.max}][Filled:#{self.fill_count}][Primary #:#{self.popper.count_primary_elements}]"
+ "[#{strategy_name}][Result Max:#{result.max}][All Max:#{all_list_max}][Current Max:#{result.max}][Filled:#{fill_count}][Primary #:#{popper.count_primary_elements}]"
end
end
end