Module: Mcfly::Model::ClassMethods
- Defined in:
- lib/marty/mcfly_model.rb
Instance Method Summary collapse
- #base_mcfly_lookup(meth, name, options = {}, &block) ⇒ Object
-
#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
- #clear_lookup_cache! ⇒ Object
- #gen_mcfly_lookup(name, attrs, options = {}) ⇒ Object
-
#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.
- #mcfly_lookup(name, options = {}, &block) ⇒ Object
Instance Method Details
#base_mcfly_lookup(meth, name, options = {}, &block) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/marty/mcfly_model.rb', line 55 def base_mcfly_lookup(meth, name, = {}, &block) priv = [:private] send(meth, name, ) do |ts, *args| raise "time cannot be nil" if ts.nil? ts = Mcfly.normalize_infinity(ts) q = self.where("#{table_name}.obsoleted_dt >= ? AND " + "#{table_name}.created_dt < ?", ts, ts).scoping do block.call(ts, *args) end next q if priv fa = get_final_attrs q = q.select(*fa) if fa.present? && q.is_a?(ActiveRecord::Relation) case when q.is_a?(ActiveRecord::Relation) # shouldn't happen - lookups that are mode nil should be # private raise "#{self}.#{name} can't convert # ActiveRecord::Relation to OpenStruct" q when q.is_a?(ActiveRecord::Base) make_openstruct(q) else q end end end |
#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 |
# 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
87 88 89 |
# File 'lib/marty/mcfly_model.rb', line 87 def cached_mcfly_lookup(name, = {}, &block) base_mcfly_lookup(:cached_delorean_fn, name, , &block) 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
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 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/marty/mcfly_model.rb', line 95 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_delorean_fn : :delorean_fn base_mcfly_lookup(fn, name, + {sig: attrs.length+1}) 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
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/marty/mcfly_model.rb', line 163 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 |
#mcfly_lookup(name, options = {}, &block) ⇒ Object
91 92 93 |
# File 'lib/marty/mcfly_model.rb', line 91 def mcfly_lookup(name, = {}, &block) base_mcfly_lookup(:delorean_fn, name, , &block) end |