Class: Mongoid::Matcher::RegexpBudget::Budget Private
| Relationships & Source Files | |
| Inherits: | Object |
| Defined in: | lib/mongoid/matcher/regexp_budget.rb |
Overview
The state of one open budget: what is left of the limit, and the patterns compiled under it.
Class Method Summary
- .new(limit) ⇒ Budget constructor Internal use only
Instance Attribute Summary
- #exhausted? ⇒ true | false readonly Internal use only
- #limit ⇒ Float readonly Internal use only
- #remaining ⇒ Float readonly Internal use only
Instance Method Summary
-
#charge(elapsed)
Internal use only
Draws the elapsed time down from the budget.
-
#compile(condition) ⇒ Regexp
Internal use only
Returns the condition as a
::Regexpwhich, where the Ruby in use supports it, gives up once its timeout is spent. -
#bake(regexp)
private
Internal use only
Rebuilds the pattern with the budget's timeout, where the Ruby in use has them.
-
#cache_key(condition)
private
Internal use only
BSON::Regexp::Raw aliases eql? to == but leaves hash alone, so two equal instances hash differently and cannot key the cache.
Constructor Details
.new(limit) ⇒ Budget
# File 'lib/mongoid/matcher/regexp_budget.rb', line 62
def initialize(limit) @limit = limit @remaining = limit @cache = {} # A per-Regexp timeout bounds one match; the cumulative budget bounds # the operation. So the timeout is fixed for the life of the scope # rather than following the drawdown, which is what lets a pattern be # compiled once instead of once per match. It means a match that # starts with almost nothing left can still run for a whole limit, so # an operation can overshoot by at most one limit -- bounded, which is # the point, and far cheaper than recompiling. # # An application that has set a stricter global Regexp.timeout keeps # it: baking in a larger value would leave it less protected than it # asked to be. A timeout set on one individual pattern is a different # matter, and is not preserved -- Budget#compile rebuilds the pattern # from its source and flags, neither of which carries one, so this # value takes its place. Only trusted code can supply such a pattern: # a condition decoded from JSON or BSON arrives as a string or a # BSON::Regexp::Raw, with no timeout of its own. @timeout = [ limit, ::Regexp.timeout ].compact.min if PER_REGEXP_TIMEOUT end
Instance Attribute Details
#exhausted? ⇒ true | false (readonly)
# File 'lib/mongoid/matcher/regexp_budget.rb', line 93
def exhausted? @remaining <= 0 end
#limit ⇒ Float (readonly)
# File 'lib/mongoid/matcher/regexp_budget.rb', line 56
attr_reader :limit
#remaining ⇒ Float (readonly)
# File 'lib/mongoid/matcher/regexp_budget.rb', line 59
attr_reader :remaining
Instance Method Details
#bake(regexp) (private)
Rebuilds the pattern with the budget's timeout, where the Ruby in use has them.
# File 'lib/mongoid/matcher/regexp_budget.rb', line 128
def bake(regexp) return regexp unless PER_REGEXP_TIMEOUT ::Regexp.new(regexp.source, regexp., timeout: @timeout) end
#cache_key(condition) (private)
BSON::Regexp::Raw aliases eql? to == but leaves hash alone, so two equal instances hash differently and cannot key the cache. What they are equal by can. Anything else keys on itself and is left to coerce to reject.
#charge(elapsed)
Draws the elapsed time down from the budget.
# File 'lib/mongoid/matcher/regexp_budget.rb', line 88
def charge(elapsed) @remaining -= elapsed end
#compile(condition) ⇒ Regexp
Returns the condition as a ::Regexp which, where the Ruby in use
supports it, gives up once its timeout is spent.
The condition is taken uncompiled so that the cache can answer before
any compiling happens. A BSON::Regexp::Raw memoizes its own compile,
but ::Mongoid::Matcher::FieldExpression builds a fresh one for every $regex it evaluates,
so that memo is worth nothing across documents and the source would
otherwise be compiled once per document.
# File 'lib/mongoid/matcher/regexp_budget.rb', line 109
def compile(condition) @cache[cache_key(condition)] ||= bake(RegexpBudget.coerce(condition)) end