require 'rgb' class Numeric #:nodoc: def diff(n) return (self > n ? self - n : n - self) end end module Enumerable # Sorts objects in the enumeration and applies a colour scale to them. # # Colour ranges must be in the form [x, y], where x and y are either fixnums # (e.g. 255, 0xFF) or hexadecimal strings (e.g. 'FF'). # # Ranges can be provided for each RGB colour e.g. # # gradiate(:red => red_range) # # ...and a default range (for all colours) can be set using :all e.g. # # gradiate(:all => default_range, :green => green_range) # # If no colour ranges are supplied then the _sorted_ enumeration will be returned. # # Objects contained in the enumeration are expected to have a colour (or color) # attribute/method that returns a Colour::RGB object (or similar). # # By default, objects are sorted using :to_i. This can be overidden # by setting options[:compare_using] to a different method symbol. # # By default, objects are ordered "smallest" first. To reverse this set # options[:order] to either :desc or :reverse. # def gradiate(options={}) ranges = [:red, :green, :blue].map do |col| if range = (options[col] || options[:all]) a, b = range.map { |x| x.respond_to?(:hex) ? x.hex : x.to_i } a, b = b, a if a > b # smallest first c = b.diff(a) / (self.size - 1) next (a..b).every(c) else [] end end objects = sort_by { |object| object.send(options[:compare_using] || :to_i) } objects = objects.reverse if [:desc, :reverse].include?(options[:order]) objects.zip(*ranges).collect do |object, red, green, blue| colour = object.respond_to?(:colour) ? object.colour : object.color colour.red = red if red colour.green = green if green colour.blue = blue if blue next object end end # Yields every nth object (if invoked with a block), # or returns an array of every nth object e.g. # # [1, 2, 3, 4, 5, 6].every(2) -> [1, 3, 5] # [1, 2, 3, 4, 5, 6].every(2) { |i| ... } -> nil # def every(n) result = [] unless block_given? each_with_index do |object, i| if i % n == 0 block_given?? yield(object) : result << object end end return block_given?? nil : result end end