Aliasing and Options in Rails ActiveRecord Associations

The Sinatra track on Learn.co introduced us to the powerful – and almost magical –  ActiveRecord ORM. ActiveRecord (AR) association methods simplify the process of creating the right links between our models by handling a lot of work behind the scenes. This allows us to focus on more important aspects of our apps, such as the logic and behavior.  However, these same association methods can become even more powerful once we start associating them with options.

Through the use of these options, we can come up with complex associations that give us access to more methods than we would otherwise have. All without needing to add too many extra models and/or extra tables to our app and database.

In this blog post, I hope to explain (while at the same time test my own understanding) of aliasing active record associations, and the use of options such as :class_name, :foreign_key, :through, and :source.

The domain we will be using contains User,Post and Comment models. Assume their associated tables have been migrated and the schema looks like this:

screen-shot-2016-09-13-at-11-36-54-pm
Database schema

 

Let’s get started!

A post belongs to a user, and a user has many posts


# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
end
# app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
end

WAIT! Didn’t we name the foreign key in the posts table as author_id? We don’t even have an Author model…

If we try to create an association between user and post, it won’t work:


post = Post.create(title: "Best Post Evaaaar!")
user = User.create(name: "Someone Important")
post.user = user
#=> can't write unknown attribute 'user_id'

So, let’s try to fix the association in Post:


# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :author
end
# However, this now leads us to another error when we try the following:
post = Post.first # returns first record from posts table
user = User.first # returns first record from users table
post.author = user
#=> NameError: uninitialized constant Post::Author

This error lets us know that Post still doesn’t know what we are talking about.

Using the option :class_name

We can tell it what we are referring to with the option :class_name.


# app/models/post.rb
class Post < ActiveRecord::Base
# alias the user association as "author"
belongs_to :author, class_name: "User"
end

We are letting Rails know that when we call .author on a post, we want the return to be an instance of User. The above is what is referred to as an aliased association.

OK. But Why?

The beauty of aliasing as we did above, is that we don’t need to define a separate class – for example, Author and then have it inherit from User . We also don’t need another table in our database.

Now in Post, we want to make sure the User (aka, Author) has many authored_posts as an author . So we have to let the User model know what foreign key to use to find the right posts.

As you may have guessed, we accomplish that by using the :foreign_key option…


# app/models/post.rb
class User < ActiveRecord::Base
has_many :authored_posts, class_name: "Post", foreign_key: :author_id
end
# The above is an example of an aliased has_many association
# And thanks to it, we can now do the following:
jk_rowling = User.find_by(name: "J.K. Rowling")
jk_rowling.authored_posts
#=> [collection of posts by this particular author]

With the above we are telling Rails to go to the posts table and retrieve all posts that are associated with this user id, but to make sure to look for the id in the column named “author_id”.

The norm is for the foreign_key in the has_many table to be named after the model that it belongs_to. This means a user would be looking for “user_id” in the authored_posts table.

Note that more we use aliases, the more explicit we have to be with telling AR how things are actually associated.

Now let’s look at the Comment model, which on top of containing content, will act as a join table.


# app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentor, class_name: "User", foreign_key: :user_id
belongs_to :post
end
# Without adding the foreign_key: :user_id we get the following error
# when trying to set a user as a comment’s commentor:
jk_rowling = User.first
comment = Comment.create(content: "Still 10 more books left in the Harry Potter universe!!!")
comment.commentor = jk_rowling
#=> ActiveModel::MissingAttributeError: can't write unknown attribute 'comment_id'

In order to complete the association between User and Comment, we need to say that a User has many Comment(s)…

Using the option :through


# app/models/user.rb
class User < ActiveRecord::Base
has_many :authored_posts, class_name: "Post", foreign_key: :author_id
has_many :comments
has_many :commented_posts, through: :comments
end

With that last addition, we can now say jk_rowling.comments, which will return a collection (array) containing all of the comments left by J.K. Rowling herself.

But what happens if we want to know about all the posts where J.K Rowling has left a comment?

As it currently stands, calling .commented_posts on an instance of User, results in an ActiveRecord error:


jk_rowling.commented_posts
#=> ActiveRecord::HasManyThroughSourceAssociationNotFoundError:
# Could not find the source association(s) "commented_post" or :commented_post in model Comment.
# Try 'has_many :commented_posts, through => :comments, source => <name>'. Is it one of commentor or post?

Look at that! ActiveRecord is nice enough to let us know where we are going wrong. It is trying to figure out which of the two models that a Comment belongs_to (commentor or post) represents a commented_post. What is the source of this association?

The :source Option

In order to fix the error, we have to do the following:


# app/models/user.rb
class User < ActiveRecord::Base
has_many :authored_posts, class_name: "Post", foreign_key: :author_id
has_many :comments
has_many :commented_posts, through: :comments, source: :post
end

We can read that last relationship as a user has many commented posts, through comments and the existing relationship between comments and posts.

With the above, we have basically created an extra join table, even though we didn’t hard code a join table for commented_posts into our database. However, because of the way the relationships are set up between these models we get to reap the benefits of one. What kind of magic is this!?

Another benefit of aliasing like we did above, is that we get to decide what we want to call this virtual join table. We can make sure our code still reads well.

Now we just need to complete the relationship that a post has many comments and many commentors by adding the following to Post:


# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :author, class_name: "User"
has_many :comments
has_many :commentors, through: :comments, source: :commentor
end

And thanks to the this we can ask a post for all its commentors using post.commentors

Conclusion

Although active record and all its magic can make a lot happen on its own, proper usage of these association options, along with aliasing can extend that functionality even more. Learning how to use these isn’t  easy at first (I am still struggling with it all), but I feel like it’s one of the better tools we can add to our tool belt as aspiring Ruby on Rails developers.
If you’re also struggling with this topic, I highly recommend checking out the links in the resources section below.
Lastly, I just want to point out that there is another syntax for using associations with options. It looks like this:
has_many :commented_posts, :through => :comments, :source => :post
I haven’t figured out which one adheres to best practices. Personally, I find it more readable and concise to do it the way I have coded the examples on this post.

Resources:

Rails Guides – Association Basics

Building a URL Shortener Using Sinatra

I’m not sure how to start this blog post, so I’m just going to say, maybe shout, scream the following:

WHOO! I ACTUALLY DID IT!

Image

As I approached the second assessment for the Flatiron School WebDev Fellowship, I didn’t have much confidence going in. For some reason, I had a lot of trouble grasping some of the new concepts and terms being introduced. Terms like actions and routes left me looking like this most of the time:

Image

Things didn’t get any better once I got to the final three labs leading up to the actual assessment. The names of those labs, Playlister, Sinatra NYC, and Fwitter will continue to haunt me for the foreseeable future – I’m sure of it. In fact, I didn’t even have to reference the curriculum to make sure I had their names right.

After spending a full day on Playlister (which was pretty much the case for the other two as well,) I felt completely defeated. My confidence took another hit and thoughts of not being good enough started to get a foothold in my head.

At the same time, I was brainstorming something to build. I knew I didn’t want to go for any of the domains we had been practicing during the Sinatra track. It was actually discouraged to do so in the instructions for the assessment.

And then it hit me… A URL shortener. If anything was going to test my understanding of actions and routes, this was as good a candidate as anything else. I took my inspiration from Bit.ly and TinyURL.com.

Keeping in mind that I was still struggling with the previously mentioned labs, I decided I was just going to build this as barebones as I could. Once it was successfully shortening a link, I didn’t care if the thing looked hideous. I just wanted to pass the assessment and finally, move on to Rails. Sinatra didn’t know it, but it was my sworn enemy!

Image

Goals

My original goal list looked somewhat like this:

  • A user can shorten a link. Period.

The first only bullet-point on that crazy list was to randomly shorten a long link. I wasn’t sure how to tackle that. My first instinct was that I’d have to use some RegEx, but I keep reading that programmers are lazy, so I decided to do my best impersonation of a good programmer and went to The Oracle for some advice…

Image

SecureRandom

While asking The Oracle (okay, it was Google) some questions, I came across SecureRandom, a Ruby library that felt like an answer straight from the programming Gods (if there is such a thing).

This library is an interface for secure random number generator which is suitable for generating session key in HTTP cookies, etc.

The ::urlsafe_base64 method was exactly what I needed. As the name suggests, it generates a random URL-safe string.

p SecureRandom.urlsafe_base64 #=> "b4GOKm4pOYU_-BOXcrUGDg"

You can shorten the output by passing lower integers as an argument.

p SecureRandom.urlsafe_base64(3) #=> "WtMt"

After getting that basic feature working, it felt like the project snowballed from there on. I decided to make use of user stories to plan out the rest of the application:

As a User, I should be able to:

  1. Have many links
  2. Add, edit, and delete a link
  3. Give a name (optional) to a link if I want to, for remembering what a link is about
  4. Be able to click on a shortened link and be redirected to the proper URL
  5. Be notified upon successful link creation, edition, and deletion
  6. Be notified upon successful signup and login/logout

Programming features 1-6 took me less than I thought they would. Apparently, things were clicking better than I thought, after all! A very barebones version of the app was fully working after just a few short hours.

I spoke to my one-on-one instructor, Antoin to run a few ideas by him and to get a sense whether or not the app would meet the requirements. He seemed excited about what I had so far, and actually sort of challenged (read encouraged) me to add some of the extra features I had in mind.

So the following two stretch goals got added to the features list:

  • Have a button to easily copy links without the need to select link text (Stretch goal #1)
  • Be able to easily share a link (Stretch goal #2)

Flash? In 2016?

Since I got my inspiration from Bit.ly, I went to check out how they  handled copying to the clipboard. To my surprise, they are using some obscure, ancient technology of days past. Something called Flash or something…

 

adobe-flash-animate-cc
Flash isn’t actually dead; it’s just renamed – Image Credit: THN

I was under the impression that HTML5 and JavaScript are working hard to make Flash obsolete, but it seems like it is the cockroach of the web – it just won’t die.

Clipboard.js

After getting the suggestion that I should be able to handle that feature using JavaScript (thanks again, Antoin,) The Oracle revealed Clipboard.js.

Image

This JavaScript library made adding the functionality for copying to the clipboard very straightforward. Even better, it was readily available via a few CDNs (Content Delivery Networks.)

Using it is simple. You add the following to the bottom of the view where you want that functionality:


<!-- Link to the jsDelivr CDN for Clipboard.js -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/clipboard.js/1.5.12/clipboard.min.js"></script>

<!-- Below is the actual function that gets called when the button is clicked -->
<script>
 var clipboard = new Clipboard('.btn');
 clipboard.on('success', function(e) {
 console.log(e);
 });
 clipboard.on('error', function(e) {
 console.log(e);
 });
</script>

Then, you have to make sure to set a target in your HTML.


<button type="button" class="btn btn-success" data-clipboard-target="#link-id-<%= link.id %>">Copy to Clipboard</button>

The important part of the code above is the ‘data-clipboard-target=”#link-id-1″‘ which is what sets the target to link-id-1 in this case. As each link is created, embedded Ruby takes care of assigning the proper id for each target.

With that feature taken care of, I moved on to the second stretch goal of making links shareable…

Sinatra and Pony

Reading through the Sinatra FAQs, I came across Pony, a gem for handling sending of emails from a Sinatra application.

Its usage is easy, we can send parameters from a form via a POST request, and then handle the actual sending of the email from the corresponding action in the controller.

The code in the controller looks like this:


# Action for handling sharing via email, using Gmail
post '/email' do
Pony.mail ({
:to => params[:send_to],
# :from => params[:send_from],
:subject => params[:subject],
:body => "#{params[:link]}\n\n#{params[:email_body]}",
:via => :smtp,
:via_options => {
:address => 'smtp.gmail.com',
:port => '587',
:enable_starttls_auto => true,
:user_name => 'gmail-account-name', # in this case my Gmail account
:password => 'some-password', # as well as my Gmail password
:authentication => :plain, # :plain, :login, :cram_md5, no auth by default
:domain => "localhost.localdomain" # the HELO domain provided by the client to the server
}
})
redirect '/'
end

One problem that I ran into while working on this functionality was that I had to provide credentials for Gmail to be able to send email from my account. Another thing is that the password is currently visible. I have chosen not to push this part of the code to GitHub until I learn more about storing API keys and other personal data in a way that won’t be a security risk.

Rucchi and Alex both mentioned a gem that can handle all that, which basically automatically adds files and folders you specify to the .gitignore file. However, I felt like for the time being, I just wanted to prove to myself that I could get the app working the way I wanted to – and I feel I accomplished about 95% of it.

Conclusion

One feature that I still want to figure out is how to make the modal that displays the email form automatically populate the link field so that a user doesn’t have to copy/paste the link. Maybe for version 2.0.

All in all, this project was incredibly fun to build. I thought I hated Sinatra, but looking back, I think it was all a simple misunderstanding. Going through this assessment, provided me with a thrill while coding that I haven’t had while going through the labs, or even the first assessment.

The hours seemed to fly by, and bugs and issues were actually something I cherished. They meant there was some more work to be done. More knowledge to be gained. (Also special thanks go to Raycent for his amazing ability to break my app in all the ways I couldn’t think of. It forced me to make the app that much better.)

At the moment, I’m feeling pretty good. I know the feeling probably won’t last more than a week until I run into another difficult lab that makes me feel like I’m one of the stupidest humans all over again. But for now I’ll just let Drama convey how I feel:

Image

A Simple Way to Get a Date Using Ruby

date-time

One of the early labs in Sinatra that I was working on yesterday requires that you make a route for /date, which will write the current date to the web page when a request is made to http://somegreaturl.com/date. Thanks to this lab’s readme, I learned about a new Ruby library for working with time and date. That library is called DateTime.

I immediately remembered struggling with the Time class in one of the very early labs where the date and time had to be outputted in a very specific format.

Previously, if you asked me to output the current date as Friday, August 5, 2016, I would have done something like this:

days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday"]

# Setting first index to nil for easier match with return from
# Time.month (1-12)
months = [nil, "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]

time = Time.new
year = time.year
month = months[time.month]
weekday = days[time.wday]
day_of_month = time.day
hour = time.hour
minute = time.min

puts "Today is #{weekday}, #{month} #{day_of_month}, #{year}"
#=> Today is Friday, August 5, 2016

Little did I know that both the Time class and DateTime can make this task almost trivial.

easy button

Enter #strftime and Time Formatting Directives

Both Time and DateTime have a variety of methods for working with time, but the #strftime method is one I found particularly useful and one that I know I’ll make use of anytime I need to work with either date or time from here on.

This method takes in a string as an argument, which allows you to be very precise with how the date/time will be formatted. Within the string argument, you use % signs coupled with different letters, to target individual parts of the current date and/or time. These are called time formatting directives.

These formatting directives provide many options for formatting in pretty much any way you would want to. For example, here are some of the things you can target individually if you want to work with dates (year, month, day):

Date (Year, Month, Day):
%Y - Year with century (can be negative, 4 digits at least)
-0001, 0000, 1995, 2009, 14292, etc.
%C - year / 100 (round down. 20 in 2009)
%y - year % 100 (00..99)

%m - Month of the year, zero-padded (01..12)
%_m blank-padded ( 1..12)
%-m no-padded (1..12)
%B - The full month name (``January'')
%^B uppercased (``JANUARY'')
%b - The abbreviated month name (``Jan'')
%^b uppercased (``JAN'')
%h - Equivalent to %b

%d - Day of the month, zero-padded (01..31)
%-d no-padded (1..31)
%e - Day of the month, blank-padded ( 1..31)

%j - Day of the year (001..366)

Using the above, we can replicate the output from earlier like this:

DateTime.now.strftime("Today is %A, %B %d, %Y")
#=> "Today is Friday, August 05, 2016"

Notice that this time, we can be much more specific with how we want to format the date, and also, that we are working with a string. This means we can easily change the return.

DateTime.now.strftime("Today is %a, %b%e, %Y. Current time: %T %p")
#=> "Today is Fri, Aug 5, 2016. Current time: 09:25:05 AM"

 

What’s the Difference Between DateTime and Time?

After learning about DateTime, I went back to the Time class documentation and realized I just never went through all of its available methods. Funnily enough, Time has access to the same #strftime method! So what is the difference between the two? When should you use one over the other?

Well here’s an excerpt from an answer to just such a question posted on Stack Overflow:

Newer versions of Ruby (2.0+) do not really have significant differences between the two classes. Some libraries will use one or the other for historical reasons, but new code does not necessarily need to be concerned. Picking one for consistency is probably best, so try and mesh with what your libraries expect. For example, ActiveRecord prefers DateTime.

Apparently, they can be used interchangeably unless you’re using a library that expects or prefers a specific one.

 

Conclusion

Formatting date and time should now be much easier thanks to #strftime. Thanks for reading.

 

Resources

The Quirks of Ruby Method Arguments

As I make my way through the learn.co curriculum as part of the Web Development Fellowship, I find myself supplementing the content with various resources. Currently, I’m slowly reading through The Well-Grounded Rubyist (TWGR) by David A. Black in an effort to get more comfortable with the language.

While reading chapter 2, which is titled “Objects, methods, and local variables”, there was something that stood out to me… Apparently method arguments are more complex than they appeared at first glance. It turns out there is a specific order, as well as certain rules that apply when supplying parameters during a method definition. Read on for a beginner-friendly overview on the behavior of method arguments.

Required and Optional Arguments

During method definition, you can make arguments required by simply supplying a parameter for it. If you provide more (or less) arguments than the number required, Ruby will throw up an error:

example1

This is Ruby’s way of letting you know you have provided too many arguments. The #awesome_method needs to be provided exactly one argument for the method to work.

Just like you can require a predetermined amount of arguments, you can also make them optional. To accomplish this all you have to do is provide a default value for any parameters you want to make optional. This is done during the method definition like so:

example1

As you can see, when we call #greet_programmer without an argument, the default value is used for the name parameter.

The Splat Operator

The what operator? Yes, that’s what I thought as well when first introduced to an asterisk placed before a parameter.

example4
This example is not from the actual lab on Learn.co

Although I was able to pass that particular lab on Learn, the Ruby splat still felt like a complete mystery. It wasn’t until going through this chapter of TWGR that I finally gained more of an understanding as to what it’s actually happening.

In a nutshell, the splat allows you to include any number of arguments in a method. Mr. Black uses a sponge metaphor to explain its behavior – the splat sponges up any remaining, non-required arguments, and turns them into an array.

Using p instead of puts shows that arguments refers to an actual array:

example5

Mixing And Matching – What Gets Assigned To What?

This is where things start to get a bit more complicated (read interesting). When you mix required with optional arguments, how do you know what argument is getting each value? Well, there is a specific order that arguments must follow for Ruby to make sense of everything that’s happening.

example6

In the above method call, you can see a and b were required arguments, and therefore, got assigned the first two values of “apple” and “banana”; *c took the rest of the arguments and turned them into an array as expected.

But what about when you have the following:

example7

Notice that this time after a and b receive their required values, *x only gets two values – “grape” and 15 – while y gets the last value passed in.

In addition, if we take this same example a bit further:

example13

Passing two values and mixing in a default value for b makes things more confusing. This time, there are just enough values to satisfy all the requirements that will allow the method to still work.

The argument assignment for the above call on #mix_and_match goes like this:

variable_assignment

If you pass only one argument to #mix_and_match as coded in that last example, Ruby throws up the following error:

terminal error

Order Please!

While it looks like you can do almost anything mixing and matching required and optional arguments, the reality is that there is one rule you must always follow – pay attention to the order of optional parameters and default value parameters in your method definition.

The main thing to keep in mind is that default value parameters must come before optional parameters. Otherwise, Ruby responds with a syntax error.

correct_vs_incorrect - example 10

terminal error 2

If you think about it, it doesn’t make much sense to have an optional argument before the one that carries a default value. Just like it doesn’t make sense to have more than one splat operator in your argument list.

The order in which Ruby assigns priority to arguments goes like this: required arguments get assigned first, regardless of whether they are on the left-end or right-end of the argument list, and are followed by optional arguments taking up anything that is left in the middle.

Hopefully, this post was helpful in any way! Comments are welcome!