Class: XDry::Generators::DictionaryCoding

Inherits:
Generator
  • Object
show all
Defined in:
lib/xdry/generators/dictionary_coding.rb

Instance Attribute Summary

Attributes inherited from Generator

#patcher

Instance Method Summary collapse

Methods inherited from Generator

id, inherited, #initialize, #process_attribute, #verbose?

Constructor Details

This class inherits a constructor from XDry::Generators::Generator

Instance Method Details

#each_persistent_attr(oclass) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/xdry/generators/dictionary_coding.rb', line 109

def each_persistent_attr oclass
  oclass.attributes.select { |a| a.persistent? }.each do |oattr|
    name, type = oattr.name, oattr.type
    field_name = oattr.field_name
    capitalized_name = name.capitalized_identifier
    key_const = "#{capitalized_name}Key"

    type_boxer = Boxing.converter_for type
    if type_boxer.nil?
      puts "Persistence not (yet) supported for type #{type}"
      next
    end

    yield oattr, capitalized_name, key_const, type_boxer
  end
end

#process_class(oclass) ⇒ Object



8
9
10
11
12
13
14
15
16
17
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
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
# File 'lib/xdry/generators/dictionary_coding.rb', line 8

def process_class oclass
  return unless oclass.attributes.any? { |a| a.persistent? }

  dictionary_var = "dictionary"

  defines_emitter = Emitter.new
  init_out        = Emitter.new
  repr_out        = Emitter.new

  dictionary_var = "dictionary"

  oclass.attributes.select { |a| a.persistent? }.each do |oattr|
    name, type = oattr.name, oattr.type
    field_name = oattr.field_name
    raw_name = "#{name}Raw"
    capitalized_name = name.capitalized_identifier
    key_const = "#{capitalized_name}Key"

    type_boxer = Boxing.converter_for type
    if type_boxer.nil?
      puts "Persistence not (yet) supported for type #{type}"
      next
    end

    defines_emitter << %Q`\#define #{key_const} @"#{capitalized_name}"`

    init_out << %Q`id #{raw_name} = [#{dictionary_var} objectForKey:#{key_const}];`
    init_out.if "#{raw_name} != nil" do
      unboxed = type_boxer.unbox_retained(init_out, raw_name, name)
      init_out << "#{field_name} = #{unboxed};"
    end

    boxed = type_boxer.box(repr_out, field_name, name)
    repr_out << %Q`[dictionary setObject:#{boxed} forKey:#{key_const}];`
  end

  init_code = Emitter.capture do |o|
    o.method "(id)initWithDictionary:(NSDictionary *)#{dictionary_var}" do
      o.if "self = [super init]" do
      end
      o << "return self;"
    end
  end

  repr_code = Emitter.capture do |o|
    o.method "(NSDictionary *)dictionaryRepresentation" do
      o << "NSMutableDictionary *#{dictionary_var} = [NSMutableDictionary dictionary];"
      o << "return #{dictionary_var};"
    end
  end

  file_scope = oclass.main_implementation.parent_scope
  define_ip = MultiIP.new(AfterDefineIP.new(file_scope), BeforeImplementationStartIP.new(oclass))
  define_lines = Emitter.capture do |o|
    each_persistent_attr(oclass) do |oattr, capitalized_name, key_const, type_boxer|
      unless file_scope.children.any? { |n| NDefine === n && n.word == key_const }
        o << %Q`\#define #{key_const} @"#{capitalized_name}"`
      end
    end
  end
  define_ip.wrap_with_empty_lines_if_last!
  define_ip.insert @patcher, define_lines

  MethodPatcher.new(patcher, oclass, 'dictionaryRepresentation', ImplementationStartIP.new(oclass), repr_code) do |omethod|
    impl = omethod.impl
    ip = BeforeReturnIP.new(impl)

    lines = Emitter.capture do |o|
      each_persistent_attr(oclass) do |oattr, capitalized_name, key_const, type_boxer|
        unless impl.children.any? { |n| NLine === n && n.line =~ /\bsetObject:.*(?:\b#{oattr.name}|\b#{oattr.field_name}\b)/ }
          boxed = type_boxer.box(o, oattr.field_name, oattr.name)
          o << %Q`[dictionary setObject:#{boxed} forKey:#{key_const}];`
        end
      end
    end

    ip.insert @patcher, lines unless lines.empty?
  end

  MethodPatcher.new(patcher, oclass, 'initWithDictionary:', ImplementationStartIP.new(oclass), init_code) do |omethod|
    impl = omethod.impl
    ip = InsideConstructorIfSuperIP.new(impl)
    var_name = impl.start_node.selector_def.var_name_after_keyword('initWithDictionary:')

    lines = Emitter.capture do |o|
      each_persistent_attr(oclass) do |oattr, capitalized_name, key_const, type_boxer|
        unless impl.children.any? { |n| NLine === n && n.line =~ /^(?:self\s*.\s*#{oattr.name}|#{oattr.field_name})\s*=/ }
          raw_name = "#{oattr.name}Raw"
          o << %Q`id #{raw_name} = [#{var_name} objectForKey:#{key_const}];`
          o.if "#{raw_name} != nil" do
            unboxed = type_boxer.unbox_retained(o, raw_name, oattr.name)
            o << "#{oattr.field_name} = #{unboxed};"
          end
        end
      end
    end

    ip.insert @patcher, lines unless lines.empty?
  end
end