Module: Mcfly::Model::ClassMethods
- Defined in:
- lib/marty/mcfly_model.rb
Instance Method Summary collapse
-
#cached_delorean_fn(name, options = {}, &block) ⇒ Object
Implements a VERY HACKY class-based (per process) caching mechanism for database lookup results.
-
#cached_mcfly_lookup(name, options = {}, &block) ⇒ Object
FIXME: duplicate code from Mcfly’s mcfly_lookup.
- #clear_lookup_cache! ⇒ Object
-
#gen_mcfly_lookup(name, attrs, options = {}) ⇒ Object
FIXME: add private mode.
-
#gen_mcfly_lookup_cat(name, catrel, attrs, options = {}) ⇒ Object
rel_attr = :security_instrument cat_assoc_klass = Gemini::SecurityInstrumentCategorization cat_attr = :g_fee_category name = :lookup_q pc_name = :pc_lookup_q pc_attrs = true, security_instrument: true, coupon: true.
Instance Method Details
#cached_delorean_fn(name, options = {}, &block) ⇒ Object
Implements a VERY HACKY class-based (per process) caching mechanism for database lookup results. Issues include: cached values are ActiveRecord objects. Query results can be very large lists which we count as one item in the cache. Caching mechanism will result in large processes.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/marty/mcfly_model.rb', line 23 def cached_delorean_fn(name, = {}, &block) @LOOKUP_CACHE ||= {} delorean_fn(name, ) do |ts, *args| cache_key = [name, ts] + args.map{ |a| a.is_a?(ActiveRecord::Base) ? a.id : a } unless Mcfly.is_infinity(ts) next @LOOKUP_CACHE[cache_key] if cache_key && @LOOKUP_CACHE.has_key?(cache_key) res = block.call(ts, *args) if cache_key # Cache has >1000 items, clear out the oldest 200. FIXME: # hard-coded, should be configurable. Cache # size/invalidation should be per lookup and not class. # We're invalidating cache items simply based on age and # not usage. This is faster but not as fair. if @LOOKUP_CACHE.count > 1000 @LOOKUP_CACHE.keys[0..200].each{|k| @LOOKUP_CACHE.delete(k)} end @LOOKUP_CACHE[cache_key] = res # Since we're caching this object and don't want anyone # changing it. FIXME: ideally should freeze this object # recursively. res.freeze unless res.is_a?(ActiveRecord::Relation) end res end end |
#cached_mcfly_lookup(name, options = {}, &block) ⇒ Object
FIXME: duplicate code from Mcfly’s mcfly_lookup.
57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/marty/mcfly_model.rb', line 57 def cached_mcfly_lookup(name, = {}, &block) cached_delorean_fn(name, ) do |ts, *args| raise "nil timestamp" if ts.nil? ts = Mcfly.normalize_infinity(ts) self.mcfly_pt(ts).scoping do block.call(ts, *args) end end end |
#clear_lookup_cache! ⇒ Object
9 10 11 |
# File 'lib/marty/mcfly_model.rb', line 9 def clear_lookup_cache! @LOOKUP_CACHE.clear if @LOOKUP_CACHE end |
#gen_mcfly_lookup(name, attrs, options = {}) ⇒ Object
FIXME: add private mode. This should make the function unavailable to delorean.
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/marty/mcfly_model.rb', line 71 def gen_mcfly_lookup(name, attrs, ={}) raise "bad options #{.keys}" unless (.keys - [:mode, :cache, :private]).empty? mode = .fetch(:mode, :first) # if mode is nil, don't cache -- i.e. don't cache AR queries cache = mode && [:cache] # the older mode=:all is not supported (it's bogus) raise "bad mode #{mode}" unless [nil, :first].member?(mode) assoc = Set.new(self.reflect_on_all_associations.map(&:name)) qstr = attrs.map {|k, v| k = "#{k}_id" if assoc.member?(k) v ? "(#{k} = ? OR #{k} IS NULL)" : "(#{k} = ?)" }.join(" AND ") if Hash === attrs order = attrs.select {|k, v| v}.keys.reverse.map { |k| k = "#{k}_id" if assoc.member?(k) "#{k} NULLS LAST" }.join(", ") attrs = attrs.keys else raise "bad attrs" unless Array === attrs end fn = cache ? :cached_mcfly_lookup : :mcfly_lookup # hacky: if private, set sig to bad value -- i.e. can't be # called from delorean. Ideally, we should have a 'private' # option for delorean_fn. sig = [:private] ? -1 : attrs.length+1 send(fn, name, sig: sig) do |t, *attr_list| attr_list_ids = attr_list.each_with_index.map {|x, i| assoc.member?(attrs[i]) ? (attr_list[i] && attr_list[i].id) : attr_list[i] } q = self.where(qstr, *attr_list_ids) q = q.order(order) if order mode ? q.send(mode) : q end end |
#gen_mcfly_lookup_cat(name, catrel, attrs, options = {}) ⇒ Object
rel_attr = :security_instrument cat_assoc_klass = Gemini::SecurityInstrumentCategorization cat_attr = :g_fee_category name = :lookup_q pc_name = :pc_lookup_q pc_attrs = true, security_instrument: true, coupon: true
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/marty/mcfly_model.rb', line 145 def gen_mcfly_lookup_cat(name, catrel, attrs, ={}) rel_attr, cat_assoc_name, cat_attr = catrel raise "#{rel_attr} should be mapped in attrs" if attrs[rel_attr].nil? cat_assoc_klass = cat_assoc_name.constantize # replace rel_attr with cat_attr in attrs pc_attrs = attrs.each_with_object({}) {|(k, v), h| h[k == rel_attr ? "#{cat_attr}_id" : k] = v } pc_name = "pc_#{name}".to_sym gen_mcfly_lookup(pc_name, pc_attrs, + {private: true}) lpi = attrs.keys.index rel_attr raise "should not include #{cat_attr}" if attrs.member?(cat_attr) raise "need #{rel_attr} argument" unless lpi # cache if mode is not nil fn = .fetch(:mode, :first) ? :cached_delorean_fn : :delorean_fn send(fn, name, sig: attrs.length+1) do |ts, *args| # Example: rel is a Gemini::SecurityInstrument instance. rel = args[lpi] raise "#{rel_attr} can't be nil" unless rel args[lpi] = cat_assoc_klass. mcfly_pt(ts). # FIXME: XXXX why is this join needed??? # joins(cat_attr). where(rel_attr => rel). pluck("#{cat_attr}_id"). first self.send(pc_name, ts, *args) end end |