2

How can I reuse an active record object, while creating a set of new objects.

Take the following code for example:

loop do
  foo = Foo.new
  foo.bar = 'value'
  foo.save!
end

This causes ruby to allocate a new object every time Foo.new is called. Which is slow, and stabs the GC right in the heart.

So is there a way where I can re use the foo object like this (in this case .reset is incorrectly used)

foo = Foo.new
loop do
  foo.reset
  foo.bar = 'value'
  foo.save!
end

So this way, the object is duplicated by inserting it multiple times into the database, rather than creating new objects in memory.

A more complete use case of this is pulling data in off a message queue.

queue.subscribe do |data|
  foo = Foo.new
  foo.data = data
  foo.save!
end
steakunderscore
  • 1,076
  • 9
  • 18
  • Short answer: Not without executing raw SQL, and ignoring AR objects altogether. – Paul Richter Dec 18 '14 at 03:59
  • ActiveRecord objects are by design one-per-row. Going against this goes against the AR pattern and "stuff blows up". Even if the attributes are reset to `nil`'s, ActiveRecord will still think it's not a new object after it was saved: it will literally attempt to save it with `NULL` as primary key and fail. Just tested it out. – D-side Dec 18 '14 at 22:48
  • @D-side you should make that an answer. – Jesse Whitham Dec 19 '14 at 01:36
  • I think you both might be correct. I guess it is breaking the ORM model a bit. So the options is to thrash memory, or skip AR and use straight SQL. – steakunderscore Dec 19 '14 at 02:03
  • @JesseWhitham done. I doubt it's very helpful though. – D-side Dec 19 '14 at 18:28
  • @D-side on the contrary, while it may not give the OP exactly what they want, it's an excellent explanation. It needs more up votes. – Paul Richter Dec 20 '14 at 16:39

2 Answers2

2

I'd hate to say no, but there's no way to do that and keep following the Active Record pattern, which is a principle under the hood of Rails' ActiveRecord. As the Wikipedia article states:

an object instance is tied to a single row in the table

From that you can tell, that you cannot map a single object to multiple distinct rows.

Rails' ActiveRecord objects come in two different forms that look alike but work differently inside:

  • Not persisted objects (results of new, association.build and maybe some others)
    They don't have ids yet, they're not yet valid "Active Records", that implies that on save a new row in the database is created and the object is considered persisted from that moment.
  • Persisted objects (received from the database in any way)
    An object considered a valid row in the database without most implications. Any operation with that object should be reasonable in context of a RDBMS.

That said, to do that, you'd have to dive into the depths of AR implementation, because to "unpersist" an AR object (if you follow the pattern) you have to remove it from the database. No exceptions in current AR's implementation.

Here's a little experiment. Once we have an unpersisted object, we can examine it and find out it has an id equal to nil. Now let's get a persisted object and do this:

# assume we did `t = Thing.first` before
t.id = nil
t.save!

Create a new object? No! Update existing one!

UPDATE `things` SET `id` = NULL WHERE `things`.`id` = 1

Of course, DB rejects this query because it's not reasonable in terms of RDBMS to set primary key of any record to NULL:

Column 'id' cannot be null

Why? See this question. Briefly, that's because NULL != NULL

Community
  • 1
  • 1
D-side
  • 9,150
  • 3
  • 28
  • 44
0
foo = [Foo.new, bar: []]
// and foo[0] is the Foo.new object 
loop do
  foo.bar.pop()
  foo.bar.push('value')
end

I think your code was a bit overcensored and you should've given us more info, but this is how I would store a Foo object and a changing foo.bar value, and never creating new arrays or causing memory leaks

J-Dizzle
  • 3,176
  • 6
  • 31
  • 49