Class: ReeMapper::DbLoadCompiler

Inherits:
Object
  • Object
show all
Defined in:
lib/ree_lib/packages/ree_mapper/package/ree_mapper/db_load_compiler.rb

Overview

Compiles a specialized :db_load for composite mappers when Ree.db_load_performance_mode? is on at mapper build time.

The compiled method handles the hot call shape db_load(hash) with no role/filters as straight-line per-field code: values of primitive types skip casting behind a cheap type guard (see AbstractType#db_load_trusted_check), non-trusted types (custom, enums, pg wrappers, nested mappers) are cast as usual, and dto construction goes through a single attrs hash instead of build-then-copy. Every other call shape falls back to the untouched generic method, kept as __generic_db_load.

Codegen targets the mapper's anonymous class (each Mapper.build creates one), never the instance: registered mappers are frozen.

Class Method Summary collapse

Class Method Details

.compile(mapper) ⇒ Object



18
19
20
21
22
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/ree_lib/packages/ree_mapper/package/ree_mapper/db_load_compiler.rb', line 18

def compile(mapper)
  return if mapper.type # leaf mappers keep their generic delegation

  strategy = mapper.find_strategy(:db_load)
  return unless strategy

  case (output = strategy.output)
  when ReeMapper::ReeDtoOutput
    builder  = "acc = {}"
    finisher = "DB_LOAD_DTO.build_from_attrs(acc)"
  when ReeMapper::HashOutput
    return unless strategy.dto == ::Hash # OpenStruct keeps the generic path
    builder  = "acc = {}"
    finisher = "acc"
  when ReeMapper::ObjectOutput
    builder  = "acc = DB_LOAD_DTO.allocate"
    finisher = "acc"
  else
    return # StructOutput and unknown outputs keep the generic path
  end

  klass = mapper.class
  return if klass.method_defined?(:__generic_db_load, false) # already compiled

  fields = mapper.fields.values

  set_const(klass, :DB_LOAD_FIELDS, fields.freeze)
  set_const(klass, :DB_LOAD_DTO, strategy.dto)
  set_const(
    klass,
    :DB_LOAD_NESTED_FILTERS,
    fields.map { _1.fields_filter ? [_1.fields_filter].freeze : nil }.freeze
  )

  body = fields.each_with_index.filter_map { |field, idx|
    # the generic loop skips role-scoped fields when role is nil; calls
    # with an actual role go through the generic fallback
    next unless field.roles.empty?
    field_code(field, idx, output, strategy.always_optional)
  }.join("\n")

  klass.send(:alias_method, :__generic_db_load, :db_load)

  klass.class_eval("    def db_load(obj, role: nil, only: nil, except: nil, fields_filters: nil)\n      if role || only || except || fields_filters || !obj.is_a?(::Hash)\n        return __generic_db_load(obj, role: role, only: only, except: except, fields_filters: fields_filters)\n      end\n\n      \#{builder}\n      \#{body}\n      \#{finisher}\n    end\n  RUBY\n\n  # keep the generated source inspectable for debugging\n  klass.instance_variable_set(:@compiled_db_load_source, body)\n\n  nil\nend\n", __FILE__, __LINE__ + 1)