New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

undefined method insert_record for Array #11026

Closed
amitavmohanty01 opened this Issue Jun 20, 2013 · 19 comments

Comments

Projects
None yet
9 participants
@amitavmohanty01
Copy link

amitavmohanty01 commented Jun 20, 2013

I have a Model A which has many objects of Model B using a :has_many association. When I create a new object of model A, set its attributes, build associated objects and inspect the non-persisted objects, it works fine. However, as soon as I try to persist the objects using save or save!, I am getting the following back-trace.

NoMethodError: undefined method insert_record' for #Array:0x000000052541f8
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:154:in send' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:306:in block in save_collection_association'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/associations/association_collection.rb:431:in block in method_missing' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:216:in block in method_missing'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:216:in each' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/associations/association_proxy.rb:216:in method_missing'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/associations/association_collection.rb:431:in method_missing' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:297:in save_collection_association'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:163:in block in add_autosave_association_callbacks' from /usr/lib/ruby/gems/2.0.0/gems/activesupport-3.0.5/lib/active_support/callbacks.rb:415:in _run_create_callbacks'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/callbacks.rb:281:in create' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/persistence.rb:246:in create_or_update'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/callbacks.rb:277:in block in create_or_update' from /usr/lib/ruby/gems/2.0.0/gems/activesupport-3.0.5/lib/active_support/callbacks.rb:418:in _run_save_callbacks'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/callbacks.rb:277:in create_or_update' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/persistence.rb:39:in save'
... 2 levels...
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:240:in block (2 levels) in save' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:292:in block in with_transaction_returning_status'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in transaction' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:207:in transaction'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:290:in with_transaction_returning_status' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:240:in block in save'
from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:251:in rollback_active_record_state!' from /usr/lib/ruby/gems/2.0.0/gems/activerecord-3.0.5/lib/active_record/transactions.rb:239:in save'
from (irb):104
from /usr/lib/ruby/gems/2.0.0/gems/railties-3.0.5/lib/rails/commands/console.rb:44:in start' from /usr/lib/ruby/gems/2.0.0/gems/railties-3.0.5/lib/rails/commands/console.rb:8:in start'
from /usr/lib/ruby/gems/2.0.0/gems/railties-3.0.5/lib/rails/commands.rb:23:in <top (required)>' from /home/amitav/WMS/trunk/script/rails:6:in require'
from /home/amitav/WMS/trunk/script/rails:6:in <top (required)>' from -e:1:in load'
from -e:1:in <main>'

I am using ruby 2.0.0p195 with rails 3.0.5.

@pftg

This comment has been minimized.

Copy link
Contributor

pftg commented Jun 20, 2013

@amitavmohanty01

This comment has been minimized.

Copy link

amitavmohanty01 commented Jun 20, 2013

Sure. Just to verify the issue, I created a test app.

rails new test1 -T -G -d mysql

I created two models in it.

Ghara(id: integer, field2: integer, created_at: datetime, updated_at: datetime) Pila(id: integer, field1: integer, ghara_id: integer, created_at: datetime, updated_at: datetime)

The models just have a has_many association.

class Ghara < ActiveRecord::Base
  has_many :pilas
end

class Pila < ActiveRecord::Base
  belongs_to :ghara
end

From console, I tried the following and I got the exception as posted earlier.

g = Ghara.new
g.field2 = 2
pilas = [{:field1=> 1}, {:field1 => 2}]
g.pilas.build(pilas)
g.save

A workaround to the issue is the following.

g = Ghara.new
g.field2 = 2
g.save
pilas = [{:field1=> 1}, {:field1 => 2}]
g.pilas.build(pilas)
g.pilas.each {|p| p.save!}
@amitavmohanty01

This comment has been minimized.

Copy link

amitavmohanty01 commented Jun 20, 2013

In the form of the test case, it would be as follows:

gem 'activerecord', '3.0.5'
require 'active_record'
require 'minitest/autorun'
require 'logger'

ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: ':your db:', username: 'me', password: 'mypas')
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :pilas do |t|
    t.integer :field1
    t.integer :ghara_id
  end
  create_table :gharas do |t|
    t.integer :field2
  end
end

class Ghara < ActiveRecord::Base
  has_many :pilas
end

class Pila < ActiveRecord::Base
  belongs_to :ghara
end

class BugTest < MiniTest::Unit::TestCase
  def test_association_stuff
    g = Ghara.new
    g.field2 = 2
    pilas = [{:field1=> 1}, {:field1 => 2}]
    g.pilas.build(pilas)
    g.save
  end
end
@senny

This comment has been minimized.

Copy link
Member

senny commented Jun 22, 2013

I'll take a look.

@senny senny closed this in 951bde4 Jun 22, 2013

@senny

This comment has been minimized.

Copy link
Member

senny commented Jun 22, 2013

I can't reproduce this issue. I tried both master and 3-2-stable and both passed. I submitted a regression test-case to master to prevent the issue from appearing again: 951bde4

I see you are using 3.0.5, which does no longer receive bugfixes, please upgrade to the latest 3.2.x release.

@amitavmohanty01

This comment has been minimized.

Copy link

amitavmohanty01 commented Jun 22, 2013

Yes, the issue does not appear in 3.2.13 version. Thanks @senny

@tisba

This comment has been minimized.

Copy link

tisba commented Aug 14, 2013

Has anyone any insight on why this was a problem? I'm working on upgrading a legacy project (Rails 2.3.18) to Ruby 2.0 and I'm running into this issue. Before anyone states the obvious: A Rails upgrade is not feasible right now :-/ The case provided by @amitavmohanty01 gives me

-- create_table(:pilas)
  SQL (0.2ms)   select sqlite_version(*)
  SQL (0.2ms)   CREATE TABLE "pilas" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "field1" integer, "ghara_id" integer)
   -> 0.0611s
-- create_table(:gharas)
  SQL (0.1ms)   CREATE TABLE "gharas" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "field2" integer)
   -> 0.0004s
Run options: --seed 57316

# Running tests:

  Ghara Create (0.1ms)   INSERT INTO "gharas" ("field2") VALUES(2)
E

Finished tests in 0.008600s, 116.2791 tests/s, 0.0000 assertions/s.

  1) Error:
BugTest#test_association_stuff:
NoMethodError: undefined method `insert_record' for #<Array:0x007fa92b23b5a0>
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/associations/association_proxy.rb:149:in `send'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/autosave_association.rb:323:in `block in save_collection_association'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/associations/association_collection.rb:392:in `block in method_missing'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/associations/association_proxy.rb:215:in `each'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/associations/association_proxy.rb:215:in `method_missing'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/associations/association_collection.rb:392:in `method_missing'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/autosave_association.rb:314:in `save_collection_association'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/autosave_association.rb:176:in `block in add_autosave_association_callbacks'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-2.3.18/lib/active_support/callbacks.rb:178:in `evaluate_method'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-2.3.18/lib/active_support/callbacks.rb:166:in `call'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-2.3.18/lib/active_support/callbacks.rb:93:in `block in run'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-2.3.18/lib/active_support/callbacks.rb:92:in `each'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-2.3.18/lib/active_support/callbacks.rb:92:in `run'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activesupport-2.3.18/lib/active_support/callbacks.rb:276:in `run_callbacks'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/callbacks.rb:344:in `callback'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/callbacks.rb:267:in `create_with_callbacks'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/base.rb:2935:in `create_or_update'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/callbacks.rb:250:in `create_or_update_with_callbacks'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/base.rb:2585:in `save'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/validations.rb:1089:in `save_with_validation'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/dirty.rb:79:in `save_with_dirty'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/transactions.rb:229:in `block in with_transaction_returning_status'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/connection_adapters/abstract/database_statements.rb:136:in `transaction'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/transactions.rb:182:in `transaction'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/transactions.rb:228:in `with_transaction_returning_status'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/transactions.rb:196:in `block in save_with_transactions'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/transactions.rb:208:in `rollback_active_record_state!'
    /Users/basti/.rvm/gems/ruby-2.0.0-p247/gems/activerecord-2.3.18/lib/active_record/transactions.rb:196:in `save_with_transactions'
    ar_debug.rb:35:in `test_association_stuff'

1 tests, 0 assertions, 0 failures, 1 errors, 0 skips
@senny

This comment has been minimized.

Copy link
Member

senny commented Aug 14, 2013

@tisba you could use my test-case and do a bisect to find the commit that fixed it.

@tisba

This comment has been minimized.

Copy link

tisba commented Aug 14, 2013

@senny good idea, thanks! What I find interesting though is that this issue does not occur with 1.8.7 for some reason.

@senny

This comment has been minimized.

Copy link
Member

senny commented Aug 14, 2013

@tisba you will learn more once you identified the fixing commit.

@tisba

This comment has been minimized.

Copy link

tisba commented Aug 14, 2013

@senny I'm on it. Need to get the rails test suite running first though :)

@tisba

This comment has been minimized.

Copy link

tisba commented Aug 15, 2013

I'm having a little difficulty using git bisect to identify the fixing commit. But I've pinned the fixing commit between v3.0.20...v3.1.0.rc1 (which still is about 5k commits). I doubt though that I'll be able to easily backport the change to 2.3.18 - a lot of stuff happened in the mean time :-/

@saneshark

This comment has been minimized.

Copy link

saneshark commented Sep 3, 2013

@tisba I'm having this same issue. Were you able to find a commit that fixed this?

@MasterLambaster

This comment has been minimized.

Copy link
Contributor

MasterLambaster commented Oct 23, 2013

Ruby 2.0 is no longer returns true from respond_to? for protected methods. That's why it's failing in for rails 3.0 on ruby 2.0.
AssociationProxy redefines send method.

def send(method, *args)
  if proxy_respond_to?(method)
    super
  else
    load_target
    @target.send(method, *args)
   end
end

insert_record is a protected method, so in ruby 2.0 this method will go to @target which is an array.

Here's small monkey patch that one can use to fix this issue. It's been tested only for rails 3.0.x, but i guess that it should work for earlier versions as well.

if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 0 && RUBY_VERSION >= "2.0.0"
  module ActiveRecord
    module Associations
      class AssociationProxy
        def send(method, *args)
          if proxy_respond_to?(method, true)
            super
          else
            load_target
            @target.send(method, *args)
          end
        end
      end
    end
  end
end

/cc @tisba, @saneshark

@tisba

This comment has been minimized.

Copy link

tisba commented Oct 25, 2013

@MasterLambaster awesome, thanks!

@bernardeli

This comment has been minimized.

Copy link

bernardeli commented Nov 1, 2013

@MasterLambaster

You're rockstar! thanks a lot. That saves me a lot of time. Own you a beer! 🍺

@gamov

This comment has been minimized.

Copy link

gamov commented Feb 13, 2014

@gamov

This comment has been minimized.

Copy link

gamov commented Feb 14, 2014

I also had to add:

def respond_to?(symbol, include_private=false)
  super(symbol, true)
end

Because autosave_association.rb:334 was not triggered:

association.send(:construct_sql) if association.respond_to?(:construct_sql)
@juanrobles

This comment has been minimized.

Copy link

juanrobles commented Mar 18, 2015

We are using a different implementation of respond_to? since we were having issues with some associations:

    def respond_to?(*args)                                                   
      args << true unless args.size > 1                                      
      proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
    end                                                                      

danp60 added a commit to danp60/bullet that referenced this issue Feb 10, 2016

Fix Rails 3.0 with Ruby 2.0
rails/rails#11026 (comment)

Monkey patch rails 3.0 to work with ruby 2.0

danp60 added a commit to danp60/bullet that referenced this issue Feb 10, 2016

Fix Rails 3.0 with Ruby 2.0
rails/rails#11026 (comment)

Monkey patch rails 3.0 to work with ruby 2.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment