简体   繁体   中英

NoMethodError - undefined method `email' for #<ActionDispatch::

Devise 3.5.2, Rails 4.2.3

While logging in, I'm trying to pass a hidden role_id along with the email/password combination. I am allowing the same email to register again, on a different subdomain, which causes a different role_id to be passed. The email+role_id is the unique index for the user.

I can create a user, but cannot log in. When I submit the log in form, I am faced with the following error:

undefined method 'email' for #<ActionDispatch::Request:0x007fa21628bda0>

EDIT:

If anyone can explain the process of changing the email uniqueness validation to email+role_id (not either/or, but and), that's all I need to accomplish. Following that process properly may avoid this error.

Debugging info:

The POST parameters are as follows:

{"utf8"=>"✓",
 "authenticity_token"=>"[FILTERED]",
 "member"=>{"role_id"=>"1",
 "email"=>"some.user@email.com",
 "password"=>"[FILTERED]",
 "remember_me"=>"0"},
 "commit"=>"Log in"}

Here is my Member model:

class Member < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         :confirmable, :lockable, :timeoutable, :omniauthable

  belongs_to :role

  def self.find_for_authentication(warden_conditions)
    where(:email => warden_conditions[:email], :role_id => warden_conditions[:role_id]).first
  end
end

In config/initializers/devise.rb , the following is set:

config.authentication_keys = [:email, :role_id]

config.request_keys = [:email, :role_id]

My views/devise/sessions/new.html.erb includes:

<%= f.hidden_field :role_id, :value => Role.find_by_name(current_subdomain).id %>

I adjusted vendor/bundle/ruby/1.9.1/gems/devise-3.5.2/lib/devise/models/validatable.rb by changing this line:

validates_uniqueness_of :email, allow_blank: true, if: :email_changed?

to:

validates_uniqueness_of :email, :scope => :role_id, allow_blank: true, if: :email_changed? #using subdomains for validation

The relevant database migrations for the member are found here:

...devise_create_members.rb

class DeviseCreateMembers < ActiveRecord::Migration
  def change
    create_table(:members) do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      t.string   :confirmation_token
      t.datetime :confirmed_at
      t.datetime :confirmation_sent_at
      t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      t.string   :unlock_token # Only if unlock strategy is :email or :both
      t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :members, :email,                unique: true
    add_index :members, :reset_password_token, unique: true
    add_index :members, :confirmation_token,   unique: true
    add_index :members, :unlock_token,         unique: true
  end

...add_columns_to_member.rb

class AddColumnsToMember < ActiveRecord::Migration
  def change
    add_reference :members, :contact, index: true
    add_reference :members, :role, index: true
    add_reference :members, :ownership, index: true
    add_column :members, :account_status, :string
  end
end

...reindex_members_email_and_role.rb

class ReindexMembersEmailAndRole < ActiveRecord::Migration
  def change
    add_index :members, [:email, :role_id], :unique => true
  end
end

The last item on the trace is:

vendor/bundle/ruby/1.9.1/gems/devise-3.5.2/lib/devise/strategies/authenticatable.rb:152:in `block in request_values'

    keys = request_keys.respond_to?(:keys) ? request_keys.keys : request_keys
    values = keys.map { |k| self.request.send(k) } <--ERROR THIS LINE
    Hash[keys.zip(values)]
  end

What am I missing?

To fix this, I changed my config/initializers/devise.rb to reflect the following:

config.request_keys = { role_id: false }

This fixed the issue, but still prevented the same email from signing up with a different role ID. To fix this, I removed :validatable from my User model and added:

  validates_uniqueness_of   :email,    :case_sensitive => false, :scope => :role_id, :allow_blank => true, :if => :email_changed?
  validates_format_of       :email,    :with  => Devise.email_regexp, :allow_blank => true, :if => :email_changed?
  validates_presence_of     :password, :on=>:create
  validates_confirmation_of :password, :on=>:create
  validates_length_of       :password, :within => Devise.password_length, :allow_blank => true

This allows the same email address to sign up with a different role_id.

I also changed the following in authenticatable.rb:

      def request_values
        keys = request_keys.respond_to?(:keys) ? request_keys.keys : request_keys
        values = keys.map { |k| self.request[self.scope][k] }
#        values = keys.map { |k| self.request.send(k) }
        Hash[keys.zip(values)]
      end

UPDATE

I got tired of always having to re-hack the devise library, especially after I updated gems or transferred the app. I found this page that offered a better work-around (still follow the step regarding validations the User model listed above):

(From https://github.com/plataformatec/devise/pull/3965 )

Comment out the following line we edited above:

# config.request_keys = { role_id: false }

Edit the config.authentication_keys line as follows:

config.authentication_keys = { email: true, role_id: false }

The issue is that request_keys honors only predefined keys such as :subdomain .

That should work now for creating a combination of custom keys to authenticate with.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM