Class: Pause::Action

Inherits:
Object
  • Object
show all
Defined in:
lib/pause/action.rb

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(identifier, &block) ⇒ Action

Returns a new instance of Action.



7
8
9
10
11
# File 'lib/pause/action.rb', line 7

def initialize(identifier, &block)
  @identifier = identifier
  self.class.checks ||= []
  instance_exec(&block) if block
end

Class Attribute Details

.checksObject

Returns the value of attribute checks.



18
19
20
# File 'lib/pause/action.rb', line 18

def checks
  @checks
end

Instance Attribute Details

#identifierObject

Returns the value of attribute identifier.



5
6
7
# File 'lib/pause/action.rb', line 5

def identifier
  @identifier
end

Class Method Details

.adapterObject



105
106
107
# File 'lib/pause/action.rb', line 105

def adapter
  Pause.adapter
end

.check(*args, **opts) ⇒ Object

Action subclasses should define their checks as follows

period_seconds - compare all activity by an identifier within the time period
max_allowed    - if the number of actions by an identifier exceeds max_allowed for the time period marked
                 by period_seconds, it is no longer ok.
block_ttl      - time to mark identifier as not ok

   class MyAction < Pause::Action
     check period_seconds: 60,   max_allowed: 100,  block_ttl: 3600
     check period_seconds: 1800, max_allowed: 2000, block_ttl: 3600
   end


78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/pause/action.rb', line 78

def check(*args, **opts)
  self.checks ||= []

  params =
    if args.empty?
      # if block_ttl is not provided, just default to the period
      opts[:block_ttl] ||= opts[:period_seconds]
      [opts[:period_seconds], opts[:max_allowed], opts[:block_ttl]]
    else
      args
    end

  self.checks << Pause::PeriodCheck.new(*params)
end

.disableObject



54
55
56
# File 'lib/pause/action.rb', line 54

def disable
  adapter.disable(scope)
end

.disabled?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'lib/pause/action.rb', line 62

def disabled?
  !enabled?
end

.enableObject

Actions can be globally disabled or re-enabled in a persistent way.

MyAction.disable
MyAction.enabled? => false
MyAction.disabled? => true

MyAction.enable
MyAction.enabled? => true
MyAction.disabled? => false


50
51
52
# File 'lib/pause/action.rb', line 50

def enable
  adapter.enable(scope)
end

.enabled?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'lib/pause/action.rb', line 58

def enabled?
  adapter.enabled?(scope)
end

.inherited(klass) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/pause/action.rb', line 20

def inherited(klass)
  klass.instance_eval do
    # Action subclasses should define their scope as follows
    #
    #     class MyAction < Pause::Action
    #       scope "my:scope"
    #     end
    #
    @scope = klass.name.downcase.gsub('::', '.')
    class << self
      # @param [String] args
      def scope(*args)
        @scope = args.first if args && args.size == 1
        @scope
      end
    end
  end
end

.rate_limited_identifiersObject



97
98
99
# File 'lib/pause/action.rb', line 97

def rate_limited_identifiers
  adapter.rate_limited_keys(scope)
end

.tracked_identifiersObject



93
94
95
# File 'lib/pause/action.rb', line 93

def tracked_identifiers
  adapter.all_keys(scope)
end

.unblock_allObject



101
102
103
# File 'lib/pause/action.rb', line 101

def unblock_all
  adapter.delete_rate_limited_keys(scope)
end

Instance Method Details

#analyze(recalculate: false) ⇒ Object



148
149
150
# File 'lib/pause/action.rb', line 148

def analyze(recalculate: false)
  Pause.analyzer.check(self, recalculate: recalculate)
end

#block_for(ttl) ⇒ Object



129
130
131
# File 'lib/pause/action.rb', line 129

def block_for(ttl)
  adapter.rate_limit!(scope, identifier, ttl)
end

#checksObject



125
126
127
# File 'lib/pause/action.rb', line 125

def checks
  self.class.checks
end

#if_rate_limited {|check_result| ... } ⇒ Object

Yields:

  • (check_result)


120
121
122
123
# File 'lib/pause/action.rb', line 120

def if_rate_limited(&)
  check_result = analyze(recalculate: true)
  yield(check_result) unless check_result.nil?
end

#increment!(count = 1, timestamp = Time.now.to_i) ⇒ Object



133
134
135
# File 'lib/pause/action.rb', line 133

def increment!(count = 1, timestamp = Time.now.to_i)
  adapter.increment(scope, identifier, timestamp, count)
end

#ok?Boolean

Returns:

  • (Boolean)


141
142
143
144
145
146
# File 'lib/pause/action.rb', line 141

def ok?
  Pause.analyzer.check(self).nil?
rescue ::Redis::CannotConnectError => e
  Pause::Logger.fatal "Error connecting to redis: #{e.inspect} #{e.message} #{e.backtrace.join("\n")}"
  false
end

#rate_limited?Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/pause/action.rb', line 137

def rate_limited?
  !ok?
end

#scopeObject



13
14
15
# File 'lib/pause/action.rb', line 13

def scope
  self.class.scope
end

#unblockObject



152
153
154
# File 'lib/pause/action.rb', line 152

def unblock
  adapter.delete_rate_limited_key(scope, identifier)
end

#unless_rate_limited(count: 1, timestamp: Time.now.to_i, &_block) ⇒ Object



110
111
112
113
114
115
116
117
118
# File 'lib/pause/action.rb', line 110

def unless_rate_limited(count: 1, timestamp: Time.now.to_i, &_block)
  check_result = analyze
  if check_result.nil?
    yield
    increment!(count, timestamp)
  else
    check_result
  end
end