状況説明
ログインは Facebook アカウントの認証のみのつもり。
なので User モデルは実体がなく、ほかのモデルとのリレーションと、後は UserSession の管理だけの空っぽのモデル。
create_table "users", force: true do |t|
t.string "persistence_token", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
関連モデルもまだ作っていないので、本当にこれだけ。
class User < ActiveRecord::Base
acts_as_authentic
end
で、 "任意で構わないけど email を保存したい" という話になったので、 nullable な email カラムを追加。
create_table "users", force: true do |t|
t.string "persistence_token", null: false
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
そうしたら email が空だとバリデーションエラーになって、保存できなくなった。
> User.create!
(0.2ms) BEGIN
User Exists (0.3ms) SELECT 1 AS one FROM "users" WHERE "users"."persistence_token" = '689...' LIMIT 1
(0.2ms) ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Email should look like an email address.
え、いやいやいやいや。
そんな validation 作ってないし!
nullable だし!
User モデルだって空っぽだし!
って混乱したんだけれども。
どうも Authlogic を使うための acts_as_authentic
が怪しい。というか User モデルにはそれしかないし。
という調査内容と、回避方法についての話。
前置き完了。
これの調査した時は、 authlogic 3.4.2 でした。
Rails は 4.2.0 系になってたかな……(うろ覚え)。
原因
authlogic/acts_as_authentic/base.rb
を見ると
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::Base
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::Email
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::LoggedInStatus
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::Login
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::MagicColumns
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::Password
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::PerishableToken
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::PersistenceToken
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::RestfulAuthentication
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::SessionMaintenance
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::SingleAccessToken
::ActiveRecord::Base.send :include, Authlogic::ActsAsAuthentic::ValidationsScope
なんかすごい勢いで include されてました。
Authlogic::ActsAsAuthentic::Email
とかあるので、それを見てみる。
module Methods
def self.included(klass)
klass.class_eval do
if validate_email_field && email_field
validates_length_of email_field, validates_length_of_email_field_options
validates_format_of email_field, validates_format_of_email_field_options
validates_uniqueness_of email_field, validates_uniqueness_of_email_field_options
end
end
end
end
おまえたちか!
回避方法を探る
authlogic/acts_as_authentic/base.rb
に acts_as_authentic
メソッドがいる
module Config
# This includes a lot of helpful methods for authenticating records which The Authlogic::Session module relies on.
# To use it just do:
#
# class User < ActiveRecord::Base
# acts_as_authentic
# end
#
# Configuration is easy:
#
# acts_as_authentic do |c|
# c.my_configuration_option = my_value
# end
#
# See the various sub modules for the configuration they provide.
def acts_as_authentic(unsupported_options = nil, &block)
# Stop all configuration if the DB is not set up
return if !db_setup?
if !unsupported_options.nil?
raise ArgumentError.new(
"You are using the old v1.X.X configuration method for Authlogic. Instead of passing a hash of " +
"configuration options to acts_as_authentic, pass a block: acts_as_authentic { |c| c.my_option = my_value }"
)
end
yield self if block_given?
acts_as_authentic_modules.each { |mod| include mod }
end
# (略)
unsupported_options
ってあるから一瞬喜んだんだけど、これは古いバージョンの書き方で、今はブロックを使えよってことらしい。
authlogic/acts_as_authentic/email.rb
を見てみると、こっちも Config を持っていて、個別に設定できる項目があるらしい。
# Configuration to modify how Authlogic handles the email field.
module Config
# The name of the field that stores email addresses.
#
# * <tt>Default:</tt> :email, if it exists
# * <tt>Accepts:</tt> Symbol
def email_field(value = nil)
rw_config(:email_field, value, first_column_to_exist(nil, :email, :email_address))
end
alias_method :email_field=, :email_field
# Toggles validating the email field or not.
#
# * <tt>Default:</tt> true
# * <tt>Accepts:</tt> Boolean
def validate_email_field(value = nil)
rw_config(:validate_email_field, value, true)
end
alias_method :validate_email_field=, :validate_email_field
# (略)
なるほど。
email
とか email_address
というカラムがあると、勝手に email 用のバリデーションしてくれると。
email_field
に nil を指定しても、デフォルトのカラム名が使われてしまうので、今回のケースにそちらは仕えない。
(例えば main_address
みたいなカラムに email を保存したいという時に、こちらの設定が使える)
validate_email_field
はデフォルトが true になってるけれども、これが false になっていれば、今は必要ないバリデーションを動作させずに済みそう。
回避!
acts_as_authentic
にブロックを渡して validate_email_field
を false
に指定する。
class User < ActiveRecord::Base
- acts_as_authentic
+ acts_as_authentic do |config|
+ config.validate_email_field = false
+ end
これで。
pry> User.create!
(0.1ms) BEGIN
User Exists (0.6ms) SELECT 1 AS one FROM "users" WHERE "users"."persistence_token" = '301...' LIMIT 1
SQL (0.5ms) INSERT INTO "users" ("created_at", "persistence_token", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["created_at", "2014-10-03 03:52:22.984187"], ["persistence_token", "301..."], ["updated_at", "2014-10-03 03:52:22.984187"]]
(0.3ms) COMMIT
=> #<User:0x007ffbdfd1bb30
id: 1,
created_at: Fri, 03 Oct 2014 03:52:22 UTC +00:00,
updated_at: Fri, 03 Oct 2014 03:52:22 UTC +00:00,
persistence_token: "3011...",
email: nil>
できた!