Ruby On Rails

Things to know

ApplicationController

Almost all our controllers inherit from the ApplicationController, in which a lot of stuff is initialized.

This controller implements 2 before_action:

  • :set_locale ⇒ sets the right preferred_locale

  • :initialize_shared_store ⇒ initialize some attributes (mainly current_recruiter & current_candidate).

So every request made to a controller that inherits from this controller will go through both those before_action. This is how the language is set and the current resource is initialized.

As we initialize current_recruiter there, that’s how it’s available as a variable in almost all our controllers.

Preferred Locale

As we just saw, the I18n locale is set in the ApplicationController, at every request.

Let’s look at the before_action that runs it:

def set_locale
  I18n.locale = params[:locale] || I18n.default_locale
end

It’s set to either the params[:locale] or the I18n.default_locale.

Let’s look at the default first, how does Rails knows what language it should use as the default?

This is all defined in the locale.rb config file:

Now, let’s have a look at the params[:locale]. As we’re in a controller, that means we’re handling a HTTP request that comes with its params. If we look at the route, we can see it’s encapsulated in a scope:

Scope are like a “folder”, so in this example:

  • /recruiter/job_offer_creation does not match ❌

  • /fr/recruiter/job_offer_creation matches ✅

⇒ The first element of this route is the /:locale, which corresponds to the preferred locale and is accessible in the controller like this: params[:locale]

If you have more questions about routing, it will be covered in the React on Rails section.

Active Record

Active Record is an implementation of the Object-Relational Mapping (ORM) pattern, which allows developers to work with databases in an object-oriented way. Basically, instead of interacting with the DB by writing SQL queries, you’ll use Rails’ ORM which is Active Record.

Basically, it creates a link between Rails models and database tables. It also allows some flexibility if you were to change your DBMS (for example, switching from PGSql to MySql) but it’s not something you’ll encounter that much.

Let’s have a simple example:

In Rails, to get all the candidates you might want to do:

Active Record then "translates" this query into a PG query, which becomes something like:

How does Active Record handles queries?

ActiveRecord is lazy and will only execute your query when it's effectively needed.

With the above example, just having Candidate.where(first_name: "Jules") will not trigger any SQL request.

To understand this, let's first launch a rails console and run this:

This will display SQL queries in your rails console, so that you can effectively see what ActiveRecord is doing and when it's doing it.

Now, let's have a look at this simple piece of code:

And here is the output in the console when we run SqlService.execute

As you can see, there is no SQL query anywhere to be found. ActiveRecord did not contact the database at all!

Let's tweak the code, and actually use the data we want to fetch from the DB:

If we run it now, here is the output we get:

As you can see, this time a SQL request was triggered (you can even see the query in its SQL syntax). The difference with the first try is that time we use the data returned by the DB (we access the first element, and print its first_name).

What does ActiveRecord returns?

We just saw that ActiveRecord was lazy, it will only load the data when you need it. Yet, even before you access the data, ActiveRecord still returns something!

Single Data

Some ActiveRecord queries will only return exactly ONE instance of a record.

  • find: Candidate.find(13) => will throw an error if the record is not found!

  • find_by(key: value): Candidate.find_by(id: 13) => won't throw an error, returns nil if not found

In our example, the data returned by ActiveRecord is an instance of the Candidate model. Again, the record will only be fetched (aka the SQL request will be triggered) only when you use the variable.

Multiple Data: ActiveRecord Collection

ActiveRecord does not only return single instance of a record, it can also return ActiveRecord Collections.

For example, Candidate.where(first_name: "Jules") will return a collection of every record matching the where condition.

This can be used as an array, but ActiveRecord also offers dedicated functions.

N+1 issue

ORM like ActiveRecord are great because they simplify the connection of the app to the database.

However, ORMs can trigger some unwanted side effects, the most famous one being the N+1 issue.

Let's take a simple example: we want to display the name of the company of every recruiters.

We might want to do something easy, such as:

And this is exactly how you introduce N+1 into your app! Let's count the SQL requests it triggers:

  • 1: Recruiter.all

  • N: recruiter.company.name

The first request will be fetching all the recruiters. Then, you iterate on every recruiter and fetch its company to acces its name. This means that for every recruiters, you're doing an additional request. Thus the N+1, if you have N recruiters, you'll be doing N+1 requests to the DB. If you've got millions of them, you're screwed.

Here is what the logs look like, with all the SQL calls (5 recruiters = 6 requests):

To prevent that, Rails offers some functions such as .includes or .joins which allows ActiveRecord to build an optimized SQL query. In our example, that would become:

And the logs would become:

Which shows only 2 requests, yeah! Wait, why 2 requests? This behaviour depends if you use .joins or .includes, but it'll at maximum only trigger 2 requests, instead of N+1.

If you want to dive deeper into those concepts, here is a nice article on Medium 😇

Mailing with Mailchimp

Let's have a look at a sample email, in one of our mailer class:

Variables

First thing we see is the initialization of the merge_vars variable. This is where you put all the value of variables you want to replace in the mail template.

Here is a simple example of a variable in Mailchimp:

So in the mailer you would define merge_vars this way:

The archive: "" is always necessary because Mailchimp is a great product that will uglify your emails if you forget to put it.

Headers

Next comes the headers, there are always three:

  • X-MC-MergeLanguage: this must contain the value of the type of variable you want to use (either mailchimp or handlebars, and most often mailchimp)

=> Mailchimp is *|first_name|* while handlebars would be {first_name}, it depends what was used in the template but 99% of the time the marketing team should and will provide you simple Mailchimp templates.

  • X-MC-Template: the name of the template, which can be found in Mandrill. Do not forget to append the locale!

  • X-MC-MergeVars: the variables sent to the template, which you defined above. Notice that it uses a .to_json, so you need to transform the merge_vars hash into a json object!

Actual sending

Well this where we finally send the email, using the mail function with three arguments:

  • to

  • subject

  • locale

That was our back-end! Time to go onto our front-end, using React.

Last updated