Skip to content
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 `find_all' when saving an Record #411

Open
Ladeburger opened this issue Mar 15, 2015 · 7 comments
Open

undefined method `find_all' when saving an Record #411

Ladeburger opened this issue Mar 15, 2015 · 7 comments

Comments

@Ladeburger
Copy link
Contributor

Hi

I have this Models:

Node has_many Interfaces
Node has_many Connections through interfaces

Interface has_many Connections
Network has_many Connections

Connection has_many Ips
Connection belongs_to Interface
Connection has_one Node through Interface
in Connection this Statement:
default_scope{ includes(:interface, :node) }

when i try to save an existing(via Seed) Connection, i get this Error:

 NoMethodError: undefined method `find_all' for #<Connection id: 1, interface_id: 1, network_id: 1>

as my english is not the best, i have set up an Example at: https://github.com/Ladeburger/find_all

In this Sample, the Bug can be triggered by:

rails c
Connection.first.save!

@scambra
Copy link
Member

scambra commented Mar 16, 2015

If bug is triggered by Connection.first.save! on rails console, it's a rails bug
Please report on rails, but it may be a fixed bug if you are not running latest rails

@Ladeburger
Copy link
Contributor Author

Am 16.03.2015 um 09:45 schrieb Sergio Cambra:

If bug is triggered by Connection.first.save! on rails console, it's a
rails bug

Hmm, but the Bug is only triggered, if i have activescaffold active
(Read: in the Gemfile).

Please report on rails, but it may be a fixed bug if you are not
running latest rails

Rails is Version 4.2.0


Reply to this email directly or view it on GitHub
#411 (comment).

@scambra
Copy link
Member

scambra commented Mar 16, 2015

Post a backtrace including active_scaffold lines so I can check what is triggering find_all call

@Ladeburger
Copy link
Contributor Author

Am 16.03.2015 um 15:28 schrieb Sergio Cambra:

Post a backtrace including active_scaffold lines so I can check what
is triggering find_all call

jan@tablette:~/src/findall> rails c
Loading development environment (Rails 4.2.0)
irb(main):001:0> Connection.first.save!
Connection Load (0.3ms) SELECT "connections".* FROM "connections"
ORDER BY "connections"."id" ASC LIMIT 1
Interface Load (0.2ms) SELECT "interfaces".* FROM "interfaces" WHERE
"interfaces"."id" IN (1)
Node Load (0.1ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."id" IN (1)
(0.2ms) begin transaction
#<Connection:0x00000004df9310 id: 1, interface_id: 1, network_id: 1>
(0.1ms) rollback transaction
NoMethodError: undefined method find_all' for #<Connection id: 1, interface_id: 1, network_id: 1> from /usr/lib64/ruby/gems/2.1.0/gems/activemodel-4.2.0/lib/active_model/attribute_methods.rb:433:in method_missing'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:271:in
associated_records_to_validate_or_save' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:299:in validate_collection_association'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:209:in
block in add_autosave_association_callbacks' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:157:in instance_eval'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:157:in
block in define_non_cyclic_method' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:427:in block in make_lambda'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:189:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:189:in block in simple'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:190:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:190:in block in simple'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in _run_callbacks'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:734:in
_run_validate_callbacks' from /usr/lib64/ruby/gems/2.1.0/gems/activemodel-4.2.0/lib/active_model/validations.rb:395:in run_validations!'
from
/usr/lib64/ruby/gems/2.1.0/gems/activemodel-4.2.0/lib/active_model/validations/callbacks.rb:113:in
block in run_validations!' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:88:in call'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:88:in
_run_callbacks' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:734:in _run_validation_callbacks'
from
/usr/lib64/ruby/gems/2.1.0/gems/activemodel-4.2.0/lib/active_model/validations/callbacks.rb:113:in
run_validations!' from /usr/lib64/ruby/gems/2.1.0/gems/activemodel-4.2.0/lib/active_model/validations.rb:334:in valid?'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/validations.rb:58:in
valid?' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/validations.rb:83:in perform_validations'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/validations.rb:37:in
save' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute_methods/dirty.rb:21:in save'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:286:in
block (2 levels) in save' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:347:in block in with_transaction_returning_status'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/abstract/database_statements.rb:211:in
transaction' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:220:in transaction'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:344:in
with_transaction_returning_status' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:286:in block in save'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:301:in
rollback_active_record_state!' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:285:in save'
from
/home/jan/.bundler/ruby/2.1.0/active_scaffold-2dbfe0315502/lib/active_scaffold/extensions/unsaved_record.rb:15:in
save' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:401:in save_has_one_association'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/autosave_association.rb:191:in
block in add_autosave_association_callbacks' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:427:in block in make_lambda'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:225:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:225:in block in halting_and_conditional'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in _run_callbacks'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:734:in
_run_update_callbacks' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/callbacks.rb:310:in _update_record'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/timestamp.rb:70:in
_update_record' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/persistence.rb:501:in create_or_update'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/callbacks.rb:302:in
block in create_or_update' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:117:in call'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:117:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in block in halting'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in block in halting'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:169:in block in halting'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in
call' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:92:in _run_callbacks'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/callbacks.rb:734:in
_run_save_callbacks' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/callbacks.rb:302:in create_or_update'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/persistence.rb:142:in
save!' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/validations.rb:43:in save!'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/attribute_methods/dirty.rb:29:in
save!' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:291:in block in save!'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:347:in
block in with_transaction_returning_status' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/abstract/database_statements.rb:213:in block in transaction'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/abstract/transaction.rb:188:in
within_new_transaction' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/abstract/database_statements.rb:213:in transaction'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:220:in
transaction' from /usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:344:in with_transaction_returning_status'
from
/usr/lib64/ruby/gems/2.1.0/gems/activerecord-4.2.0/lib/active_record/transactions.rb:291:in
save!' from (irb):1 from /usr/lib64/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/console.rb:110:in start'
from
/usr/lib64/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/console.rb:9:in
start' from /usr/lib64/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:68:in console'
from
/usr/lib64/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:39:in
run_command!' from /usr/lib64/ruby/gems/2.1.0/gems/railties-4.2.0/lib/rails/commands.rb:17:in <top (required)>'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274:in
require' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274:in block in require'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:240:in
load_dependency' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:274:in require'
from /home/jan/src/findall/bin/rails:8:in <top (required)>' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in load'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in
block in load' from /usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:240:in load_dependency'
from
/usr/lib64/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in
load' from /usr/lib64/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:inrequire'
from
/usr/lib64/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in require' from -e:1:in

'
irb(main):002:0>

@scambra
Copy link
Member

scambra commented Mar 18, 2015

You can avoid this error setting :autosave => false on has_one :node, :through => :interfaces association:

has_one :node , :through => :interface, :autosave => false

It's a rails bug, you can reproduce it without activescaffold setting :inverse_of in through associations:

# Connection
has_one :node , :through => :interface, :inverse_of => :connections
# Node
has_many :connections, :through => :interfaces, :inverse_of => :node

ActiveScaffold improves autodetection of inverse association, and adds autodetection for through association, so it has the same effect as defining inverse_of on these through associations. When running Connection.first.save it tries to save Connection.first.node too, and defining inverse_of sets a wrong association object for :connections association, because target is not an array.

This is association object in node record:
#<ActiveRecord::Associations::HasManyThroughAssociation:0x0000000588c660 @reflection=#<ActiveRecord::Reflection::ThroughReflection:0x00000003dece90 @delegate_reflection=#<ActiveRecord::Reflection::HasManyReflection:0x00000003dee088 @name=:connections, @scope=nil, @options={:through=>:interfaces, :inverse_of=>:node}, @active_record=Node(id: integer, name: string, serial: string, node_id: integer), @klass=Connection(id: integer, interface_id: integer, network_id: integer), @plural_name="connections", @automatic_inverse_of=nil, @type=nil, @foreign_type="connections_type", @constructable=true, @association_scope_cache={}, @scope_lock=#<Mutex:0x00000003decf08>, @class_name="Connection", @inverse_of=#<ActiveRecord::Reflection::ThroughReflection:0x00000005b5c0c0 @delegate_reflection=#<ActiveRecord::Reflection::HasOneReflection:0x00000005b5c638 @name=:node, @scope=nil, @options={:through=>:interface, :inverse_of=>:connections}, @active_record=Connection(id: integer, interface_id: integer, network_id: integer), @klass=Node(id: integer, name: string, serial: string, node_id: integer), @plural_name="nodes", @automatic_inverse_of=nil, @type=nil, @foreign_type="node_type", @constructable=false, @association_scope_cache={}, @scope_lock=#<Mutex:0x00000005b5c0e8>, @class_name="Node", @inverse_of=#<ActiveRecord::Reflection::ThroughReflection:0x00000003dece90 ...>>, @klass=Node(id: integer, name: string, serial: string, node_id: integer), @source_reflection_name=:node, @class_name="Node">>, @klass=nil, @source_reflection_name=:connections>, @owner=#<Node id: 1, name: "Testnode", serial: "08154711", node_id: nil>, @loaded=true, @target=#<Connection id: 1, interface_id: 1, network_id: 1>, @stale_state=nil, @inversed=true, @association_scope=nil, @through_records={}, @through_association=nil>

target is #<Connection id: 1, interface_id: 1, network_id: 1> instead of [#<Connection id: 1, interface_id: 1, network_id: 1>]

@scambra
Copy link
Member

scambra commented Mar 18, 2015

More investigation in this issue:

  • Connection.first.node.association(:connections).target should return an array, but it returns a connection record if inverse_of is set.
  • When inverse_of is not set (and activescaffold is not loaded), Connection.first.node.association(:connections).target returns an empty array, because connections association is not loaded yet.

When association is preloaded, it calls set_inverse_instance, which sets target for inverse association, with "inverse.target = owner". However if inverse association is a has_many association it should set [owner] as target.

This doesn't happen when a belongs_to association has inverse_of, because it only sets target for inverse association if it's a has_one association. So target should be set to [owner] or has_one through associations with inverse has_many should skip set_inverse_instance as belongs_to does.

First fix would be:

ActiveRecord::Associations::CollectionAssociation.class_eval do
      def target=(target)
        super Array(target)
      end
end

Second fix:

ActiveRecord::Associations::ThroughAssociation.module_eval do
      # NOTE - for now, we're only supporting inverse setting back onto has_one associations.
      def invertible_for?(record)
        inverse = super
        inverse && !inverse.collection?
      end
end

@scambra
Copy link
Member

scambra commented Mar 18, 2015

I'm adding second fix to activescaffold, so it works while this bug is not fixed in rails.
However it would be good if this bug is reported to rails too.

scambra added a commit that referenced this issue Mar 18, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants