Writing

13 September 2016

Sinatra is a domain-specific language (DSL) for creating web applications in Ruby. Today, I'd like to briefly discuss a project I wrote with Sinatra, and use this discussion as an expository opportunity for some of the nice features that Sinatra offers.

My project, available here, via GitHub, is called GearTracker. GearTracker is a simple create-read-update-delete (CRUD) web application for bicycle components with password-protected user accounts. The application uses the model-view-controller (MVC) pattern, which helps ensure that disparate parts of the application are logically insulated from each other to reduce the probability of bugs. Let's discuss each part of this pattern separately.

The models are the core objects of interest in the application. Because GearTracker has users, each of whom has numerous components that fall into one or more categories, the models in my web application are User, Category, and Component. These entities are defined by Ruby classes which inherit from the ActiveRecord::Base class, which gives them access to a number of useful instance and class methods. One such method is belongs_to, a class method which takes a symbol representing the name of another class (which must also inherit from ActiveRecord::Base) and in turn specifies an association between models. For example, in the Category class definition in category.rb, I write `belongs_to :user` to specify that a Category object can belong to at most one User object. Reciprocally, in the User class definition in user.rb, I write `has_many :categories` to indicate that a User object may have any number of Category objects associated with it. There are several other associations in GearTracker, namely that a Category has many Components and a Component belongs to a Category. Because a User has many Categories and a Category has many Components, it follows that a User has many Components through its association with Category.


class Category < ActiveRecord::Base
  belongs_to :user
  /* Remaining code omitted. */
end


class User < ActiveRecord::Base
  has_many :categories
  /* Remaining code omitted. */
end

The views are .ERB templates which are a mixture of HTML5 and Ruby code, wrapped in special <% %> or <%= %> tags. The views are used to display information and interact with the user through forms. Embedded Ruby code within the views can be used to modify web pages dynamically based on code implemented within the controllers. One important example of embedded Ruby code within the views is the code which determines whether a warning message is displayed to the user upon unsuccessful form submission. In create_user.erb, the following conditional checks if a value has been set for the key :error in the FlashHash flash, and if so it displays each of the error messages stored in flash[:error]. Behind the scenes, the FlashHash flash is being set by code within the ApplicationController that validates the data being entered by the user.


<!-- Code omitted. -->
<div class="jumbotron text-center">
  <!-- Code omitted. -->
  <% if flash[:error] %>
    <% flash[:error].each do |warning| %>
      <p><%= warning %></p>
    <% end %>
  <% end %>
</div>
<!-- Code omitted. -->

This naturally brings us to the controllers, which parse routes for matches and execute code conditionally. The route is the part of a URI which follows the domain name, such as `/signup`. Controllers encapsulate the logic of any web application and help ensure that data is being passed between models and views correctly. Depending on the type of HTTP request that the controller receives, it can perform different types of action, such as displaying a web page or instantiating a new user. The `post '/signup'` method from ApplicationController, part of which is posted below, is executed when the create_user.erb form is submitted. Using data from the form which is stored in the params hash, ApplicationController determines if the data can be used to create and store in the database a valid new User instance. If the creation of the new User object fails, the :error key of the flash object is set to the value of an errorArray which contains strings describing each of the errors. This data is in turn delivered to the create_user.erb form and displayed, as discussed before.


post '/signup' do
  @user = User.new(username: params[:username], email: params[:email], password: params[:password])
  if @user.save
    session[:id] = @user.id
    redirect to('/categories')
  else
    errorArray = []
    @user.errors.messages.each do |key, value|
      value.each do |warning|
        errorArray << "#{key.capitalize} #{warning}. Please try again."
      end
    end
    flash[:error] = errorArray
    /* Code omitted. */
    redirect to('/signup')
  end
end

Sinatra is a flexible and robust DSL with many great features, but discussing all of them is beyond the scope of this short post. If you are interested in learning more about Sinatra, read the excellent documentation available on its website.


17 December 2015

Hello, and welcome to my blog. My name is Ed Karabinus, and although I studied mathematics as an undergraduate at the University of Chicago, I am now training to become a software developer through the Flatiron School’s Learn-Verified Program (L-V, henceforth). This blog will mainly be a space for various thoughts about computer science, programming, software development, and related subjects. With my first post, I thought I’d share some insight into the inner workings of a student project I wrote for L-V.

The assignment for this project was to write (and publish) a Ruby Gem that: (a) scraped or accessed an external data source; and (b) installed a command line interface (CLI) on installation. My response to this prompt was to write wiki_on_this_day, a small tool that uses Nokogiri, an excellent web-scraping gem, to pull the short abstracts listed on the English-language Wikipedia homepage under the “On This Day” historical section. For those unfamiliar, this section of the Wikipedia homepage lists short descriptions of noteworthy historical events which happened on the same date in the past. Here is the GitHub repository for my project.

Let’s look at some of the code for wiki_on_this_day, and take the opportunity to learn more about the general utility of Nokogiri. The main scraping method, which accepts the Wikipedia Homepage in the form of a Nokogiri XML document, parses it with the following code:

def on_this_day
  events_hash = {}
  self.html_doc.css('div#mp-otd div#mp-otd-img + ul li').each do |event|
    events_hash[event.css('b a').text] = {}
    events_hash[event.css('b a').text][:year] = event.css('a').first.text
    events_hash[event.css('b a').text][:text] = event.children.text.split(/ – /)[1]
    events_hash[event.css('b a').text][:link_url] = 'https://en.wikipedia.org' + event.css('b a')[0]["href"]
  end
  events_hash
end

We observe that the #css method, once called on the Nokogiri XML document, allows our program to parse through and select HTML tags in the same way that CSS does. Since the English-language Wikipedia homepage uses the semantic ID of “#mp-otd” to mark the “On This Day” section, it is straightforward to select the relevant elements within the section and store them as elements of a nested hash. The surface level of this hash contains five keys—one for each article/event featured in the On This Day section—each pointing to the value of another hash which contains the data about the relevant article. We store the year and the abstract of the event, so that wiki_on_this_day can display the same events featured on the homepage. The URL for the article corresponding to each event is also stored, in case the user requests more information about that event through the CLI for wiki_on_this_day.

There are a lot of other things I can say about this code, but I want to keep my posts concise. Please check out the repository and send me any questions via email. My email address is available through the RubyGems.org listing for wiki_on_this_day.

A final note: I hope to add image support to this gem in the future. The “On This Day” section typically features a small illustrative picture, and I’d like to add support for displaying this picture at the command line using the useful gem Catpix. As the diligent reader can see, there are some lines within my code (appropriately commented out) which stub out the basic features of this functionality. However, the dependency support for Catpix was giving me some issues in building wiki_on_this_day, so I left out this feature for now. If anyone would like to add this feature (or make other improvements), please don’t hesitate to fork the GitHub repository for wiki_on_this_day, work on the code, and submit a pull request. Happy coding!