Module: EnterpriseMti::ClassMethods

Defined in:
lib/enterprise_mti/class_methods/class_methods.rb

Instance Method Summary collapse

Instance Method Details

#has_one_superclass(superclass_symbol, options = {}) ⇒ Object



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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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
# File 'lib/enterprise_mti/class_methods/class_methods.rb', line 82

def has_one_superclass(superclass_symbol, options={})

  superclass_name                 = superclass_symbol.to_s.camelize
  superclass_qualified_name       = (options[:module] || '') + '::' + superclass_name
  superclass                      = superclass_qualified_name.constantize
  container_class                 = self
  container_class_camel_name      = self.name.demodulize
  container_class_underscore_name = self.name.demodulize.underscore
  superclass_relation_name        = superclass_symbol.to_s + "_superclass"
  superclass_relation_symbol      = superclass_relation_name.to_sym

  has_one superclass_relation_symbol, class_name: superclass_qualified_name

  # Create build and create methods for each subclass
  superclass.descendants.each do |subclass|
    subclass_underscore_name = subclass.name.demodulize.underscore
    actions = [:build, :create, :create!]
  
    actions.each do |action|
      action_suffix = nil
      action_name   = action.to_s
    
      if action.to_s.last(1) == '!'
        action_suffix = '!'
        action_name   = action.to_s[0..-2]
      end

      subclass.define_singleton_method "#{action_name}_with_superclass_instance#{action_suffix}" do |*args, &block|
        self.send("#{action_name}#{action_suffix}", *args, &block).superclass_instance = args.last[:superclass_instance]
      end
    
      superclass.class_eval do
      
        define_method "#{action_name}_#{subclass_underscore_name}_subclass#{action_suffix}" do |*args, &block|
          args.last[:superclass_instance] = self
          subclass.send "#{action_name}_with_superclass_instance#{action_suffix}", *args, &block
        end
      
        define_method "#{subclass_underscore_name}_with_superclass_instance=" do |value|
          value.superclass_instance = self
          self.send "#{subclass_underscore_name}=", value
        end
      end

      container_class.class_eval do
      
        define_method "#{action_name}_#{subclass_underscore_name}#{action_suffix}" do |*args, &block|
          unless superclass_instance = self.send(superclass_relation_symbol)
            superclass_instance = self.send("build_#{superclass_relation_name}")
          end
          superclass_instance.instance_eval do
            send "#{action_name}_#{subclass_underscore_name}_subclass#{action_suffix}", *args, &block
          end
        end
      end
    end
  end

  # Create getter
  define_method superclass_symbol do

    association_methods = superclass.descendants.collect { |subclass|
      reflection_symbol =
        subclass.to_s.demodulize.underscore.to_sym
      assoc = superclass.reflect_on_association(reflection_symbol)
      assoc ? assoc.name : nil
    }.compact
  
    if superclass_model_instance = self.send(superclass_relation_symbol)
      association_methods.collect{ |a|
        superclass_model_instance.send a
      }.inject do |a, b|
        a || b
      end
    end
  end

  # Create setter
  define_method "#{superclass_symbol.to_s}=" do |value|
    reflection_symbol =
      value.class.name.demodulize.underscore.to_sym

    unless superclass_instance = self.send(superclass_relation_symbol)
      superclass_instance = self.send("build_#{superclass_relation_name}")
    end
  
    reflection_assignment_method =
      superclass.reflect_on_association(reflection_symbol).name.to_s + '_with_superclass_instance='
    
    superclass_instance.send reflection_assignment_method, value
  end
end

#has_subclass(subclass_symbol, options = {}) ⇒ Object



3
4
5
6
7
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
# File 'lib/enterprise_mti/class_methods/class_methods.rb', line 3

def has_subclass(subclass_symbol, options={})

  subclass_name           = subclass_symbol.to_s.camelize
  subclass                = nil
  subclass_qualified_name = (options[:module] || '') + '::' + subclass_name
  superclass              = self
  superclass_name         = self.name.demodulize.underscore
  superclass_symbol       = self.name.demodulize.underscore.to_sym
  table_name              = options[:table_name] || subclass_symbol.to_s.pluralize

  # Get subclass
  Kernel.const_get(options[:module] || 'Kernel').module_eval {
    subclass = const_get subclass_name }

  subclass.class_eval do
    # Set table name on subclass to prevent it from inheriting
    # superclass' attributes, etc.
    self.table_name = table_name
    has_one superclass_symbol
    attr_accessor :superclass_instance
    validates :superclass_instance, presence: true
  
    define_method "#{superclass_name}_transaction" do
      superclass_instance.send "#{subclass_symbol.to_s}_id=", self.id
      superclass_instance.save
    end
    after_save "#{superclass_name}_transaction".to_sym, on: :create
  end

  # Superclass belongs to subclass
  self.belongs_to subclass_symbol, :class_name => subclass_qualified_name
end

#is_a_superclassObject



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
# File 'lib/enterprise_mti/class_methods/class_methods.rb', line 36

def is_a_superclass

  # Require subclasses' files as dependencies, otherwise we get circular
  # dependency errors (see https://github.com/rails/rails/issues/3364)
  if descendants.empty?
    module_path_tokens = self.name.underscore.split('/')
    module_path_tokens[-1] += '_subclasses'
    Dir[Rails.root.join('app', 'models', File.join(module_path_tokens), "*.rb")].each do |file|
      require_dependency file
    end
  end

  # Call has_sublcass on the superclass for each subclass
  descendants.each do |subclass|
    name_tokens = subclass.to_s.split('::')
    subclass_name = name_tokens.last
    module_name = name_tokens[0...-1].join('::') if name_tokens.count > 1
    self.send :has_subclass, subclass_name.underscore.to_sym, :module => module_name
  end

  # Populate container_classes, an array that contains classes that "have"
  # the superclass (and thus have foreign key constraints on the
  # superclass)
  reflection_class = Proc.new do |r|
    if r.options[:class_name]
      r.options[:class_name].constantize
    else
      prefix = self.name.deconstantize || ''
      "#{prefix}::#{r.name.to_s.camelize}".constantize
    end
  end
  
  @container_classes = self.reflect_on_all_associations.keep_if { |r|
    r.macro == :belongs_to &&
    #self.column_names.include?(r.association_foreign_key) &&
    !self.descendants.include?(reflection_class.call(r))
  }.collect { |r| reflection_class.call(r) }

  # Add read-only access to container_classes
  class << self
    def container_classes
      @container_classes
    end
  end
end