Skip navigation

Build your first Ruby on Rails app

8 min read
Download PDF

In this tutorial, you'll learn how to create a basic Ruby on Rails app using the Delivery Ruby SDK. It's never been easier to integrate Kontent.ai into your Rails applications and benefit from all the useful features we offer!

Table of contents

    Create a new project

    Prepare the environment

    To begin your project, you’ll need Ruby 3.1 and Rails installed. You can get Ruby from ruby-lang.org. After that, install Rails using this command:

    • shell
    gem install rails
    gem install rails

    Create a blank Rails project

    To create a new Rails project, we recommend using the –O parameter, which will create the project without a database. In this tutorial, you won't need a local database.

    • shell
    rails new kontentairails –O
    rails new kontentairails –O

    Open your new Rails project in an IDE such as Visual Studio Code and open a file named Gemfile. Inside the file, you'll add Ruby gems that your project needs to run.

    Add dependencies

    In the Gemfile file, add the kontent-ai-delivery gem, and also the render_async gem like this:

    • Ruby
    gem 'kontent-ai-delivery' gem 'render_async'
    gem 'kontent-ai-delivery' gem 'render_async'

    First run

    At this point, you can run your Rails application by executing the following commands in your command line.

    • Ruby
    bundle install rails server
    bundle install rails server

    The first command installs the necessary Ruby gems and the second starts a local Rails server.

    Navigate to localhost:3000 in your browser and you'll see the default Rails welcome page:

    The default welcome page of a Ruby on Rails app

    Creating a controller and view

    Rails creates the default controller /app/controllers/application_controller.rb to use in your application. Let’s modify it so that you can use the Kontent::Ai::Delivery::DeliveryClient in your controllers:

    • Ruby
    class ApplicationController < ActionController::Base PROJECT_ID = '<YOUR_ENVIRONMENT_ID>'.freeze # PROJECT_ID identifies a project environment @@delivery_client = Kontent::Ai::Delivery::DeliveryClient.new project_id: PROJECT_ID end
    class ApplicationController < ActionController::Base PROJECT_ID = '<YOUR_ENVIRONMENT_ID>'.freeze # PROJECT_ID identifies a project environment @@delivery_client = Kontent::Ai::Delivery::DeliveryClient.new project_id: PROJECT_ID end

    Create a new controller named home_controller.rb in the same directory. You'll use the controller to display a list of articles on the home page.

    • Ruby
    class HomeController < ApplicationController def index; end end
    class HomeController < ApplicationController def index; end end

    For now, this controller has one action that will display index.html.erb. You’ll add the cool stuff in a bit!

    In the /app/views directory, create a new folder named home. Then create the index.html.erb file inside of it. You can add any HTML you want here. For tutorial purposes, let's use a placeholder for your list of articles.

    • HTML
    <h1>Latest Dancing Goat Articles</h1> <div> (coming soon) </div>
    <h1>Latest Dancing Goat Articles</h1> <div> (coming soon) </div>

    Open the /app/config/routes.rb file and register our HomeController. Then tell Rails you want to view HomeController’s index() action when accessing the main domain.

    • Ruby
    Rails.application.routes.draw do resources :home root 'home#index' end
    Rails.application.routes.draw do resources :home root 'home#index' end

    If you haven’t already, shut down the previous server by pressing CTRL + C in the command line and run the rails server command again to view your changes. There are no articles displaying yet, so let’s fix that.

    Asynchronous rendering

    You’re going to add a new action to the HomeController that will asynchronously request articles from Kontent.ai and display them on the home page. This is what the render_async gem is for: using this gem will allow your home page to load before Kontent.ai even responds, and the content will be rendered once it’s received. Note that this is not necessary, but may speed up your site. Later in this tutorial, you’ll create a controller that doesn’t use render_async.

    Let’s start with the required scripts for render_async to do its magic. Add the following to the app/views/layouts/application.html.erb file just above the closing </body> tag.

    • HTML
    <body> <%= yield %> <%= content_for :render_async %> </body>
    <body> <%= yield %> <%= content_for :render_async %> </body>

    In the app/views/home/index.html.erb file you need to tell render_async where to load your list of articles.

    Add the following block to the view where you want the articles to show.

    • HTML
    <h1>Latest Dancing Goat Articles</h1> <div> <%= render_async article_list_path %> </div>
    <h1>Latest Dancing Goat Articles</h1> <div> <%= render_async article_list_path %> </div>

    The render_async gem will request data from the article_list_path and load the response where the code block is placed. Let’s define the article listing path in app/config/routes.rb like this:

    • Ruby
    Rails.application.routes.draw do resources :home get :article_list, :controller => :home root 'home#index' end
    Rails.application.routes.draw do resources :home get :article_list, :controller => :home root 'home#index' end

    Create an article_list action in your HomeController. This action will render a partial view when called.

    • Ruby
    class HomeController < ApplicationController def index; end def article_list render partial: "article_tile" end end
    class HomeController < ApplicationController def index; end def article_list render partial: "article_tile" end end

    Finally, create a partial view n _article_tile.html.erb in the app/views/home folder which will render when the article_list action is called. It can contain anything right now.

    • HTML
    <b>Articles are almost here..</b>
    <b>Articles are almost here..</b>

    Run rails server and reload the home page.

    The render_async has rendered _article_tile.html.erb view within the index view.

    A screenshot of Kontent ruby sample app

    Now for the fun stuff!

    Adding Rails to Kontent.ai

    It’s finally time to get some data from Kontent.ai and display it on the home page. You’ll be using the .items method and a few filtering options. Change the body of your article_list action to the following.

    • Ruby
    def article_list @response = @@delivery_client.items( 'system.type'.eq 'article' ) .order_by('elements.post_date', '[desc]') .execute if @response.http_code == 200 render partial: "article_tile", collection: @response.items, as: :article else logger.info @response.to_s render html: 'Sorry, articles are not available at this time' end end
    def article_list @response = @@delivery_client.items( 'system.type'.eq 'article' ) .order_by('elements.post_date', '[desc]') .execute if @response.http_code == 200 render partial: "article_tile", collection: @response.items, as: :article else logger.info @response.to_s render html: 'Sorry, articles are not available at this time' end end

    So, what’s going on here? The client you defined in the ApplicationController controller is getting all content items from Kontent.ai of the “article” type and storing them in a variable which is passed to the _article_tile.html.erb file.

    If the response is successful, the app renders the view for each content item, which will be accessible in the partial view as article. Otherwise, the app logs some data about the response and renders plain HTML instead.

    Change the _article_tile.html.erb partial view to render a preview of each article and link to another page to read the full article.

    • HTML
    <a style="color:black" href="/article/<%= article.system.codename %>"> <div style="background-color:#eee;float:left;padding:10px;width:300px;height:400px;margin-bottom:20px;margin-right:20px;display:inline-block"> <span style="font-weight:bold;font-size:1.3em"><%= article.elements.title.value %></span> <br><span style="color:#888"><%= DateTime.parse(article.elements.post_date.value).strftime("%A, %B %e, %Y") %></span> <br/><br/><img src=<%= article.get_assets('teaser_image').first.url %> style="width:100%" /> <br/><span><%= article.elements.summary.value %></span> </div> </a>
    <a style="color:black" href="/article/<%= article.system.codename %>"> <div style="background-color:#eee;float:left;padding:10px;width:300px;height:400px;margin-bottom:20px;margin-right:20px;display:inline-block"> <span style="font-weight:bold;font-size:1.3em"><%= article.elements.title.value %></span> <br><span style="color:#888"><%= DateTime.parse(article.elements.post_date.value).strftime("%A, %B %e, %Y") %></span> <br/><br/><img src=<%= article.get_assets('teaser_image').first.url %> style="width:100%" /> <br/><span><%= article.elements.summary.value %></span> </div> </a>

    If you rerun the Rails server now, your home page will look like this.

    A screenshot of Ruby sample app with articles

    Resolving rich text

    Rich text elements, such as the one in our articles, can contain links to other content items, components, and many other things. You’ll now create a page that will display the article text, resolving any of these objects contained in the rich text field.

    First, create the ArticleController controller in /app/controllers which will use the codename of the article from the request to get the content item from Kontent.ai. A URL such as ~/article/some_code_name will automatically map to the controller’s show() action thanks to default routing rules.

    • Ruby
    class ArticleController < ApplicationController def show codename = params[:id] response = @@delivery_client.item(codename).execute if response.http_code == 200 @article = response.item render partial: 'show' else logger.info response.to_s render html: 'The article you requested couldn\'t be found' end end end
    class ArticleController < ApplicationController def show codename = params[:id] response = @@delivery_client.item(codename).execute if response.http_code == 200 @article = response.item render partial: 'show' else logger.info response.to_s render html: 'The article you requested couldn\'t be found' end end end

    Register this controller in /app/config/routes.rb.

    • Ruby
    Rails.application.routes.draw do resources :home, :article get :article_list, :controller => :home root 'home#index' end
    Rails.application.routes.draw do resources :home, :article get :article_list, :controller => :home root 'home#index' end

    Now, create the _show.html.erb partial view in /app/views/article.

    • Ruby
    <h1><%= @article.elements.title.value %></h1> <%= @article.elements.body_copy.value.html_safe %>
    <h1><%= @article.elements.title.value %></h1> <%= @article.elements.body_copy.value.html_safe %>

    If you run rails server now and access an article that contains inserted content items (e.g. /articles/ coffee_beverages_explained), you'll notice that the these content items are rendered with their <object> HTML tags like this.

    • HTML
    <object type="application/kenticocloud" data-type="item" data-rel="component" data-codename="n373888cc_34e2_01e1_1820_3cb52ab1b2a1"></object>
    <object type="application/kenticocloud" data-type="item" data-rel="component" data-codename="n373888cc_34e2_01e1_1820_3cb52ab1b2a1"></object>

    This is meant to be a Hosted Video component, but you need to use the InlineContentItemResolver to change how it appears in the app. Change /app/controllers/ApplicationController to use an inline item resolver.

    • Ruby
    class ApplicationController < ActionController::Base PROJECT_ID = '<YOUR_ENVIRONMENT_ID>'.freeze # PROJECT_ID identifies a project environment item_resolver = Kontent::Ai::Delivery::Resolvers::InlineContentItemResolver.new(lambda do |item| if (item.system.type.eql? 'hosted_video') && (item.elements.video_host.value[0].codename.eql? 'youtube') return "<iframe class='hosted-video__wrapper' width='560' height='315' src='https://www.youtube.com/embed/#{item.elements.video_id.value}' frameborder='0' allowfullscreen > </iframe>" else return '' end end) @@delivery_client = Kontent::Ai::Delivery::DeliveryClient.new project_id: PROJECT_ID, inline_content_item_resolver: item_resolver end
    class ApplicationController < ActionController::Base PROJECT_ID = '<YOUR_ENVIRONMENT_ID>'.freeze # PROJECT_ID identifies a project environment item_resolver = Kontent::Ai::Delivery::Resolvers::InlineContentItemResolver.new(lambda do |item| if (item.system.type.eql? 'hosted_video') && (item.elements.video_host.value[0].codename.eql? 'youtube') return "<iframe class='hosted-video__wrapper' width='560' height='315' src='https://www.youtube.com/embed/#{item.elements.video_id.value}' frameborder='0' allowfullscreen > </iframe>" else return '' end end) @@delivery_client = Kontent::Ai::Delivery::DeliveryClient.new project_id: PROJECT_ID, inline_content_item_resolver: item_resolver end

    To get the resolved value for a content element you need to use the get_string method. Change the _show.html.erb partial view to use the method.

    • Ruby
    <h1><%= @article.elements.title.value %></h1> <%= @article.get_string('body_copy').html_safe %>
    <h1><%= @article.elements.title.value %></h1> <%= @article.get_string('body_copy').html_safe %>

    If you now access the /article/coffee_beverages_explained route in your browser, you’ll see the YouTube video rendered correctly in the article detail.

    A screenshot of a ruby app with article and a video

    If a rich text element contains links to other content items, they will be rendered as empty <a> HTML tags by default. You can see an example of this on the /article/coffee_processing_techniques page of your application. To specify the URLs to other pages on your site, you need to implement a ContentLinkResolver.

    In the /app/controllers/application_controller.rb controller, register a ContentLinkResolver similarly to the way the InlineContentItemResolver was created.

    • Ruby
    link_resolver = Kontent::Ai::Delivery::Resolvers::ContentLinkResolver.new(lambda do |link| return "/coffees/#{link.url_slug}" if link.type.eql? 'coffee' return "/article/#{link.code_name}" if link.type.eql? 'article' end) @@delivery_client = Kontent::Ai::Delivery::DeliveryClient.new project_id: PROJECT_ID,inline_content_item_resolver: item_resolver,content_link_url_resolver: link_resolver
    link_resolver = Kontent::Ai::Delivery::Resolvers::ContentLinkResolver.new(lambda do |link| return "/coffees/#{link.url_slug}" if link.type.eql? 'coffee' return "/article/#{link.code_name}" if link.type.eql? 'article' end) @@delivery_client = Kontent::Ai::Delivery::DeliveryClient.new project_id: PROJECT_ID,inline_content_item_resolver: item_resolver,content_link_url_resolver: link_resolver

    If you now access the /article/coffee_processing_techniques route in your browser, the links to coffees on your site will correctly point to other pages such as /coffees/kenya-gakuyuni-aa!

    What's next?

    You now have a very basic Rails application! This should provide the foundation for developing your next cool Rails project with Kontent.ai.