myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [2011] branches/invitation_throttling/app/control


From: noreply
Subject: [myexperiment-hackers] [2011] branches/invitation_throttling/app/controllers: Throttling.
Date: Thu, 27 Nov 2008 14:03:15 -0500 (EST)

Revision
2011
Author
alekses6
Date
2008-11-27 14:03:12 -0500 (Thu, 27 Nov 2008)

Log Message

Throttling.

First working prototype of throttling. Implemented on internal messages (yet to be tested).

"Promoting" users to further levels (based on membership duration) is not yet implemented.

Modified Paths

Diff

Modified: branches/invitation_throttling/app/controllers/application.rb (2010 => 2011)


--- branches/invitation_throttling/app/controllers/application.rb	2008-11-27 16:39:43 UTC (rev 2010)
+++ branches/invitation_throttling/app/controllers/application.rb	2008-11-27 19:03:12 UTC (rev 2011)
@@ -39,6 +39,73 @@
   end
   
   
+  # 
+  def check_activity_limit(contributor, limit_feature, update_counter=true)
+    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 -
+        # 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
+    else
+      # limit doesn't exist yet - create it, then proceed to validation and saving
+      time_now = Time.now
+      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,
+                                :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
+    
+    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)]
+    end
+  end
+  
+  
+  def formatted_timespan(time_period)
+    # Takes a period of time in seconds and returns it in human-readable form (down to minutes)
+    # from (http://www.postal-code.com/binarycode/category/devruby/)
+    out_str = ""
+        
+    interval_array = [ [:weeks, 604800], [:days, 86400], [:hours, 3600], [:minutes, 60] ]
+    interval_array.each do |sub|
+      if time_period >= sub[1]
+        time_val, time_period = time_period.divmod(sub[1])
+            
+        time_val == 1 ? name = sub[0].to_s.singularize : name = sub[0].to_s
+              
+        ( sub[0] != :minutes ? out_str += ", " : out_str += " and " ) if out_str != ''
+        out_str += time_val.to_s + " #{name}"
+      end
+    end
+   
+    return out_str  
+  end
+  
+  
   # this method is only intended to check if entry
   # in "viewings" or "downloads" table needs to be
   # created for current access - and this is *only*

Modified: branches/invitation_throttling/app/controllers/messages_controller.rb (2010 => 2011)


--- branches/invitation_throttling/app/controllers/messages_controller.rb	2008-11-27 16:39:43 UTC (rev 2010)
+++ branches/invitation_throttling/app/controllers/messages_controller.rb	2008-11-27 19:03:12 UTC (rev 2011)
@@ -81,7 +81,11 @@
         flash[:error] = "You cannot send a message to yourself"
         format.html { redirect_to new_message_url }
       end
-    else
+    elsif (allowed_plus_timespan = check_activity_limit(current_user, "internal_message", false))[0]
+      # the user is allowed to send messages - limit not yet reached; show the new message screen 
+      # (but the counter is not updated just yet - the user might not send the message after all,
+      #  so this is a mere validation - which saves user from typing the message in and learning that
+      #  it can't be set because of the limit, which is expired)
       if params[:reply_id]
         @message = Message.new(:to => @reply.from,
                                :reply_id => @reply.id,
@@ -90,41 +94,59 @@
       else
         @message = Message.new
       end
+    else
+      # no more messages can be sent because of the activity limit
+      respond_to do |format|
+        error_msg = "You have reached the message limit, "
+        if allowed_plus_timespan[1] <= 60
+          error_msg += "please try again within a couple of minutes"
+        else
+          error_msg += "it will be reset in " + formatted_timespan(allowed_plus_timespan[1])
+        end
+        
+        flash[:error] = error_msg 
+        format.html { redirect_to messages_path }
+      end
     end
   end
 
   # POST /messages
   def create
-    @message = Message.new(params[:message])
-    @message.from ||= current_user.id
+    # check if sending is allowed and increment the message counter
+    sending_allowed = check_activity_limit(current_user, "internal_message")[0]
     
-    # set initial datetimes
-    @message.read_at = nil
-    
-    # test for spoofing of "from" field
-    unless @message.from.to_i == current_user.id.to_i
-      errors = true
-      @message.errors.add :from, "must be logged on"
-    end
-    
-    # test for existance of reply_id
-    if @message.reply_id
-      begin
-        reply = Message.find(@message.reply_id) 
+    if sending_allowed
+      @message = Message.new(params[:message])
+      @message.from ||= current_user.id
       
-        # test that user is replying to a message that was actually received by them
-        unless reply.to.to_i == current_user.id.to_i
+      # set initial datetimes
+      @message.read_at = nil
+      
+      # test for spoofing of "from" field
+      unless @message.from.to_i == current_user.id.to_i
+        errors = true
+        @message.errors.add :from, "must be logged on"
+      end
+      
+      # test for existance of reply_id
+      if @message.reply_id
+        begin
+          reply = Message.find(@message.reply_id) 
+        
+          # test that user is replying to a message that was actually received by them
+          unless reply.to.to_i == current_user.id.to_i
+            errors = true
+            @message.errors.add :reply_id, "not addressed to sender"
+          end
+        rescue ActiveRecord::RecordNotFound
           errors = true
-          @message.errors.add :reply_id, "not addressed to sender"
+          @message.errors.add :reply_id, "not found"
         end
-      rescue ActiveRecord::RecordNotFound
-        errors = true
-        @message.errors.add :reply_id, "not found"
       end
     end
     
     respond_to do |format|
-      if !errors and @message.save
+      if sending_allowed && !errors && @message.save
         
         begin
           Notifier.deliver_new_message(@message, base_host) if @message.u_to.send_notifications?
@@ -137,6 +159,9 @@
         
         flash[:notice] = 'Message was successfully sent.'
         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
+        format.html { redirect_to new_message_url }
       else
         format.html { render :action ="" "new" }
       end

reply via email to

[Prev in Thread] Current Thread [Next in Thread]