Playing out with new features in rails 6

MaJeD BoJaN
8 min readAug 31, 2019

--

Recently i got an e-mail from ruby weakly says, Hey Majed Rails 6 is out! and you’ve probably heard of all the big, new features and many other interesting things the most features that rails core team mentioned in rails blog are Action Text, Action Mailbox, Multiple DBs, Parallel Testing, Webpacker by default, and Zeitwerk.

Let’s look at these features and figure out what actually they are. The first feature i would like to start with is action text as i already wrote about it here and i was waiting for this framework in rails 6. so what is an action text.

ACTION TEXT: is a rich text framework that’s going to create, edit and display rich text content in your applications super easy, handles everything from formatting to links to quotes to lists to embedded images and galleries

Firstly we need to create new rails app with ruby 2.6.2 and rails 6 to implement these features once we create the new app we will generate scaffold for post model to use the action text in post model

rails new playing_out_with_rails_6_features
rails g scaffold user name email
rails g scaffold post title body

Then run the migration. here we go, let’s install action text framework.

rails action_text:install

The above command will add the Yarn package and copy over the necessary migration files to both ActiveStorage and ActionText.

Run rails db:migrate to migrate copied files.

Add the rich text field to an existing post model:

class Post < ApplicationRecord  has_rich_text :bodyend

Then refer rich_text_area to body field in the form for the model:

<div class="field"><%= form.label :body %><%= form.rich_text_area :body %></div>

Usage:

ACTION MAILBOX: is the second brand new framework coming to Rails 6 which adds the capability to route incoming emails to the controller like mailboxes for processing in Rails. it supports all major platforms or ingresses like Mailgun, Mandrill, Postmark, and SendGrid.

The inbound emails will be routed asynchronously using Active Job to one or several dedicated mailboxes, which are capable of interacting directly with the rest of your domain model.

let’s install action mailbox and see how we can use it in our app

Install migrations needed for InboundEmail, ensure Active Storage is set up and run the migration

rails action_mailbox:install
rails db:migrate

Generate some scaffolds for discussion and comment models

rails g scaffold discussion title
rails g scaffold comment user:references discussion:references body:text

We already generated user scaffold so now no need to repeat our self just run db:migrate and take look at schema file we got action_mailbox_inbound_emails table that created by action mailbox also app/mailboxes/application_mailbox.rb file created by action text it’s the file where we can define our routes

Generate new class for the job you need like in our case we are going to create replay class that will be inheriting from ApplicationMailbox

rails g mailbox replies

Here we go.

class ApplicationMailbox < ActionMailbox::Base   routing RepliesMailbox::MATCHER => :replies
# routing :all => :replies either we can process any email comes #to our application or we can specify which email we need to process
end# app/mailboxes/replies_mailbox.rbclass RepliesMailbox < ApplicationMailbox def process end
end

We will find user and the discussion id that user will add his comment on and whenever the user adds comment on specific discussion we are going to store that comment in our database and later on we will list those comments in discussion page

# app/mailboxes/replies_mailbox.rbclass RepliesMailbox < ApplicationMailboxMATCHER = /reply-(.+)@reply.example.com/def processreturn unless userdiscussion.comments.create(user: user,body: mail.decoded )enddef user@user ||= User.find_by(email: mail.from)enddef discussion@discussion ||= Discussion.find(discussion_id)enddef discussion_idrecipient = mail.recipients.find{ |r| MATCHER.match?(r)}recipient[MATCHER, 1]endend

Update app/views/discussions/show.html.erb to show the comments related to discussion topic

<p id="notice"><%= notice %></p><p><strong>Title:</strong><%= @discussion.title %></p><%= link_to 'Edit', edit_discussion_path(@discussion) %> |<%= link_to 'Back', discussions_path %><h4>Comments</h4><%= render @discussion.comments%>

Create! comments form that will be displayed to discussion topic

# app/views/comments/_comment.html.erb<div><strong><%= comment.user.name %></strong> Commented: </br><%= simple_format comment.body%></div>

Add the relationships to the model and let’s test it

#app/models/comment.rbclass Comment < ApplicationRecordbelongs_to :discussionbelongs_to :userend# app/models/discussion.rbclass Discussion < ApplicationRecordhas_many :commentsend# app/models/user.rbclass User < ApplicationRecord  has_many :commentsend

Rails has created test inbound mail for development no need to do it from console.

http://localhost:3000/rails/conductor/action_mailbox/inbound_emails/new

Now we could process the email and store it in our database all you have to do is to get the fun and integrate it with one of the email providers we mentioned them above and start sending email from your real email. you can get the configuration instruction here.

Usage:

Multiple DBs

Recently i worked on a project that deals with Third-party called juniper. juniper has huge data for flights, hotels, cars etc. some of these data are static data like the list of hotels around the world, list of regions cities and list of the airlines companies around the world. as we have to fetch the static data like hotels, zones and etc once a week by running cron job every friday as almost the server traffic will be lower at that time, so we decided split our db and store these static data in other db because more then 100,000 record will be fetching every week for each of these models. Rails now has support for multiple databases so you don’t have to store your data all in one place.

we will use different database connections for reading / writing with use of replicas to improve performance of the application rails already did all the configuration for us just we need to update our database.yml to add new database we are doing this setups in development environment later on we can do the same thing in staging and production environment as well.

# config/database.ymldevelopment:adapter: postgresqlencoding: unicodedatabase: amyalpool: 5host: localhostusername: <%= ENV['DB_USERNAME'] %>password: <%= ENV['DB_PASSWORD'] %>

let’s add replica for old database and new one called amyal_static_data and specify the path of the new database migration in our database.yml file

development:
primary:
database: amyal
user: <%= ENV['DB_USERNAME'] %>
adapter: postgresql
primary_replica:
database: amyal
user: <%= ENV['DB_USERNAME_READ_ONLY'] %>
adapter: postgresql
replica: true
static_data:
database: amyal_static_data
user: <%= ENV['DB_USERNAME'] %>
adapter: postgresql
migrations_paths: db/static_data_migrate
static_data_replica:
database: amyal_static_data
user: <%= ENV['DB_USERNAME_READ_ONLY'] %>
adapter: postgresql
replica: true

When using multiple databases there are a few important settings.

First, the database name for the primary and replica should be the same because they contain the same data. Second, the username for the primary and replica should be different, and the replica user’s permissions should be to read and not write.

When using a replica database you need to add a replica: true entry to the replica in the database.yml. This is because Rails otherwise has no way of knowing which one is a replica and which one is the primary.

Lastly, for new primary databases you need to set the migrations_paths to the directory where you will store migrations for that database. We'll look more at migrations_paths later on in this guide. click here for more details

Here are interesting ways of specifying how to switch to different databases

class Zone < ApplicationRecord
connects_to database: { writing: :static_data_primary, reading: :static_data_replica }
end

Zeitwerk

Zeitwerk: is the new code loader for Ruby. Given a conventional file structure, Zeitwerk loads your project’s classes and modules on demand, meaning you don’t need to write require calls for your own files. To enable it in Rails 6, you can do the following:

config.autoloader = :zeitwerk

You can read more about zeitwerk

Bulk Insert and Upsert

Rails 6 comes with a new method out of the box — insert_all, similar to update_all. one of my project was having issue performance due of multiple inserting at once to fix this issue i landed with sql query that solved my issue but increased the number of lines in my controller which i refactor it latter to ruby class.
This was my class

With rails 6 we can refactor this class and use the methods provided by rails team. Single line of code will solve the issue how rails is cool !!!!!!!

UserNotification.insert_all([{notification_id: 1, user_id: 1, created_at: DateTime.now, updated_at: DateTime.now}, {notification_id: 1, user_id: 2, created_at: DateTime.now, updated_at: DateTime.now}])

Credentials in Rails 6

Since the days of Rails 5.2, credentials have been named a new “Rails way” to deal with sensitive information with a promise to get rid of infamous .env files once and for all. With credentials, encrypted keys for third-party services can be checked directly into the source control.

Until now, however, Rails used the same encrypted file for all environments, which made dealing with different keys in development and production a little tricky, especially when dealing with big projects and legacy code.

In Rails 6, this is finally solved with support for per-environment credentials. Again, further details can be explored on the official GitHub thread.

You can get the code for action text and action mailbox here

References

--

--

MaJeD BoJaN

Self-Taught Full-Stack ROR Developer || Tech enthusiast.