I'm getting ready to add notification settings to my app. Allowing a user to select true/false, for which type of activities they want notifications for.
My idea for a model is: UserSettingsNotifications. I picked UserSettings, as it's tied to the User, and the user will have several setting types including notifications and others.
Something like:
rails generate model UserSettingNotifications CommentReply:boolean .....
What do you think? Good to go, or not- rails friendly?
You phrased it "notification settings". I suggest dropping the user prefix and naming it NotificationSettings since it's likely clear in the model it belongs_to :user
Related
My app has several events based on which a user gets an email notification. What's the best way to handle this from a software/database design perspective?
Here are two instances when I send out an email to a user:
Someone replies to their comment.
Someone likes their comment.
I also need a way for the user to turn these email events off individually in their user settings.
Here is what I'm thinking of doing (which doesn't feel like a good way):
Have a bunch of boolean fields in the user table that turn on or off each email notification (eg: is_send_email_replies, is_send_comment_likes).
The user can then turn these bool values on or off in their setting.
Is there a better more pragmatic way to handle this?
This is considered a typical user settings where you can save it as a Rails json field or use gems like rails-settings
So assuming you'll use rails-settings gem you can do it as follows:
class User < ActiveRecord::Base
has_settings :email_notifications
end
then you can set and get settings like this
user.settings(:email_notifications).comments = true
user.settings(:email_notifications).likes = false
user.settings(:email_notifications).comments
# => true
I suggest you watch Ryan Bates' Railscasts on activity (ep. #406) and then reading into the public-activity gem. That can be an elegant way to handle the events that send out notifications to users.
As for how you will store and deal with preferences, check out the Preferences gem As the description itself says: ...sometimes it's necessary if you want users to be able to disable things like e-mail notifications.
One way that I've handled this in the past is to have a separate model for the preferences and for the email type. The preferences table acts as sort of a many to many relationship between users and types where they can set there own preference.
The main advantage here is you can add as many email types as you want (over time) and you wont be clogging up your user model.
I'm working on an iPhone app where I need to alert the user when a person on the addressbook joins the service. I'm using rails as backend and currently have phone number as an attribute under User.
So far my idea was to:
When a user signs up for the app, access addressbook and send it to the server (with consent)
Have a separate "Phone" model which has only one attribute "number" of string type
Have another Association model "PhoneAssociation" which belongs_to Phone via self referential join
Query this table whenever a new user joins and notify the relevant party.
But I wanted to make sure this is how it's supposed to be implemented. Are there better ways to do this? Thank you.
EDIT: To clarify, I am curious about if this is the right way to data model "Find Friends" feature> Above solution was just something I came up with but I thought there could be other ways to do this.
It sounds like you're using the PhoneAssociation model as a join object from Phone to Phone. In this case, since you don't seem to need much other than the join, you should use the has_and_belongs_to_many association, detailed here, in section 2.6. With this association, you wouldn't need to explicitly create the PhoneAssociation model you would only need Phone.
I have been trying different techniques while designing this application, which to me is very complicated as there are many solutions to chose from. Deciding which of these solutions is the best has become my full time job for the last few weeks.
Background:
I have a User model which contains some very basic authentication functionality. The application will have three different types of users; admins, buyers, and sellers. The user type will be determined during sign up, where a check box will ask if the user is a seller. If the box is checked, the user is a seller, and if the box is unchecked, it's assumed their just a buyer. I would rather not have a drop down menu to select the user type (Buyer, Seller). Easy so far.
Details:
Sellers have a profile, and users who visit the site will be able to browse through the different sellers and view their profile. Buyers do not have a profile, and should not be listed on the site for users to see. If that's not complicated enough, buyers should have the option to change their account type and become a seller. Similarly, sellers can change their account type and "deactivate" their seller account, removing them from the list of sellers.
Design options:
Single table inheritance:
What I came up with when attempting to implement this using STI was suboptimal. I was faced with two options: a messy controller, which made the decision of what type of user to create based on the check box mentioned above (one controller - UsersController), or two different sign up forms that were identical (two controllers - BuyersController, SellersController).
has_one or "has_none" Profile association:
class User < ActiveRecord::Base
has_one :profile # only if the user_type or role is "seller"
end
class Profile < ActiveRecord::Base
belongs_to :user
end
Here I would use something like CanCan or declarative_authorization, and let the user chose his/her role via the check box mentioned above. This introduces a security risk, as there will be an admin role and I don't want this to be open for mass assignment.
I guess either way you look at it I'll have a messy controller with conditionals on how to create the user. Unless I choose to have two check boxes ("I am a seller," and "I am a buyer"), but this seems redundant. It also seems as though whichever design I choose, I'll be faced with some hackish solutions.
Maybe introduce some model that lies between the User and user type?
Any opinions?
I don't really think you need either STI or Polymorphism. A single user model should suffice. I would add three methods to your user model (administrator?, buyer?, seller?) and also add scopes that will return only buyers, only sellers, or only admins.
In your case, it sounds like you have a real minimal number of roles for users (2 now, expanding to 3 with admins). I would probably use the technique Ryan discusses in the "Embedded Associations" Railscast.
I've done something very similar in my latest project, and then used CanCan (in my case, the 2.0 alpha branch, as I find it simpler) on top of that. Your ability class would look something like this (in 2.0 syntax, but you can do the same with 1.x, I'm sure)
class Ability
include CanCan::Ability
def initialize(user)
if user.seller?
can :create, :profile
else
...
end
end
end
So far it sounds like you have a flag that indicates their user type, or a column with type string, etc.
You haven't indicated any behavior that depends on this--not showing up in listings etc. can be handled with scopes. Admin can be handled with a flag.
I don't see the problem yet, at least not a problem of a few weeks scope.
A 3rd option.
...buyers should have the option to change their account type and become
a seller. Similarly, sellers can change their account type and
"deactivate" their seller account, removing them from the list of
sellers.
Have you considered not allowing switching of accounts? If a buyer wants to become a seller, then they create a new "seller" account. It would work the same for seller to buyer. It would mean having 2 controllers for the signup, etc. but it would keep the separation you're looking for. If the two must be mutually exclusive you could do a rudimentary email check to verify the user doesn't already have an account and if they do, they must de-activate the existing account prior to creating a new one.
I'm thinking that notifications would be it's own resource and have a has_many, through relationship with the user model with a join table representing the associations.
A user having many notifications is obvious, and then a notification would have many users because there would be a number of standardized notifications (a commenting notification, a following notification etc.) that would be associated with many users.
Beyond this setup, I'm unsure how to trigger the creation of notifications based on certain events in your application. I'm also a little unsure of how I'd need to set up routing - would it be it's own separate resource or nested in the user resource? I'd find it very helpful if someone could expand on this.
Lastly, ajax polling would likely improve such a feature.
There's probably some things I'm missing, so please fill this out so that it is a good general resource.
So the general gist:
1) Notifications would be a polymorphic association in that comments can have many notifications, users can have many notifications, a 'following' can have many notifications etc.
2) You can have Model Observers, where you can "observe" certain events, such as when a new comment is created. This is would be your triggers.
In terms of routing, you really don't need to do anything out of the norm. The only routing you may have is a domain.com/notifications where it shows all the notifications.
Notification table might look like:
sender_id: integer, receiver_id: integer, notifiable_id: integer, notifiable_type: string
For a notification system I personally prefer server push technology. Ryan Bates (the voice behind Railscasts) has a great screen cast that you might want to watch
For triggering actions for particular event, have a look at 'Observers' as #mike mentioned
I want to handle two kinds of global configuration settings:
Settings which can be altered by the user, like if notification mails for certain events are sent or not.
Settings which are tied to a specific product edition, like disabling a feature in a free version, which is only available in the commercial version.
What's the best way to store these settings? Database, configuration file, hardcoded in the source, ...?
For both cases database. You're going to be using the same structures for multiple people/products so it makes sense. Also it allows you to change things without restarting the server.
I've handled it this way in the past:
For settings specific to the user, I've created a UserSettings model/table, that has a one-to-one to relationship with a user. The reasoning for this is that the majority of my operations involving users do no not require these settings to be loaded, so they're only included on user loads from the database when I need them.
When I do this, I'll usually group my column names, so that I can write helpers that dynamically create based on the names. Meaning that I won't have to modify my views to incorporate new settings unless I add one with a different naming scheme.
For the settings specific to a product, well that depends on how you are doing things. And there are a couple of ways to interpret your question.
The way I read it is that you want to decide on a product level. What settings users can overriding or disabling a user's setting. And possibly define some product specific settings.
I would use a one-to-many product to setting relationship. The setting table would be something simplistic (product_id, setting_name, setting_default_value, allow_user_change)
This does a number of things. It lets you have a variable list of settings for different products (Perfect for the case where you're offering many different products instead of varying tiers of access to services). It also lets you define what settings a user can/can't change and give values for that product type. That can be changed from an administrator view without restarting the application. It's also not tied to user settings, to the point where if a user doesn't have a setting listed in the product_settings there will be no problems.
The downside is you will have multiple common settings in this table. I would move settings that every product will have a different value to a field in the product table.
You will also have to write validations to ensure that a user does not change a setting their product says they can't. You will also have to write helper methods to merge settings from the product and user sides.
class Flag < ActiveRecord::Base
# id, user_id, name, value (serialized probably)
belongs_to :user
DEFAULTS = {
"newsletter" => false
}
def self.lookup(user, flag)
# Please involve memcached here
case flag
when "ssl_enabled"
# Check if user has paid for sufficient access to SSL
return false
else
stored_flag = self.find_by_user_id_and_name(user.id, flag)
if stored_flag
return stored_flag.value
else
return DEFAULTS[flag]
end
end
end
end
class User < ActiveRecord::Base
has_many :flags
def flag(name)
return Flag.lookup(self, name)
end
end
For stuff that's product edition based, you probably can't really store things in the database, because the flag is going to be based on some piece of authorization code, rather than static data.
Here's my experience with this kind of stuff: don't override behavior.
You see, your first thought is going to be something like this:
Hmm.... There are system-wide settings that may or may not be overridden by users (or products). Hey! I know this! It's composition!
And technically, you'd be correct. So, you'll make a Settings table and put all your settings in there. And then you'll have a user_settings table, where you will override those settings if the user so decides. And it'll work fine.
Until you add a setting to one table and not the other.
Or you get a bug that Setting X can't be overridden at the user or product level and it takes more than 5 seconds to figure out exactly where that setting is set.
And then you'll realize:
Hey, I'm keeping track of all these settings in at least two different places. That seems kinda dumb.
And you'd be right.
So, yes. Go ahead and keep the settings in the DB, but save them distinctly for each user or product. Use smart default values on row creation and it'll keep things nice and simple.
For the first kind of settings, I would keep them in the User model (Users table).
The second kind of settings, would go to the database again. For example if a user had a free account, that would be somehow saved in the database. I would have some helpers in Application, for example "free?" or "commercial?". These helpers could find out if they are true or false, asking the currently connected User/Account model. You could then use these helpers across different parts in your application to decide if you show or hide certain functionality.