lib/Limits.rb in taskjuggler-0.0.5 vs lib/Limits.rb in taskjuggler-0.0.6

- old
+ new

@@ -23,18 +23,20 @@ # This class implements a mechanism that can be used to limit certain events # within a certain time period. It supports an upper and a lower limit. class Limit attr_accessor :resource + attr_reader :name, :interval, :upper # To create a new Limit object, the Interval +interval+ and the - # period duration (+period+ in seconds) must be specified. This creates a - # counter for each period within the overall interval. +value+ is the value - # of the limit. +upper+ specifies whether the limit is an upper or lower - # limit. The limit can also be restricted to certain a Resource specified - # by +resource+. - def initialize(interval, period, value, upper, resource) + # period duration (+period+ in seconds) must be specified. This creates + # a counter for each period within the overall interval. +value+ is the + # value of the limit. +upper+ specifies whether the limit is an upper or + # lower limit. The limit can also be restricted to certain a Resource + # specified by +resource+. + def initialize(name, interval, period, value, upper, resource) + @name = name @interval = interval @period = period @value = value @upper = upper @resource = resource @@ -45,75 +47,89 @@ reset end # Returns a deep copy of the class instance. def copy - Limit.new(@interval, @period, @value, @upper, @resource) + Limit.new(@name, @interval, @period, @value, @upper, @resource) end # This function can be used to reset the counter for a specific period # specified by +date+ or to reset all counters. def reset(date = nil) return unless @dirty if date.nil? - @scoreboard = Scoreboard.new(@interval.start, @interval.end, @period, 0) + @scoreboard = Scoreboard.new(@interval.start, @interval.end, + @period, 0) else return unless @interval.contains?(date) @scoreboard.set(date, 0) end @dirty = false end - # Increase the counter for a specific period specified by +date+. If - # +resource+ is not nil, the counter is only increased if +resource+ - # matches resource. + # Increase the counter if the _date_ matches the @interval. The + # relationship between @resource and _resource_ is described below. + # @r \ _r_ nil y + # nil inc inc + # x - if x==y inc else - def inc(date, resource) - return if !@interval.contains?(date) || (!resource.nil? && @resource != resource) - - @dirty = true - @scoreboard.set(date, @scoreboard.get(date) + 1) + if @interval.contains?(date) && + (@resource.nil? || @resource == resource) + # The condition is met, increment the counter for the interval. + @dirty = true + @scoreboard.set(date, @scoreboard.get(date) + 1) + end end - # Returns true if the counter for the time slot specified by +date+ or all - # counters are within the limit. If +upper+ is true, only upper limits are - # checked. If not, only lower limits are checked. If +resource+ is not - # nil, only limits for this resource are checked. + # Returns true if the counter for the time slot specified by +date+ or + # all counters are within the limit. If +upper+ is true, only upper + # limits are checked. If not, only lower limits are checked. The + # dependency between _resource_ and @resource is described in the matrix + # below: + # @r \ _r_ nil y + # nil test true + # x true if x==y test else true def ok?(date, upper, resource) - if date.nil? - # if @upper does not match, we can ignore this limit. - return true if @upper != upper || (!resource.nil? && @resource != resource) + # if @upper does not match or the provided resource does not match, + # we can ignore this limit. + return true if @upper != upper || + (!@resource.nil? ^ !resource.nil?) || + (@resource && resource && @resource != resource) - # Check all counters. + if date.nil? + # No date given. We need to check all counters. @scoreboard.each do |i| return false if @upper ? i >= @value : i < @value end return true else - # If the date is outside the interval or @upper does not match, ignore - # this limit. - return true if !@interval.contains?(date) || @upper != upper - return @upper ? @scoreboard.get(date) < @value : @scoreboard.get(date) >= @value + # If the date is outside the interval we don't have to check + # anything. Everything is ok. + return true if !@interval.contains?(date) + + return @upper ? @scoreboard.get(date) < @value : + @scoreboard.get(date) >= @value end end end - attr_reader :limits, :project + attr_reader :project, :limits # Create a new Limits object. If an argument is passed, it acts as a copy # contructor. def initialize(limits = nil) if limits.nil? # Normal initialization - @limits = {} + @limits = [] @project = nil else # Deep copy content from other instance. - @limits = {} + @limits = [] limits.limits.each do |name, limit| - @limits[name] = limit.copy + @limits << limit.copy end @project = limits.project end end @@ -126,36 +142,44 @@ @project = project end # Reset all counter for all limits. def reset - @limits.each_value { |limit| limit.reset } + @limits.each { |limit| limit.reset } end - # Call this function to create or change a limit. The limit must be uniquely - # identified by +name+. +value+ is the new limit value (in time slots). - # +period+ is the Interval where the limit is active. In case the interval + # Call this function to create or change a limit. The limit is uniquely + # identified by the combination of +name+, +interval+ and +resource+. + # +value+ is the new limit value (in time slots). In case the interval # is nil, the complete project time frame is used. def setLimit(name, value, interval = nil, resource = nil) - @limits.delete(name) if @limits[name] - newLimit(name, value, interval.nil? ? - Interval.new(@project['start'], @project['end']) : interval, resource) + iv = interval || Interval.new(@project['start'], @project['end']) + # If we have already a limit for the name + interval + resource + # combination, we delete it first. + @limits.delete_if do |l| + l.name == name && l.interval.start == iv.start && + l.interval.end == iv.end && l.resource == resource + end + + newLimit(name, value, iv, resource) end # This function increases the counters for all limits for a specific # interval identified by _date_. def inc(date, resource = nil) - @limits.each_value do |limit| + @limits.each do |limit| limit.inc(date, resource) end end - # Check all upper limits and return true if none is exceeded. If a _date_ is - # specified only the counter for that specific period is tested. Otherwise - # all periods are tested. + # Check all upper limits and return true if none is exceeded. If a _date_ + # is specified only the counters for that specific period are tested. + # Otherwise all periods are tested. If _resource_ is nil, only + # non-resource-specific counters are checked, otherwise only the ones that + # match the _resource_. def ok?(date = nil, upper = true, resource = nil) - @limits.each_value do |limit| + @limits.each do |limit| return false unless limit.ok?(date, upper, resource) end true end @@ -174,16 +198,18 @@ upper = true when 'dailymin' period = 60 * 60 * 24 upper = false when 'weeklymax' - interval.start = interval.start.beginOfWeek(@project['weekStartsMonday']) + interval.start = interval.start.beginOfWeek( + @project['weekStartsMonday']) interval.end = interval.end.beginOfWeek(@project['weekStartsMonday']) period = 60 * 60 * 24 * 7 upper = true when 'weeklymin' - interval.start = interval.start.beginOfWeek(@project['weekStartsMonday']) + interval.start = interval.start.beginOfWeek( + @project['weekStartsMonday']) interval.end = interval.end.beginOfWeek(@project['weekStartsMonday']) period = 60 * 60 * 24 * 7 upper = false when 'monthlymax' interval.start = interval.start.beginOfMonth @@ -210,10 +236,10 @@ else raise "Limit period undefined" end endDate = @project['end'] - @limits[name] = Limit.new(interval, period, value, upper, resource) + @limits << Limit.new(name, interval, period, value, upper, resource) end end end