An app for healthcare professionals to log their CEUs*
For the Rails project/assessment for Learn Verified, we are given free reign as to the content. We were to build an app with the following requirements:
- Models had to include: has_many, belongs_to, and has_many :through relationships; some validations; at least one class level ActiveRecord scope method.
- The join table had to have an additional submittable attribute — not just foreign keys and timestamps.
- Both a nested form an a nested resource
- Authentication — standard and from outside service (Devise and Omniauth Facebook).
- Lastly, we had to do our best to make it DRY (Do-Not-Repeat-Yourself).
This should be fun…and it was!
*What’s a CEU?
Let’s back up. I had asked my wife a few weeks ago what was a need that she had that could be solved with an app, albeit hoping it was something within reach! As an occupational therapist, she is required to (in a nutshell) obtain a certain number of continuing education units (CEUs) on both the state and national level. My wife told me that she’d love something to help her keep track of these. Hmm. A user, entering information about an event…I can do that!
A blog, essentially
When I thought of how this would work, a blog kept coming to the forefront. A user would signup/login (devise/omniauth), create a new CEU (think blog post), and assign a certificate to the CEU upon creation (wahoo nested form!). When a user viewed a CEU, they could add any notes (blog comments) that would be assigned to that CEU.
Models and Associations
All of this thinking led me to the following (after several redoing):
User (name, occupation & other devise attributes)
has_many :ceus, through: :notes
Ceu (title, date, location, duration)
has_one :certificate (you can only get one certificate for an event)
has_many :users, through: :notes
Notes (content, user_id, ceu_id)
Certificate (classification [State or National], ceu_id)
I wound up with this schema after fiddling with having a ceu_certificates table, but I was making it more complicated than it had to be!
I utilized a class method for my ceu model which orders all ceus by date with the most recent at the top. Here’s the method and how I called it in the show page:
- - - - - - - - - - -
<% current_user.ceus.most_recent.uniq.each do |ceu| %>
This is viewed on the user’s show page in a table that displays all of their CEUs.
Setting up omniauth was relatively painless — with 2 caveats. First, I ran into some hiccups in setting the app_id (key) and secret, but I found that the Figaro gem solved that issue nicely. This gem creates git-ignored file (application.yml) where you can store the ENV variables required for omniauth. This way, you can set up devise.rb the following way and not have to broadcast those secret details to the world.
config.omniauth :facebook, ENV['FACEBOOK_KEY'], ENV['FACEBOOK_SECRET']
Second, I hit a snag when a user attempted to edit their account. Omniauth Facebook only pulled a user’s name and email, so their occupation would have to be added later. The edit page that is supplied from devise requires a user to enter their password to confirm changes.
But, I signed up with Facebook…I didn’t enter my password!
Never fear! I was able to tweak the update method in the devise registrations controller to allow users who signed up with Facebook to forego having to enter their password to update their info. In the real world, this probably wouldn’t be the best idea, but for this simple app’s purposes I am fine with it. Here is the resulting code magic that did the trick:
@user = User.find(current_user.id)
email_changed = @user.email != params[:user][:email]
is_facebook_account = !@user.provider.blank?
successfully_updated = if !is_facebook_account
sign_in @user, :bypass => true
To make it work, you have to code out the strong params for update with or without password. Not too shabby!
For the future…
As I type this, I’m 97.99% positive that I’m done my app (God forbid any issues arise!). There are some features that I want to add in the future, namely the ability to edit and/or delete notes, but everything else is working…for now…famous last words!
♥//Learn. Love. Code!