Динамический attr_accessible в рельсах

В соответствии с рельсами № 237, динамические атрибуты должны были быть легко реализованы. Хотя я столкнулся с некоторыми ошибками при попытке создать объект в консоли rails. Пожалуйста, порекомендуйте.

Я получаю следующую ошибку:

ruby-1.9.3-p0 :005 > User.new :username => "johnsmith", :email => "johnsmith@gmail.com", :password => "changethis"
ArgumentError: wrong number of arguments (1 for 0)
    from /Volumes/Terra-Nova/jwaldrip/Sites/theirksome/config/initializers/accessible_attributes.rb:6:in `mass_assignment_authorizer'
    from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/activemodel-3.1.3/lib/active_model/mass_assignment_security.rb:209:in `sanitize_for_mass_assignment'
    from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.1.3/lib/active_record/base.rb:1744:in `assign_attributes'
    from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/activerecord-3.1.3/lib/active_record/base.rb:1567:in `initialize'
    from (irb):5:in `new'
    from (irb):5
    from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.1.3/lib/rails/commands/console.rb:45:in `start'
    from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.1.3/lib/rails/commands/console.rb:8:in `start'
    from /Volumes/Terra-Nova/jwaldrip/.rvm/gems/ruby-1.9.3-p0/gems/railties-3.1.3/lib/rails/commands.rb:40:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

/models/user.rb:

class User < ActiveRecord::Base

    # Attributes
    attr_accessible :username, :email, :password, :password_confirmation, :is_admin
    attr_accessor :password

    # Callbacks
    before_save :encrypt_password

    # Relationships
    has_many :irks

    # Validation
    validates_confirmation_of :password
    validates_presence_of :password, on: :create
    validates :password, presence: true, length: { in: 3..20 }

    validates :username, presence: true, uniqueness: true, length: { in: 3..20 }
    validates :email, presence: true, email: true, uniqueness: true

    # User Authentication
    def self.authenticate(email, password)
        user = find_by_email(email)
        if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
            user
        else
            nil
        end
    end

    # Password Encryption
    def encrypt_password
        if password.present?
            self.password_salt = BCrypt::Engine.generate_salt
            self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
        end
    end
end

/config/initializers/accessible_attributes.rb:

class ActiveRecord::Base
    attr_accessible
    attr_accessor :accessible

    private

    def mass_assignment_authorizer
        if accessible == :all
            self.class.protected_attributes
        else
            super + (accessible || [])
        end
    end
end

1 ответ

Решение

Не совсем уверен, что именно вы пытаетесь сделать или какова цель этого mass_assignment_authorizer. Похоже, есть более простые способы защиты от массовых назначений. При этом я прочитал последние пара абзацев Railscast, и кажется, что, получив этот инициализатор, вы не можете передавать аргументы в инициализатор при создании объекта. Даже если бы ты мог, он не установил бы атрибуты...

В контроллере нам также нужно применить доступную опцию к действию create. Если мы просто применим это, то это не будет работать.

@article = Article.new(params[:article])
@article.accessible = :all if admin?

Причина, по которой это не работает, заключается в том, что массовое назначение происходит в новом вызове, поэтому к тому моменту, когда мы установили доступ, уже слишком поздно. Нам нужно отделить создание новой статьи от присвоения ее атрибутов и перебросить вызов между ними.

Мне кажется, что для того, чтобы установить атрибуты для одной из ваших моделей, вам нужно сначала создать ее, а затем установить доступный для :all в классе, затем вручную назначьте нужные атрибуты, например:

u = User.create
u.accessible = :all if current_user.is_admin? # or whatever the conditional is for the admin user
u.update_attributes(:username => "johnsmith", :email => "johnsmith@gmail.com", :password => "changethis")

В зависимости от того, сколько атрибутов вам нужно иметь доступными в зависимости от разрешений, вам может быть лучше пропустить этот модуль, поскольку для его реализации требуется немного больше работы. Если в одной или двух моделях всего несколько атрибутов, вам лучше всего реализовать эту функцию вручную с помощью собственных методов и attr_accessible. Попробуйте прочитать эту статью о средствах доступа ruby, чтобы узнать, сможете ли вы получить желаемый результат без этого плагина?

Другие вопросы по тегам