Ruby on Rails - Optional Associations? - ruby-on-rails

I would like to allow users to write comments on a site. If they are registered users their username is displayed with the comment, otherwise allow them to type in a name which is displayed instead.
I was going to create a default anonymous user in the database and link every non-registered comment to that user. Would there be a better way to do it?
Any advice appreciated.
Thanks.

The problem with creating an anonymous user is then you need to check if a comment was made by a "real" user, or an anonymous one when displaying the name, so that introduces complexity. Plus, if you have a way of viewing their profile page, which may include posting history, you'd need to exclude the anonymous user with an exception.
Generally it's better to have a column on your comments which represents the user's visible name, and just show that if provided, or the registered user's name otherwise. For instance, your view helper might look like this:
class Comment < ActiveRecord::Base
belongs_to :user
def user_name
self.anonymous_name or (self.user and self.user.name) or 'Anonymous'
end
end
This will display the contents of the anonymous_name field of the Comment record, or the user's name if a user is assigned, or 'Anonymous' as a last-ditch effort to show something.
Sometimes it's advantageous to actually de-normalize a lot of the database when dealing with large numbers of comments so you don't have to load in the user table via a join simply to display a name. Populating this field with the user's name, even if they're not anonymous, may help with this, though it does mean these values need to be updated when a username changes, presuming that's even possible.

I think you can make user_id on your comment model nullable since you want to allow non registered users to add comments as well. As far as adding names for the non registered users are concerned, there are two options for that
option 1. Add a column on Comment model and name it like anonymous_user where you will store names of non registered users
option 2. Create a another model AnonymousCommentor with name and comment_id attributes.
If you are going to use anonymous users for other things as well apart from comment in your application then you can make it polymorphic and use a suitable name like AnonymousUser instead of AnonymousCommentor

Related

Allowing admin and users to sign in using same form (rails)

I've created 2 tables, one for users and one for admins.
I created 2 tables as they both collect different information, but I want to be able to allow a sign in using an email address and password from both the admin and user tables via the same form.
Is this possible? I've looked around and people seem to have created 1 users table and added an admin boolean, but I wanted to avoid this and I didn't want to collect unnecessary data if I didn't need to.
Any help and assistance about how to best go around this would be great.
If you are implementing something from scratch, then it is simply a matter of coding it. I think this approach has some inherent flaws and I would avoid it.
If you want to have some segregation on the model side of things, I suggest you use STI. That way there is some shared behaviour/attributes and the distinctions can be coded separately, so you have your protection.
If you have plenty of distinct attributes, I would suggest separating them from your user/admin and creating an "admin_profile" model that belongs_to :admin and a "user_profile" that belongs_to :user.
And to make coding "transparent", you can create accessors in your admin model class to get/set the profile attributes seamlessly. Say you have an is_cool attribute on the admin_profile model, but you'd like to access it as
imadmin.is_cool
You can have in your admin.rb model
has_one :admin_profile
def is_cool
self.admin_profile.is_cool
end
be careful cause the has_one relationship may return nil if there is no profile associated with the admin/user.

Add 'current_user' to a model. (Devise) Rails

Hi I'm new here and also new in rails.
I want to add a couple values by default to a database called books (Model: Book.erb)
there is a user who creates these books(current_user), and I thought that a way to identify who creates and deletes this content is by adding some default values from the user and clasificate them (to be specific username and password)
my table ":books" has available two fields for adding username and password
I tried to do this:
# Book.erb
class Book < ActiveRecord::Base
after_save :set_default_values
def set_default_values
self.username = current_user.username
self.password = current_user.encrypted_password
end
end
but it seems to be that I can't call 'current_user' from this model.
I was reading a pratice on this blog
but some were saying that this method violates the MVC pattern, do you agree with them?
do you guys know a better way to do this process without violating the pattern?
Well, I'm not sure I can conceive of why you'd want to store a user name and user password in a book table as even if it was easily explained, it would be in violation of normalization practices for good database design which pretty much states you should only express a field once and then share it where it needs to be shared.
Now, assuming you must do this for some reason I can't conceive, I'd have to ask if "username" is your actual field or is it just "name" which is more standard Rails. And, I believe you'll have to have a relationship between these models to pull the data from one into the other and I don't see that book has_many users or one or belongs_to or anything of that sort.
With a relationship between book and user you have access to all user properties without writing them anywhere other than the user table. So I think you probably want to look at that.

Comment belongs_to a deleted user. How to associate a "placeholder"?

My comments are placed by users. The Comment belongs_to :user and the User has_many :comments.
But users can be removed. If done, I'd rather not delete their comments, but instead associate their comments with one Dummy user.
I can think of several ways:
On load of a comment, if no associated user is found, create a User in memory with dummy data.
On load of a comment, if no associated user is found, pick a predefined one from the database.
On removal of a comment, associate all the comments with a predefined User in the database; through some post filter.
My feeling says that number one is cleanest; since the other two require a user in the database that will be hardwired in the code. If user 18394 will be that "special" user, I'd need all kinds of safetynets for that special user.
What about soft deleting users instead? Have a boolean field called User.active and set a default scope for User.active = t. When a user gets deleted, set the active field to false, and clear out any personal data.

Rails: Multiple "types" of one model through related models?

I have a User model in my app, which I would like to store basic user information, such as email address, first and last name, phone number, etc.
I also have many different types of users in my system, including sales agents, clients, guests, etc.
I would like to be able to use the same User model as a base for all the others, so that I don't have to include all the fields for all the related roles in one model, and can delegate as necessary (cutting down on duplicate database fields as well as providing easy mobility from changing one user of one type to another).
So, what I'd like is this:
User
-- first name
-- last name
-- email
--> is a "client", so
---- client field 1
---- client field 2
---- client field 3
User
-- first name
-- last name
-- email
--> is a "sales agent", so
---- sales agent field 1
---- sales agent field 2
---- sales agent field 3
and so on...
In addition, when a new user signs up, I want that new user to automatically be assigned the role of "client" (I'm talking about database fields here, not authorization, though I hope to eventually include this logic in my user authorization as well). I have a multi-step signup wizard I'm trying to build with wizardly. The first step is easy, since I'm simply calling the fields included in the base User model (such as first_name and email), but the second step is trickier since it should be calling in fields from the associated model (like--per my example above--the model client with fields client_field_1 or client_field_2, as if those fields were part of User).
Does that make sense? Let me know if that wasn't clear at all, and I'll try to explain it in a different way.
Can anyone help me with this? How would I do this?
STI is probably a good fit for your requirements, as suggested by tadman, if you are using ActiveRecord (from Rails 3, it is easy to change ORM). The basic information is available on the AR documentation page, but here is some extra information w.r.t. your target:
Define one model per file. Otherwise there are some initialization troubles. Assuming Client inherits from User all in a single file, Client objects cannot be created as long as a User constructor has not been called once. One file per model circumvents the problem.
All attributes through the hierarchy are defined one-shot in the top class. This is for performance issues, but it seems disturbing many people in blog posts. In short, the Ruby code is object-oriented and encapsulates properly the attributes. The DB contains everything in a single table, with the extra "type" column to distinguish where they belong in the hierarchy. This is only a "trick" to represent inheritance trees in relational databases. We must be aware that the ORM mapping is not straightforward. The image on this site from Martin Fowler may help understand the situation.
In addition, you would like any new user to be a client, where Client inherits from User. To do so, you may simply instantiate any new user as a client. In your controller for creating users:
user = Client.new
# Do something to user
user.save
#=> <Client id: 1, name: "Michael Bolton", email: "mike#bolton.net", created_at: "2010-05-30 03:27:39", updated_at: "2010-05-30 03:27:39">
All the above is still valid with Rails 3 when using ActiveRecords.
It looks like you have two reasonable approaches here, but it will depend on the nuances of your requirements.
You can use Single Table Inheritance (STI) to do what you want, where User is only the base class for others named SalesAgent or Client and so forth. Each of these sub-classes may define their own validations. All you need for this to work is a string column called "type" and ActiveRecord will do the rest:
class User < ActiveRecord::Base
end
class Agent < User
end
The alternative is to have a number of free-form fields where you store various bits of related data and simply interpret them differently at run-time. You may structure it like this:
class User < ActiveRecord::Base
has_one :agent_role,
:dependent => :destroy
end
class AgentRole < ActiveRecord::Base
belongs_to :user
# Represents the agent-specific role fields
end
That would have the advantage of allowing for multiple roles, and if you use has_many, then multiple roles of the same type.

multi level user groups

I'm trying to determine the best structure to approach multi level user groups. Thus far I've created one object called "User" which i assumed could potentially be broken into different levels. Or should I simply create different tables for each user group?
Have a look into Single Table Inheritance..
The short version is that you add a type(string) column to your table and subclass all other models that will use that table from User
Eg:
class SuperUser < User
...
end
I assume you are talking about differnt roles for your users. I am currently using RoleRequirement. It gets the job done fairly easily.
http://code.google.com/p/rolerequirement/
As EmFi suggested, single table inheritance is your best bet. You would need to add the type field to the users table in the database and subclass your User model as below:
class Admin < User
# You probably don't need any methods here.
end
but you would also need to create a before filter. Quite similar to the one which makes sure that the user is logged in, it simply checks the class of your user. This should go in your ApplicationController:
def check_admin
current_user.is_a? Admin
end
There you go, Bob's your uncle, you have rudimentary authorisation.
To promote a user to Admin within rails, just change the type field. Of course, in this way, the user can only hold one access level (which is fine for a lot of applications). If you should want a more complex system, acl9 is quite well equipped for the job. I personally make a habit of using it along with authlogic to form quite a powerful system.

Categories

Resources