views:

45

answers:

5

I have a model User and when I create one, I want to pragmatically setup some API keys and what not, specifically:
@user.apikey = Digest::MD5.hexdigest(BCrypt::Password.create("jibberish").to_s)

I want to be able to run User.create!(:email=>"[email protected]") and have it create a user with a randomly generated API key, and secret.

I currently am doing this in the controller, but when I tried to add a default user to the seeds.rb file, I am getting an SQL error (saying my apikey is null).

I tried overriding the save definition, but that seemed to cause problems when I updated the model, because it would override the values. I tried overriding the initialize definition, but that is returning a nil:NilClass and breaking things.

Is there a better way to do this?

+1  A: 

What if, in your save definition, you check if the apikey is nil, and if so, you set it?

notJim
Is the `save` def called when `create!` is run? (Sorry, ruby noob)
webdestroya
A: 
class User
    def self.create_user_with_digest(:options = { })
        self.create(:options)
        self.apikey = Digest::MD5.hexdigest(BCrypt::Password.create("jibberish").to_s)
        self.save
        return self
    end
end

Then you can call User.create_user_with_digest(:name => "bob") and you'll get a digest created automatically and assigned to the user, You probably want to generate the api key with another library than MD5 such as SHA256 you should also probably put some user enterable field, a continuously increasing number (such as the current date-time) and a salt as well.

Hope this helps

ThinkBohemian
+1  A: 

Have a look at ActiveRecord::Callbacks & in particular before_validation.

theIV
What I am trying to do is be able to pass an email to a new user object, and then create it. I want the model itself to generate an APIkey and a Secret, so that anytime I create a user, I know that the API key will be there.
webdestroya
I understand that. My point in the comment to Tadas' response is that if you are not accepting Users that have no apikey, you need to do it before_validation, and not before_create because before_create happens after validation.
theIV
+4  A: 

use callbacks and ||= ( = unless object is not nil ) :)

class User < ActiveRecord::Base
  before_create :add_apikey #or before_save

  private
  def add_apikey
    self.apikey ||= Digest::MD5.hexdigest(BCrypt::Password.create(self.password).to_s)
  end
end

but you should definitely take a look at devise, authlogic or clearance gems

Tadas Tamosauskas
I think `before_create` could be good but if he's validating presence of apikey, he'll need to do it in `before_validation`.
theIV
If I do `User.new(params)` then `@user.save` is the `before_create` filter run?
webdestroya
Yes, because the save is really acting as a create in this instance since `@user.new_record?` will return true.
theIV
Thanks! I ended up going with this solution, it seems to be fairly elegant and worked great! Thanks!
webdestroya
@webdestroya: What if two users have the same password?
Ryan Bigg
@Ryan Bigg - Im not sure what you mean.. I am not using a password, it is just an APIKey used to authenticate. The APIkeys are checked to be unique
webdestroya
@webdestroya: The example shows `self.password`. I assume BCrypt doesn't do anything special for the same value, so if two users have the same password it's going to generate the same apikey for them.
Ryan Bigg
@Ryan Bigg - No, everytime you run Bcrypt it will give you a new hash the salt just adds extra randomness
webdestroya
A: 

I believe this works... just put the method in your model.

def apikey=(value)  
  self[:apikey] = Digest::MD5.hexdigest(BCrypt::Password.create("jibberish").to_s)  
end
j.
I still need to be able to populate the user from the database, which stores the APIkey... will this solution present a problem, when I essentially try to assign a value to a function?
webdestroya