私はacts-as-taggable-onに奇妙なバグを見つけたと思います。他の誰かがこのエラーを再現できますか?裸のRails3プロジェクトから始める:
~ [13:57:32]: cd Development/
~/Development [13:59:44]: rvm use 1.9.3
Using /Users/andywhite/.rvm/gems/ruby-1.9.3-p194
Running /Users/andywhite/.rvm/hooks/after_use
~/Development [13:59:54]: rails -v
Rails 3.2.8
~/Development [14:00:01]: rails new foo --skip-bundle
create
create README.rdoc
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
(snip)
create vendor/assets/javascripts/.gitkeep
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.gitkeep
create vendor/plugins
create vendor/plugins/.gitkeep
~/Development [14:00:21]: cd foo
~/Development/foo [14:01:26]: rvm --rvmrc --create 1.9.3@foo
~/Development/foo [14:02:38]: cd ..
~/Development [14:02:40]: cd -
/Users/andywhite/Development/foo
====================================================================================
= NOTICE =
====================================================================================
= RVM has encountered a new or modified .rvmrc file in the current directory =
= This is a shell script and therefore may contain any shell commands. =
= =
= Examine the contents of this file carefully to be sure the contents are =
= safe before trusting it! ( Choose v[iew] below to view the contents ) =
====================================================================================
Do you wish to trust this .rvmrc file? (/Users/andywhite/Development/foo/.rvmrc)
y[es], n[o], v[iew], c[ancel]> yes
~/Development/foo [14:02:44]: vi Gemfile
~/Development/foo [14:08:18]: cat Gemfile
source 'http://rubygems.org'
gem 'rails', '3.2.8'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'sqlite3'
# Gems used only for assets and not required
# in production environments by default.
group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
# gem 'therubyracer', :platforms => :ruby
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
# To use Jbuilder templates for JSON
# gem 'jbuilder'
# Use unicorn as the app server
# gem 'unicorn'
# Deploy with Capistrano
# gem 'capistrano'
# To use debugger
# gem 'debugger'
gem 'acts-as-taggable-on'
~/Development/foo [14:08:26]: bundle
Fetching gem metadata from http://rubygems.org/.........
Using rake (0.9.2.2)
Installing i18n (0.6.1)
Installing multi_json (1.3.6)
Installing activesupport (3.2.8)
Installing builder (3.0.3)
Installing activemodel (3.2.8)
Installing erubis (2.7.0)
Installing journey (1.0.4)
Installing rack (1.4.1)
Installing rack-cache (1.2)
Installing rack-test (0.6.2)
Installing hike (1.2.1)
Installing tilt (1.3.3)
Installing sprockets (2.1.3)
Installing actionpack (3.2.8)
Installing mime-types (1.19)
Installing polyglot (0.3.3)
Installing treetop (1.4.10)
Installing mail (2.4.4)
Installing actionmailer (3.2.8)
Installing arel (3.0.2)
Installing tzinfo (0.3.33)
Installing activerecord (3.2.8)
Installing activeresource (3.2.8)
Using bundler (1.2.0)
Installing rack-ssl (1.3.2)
Installing json (1.7.5) with native extensions
Installing rdoc (3.12)
Installing thor (0.16.0)
Installing railties (3.2.8)
Installing rails (3.2.8)
Installing acts-as-taggable-on (2.3.3)
Installing coffee-script-source (1.3.3)
Installing execjs (1.4.0)
Installing coffee-script (2.2.0)
Installing coffee-rails (3.2.2)
Installing jquery-rails (2.1.3)
Installing sass (3.2.1)
Installing sass-rails (3.2.5)
Installing sqlite3 (1.3.6) with native extensions
Installing uglifier (1.3.0)
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
Post-install message from rdoc:
Depending on your version of ruby, you may need to install ruby rdoc/ri data:
<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
~/Development/foo [14:08:58]: rails g model Post body
invoke active_record
create db/migrate/20120930130941_create_posts.rb
create app/models/post.rb
invoke test_unit
create test/unit/post_test.rb
create test/fixtures/posts.yml
~/Development/foo [14:09:41]: rails g acts_as_taggable_on:migration
create db/migrate/20120930131032_acts_as_taggable_on_migration.rb
~/Development/foo [14:10:31]: rake db:migrate
== CreatePosts: migrating ====================================================
-- create_table(:posts)
-> 0.0014s
== CreatePosts: migrated (0.0015s) ===========================================
== ActsAsTaggableOnMigration: migrating ======================================
-- create_table(:tags)
-> 0.0013s
-- create_table(:taggings)
-> 0.0012s
-- add_index(:taggings, :tag_id)
-> 0.0005s
-- add_index(:taggings, [:taggable_id, :taggable_type, :context])
-> 0.0007s
== ActsAsTaggableOnMigration: migrated (0.0043s) =============================
~/Development/foo [14:10:41]: vi app/models/post.rb
~/Development/foo [14:11:32]: cat app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :body, :tag_list
acts_as_taggable
end
~/Development/foo [14:11:42]: rails c
Loading development environment (Rails 3.2.8)
1.9.3-p194 :001 > p = Post.create :body => 'My hat blew off'
(0.1ms) begin transaction
SQL (217.4ms) INSERT INTO "posts" ("body", "created_at", "updated_at") VALUES (?, ?, ?) [["body", "My hat blew off"], ["created_at", Sun, 30 Sep 2012 13:12:28 UTC +00:00], ["updated_at", Sun, 30 Sep 2012 13:12:28 UTC +00:00]]
(2.8ms) commit transaction
=> #<Post id: 1, body: "My hat blew off", created_at: "2012-09-30 13:12:28", updated_at: "2012-09-30 13:12:28">
1.9.3-p194 :002 > p.tag_list = "news, boring, hats"
ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 1 AND "taggings"."taggable_type" = 'Post' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
=> "news, boring, hats"
1.9.3-p194 :003 > p.save
(0.1ms) begin transaction
(0.6ms) UPDATE "posts" SET "updated_at" = '2012-09-30 13:13:28.210803' WHERE "posts"."id" = 1
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" WHERE (lower(name) = 'news' OR lower(name) = 'boring' OR lower(name) = 'hats')
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'news' LIMIT 1
SQL (0.2ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "news"]]
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'boring' LIMIT 1
SQL (0.0ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "boring"]]
ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'hats' LIMIT 1
SQL (0.0ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "hats"]]
ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 1 AND "taggings"."taggable_type" = 'Post' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
ActsAsTaggableOn::Tagging Exists (0.2ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 1 AND "taggings"."taggable_type" = 'Post' AND "taggings"."taggable_id" = 1 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1
SQL (0.4ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Sun, 30 Sep 2012 13:13:28 UTC +00:00], ["tag_id", 1], ["taggable_id", 1], ["taggable_type", "Post"], ["tagger_id", nil], ["tagger_type", nil]]
ActsAsTaggableOn::Tagging Exists (0.1ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 2 AND "taggings"."taggable_type" = 'Post' AND "taggings"."taggable_id" = 1 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1
SQL (0.1ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Sun, 30 Sep 2012 13:13:28 UTC +00:00], ["tag_id", 2], ["taggable_id", 1], ["taggable_type", "Post"], ["tagger_id", nil], ["tagger_type", nil]]
ActsAsTaggableOn::Tagging Exists (0.1ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 3 AND "taggings"."taggable_type" = 'Post' AND "taggings"."taggable_id" = 1 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1
SQL (0.1ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Sun, 30 Sep 2012 13:13:28 UTC +00:00], ["tag_id", 3], ["taggable_id", 1], ["taggable_type", "Post"], ["tagger_id", nil], ["tagger_type", nil]]
(1.9ms) commit transaction
=> true
1.9.3-p194 :004 > p.tag_list
=> ["news", "boring", "hats"]
1.9.3-p194 :005 > Post.tag_counts
ActsAsTaggableOn::Tag Load (0.4ms) SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN posts ON posts.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Post' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT posts.id FROM "posts" )) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: taggings.tag_id: SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN posts ON posts.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Post' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT posts.id FROM "posts" )) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/sqlite3-1.3.6/lib/sqlite3/database.rb:91:in `initialize'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/sqlite3-1.3.6/lib/sqlite3/database.rb:91:in `new'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/sqlite3-1.3.6/lib/sqlite3/database.rb:91:in `prepare'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/sqlite_adapter.rb:246:in `block in exec_query'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activesupport-3.2.8/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/sqlite_adapter.rb:242:in `exec_query'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/sqlite_adapter.rb:467:in `select'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/database_statements.rb:18:in `select_all'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/connection_adapters/abstract/query_cache.rb:63:in `select_all'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/querying.rb:38:in `block in find_by_sql'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/explain.rb:40:in `logging_query_plan'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/querying.rb:37:in `find_by_sql'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:171:in `exec_queries'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:160:in `block in to_a'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/explain.rb:33:in `logging_query_plan'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:159:in `to_a'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/activerecord-3.2.8/lib/active_record/relation.rb:498:in `inspect'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/railties-3.2.8/lib/rails/commands/console.rb:47:in `start'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/railties-3.2.8/lib/rails/commands/console.rb:8:in `start'
from /Users/andywhite/.rvm/gems/ruby-1.9.3-p194@foo/gems/railties-3.2.8/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'1.9.3-p194 :006 >
1.9.3-p194 :007 > exit
したがって、*taggings.tag_id*列が欠落しているようです。それでは、sqlite3にアクセスして調査してみましょう。
~/Development/foo [14:14:39]: sqlite3 db/development.sqlite3
SQLite version 3.7.14 2012-09-03 15:42:36
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .headers on
sqlite> select * from taggings;
id|tag_id|taggable_id|taggable_type|tagger_id|tagger_type|context|created_at
1|1|1|Post|||tags|2012-09-30 13:13:28.357091
2|2|1|Post|||tags|2012-09-30 13:13:28.360285
3|3|1|Post|||tags|2012-09-30 13:13:28.362351
sqlite> .schema taggings
CREATE TABLE "taggings" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "tag_id" integer, "taggable_id" integer, "taggable_type" varchar(255), "tagger_id" integer, "tagger_type" varchar(255), "context" varchar(128), "created_at" datetime);
CREATE INDEX "index_taggings_on_tag_id" ON "taggings" ("tag_id");
CREATE INDEX "index_taggings_on_taggable_id_and_taggable_type_and_context" ON "taggings" ("taggable_id", "taggable_type", "context");
sqlite>
うーん。だからコラムがあります!?問題のあるSQLを切り取って貼り付けて、同じ列の欠落エラーが発生することを確認しましょう。
sqlite> SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN posts ON posts.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Post' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT posts.id FROM "posts" )) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id;
id|name|count
1|news|1
2|boring|1
3|hats|1
sqlite> .exit
~/Development/foo [14:17:25]:
なに……?ここで何が起こっているのか誰か知っていますか?私は本当に明白な何かを逃したことがありますか?
更新(その日遅く):回避策を見つけました。Postsの*tag_counts*クラスメソッドを次のようにオーバーライドします。
def self.tag_counts
ActsAsTaggableOn::Tag.select("tags.*, count(taggings.tag_id) as count").
joins(:taggings).group("taggings.tag_id")
end
この方法の出発点として、ライアン・ベイツと彼の優れたRailscastsエピソードに感謝します。ただし、他の誰かがエラーを再現できるかどうかを確認することに関心があります。