Modified: branches/invitation_throttling/app/controllers/application.rb (2011 => 2012)
--- branches/invitation_throttling/app/controllers/application.rb 2008-11-27 19:03:12 UTC (rev 2011)
+++ branches/invitation_throttling/app/controllers/application.rb 2008-11-28 14:19:13 UTC (rev 2012)
@@ -39,49 +39,91 @@
end
- #
+ # the single point in the application governing validation of all features that have
+ # limited allowance of usage (e.g. 5 messages a day, etc)
def check_activity_limit(contributor, limit_feature, update_counter=true)
+ time_now = Time.now
+
if (limit = ActivityLimit.find(:first, :conditions => ["contributor_type = ? AND contributor_id = ? AND limit_feature = ?", contributor.class.name, contributor.id, limit_feature]))
# limit exists - check its validity
- if (limit.limit_frequency && limit.reset_after && Time.now > limit.reset_after)
- # "reset_after" / "limit_frequency" are not NULL - so the limit is periodic; now it's the time to
- # reset the counter to zero - no matter what its value of was before
- #
- # before this check if the contributor also needs to be "promoted" to the next level -
+ if (limit.limit_frequency && limit.reset_after && time_now > limit.reset_after)
+ # "reset_after" / "limit_frequency" are not NULL - so the limit is periodic;
+ # now it's the time to reset the counter to zero - no matter what its value was before
+ # (this will never be executed for non-periodic counters)
+ limit.current_count = 0
+ limit.reset_after = time_now + limit.limit_frequency.hours
+
+ # also check if the contributor needs to be "promoted" to the next level --
# e.g. in the first month of membership on myExperiment one can send 10 messages daily,
# but after that can send 15 (because the user becomes more "trusted")
- if (limit.promote_after && Time.now > limit.promote_after)
- # TODO: do the user promotion to the next level
- end
- limit.current_count = 0
- limit.reset_after = Time.now + limit.limit_frequency.hours
- end
+ if (limit.promote_after && time_now > limit.promote_after)
+ absolute_max_limit_value = eval("#{limit_feature.upcase}_LIMIT_MAX_VALUE")
+ limit_increment_value = eval("#{limit_feature.upcase}_LIMIT_PROMOTE_INCREMENT")
+ promote_every = eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY")
+
+ if limit_increment_value
+ if absolute_max_limit_value
+ # absolute max value set -->
+ # increase the limit only if not exceeded absolute maximum just yet
+ if (limit.limit_max < absolute_max_limit_value)
+ limit.limit_max += limit_increment_value
+ limit.promote_after = (promote_every ? (time_now + promote_every.days) : nil)
+ else
+ # absolute maximum already reached / exceeded, disable further promotions
+ limit.promote_after = nil
+ end
+
+ # make sure that it's not set to exceed the absolute maximum
+ # (which can happen if increment is not factor of the absolute maximum value)
+ if (limit.limit_max > absolute_max_limit_value)
+ limit.limit_max = absolute_max_limit_value
+ end
+ else
+ # absolute value not set --> simply increment
+ limit.limit_max += limit_increment_value
+ limit.promote_after = (promote_every ? (time_now + promote_every.days) : nil)
+ end
+ else
+ # increment not set - this will be a one-time promotion
+ # (if the absolute max value is set - set limit to it; if not - the feature becomes unlimited)
+ limit.limit_max = absolute_max_limit_value
+ limit.promote_after = nil
+ end
+ end # END of PROMOTION code
+
+ end # END of COUNTER RESET code
+
else
# limit doesn't exist yet - create it, then proceed to validation and saving
- time_now = Time.now
+ limit_frequency = eval("#{limit_feature.upcase}_LIMIT_FREQUENCY")
+ promote_every = eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY")
+
limit = ActivityLimit.new(:contributor_type => contributor.class.name, :contributor_id => contributor.id,
:limit_feature => limit_feature,
- :limit_max => eval("#{limit_feature.upcase}_LIMIT"),
- :limit_frequency => eval("#{limit_feature.upcase}_LIMIT_FREQUENCY"),
- :reset_after => time_now + eval("#{limit_feature.upcase}_LIMIT_FREQUENCY").hours,
- :promote_after => time_now + eval("#{limit_feature.upcase}_LIMIT_PROMOTE_EVERY").days,
+ :limit_max => eval("#{limit_feature.upcase}_LIMIT_START_VALUE"),
+ :limit_frequency => limit_frequency,
+ :reset_after => (limit_frequency ? (time_now + limit_frequency.hours) : nil),
+ :promote_after => (promote_every ? (time_now + promote_every.days) : nil),
:current_count => 0)
end
- # the action takes place - increment the counter and carry out
- # the actual check on the current counter value
- current_count_before_update = limit.current_count
- limit.current_count += 1 if update_counter
- limit.save
-
+ # decide if the requested action is allowed - check on the current counter value
+ action_allowed = true
if limit.limit_max
- # allow the action if the "current_count" has not exceeded the "limit_max" value
- return [(current_count_before_update < limit.limit_max), (limit.reset_after - Time.now)]
- else
- # NULL in "limit_max" means unlimited allowance
- return [true, (limit.reset_after - Time.now)]
+ # (NULL in "limit_max" would mean unlimited allowance - not this case)
+ # deny the action if the "current_count" is equal / exceeded the "limit_max" value
+ action_allowed = false if (limit.current_count >= limit.limit_max)
end
+
+ # update counted for the "current" action
+ if action_allowed && update_counter
+ limit.current_count += 1
+ end
+ limit.save # saves all changes (including counter resets, etc)
+
+
+ return [action_allowed, (limit.reset_after - time_now)]
end
Modified: branches/invitation_throttling/app/controllers/messages_controller.rb (2011 => 2012)
--- branches/invitation_throttling/app/controllers/messages_controller.rb 2008-11-27 19:03:12 UTC (rev 2011)
+++ branches/invitation_throttling/app/controllers/messages_controller.rb 2008-11-28 14:19:13 UTC (rev 2012)
@@ -161,6 +161,9 @@
format.html { redirect_to messages_url }
elsif !sending_allowed
# when redirecting, the check will be carried out again, and full error message displayed to the user
+ # (this is an unlikely event - can only happen when the user opens several "new message" pages one
+ # after another and then posts messages from each of them, rather than opening a new one for each
+ # message - therefore, it will not have significant performance effect on running the allowance check again)
format.html { redirect_to new_message_url }
else
format.html { render :action ="" "new" }