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

Broken queries when used in conjunction with friendly_id and globalize #27

Closed
lordofthelake opened this issue Jan 6, 2015 · 4 comments

Comments

@lordofthelake
Copy link

When using acts_as in conjunction with friendly_id and globalize, the finders on the model get broken due to an incorrect WHERE condition injected in the query (parent_model.child_model_translations.slug = ? where it should have been child_model_translations.slug = ?).

Models:

class Collection < ActiveRecord::Base
  extend FriendlyId

  translates :name, :slug
  friendly_id :name, use: :globalize
  acts_as :box
end

class Box < ActiveRecord::Base
  default_scope -> { where(in_home: true) }
  acts_as_list scope: [:in_home]
  actable
end

My schema.rb (relevant portions):

  create_table "boxes", force: true do |t|
    t.integer  "actable_id"
    t.string   "actable_type"
    t.integer  "position",     default: 0
    t.boolean  "in_home",      default: false
    t.boolean  "featured",     default: false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "collection_translations", force: true do |t|
    t.integer  "collection_id", null: false
    t.string   "locale",        null: false
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "name"
    t.string   "slug"
  end

  create_table "collections", force: true do |t|
    t.string   "name"
    t.string   "icon"
    t.string   "image"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "color"
    t.string   "slug"
  end

Gem versions (from bundle show)

 * active_record-acts_as (1.0.2)
 * activerecord (4.1.8)
 * friendly_id (5.0.4)
 * friendly_id-globalize (1.0.0.alpha1 058f863)
 * globalize (4.0.3)

Exception raised when invoking, e.g., Collection.friendly.find('autumn-winter') or Collection.find_by_slug('autumn-winter'):

PG::UndefinedColumn: ERROR:  column boxes.collection_translations.slug does not exist
LINE 1: ... = 'Collection' AND "boxes"."in_home" = 't' WHERE "boxes"."c...
                                                             ^
: SELECT  "collections"."id" AS t0_r0, "collections"."name" AS t0_r1, "collections"."icon" AS t0_r2, "collections"."image" AS t0_r3, "collections"."created_at" AS t0_r4, "collections"."updated_at" AS t0_r5, "collections"."color" AS t0_r6, "collections"."slug" AS t0_r7, "boxes"."id" AS t1_r0, "boxes"."actable_id" AS t1_r1, "boxes"."actable_type" AS t1_r2, "boxes"."position" AS t1_r3, "boxes"."in_home" AS t1_r4, "boxes"."featured" AS t1_r5, "boxes"."created_at" AS t1_r6, "boxes"."updated_at" AS t1_r7 FROM "collections" INNER JOIN "collection_translations" ON "collection_translations"."collection_id" = "collections"."id" LEFT OUTER JOIN "boxes" ON "boxes"."actable_id" = "collections"."id" AND "boxes"."actable_type" = 'Collection' AND "boxes"."in_home" = 't' WHERE "boxes"."collection_translations.slug" = 'autumn-winter' AND "collection_translations"."locale" = 'en' LIMIT 1
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column boxes.collection_translations.slug does not exist
LINE 1: ... = 'Collection' AND "boxes"."in_home" = 't' WHERE "boxes"."c...
                                                             ^
: SELECT  "collections"."id" AS t0_r0, "collections"."name" AS t0_r1, "collections"."icon" AS t0_r2, "collections"."image" AS t0_r3, "collections"."created_at" AS t0_r4, "collections"."updated_at" AS t0_r5, "collections"."color" AS t0_r6, "collections"."slug" AS t0_r7, "boxes"."id" AS t1_r0, "boxes"."actable_id" AS t1_r1, "boxes"."actable_type" AS t1_r2, "boxes"."position" AS t1_r3, "boxes"."in_home" AS t1_r4, "boxes"."featured" AS t1_r5, "boxes"."created_at" AS t1_r6, "boxes"."updated_at" AS t1_r7 FROM "collections" INNER JOIN "collection_translations" ON "collection_translations"."collection_id" = "collections"."id" LEFT OUTER JOIN "boxes" ON "boxes"."actable_id" = "collections"."id" AND "boxes"."actable_type" = 'Collection' AND "boxes"."in_home" = 't' WHERE "boxes"."collection_translations.slug" = 'autumn-winter' AND "collection_translations"."locale" = 'en' LIMIT 1
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/postgresql_adapter.rb:822:in `async_exec'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/postgresql_adapter.rb:822:in `block in exec_no_cache'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract_adapter.rb:373:in `block in log'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract_adapter.rb:367:in `log'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/postgresql_adapter.rb:822:in `exec_no_cache'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/postgresql/database_statements.rb:137:in `exec_query'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/postgresql_adapter.rb:954:in `select'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract/database_statements.rb:24:in `select_all'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/connection_adapters/abstract/query_cache.rb:70:in `select_all'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/relation/finder_methods.rb:347:in `find_with_associations'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/relation.rb:611:in `exec_queries'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/relation.rb:493:in `load'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/relation.rb:238:in `to_a'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/relation/finder_methods.rb:460:in `find_take'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activerecord-4.1.8/lib/active_record/relation/finder_methods.rb:98:in `take'
... 6 levels...
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.1.8/lib/rails/commands/console.rb:9:in `start'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:69:in `console'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.1.8/lib/rails/commands/commands_tasks.rb:40:in `run_command!'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/railties-4.1.8/lib/rails/commands.rb:17:in `<top (required)>'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:247:in `require'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:247:in `block in require'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:232:in `load_dependency'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:247:in `require'
    from /vagrant/bin/rails:8:in `<top (required)>'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:241:in `load'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:241:in `block in load'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:232:in `load_dependency'
    from /opt/rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/activesupport-4.1.8/lib/active_support/dependencies.rb:241:in `load'
    from /opt/rbenv/versions/2.1.5/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from /opt/rbenv/versions/2.1.5/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from -e:1:in `<main>'

Full query that triggered the exception:

SELECT  "collections"."id" AS t0_r0, "collections"."name" AS t0_r1, "collections"."icon" AS t0_r2, "collections"."image" AS t0_r3, "collections"."created_at" AS t0_r4, "collections"."updated_at" AS t0_r5, "collections"."color" AS t0_r6, "collections"."slug" AS t0_r7, "boxes"."id" AS t1_r0, "boxes"."actable_id" AS t1_r1, "boxes"."actable_type" AS t1_r2, "boxes"."position" AS t1_r3, "boxes"."in_home" AS t1_r4, "boxes"."featured" AS t1_r5, "boxes"."created_at" AS t1_r6, "boxes"."updated_at" AS t1_r7 FROM "collections" INNER JOIN "collection_translations" ON "collection_translations"."collection_id" = "collections"."id" LEFT OUTER JOIN "boxes" ON "boxes"."actable_id" = "collections"."id" AND "boxes"."actable_type" = 'Collection' AND "boxes"."in_home" = 't' WHERE "boxes"."collection_translations.slug" = 'autumn-winter' AND "collection_translations"."locale" = 'en' LIMIT 1

What the query looks like without acts_as:

SELECT  "collections".* FROM "collections" INNER JOIN "collection_translations" ON "collection_translations"."collection_id" = "collections"."id" WHERE "collection_translations"."slug" = 'autumn-winter' AND "collection_translations"."locale" = 'en' LIMIT 1
@lordofthelake
Copy link
Author

Seems related to the same issue encountered by @kidlab in #9.

My first guess is that something is going awry in lib/active_record/acts_as/querying.rb, where the ActiveRecord::QueryMethods#where override is performed.

I haven't fully made sense of the code yet, but my general impression is that the keys that should get prefixed don't get separated correctly from those that should remain instead unprefixed (lines 6 to 8).

EDIT
I haven't tried the code yet, but my best guess at this point is that line 6 should not just check for attribute_method? but also whether the key is an association.

Thus maybe it should become from:

opts, acts_as_opts = opts.stringify_keys.partition { |k,v| attribute_method?(k) }

to something along the lines of:

opts, acts_as_opts = opts.stringify_keys.partition { |k,v| attribute_method?(k) or reflections.has_key?(k) }

@lordofthelake
Copy link
Author

UPDATE:

I've made a couple of tests in my code, didn't have time to make a full PR.
This fixed the problem in my scenario:

opts, acts_as_opts = opts.stringify_keys.partition { |k,v| attribute_method?(k) or k.include?(".")}

The matter seems to be that opts had this form:

{"collection_translations.slug"=>"autumn-winter"}

attribute_method?("collection_translations.slug") would return false and thus the hash would be processed to become

{"boxes"=>{"collection_translations.slug"=>"autumn-winter"}}

@mbandrewfoster
Copy link

Just to further expand on this answer as I was having a similar issue with querying on associations in general (although I don't use the "." notation for my where clauses). I think it would be better if this statement was checking attribute_method? on the acting_as_model instance, not the specific instance. Like so:

acts_as_opts, opts = opts.stringify_keys.partition { |k,v| acting_as_model.attribute_method?(k) }

@manuelmeurer
Copy link
Collaborator

I'll close this for now. Please reopen (https://github.com/krautcomputing/active_record-acts_as/issues) if this is still an issue with the current version.

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

3 participants