Class: ActiveRecord::Associations::CollectionAssociation

Inherits:
Object
  • Object
show all
Defined in:
lib/activerecord-import/import.rb

Instance Method Summary collapse

Instance Method Details

#bulk_import(*args, &block) ⇒ Object Also known as: import



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
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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/activerecord-import/import.rb', line 128

def bulk_import(*args, &block)
  unless owner.persisted?
    raise ActiveRecord::RecordNotSaved, "You cannot call import unless the parent is saved"
  end

  options = args.last.is_a?(Hash) ? args.pop : {}

  model_klass = reflection.klass
  symbolized_foreign_key = reflection.foreign_key.to_sym

  symbolized_column_names = if model_klass.connection.respond_to?(:supports_virtual_columns?) && model_klass.connection.supports_virtual_columns?
    model_klass.columns.reject(&:virtual?).map { |c| c.name.to_sym }
  else
    model_klass.column_names.map(&:to_sym)
  end

  owner_primary_key = reflection.active_record_primary_key.to_sym
  owner_primary_key_value = owner.send(owner_primary_key)

  # assume array of model objects
  if args.last.is_a?( Array ) && args.last.first.is_a?(ActiveRecord::Base)
    if args.length == 2
      models = args.last
      column_names = args.first.dup
    else
      models = args.first
      column_names = symbolized_column_names
    end

    unless symbolized_column_names.include?(symbolized_foreign_key)
      column_names << symbolized_foreign_key
    end

    models.each do |m|
      m.public_send "#{symbolized_foreign_key}=", owner_primary_key_value
      m.public_send "#{reflection.type}=", owner.class.name if reflection.type
    end

    return model_klass.bulk_import column_names, models, options

  # supports array of hash objects
  elsif args.last.is_a?( Array ) && args.last.first.is_a?(Hash)
    if args.length == 2
      array_of_hashes = args.last
      column_names = args.first.dup
      allow_extra_hash_keys = true
    else
      array_of_hashes = args.first
      column_names = array_of_hashes.first.keys
      allow_extra_hash_keys = false
    end

    symbolized_column_names = column_names.map(&:to_sym)
    unless symbolized_column_names.include?(symbolized_foreign_key)
      column_names << symbolized_foreign_key
    end

    if reflection.type && !symbolized_column_names.include?(reflection.type.to_sym)
      column_names << reflection.type.to_sym
    end

    array_of_attributes = array_of_hashes.map do |h|
      error_message = model_klass.send(:validate_hash_import, h, symbolized_column_names, allow_extra_hash_keys)

      raise ArgumentError, error_message if error_message

      column_names.map do |key|
        if key == symbolized_foreign_key
          owner_primary_key_value
        elsif reflection.type && key == reflection.type.to_sym
          owner.class.name
        else
          h[key]
        end
      end
    end

    return model_klass.bulk_import column_names, array_of_attributes, options

  # supports empty array
  elsif args.last.is_a?( Array ) && args.last.empty?
    return ActiveRecord::Import::Result.new([], 0, [])

  # supports 2-element array and array
  elsif args.size == 2 && args.first.is_a?( Array ) && args.last.is_a?( Array )
    column_names, array_of_attributes = args

    # dup the passed args so we don't modify unintentionally
    column_names = column_names.dup
    array_of_attributes = array_of_attributes.map(&:dup)

    symbolized_column_names = column_names.map(&:to_sym)

    if symbolized_column_names.include?(symbolized_foreign_key)
      index = symbolized_column_names.index(symbolized_foreign_key)
      array_of_attributes.each { |attrs| attrs[index] = owner_primary_key_value }
    else
      column_names << symbolized_foreign_key
      array_of_attributes.each { |attrs| attrs << owner_primary_key_value }
    end

    if reflection.type
      symbolized_type = reflection.type.to_sym
      if symbolized_column_names.include?(symbolized_type)
        index = symbolized_column_names.index(symbolized_type)
        array_of_attributes.each { |attrs| attrs[index] = owner.class.name }
      else
        column_names << symbolized_type
        array_of_attributes.each { |attrs| attrs << owner.class.name }
      end
    end

    return model_klass.bulk_import column_names, array_of_attributes, options
  else
    raise ArgumentError, "Invalid arguments!"
  end
end