简体   繁体   中英

Dynamic attr_accessible in rails

According to the rails-cast #237 , dynamic attributes were to be easily implemented. Although I have run into some errors when trying to create an object in the rails console. Please advise.

The error I am getting is as follows :

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

Not entirely sure exactly what it is you're trying to do or what the purpose of this mass_assignment_authorizer would be. Seems like there are easier ways to protect against mass assignment. That being said, I read the last couple paragraphs of the railscast , and it appears as though once you have this initializer, you can't pass any arguments into the initializer when creating an object. Even if you could, it wouldn't set the attributes...

In the controller we also need to apply the accessible option to the create action. If we just apply it like this then it will not work.

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

The reason that this doesn't work is that the mass assignment happens in the new call so by the time we've set accessible it's too late. We need to separate creating a new Article from assigning its attributes and slip the call to accessible in between the two.

So it looks to me like in order to set the attributes for one of your models now you need to first create it, then set accessible to be :all on the class, then manually assign the attributes you want, like such:

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")

Depending on how many attributes you need to have accessible based on permissions, you may be better off skipping this module since it is a little bit of extra work to implement. If it's only a few attributes on one or two models you may be better off just implementing this functionality by hand with your own methods and attr_accessible. Try reading this article about ruby accessors to see if you can get the desired result without this plugin perhaps?

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