How to generate unique anonymous usernames in RoR? - ruby-on-rails

I'd like to generate unique, anonymous usernames when creating an instance of my User model. Ideally, it would be set by default if I don't specify any username.
So for example when I do User.create!() I'd like to have the username column set to 'anonymous123', where 123 is a unique number taken from a sequence (the value of the id column for that instance would be ok).

Combining the examples from a couple of the answers would do it
class User < ActiveRecord::Base
before_create :set_anonymous_username
def set_anonymous_username
username = "anonymous#{User.last.id + 1}" if username.nil?
end
end
I have used before_create here because I would expect you only to have to set an anonymous username before you create the User for the first time. The before_save will be called every time the model is saved and could be unnecessary overhead, but either will work

Use a before_save filter to set the username before the model is saved, e.g.
class User < ActiveRecord::Base
before_save :set_anonymous_username
def set_anonymous_username
username = "anonymous" + User.find_by_sql("SELECT max(id) FROM users").maxid.to_s if username.nil?
end
end

Something like
class User < ActiveRecord::Base
def initialize
super
self.username = "anonymous#{User.last.id + 1}"
end
end
Will do it when you create a new instance of User, even before it's saved - in case you want to use it in a view pre-creation. You probably want to combine it with something before_save as mentioned above to get the definitive last id in the database at the time of record creation.

Related

Methods vs Attributes Rails

Was debating with a colleague of mine tonight on methods vs attributes in Rails. I swear I have seen this before unless I am crazy. Here is an example:
We have a user model and we want to be certain we have a first name and a last name prior to saving the record. I hate callbacks, but for this example, let's say it is in a before_save callback.
class User
before_save :set_first_last
def set_first_last
first_name if self.first_name.blank?
last_name if self.last_name.blank?
end
def first_name
self.first_name = self.name.split(" ").first
end
def last_name
self.last_name = self.name.split(" ").last
end
end
So, I am curious if the method "set_first_last" name is called and it sees that the attribute first_name on user is blank it will call the method "first_name", correct? The attribute first_name on user is different than a method called "first_name." So an attribute on the user object called "first_name" would be different than a method on the user class called "first_name" in Rails, correct? This would be like a class level method vs an instance method? Just want to be sure I am correct and that I am explaining this correctly.
thanks
--Mike
I think you are misreading this bit of the code:
def set_first_last
first_name if self.first_name.blank?
last_name if self.last_name.blank?
end
This code calls the first_name method, and if it is blank, calls the first_name method again (in other words the if parts of this method are pointless)
The way this code is constructed, the value of the first_name and last_name attributes will never be used (other than if you were to use them in your queries) - any time you do user.first_name it will reconstruct it from name. A more common pattern would be to have the methods that set the names called something like ensure_first_name (Basically anything that doesn't class with the getter)
Instance variables (eg #foo) are distinct from methods of the same name, although it is of course common for foo and foo= to be the methods for getting/setting an instance variable of that name. Perhaps you were thinking of that? In an event the attributes of an Activerecord model aren't stored in individual instance variables.
To answer the part I think I understand: yes, an attribute on a model is quite different than a method.
Take, for example, a User model, a Users controller, and one of its corresponding views. Let's pretend the User model itself contains first_name, last_name, and email properties:
class User < ActiveRecord::Base
#Ensure presence of the following before saving model
validates_presence_of :first_name, :last_name, :email
#Verify unique email
validates_uniqueness_of :email
#Here's our lone User method...poor guy
def admin?
self.company_members.where('? = ANY (company_members.roles)', Role::ADMIN).any?
end
end
When you get around to working with an instance of that model in your controller, it'll look something like this:
class UsersController < ApplicationController
def index
#Let's just grab a random user
#user = User.find(1)
end
end
Finally, in your view, your properties (attributes) can be accessed in the following way:
####views/users/index.html.haml####
.random-class
= "You're logged in as #{#user.first_name}!"
But we can also call User methods in the view as well. Continuing off our last example, we could add something like this:
####views/users/index.html.haml####
.random-class
= "You're logged in as #{#user.first_name}!"
- if #user.admin?
= 'Oh my gosh, our freaking User method worked!'
So in short, you're correct in that properties and methods are very different, although they look similar at times.
Lastly, it's worth pointing out that instance methods are just that: methods called on instances of a class, whereas class methods are called on an actual model object, and not a singular instance of that model object. Here's a great link to learn more about the differences.
Hope this helps!
Yes, what you explained is correct.
Your piece of coding is going to work and assign the first_name & last_name with the splitted form.
further you can write your code in this way as well (assuming your full name consists of 2 words separated by space)
#user.rb
class user
before_save :assign_individual_names
private
def assign_individual_names
first_name, last_name = name.split(' ')
end
end

Rails Counter Cache

I have a db schema with multiple counter caches. Rather than have the counter caches spread across many tables, I'd like to have a table called user_counters with all of the various counter caches. I'd update these caches from a service object with a method like this:
def update_user_counter(column, user_id)
UserCounter.increment_counter(column, user_id)
end
But, the UserCounter record for the associated user will most likely not have the same ID as the user. What I'd like to do is something like this:
def update_user_counter(column, user_id)
UserCounter.increment_counter(column, UserCounter.where('user_id = "#{user_id}"')
end
Any thoughts as to how I can accomplish this? Thanks!!
If these are 10 attributes associated with a User, is there any reason to not simply make them a part of the User model? If that is the case, the below code will still work (just make the method part of User instead of UserCounter).
If you want to implement it your way, I'd say first make sure that your user_counters table has an index on user_id (since Rails is dumb about adding indexes and FK's).
Then, you could do this (passing in "column" as a symbol):
class UserCounter < ActiveRecord::Base
#other code
def self.update_user_counter(column, user_id)
counter = UserCounter.find_by_user_id(user_id)
counter.update_attribute(column, counter.read_attribute(column) + 1)
counter.save!
end
end
And in your other model:
class SomeOtherModel < ActiveRecord::Base
#Model code
def update_counter(user)
UserCounter.update_user_counter(:this_column, user.id)
end
end
This solution (or using the update_counter method) both require two database hits, one for the lookup and one for the update. If you need the speed, you could write this directly in SQL:
query = "UPDATE user_counters SET #{column} = #{column} + 1 WHERE user_id = #{user_id};"
ActiveRecord::Base.connection.execute(query);
This assumes that user_id is unique on user_counters.

Single Table Inheritance with Conditions

I have model User and model Recruiter. Currently, these are two separate tables, but I want to make them one.
Current:
User: id, username, password, name
Recruiter: id, user_id
Ideal:
User: id, username, password, role (recruiter, admin)
I understand the basics of STI. What I'm wondering is, when I perform methods on the new Recruiter controller (that inherits from User) how do I make sure all my methods are calling on users that are only a recruiter? Thus, queries along the lines of... SELECT * FROM users WHERE role = 'recruiter' for everything.
That is something rails takes care of for you, out of the box. You do not have to manually query on a particular type of user, just query on the right model.
I must also mention that by default rails assumes that your sti_column is called type, but can be overridden to role easily.
Let's admit you have your 2 classes:
class User < ActiveRecord::Base
end
class Recruiter < User
end
Rails will automagically add a type column in the users table so that in your controller, if you do something like this:
class RecruitersController < ApplicationController
def index
#recruiters = Recruiter.all
end
end
Rails will automatically fetch the records with type = 'Recruiter' and you don't even have to set this manually. If you do:
Recruiter.new(name: 'John').save
A new User will be created in database with the field type set to 'Recruiter'.
you would define your models something like this:
class User < ActiveRecord::Base
...
end
class Recruiter < User
...
def initialize
# ... special initialization for recruiters / you could put it here
super
end
...
end
and to create a new recruiter, you would do this:
Recruiter.create(:name => "John Smith")
and because of the type attribute in the STI user table (set to 'Recruiter'), the record will be for a recruiter.
You could put the special initialization for the STI models either in the model's initializer, or in a before filter with a if-cascade checking the type.
An initializer is probably much cleaner.
Have you tried has_one association?
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one

Ruby on Rails 3 - create an instance of one model from another model

I have the following problem, but first I will make some assumptions
The example is just to explain my problem in an easy way
The tables models are not related
I have two tables (models) Users an Emails
Users
id
name
email
Emails
id
account
So, the idea is every time I create a user, I want to create an instance of Email, where Emails.account = Users.email
I tried using callback
def after_create
Email.create!(:account => user.email)
end
But it didn't work.
Is there another way to achieve this?
You are almost there, except you don't need to reference a 'user' variable in your after_create because you are in the User model.
Try the following:
class User < ActiveRecord::Base
after_create :create_email
def create_email
Email.create!(:account => email)
end
end

virtual attribute in a query

User model has all_scores attribute and i created the method below
models/user.rb
def score
ActiveSupport::JSON.decode(self.all_scores)["local"]
end
What i'm trying to do this using this virtual attribute score to filter users. For example:
I need the users whose score is over 50. The problem is i can't use virtual attribute as a regular attribute in a query.
User.where("score > 50") # i can't do this.
Any help will be appreciated.
Well, the "easiest" solution would probably be User.all.select{|user| user.score > 50}. Obviously that's very inefficient, pulling every User record out of the database.
If you want to do a query involving the score, why don't you add a score column to the users table? You could update it whenever all_scores is changed.
class User < AR::Base
before_save :set_score, :if => :all_scores_changed?
protected
def set_score
self.score = ActiveSupport::JSON.decode(self.all_scores)["local"] rescue nil
end
end
This will also avoid constantly deserializing JSON data whenever you access user#score.

Resources