LoginSignup
8
6

More than 5 years have passed since last update.

Authlogic使ってるモデルで認証に関係ないnullableなemailカラム追加したらvalidationエラーが出る件

Posted at

状況説明

ログインは Facebook アカウントの認証のみのつもり。
なので User モデルは実体がなく、ほかのモデルとのリレーションと、後は UserSession の管理だけの空っぽのモデル。

db/schema.rb
  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 

関連モデルもまだ作っていないので、本当にこれだけ。

app/models/user.rb
class User < ActiveRecord::Base
  acts_as_authentic
end

で、 "任意で構わないけど email を保存したい" という話になったので、 nullable な email カラムを追加。

db/schema.rb
  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 を見ると

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 とかあるので、それを見てみる。

authlogic/acts_as_authentic/email.rb
      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.rbacts_as_authentic メソッドがいる

authlogic/acts_as_authentic/base.rb
      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 を持っていて、個別に設定できる項目があるらしい。

authlogic/acts_as_authentic/email.rb
      # 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_fieldfalse に指定する。

app/models/user.rb
 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>

できた!

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6