Web Framework from Scratch - Part Three

So I finally got into actually serving content through the framework!

I started by demonstrating how to use WEBrick to server static assets. Then off-camera I creating a more object-oriented approach.

I still have to write the tests for the code I wrote, which may end up changing things a little if I run into any errors. I’ll do that between this episode and the next.

Moving forward I’m going to be recording less of the actual coding since it’s going to start getting very tedious, very quickly. Instead I will be demonstrating the basic principals involved in writing the framework, and then showing the code that I wrote, with an explanation of what it does, and the reasons behind it.

Web Framework from Scratch - Part Two

Here’s episode two in the series documenting my progress of creating a web framework from scratch in Ruby using only the standard libraries.

This is a code optimization video. I didn’t like the way I left the testing set up in the first episode. It was too cumbersome to run. Every time you ran the test runner it would run every single test. I moved test/test_runner.rb into exe/test, and allowed for parameters specifying the names of the tests to run.

The code for this episode can be viewed on Github here, and a diff between episode one and two can be viewed here.

Web Framework from Scratch - Part One

Recently I was browsing through Ruby’s standard library documentation, and I noticed that everything you need to build a fully featured web application framework is already built in to Ruby. So I decided to build a new web framework that will work out the door without relying on anything other than Ruby’s standard library.

I started a series on YouTube to document my progress on building the framework from scratch. Here is the first episode.

Flash Marketing with Rails

In a previous article, I talked about how you can use ActiveRecord session store to display promotions to your site’s visitors. Since that article was written, the ActiveRecord session store code has been refactored from Rails into its own gem, along with a few other changes that make the code incompatible with Rails 4 or higher.

This is an update to that article, with a more complete tutorial. You can find the code for this article hosted on Github here.

Getting Started

To demonstrate this concept, let’s start by creating a new Rails application:

$ rails new flash_marketing
$ cd flash_marketing
$ rake db:create

Next, install activerecord-session_store:

$ echo "gem 'activerecord-session_store'" >> Gemfile
$ bundle
$ rails g active_record:session_migration
$ rake db:migrate

Then edit config/initializers/session_store.rb and update it to the following:

Rails.application.config.session_store :active_record_store

Custom Session Model

Now we can create a Session model that descends from ActiveRecord::SessionStore::Session. This will allow us to access every user’s session outside of the scope of the current user.

Create app/models/session.rb and add the following content:

class Session < ActiveRecord::SessionStore::Session
end

Normally we can only access a single user’s session data in the views or controllers. Using this model we have access to every user’s session, not just the current user, and from anywhere in our application. This includes background processes, which is what we will be using later on in this tutorial.

Flash Messages

Next, we need to be able to communicate with our site’s users. For that we’ll be taking advantage of Rails’ built in flash notification system. Let’s begin by adding a flash method to our Session model:

class Session < ActiveRecord::SessionStore::Session
  def flash(id, msg)
    data['flash'] ||= {'flashes' => {}, 'discard' => []}
    data['flash']['flashes'][id.to_sym] = msg
  end
end

Now we can display flash messages to any of our site’s visitors. Just keep in mind that we’re not actually using ActionDispatch::Flash, which is what is used in the controllers.

Next, let’s update the application’s layout to display flash messages. Add these lines to app/views/application.html.erb somewhere in the body:

<% flash.each do |id, msg| %>
  <%= content_tag :div, msg, class: "flash_#{id}" %>
<% end %>

See it in Action

So we can actually test this out, let’s remove the default Rails view by adding our own home page:

$ rails g controller home show

And then add that as our root path in config/routes.rb:

Rails.application.routes.draw do
  root to: 'home#show'
end

To see it action, open up the application in your browser. Doing so will automatically create a new session record. Next, open a Rails console so you can see what’s going on:

session = Session.first
# => #<Session id: 1, session_id: "f28ae9f7dd2066023be808c3b337e390", data: "BAh7BkkiEF9jc3JmX3Rva2VuBjoGRUZJIjFZYnFUb25sSlFXVV...", created_at: "2014-06-16 19:49:07", updated_at: "2014-06-16 19:49:07">

If you inspect session.data, you’ll notice that it returns a Hash:

session.data
# => {"_csrf_token"=>"YbqTonlJQWUQKTU/jLQFZVha7EiqN+uFlGO8g5nFgkg="}

This is because it’s automatically serialized for you on the fly. Basically session.data can be treated the same as the session variable in a controller or view, except that you’ll have to call session.save to persist the data. Go ahead and try it out now by calling our custom flash method:

session.flash :notice, "hello world"
session.save

Now refresh your browser, and you’ll see the message appear. Refresh it again, and it won’t appear twice, just like any normal flash message.

Promotions

Let’s put this feature to use by creating a Promotion model that we can use to display promotions to our site’s visitors.

$ rails g model promotion name call_to_action:string active:boolean
$ rake db:migrate

The call_to_action will be the message displayed to the user. Setting active to false will disable the promotion. You can, of course, customize it however you’d like. For example, you might want to have a start and end date, or some prerequisites for targeting specific visitors. For this example we’re keeping it purposefully simple.

Let’s seed in a couple promotions. Add the following content to db/seeds.rb:

Promotion.create!(
  :name => "Clearance",
  :call_to_action => "Save up to 90% on select items! Discount code: OVERSTOCK",
  :active => true
)

Promotion.create!(
  :name => "Upsale",
  :call_to_action => "Add another widget to your cart now for a 25% discount",
  :active => true
)

Make sure to run rake db:seed to load the promotions into your database.

Now let’s edit our Session model and add a method we can use to display the promotions to our site’s visitors:

class Session < ActiveRecord::SessionStore::Session
  def flash(id, msg)
    data['flash'] ||= {'flashes' => {}, 'discard' => []}
    data['flash']['flashes'][id.to_sym] = msg
  end

  def display_promotion(id, track=true)
    viewed_promotions << id if track
    promotion = Promotion.find(id)
    flash :promotion, promotion.call_to_action
  end

  def viewed_promotions
    data['viewed_promotions'] ||= []
  end
end

Go ahead and test that out now if you’d like. You should still have the Rails server and console running. Enter this into the console:

reload!
promotion = Promotion.all.sample
Session.first.tap do |session|
  session.display_promotion(promotion.id, false)
  session.save!
end

Now refresh your browser, and you should see the promotion appear. Refresh it again, and it goes away as expected.

Notice I set track to false for this example. This is because we don’t really want to start keeping track of the promotions while testing it out in the console. Otherwise we’d have to clear our session if we want to test it out more than once.

Running the Promotions

To run the promotions we can use a background processor. For this example I’ll be using the clockwork gem.

Start by installing the gem:

$ echo "gem 'clockwork'" >> Gemfile
$ bundle

Next, create a file called lib/promotions.rb and add the following content:

require 'clockwork'

require_relative '../config/boot'
require_relative '../config/environment'

module Clockwork
  every(1.minute, 'random.promotions') {
    Session.all.each {|session|
      promotion = Promotion.all.sample
      next if session.viewed_promotions.include?(promotion.id)
      session.display_promotion(promotion.id)
      session.save!
    }
  }
end

I won’t go over this file in depth. If you’d like to know exactly how it works you can read the clockwork documentation. Basically what it does is display a random promotion to every visitor on your site every 1 minute.

Run this command to try it out:

$ bundle exec clockwork lib/promotions.rb

Note that bundle exec is required in this case.

The way clockwork functions, the promotion will be run immediately, and then every minute thereafter.

Tracking Active Vistors

By adding data to the sessions, we’re not actually targetting active visitors. The session could have been created days ago, and the visitor might not actually be on the site at the moment.

So let’s fix that. Add the following code to app/controllers/application_controller.rb:

before_action { session[:last_seen_at] = Time.now }

We can now keep track of when the last time a visitor actually used the application. Let’s add a scope for this to the Session model:

class Session < ActiveRecord::SessionStore::Session
  scope :active, lambda { all.select(&:active?) }

  def active?
    data['last_seen_at'] && data['last_seen_at'] > 1.minute.ago
  end
end

Now we can update our clockwork process to target only the active visitors:

require 'clockwork'

require_relative '../config/boot'
require_relative '../config/environment'

module Clockwork
  every(1.minute, 'random.promotions') {
    Session.active.each {|session|
      promotion = Promotion.all.sample
      next if session.viewed_promotions.include?(promotion.id)
      session.display_promotion(promotion.id)
      session.save!
    }
  }
end

Conclusion

This method allows you to push data to your users’ sessions. Alternatively, you could do the same exact thing by adding a few more columns to the Promotion model, and some code to the ApplicationController. It would be much simpler, as you wouldn’t need a background process, however, you’d be pulling the data instead of pushing it.

What’s the difference between pushing and pulling data?

The difference is that the server is directly communicating with your users when you push data. This opens up a lot of possibilities. For example, if you wanted to put your server into maintenance mode, you could have a process that you can run to let all of your site’s active visitors know that you’re about to shut them down.

For example. Add a bin/maintenance file to the application with the following code:

#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application',  __FILE__)
require_relative '../config/boot'
require_relative '../config/environment'

ttl = 30

until ttl == 0 do
  puts "#{ttl}"

  Session.active.each {|session|
    session.flash :alert, "Server shutting down in #{ttl} seconds."
    session.save
  }

  sleep(1)

  ttl -= 1
end

Session.active.each {|session|
  # clear flash messages so visitors don't
  # see the last one next time they sign on
  session.data['flash'] = nil
  session.save
}

Then make it an executable:

$ chmod +x bin/maintenance

Now your site’s active visitors will have a nice timed warning the next time you go to shut down the server. All you have to do is run bin/maintenance before you shut it down. To accomplish the same task via pulling data from the server would be considerably more complex, as you can imagine.

Using Foreman with Jekyll

When building a website with Jekyll, you might want the site to automatically rebuild when you make a change. You can do that with the jekyll build --watch command. Then of course you’ll want to serve the website so you can preview it in the browser. For that there’s the jekyll serve command.

Running both commands at the same time requires opening separate terminal tabs. This can be a little bit frustrating. Especially when you start adding more features to your site. For example, if you want to use compass to compile SASS, you’ll need to open a 3rd tab to run compass watch.

Doing this over and over again every time you want to edit your site can become a little tedious. Each time you’ll have to open at least two tabs, and remember which commands to run to get things started. And, if you haven’t worked on the site for a while, you might not remember all the components you’re using.

Fortunately there’s an awesome gem called Foreman that can help you out with this. Here’s how to get it up and running with Jekyll.

First, install the gem:

$ gem install foreman

Then, create a new file called Procfile in the root of your Jekyll project folder, and add the tasks you want running in parallel. Using the three tasks mentioned above as an example, your Procfile might look something like this:

build: jekyll build --watch
server: jekyll serve
compass: compass watch

To start all the processes in parallel, just run:

$ foreman start

Now you can manage and monitor all the processes in just one tab. Pressing CTRL+C will automatically send the KILL signal to every task for you.