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.
Related
I have a Model Bot and I would like to ensure that there is only one Bot object in my database. I also need to make sure it is persisted and not tampered with.
My original thought was to do this in a migration, one that would follow the :bots table migration. It would include a line that is something like:
Bot.all.size == 0 ? Bot.create! : nil
Maybe this would prevent the AR object from being messed with in future migrations?
BONUS: Would be awesome to be able to have instant and global access to this class object. I was thinking using a singleton module in my Bot class that way I can always reference Bot.instance and have access to that specific object.
USE CASE:
I have 4 types of users in my DB and this bot will be the facilitator to delivery role-specific messages to them through our in-app messaging feature.
The Class Bot will have a has_many association with BotMessage/bot_messages. On the bot_messages table will be an enum field for user_role.
Messages will be created by company admins and stored in these tables because we want them to be viewable at any time by looking at the "conversation" thread between the User and the Bot.
When it comes to only having 1 bot, it's just that. I have no need for an additional Bot object. Additionally, since there is only one object it would be nice to be able to have a way of explicitly targeting that object without having to run a query to find it.
For example, unlike User where there could be 1000 records and in order to find the specific one you would do something like #user = User.find_by_email('foo#bar.com'), doing something like that for the bot would be unnecessary since there is only one record to find. That is what lead me to believe having a singleton object may be worthwhile here, so whenever I need to pull up a message for a specific role, I could run Bot.instance.bot_messages.where(user_role: 1) or something similar
Based on your Use Case, I see no reason for Bot to be a model.
Let's say you have a role called cool_user and you want to get all the bot_messages for that role, you might do something like:
class Bot
class << self
def bot_messages(user_role)
BotMessage.send(user_role)
end
end
end
As a very thoughtful but potentially anonymous super code monkey notes in the comments, you could also do:
class Bot
def self.bot_messages(user_role)
BotMessage.send(user_role)
end
end
Which some folks might find more readable. IMO, it is a bit of an issue of personal preference.
In either case, you should be able to do
Bot.bot_messages(:cool_user)
Since, as stated in the docs,
Scopes based on the allowed values of the enum field will be provided as well.
So, I believe BotMessage, with the properly set enum, should respond to cool_user and return all the bot_messages for that role.
You may need to check the docs to get the syntax exactly right.
I believe this should also satisfy your BONUS requirement.
A proven solution would be to use an STI on User (with a user_type column)
class User < ApplicationRecord
...
end
class Bot < User
has_many :bot_messages, foreign_key: :user_id
end
Is it what you're looking for ?
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.
Lets assume a website whether students, lecturers can login.
This can be done in two ways:
User model will be there, and user_type field ensure whether he is student or lecturer. But, in this way, if a student takes username 'a', then lecturer cant take same username 'a'. Since they both are different, for example like /profiles/lecturers/a. Here, user model says 'a' already exists, but that belongs to Student but not lecturer, so what should profiles/lecturers/a say? weird, right?
Here, There wont be any user model. Separate Lecturer and Student models with separate usernames. Offourse,we can have a common user model with common things like name, address, country. But, username should not be in usermodel. They should be part of Student, Lecturer.
Just not getting what to start with ?
What do you guys suggest ? 1 or 2 ?
Thanks.
I think it depends on whether you really want the username to be unique. Of course you could always have the unique field be an email and that could solve the problem.
Generally, for simplicity its probably easiest to just go with one User Model (differentiated by email for example) and then use a gem like cancan (https://github.com/ryanb/cancan) to make different roles.
For authentication you can also take at many excellent gems that fit that bill, most notably:
Devise https://github.com/plataformatec/devise
Sorcery https://github.com/NoamB/sorcery
I would lean towards #1 but use STI (single table inheritance) - its not always the best solution, but might be a good fit here, at least it is worth experimenting with
something along the lines of:
use devise and generate a User model and table, let devise handle all user authentication - it will use users.email as the login by default, go with that
add a 'type' column (string) to your user table for STI
add a 'nickname' column to the user table, show that in the UI, no need for it to be unique across Lecturer and Student
implement Lecturer and Student models which inherit from User
class User < ActiveRecord::Base
# all of the devise code
# ...
def is_student?
type == "student"
end
# ...
class Lecturer < User
# ...
class Student < User
# ...
Devise will inject the current_user method in your controllers, you might need/want to override it. Normally it would return a User object, you can use becomes to cast it to the appropriate sub-class - see http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-becomes
def current_user
return nil unless #current_user
if #current_user.is_student?
#current_user.becomes(Student)
elsif #current_user.is_lecturer?
#current_user.becomes(Lecturer)
else
#current_user
end
end
or don't mess with current_user at all, instead add in your own current_student and current_lecturer methods
# nil if not a student
def current_student
current_user.becomes(Student) if current_user.is_student?
end
NOTE: none of this code has been tested, your milage may vary
I don't think there is a simple answer for this. It will be really simple and won't matter so much which route you take if your application only have Student and Lecturer.
However, what happen when, you are required to have Classroom? A classroom can have multiple lecturers and multiple students. It is still quite simple to query either Lecturer or Student from Classroom right? What happen when you want to look for students that have classes given lecturers? Then you have Faculty, Course, Examination, Grade, GPA, then may want to define which lecturer can create what etc etc. Surely all of this can be solved using those SQL techniques rails handily provides but what are the trade-offs?
So I guess my answer is unless you have taken all those into consideration there is no right way.
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.
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