module VelocityLimiter

Public Instance Methods

_velocity_limiter_params_validator!() click to toggle source
# File lib/velocity_limiter.rb, line 21
def _velocity_limiter_params_validator!
        raise "Parent overloaded velocity_max_in_frame. Expected to be a ActiveSupport::Duration" unless velocity_max_in_frame.is_a? ActiveSupport::Duration
        raise "Parent overloaded velocity_frame. Expected to be a ActiveSupport::Duration" unless velocity_frame.is_a? ActiveSupport::Duration
        raise "Parent overloaded velocity_max. Expected to be a Integer" unless velocity_max.is_a? Integer
        raise "Parent overloaded velocity_max. Expected to be a Integer greater than 1" if velocity_max < 1
        raise "Parent overloaded cache_key. Expected to be a String" unless cache_key.is_a? String
end
cache_delineator() click to toggle source
# File lib/velocity_limiter.rb, line 42
def cache_delineator
        ','
end
cache_key() click to toggle source
# File lib/velocity_limiter.rb, line 93
def cache_key
        raise "cache_key must be defined in the parent class"
end
velocity_frame() click to toggle source
# File lib/velocity_limiter.rb, line 35
def velocity_frame
end
velocity_limit_message(metadata:) click to toggle source
# File lib/velocity_limiter.rb, line 38
def velocity_limit_message(metadata:)
        "Velocity limit reached for SMS verification. You may try again in #{metadata[:to_words]}"
end
velocity_limit_reached?() click to toggle source
# File lib/velocity_limiter.rb, line 2
def velocity_limit_reached?
        _velocity_limiter_params_validator!

        metadata = vl_metadata

        log(level: :info, msg: "#{cache_key} has attempted #{self.class.name} #{metadata[:within_attempts_count]} times since #{metadata[:threshold]}")

        if metadata[:velocity_reached]
                log(level: :warn, msg: "#{cache_key} has been velocity limited. #{metadata[:within_attempts_count]} attempts since #{metadata[:threshold]}. MAX allowed is #{velocity_max}")
                log(level: :warn, msg: "#{cache_key} may try again in #{metadata[:to_words]} :: #{metadata[:attempt_again_at]}. Will fully reset at #{metadata[:fully_reset_time]}")
                msg = velocity_limit_message(metadata: metadata)
                return {reached: true, msg: msg}
        end

        vl_write!(metadata[:vl_write])

        { reached: false }
end
velocity_max() click to toggle source
# File lib/velocity_limiter.rb, line 32
def velocity_max
end
velocity_max_in_frame() click to toggle source
# File lib/velocity_limiter.rb, line 29
def velocity_max_in_frame
end
vl_metadata(vl_arr: vl_read) click to toggle source
# File lib/velocity_limiter.rb, line 50
def vl_metadata(vl_arr: vl_read)
        threshold = vl_time - velocity_max_in_frame
        within_attempts = vl_arr.select do |time|
                time >= threshold
        end
        attempt_again_at = within_attempts.first ? (within_attempts.first + velocity_max_in_frame) : Time.zone.now

        obj = {}
        obj[:vl_write] = [within_attempts, vl_time].flatten
        obj[:fully_reset_time] = (within_attempts.last || Time.zone.now) + velocity_max_in_frame
        obj[:attempt_again_at] = attempt_again_at
        obj[:velocity_reached] = within_attempts.count >= velocity_max
        obj[:within_attempts_arr] = within_attempts
        obj[:within_attempts_count] = within_attempts.count
        obj[:attempts_remaining] = velocity_max - obj[:vl_write].count
        obj[:threshold] = threshold
        obj[:velocity_max] = velocity_max
        obj[:velocity_frame] = velocity_frame
        obj[:velocity_max_in_frame] = velocity_max_in_frame
        obj[:to_words] = distance_of_time_in_words(Time.zone.now, attempt_again_at, include_seconds: true)

        obj
end
vl_read() click to toggle source
# File lib/velocity_limiter.rb, line 74
def vl_read
        json = Rails.cache.fetch(cache_key) || ''
        begin
                array = json.split(cache_delineator).map { |time| Time.zone.parse time }
        rescue StandardError => e
                log(level: :error, msg: "Failed to parse json strings into time. #{json}")
                array = []
        end
        log(level: :info, msg: "Read from #{cache_key} :: #{array}")

        array
end
vl_time() click to toggle source
# File lib/velocity_limiter.rb, line 46
def vl_time
        @vl_time ||= Time.zone.now
end
vl_write!(write) click to toggle source
# File lib/velocity_limiter.rb, line 87
def vl_write!(write)
        cache_write = write.map(&:to_s).join(cache_delineator)
        log(level: :info, msg: "Writing [#{cache_write}] to #{cache_key}")
        Rails.cache.write(cache_key, cache_write, expires_in: velocity_frame)
end