DEV Community

Cover image for Put this in your {app} and {control} it, or how Sinatra serenades our data
Joshua Mayhew
Joshua Mayhew

Posted on

Put this in your {app} and {control} it, or how Sinatra serenades our data

As a quick preamble and summation of my Flatiron journey and thoughts on Ruby thus far, phases 1 and 2 of the software engineering bootcamp comprised a comprehensive and rigorous overview of frontend development (I.e. JavaScript and React.js), while phase 3 signaled the beginning of a paradigmatic shift toward learning the principles and practices of programming on the backend via Ruby. In striking contrast to the immediacy of being able to see code reflected in the DOM and being able to directly log code in the browser console, backend programming in Ruby presented me with a near total exclusivity of working and testing in the terminal.

At first, this lent an air of relative mystery and mystification to the idea of learning how to work in the backend. How could I build something if the data and methods and testing all take place in my terminal and, furthermore, what would I really be building if the backend deals in the realm of what is felt but not directly seen by users in the browser/client side of programming??

As I delved deeper, however, I soon realized that Ruby is a syntactically straightforward and powerful language that simplifies and streamlines code, and that the gulf between JavaScript and Ruby was not nearly as big as I had first thought when viewing lines of code scrolling down the terminal window. After familiarizing myself on how to write code in Ruby and 'puts' output to the terminal, I have rapidly embraced both the new environment and nature of backend programming.

In the more specific context of building my phase 3 project, I have since gained a far deeper and more practical grasp of MVC through my exposure to full stack development. As detailed on MDN https://developer.mozilla.org/en-US/docs/Glossary/MVC, the Model View Controller (MVC) describes a core pattern in software design that "emphasizes a separation between the software's business logic and display"(MDN, 2023). Aiming at a better division of labor for software applications, MVC delineates three distinct parts to ensure a clear separation of concerns:

  1. Model: Defines and manages data in db and handles business logic
  2. View: Handles layout and display
  3. Controller: Routes commands to the model and view

To illustrate this division more practically, a simple shopping list app wants to list the name, quantity, and price of every item that a user would want to purchase for the week. In this particular use-case, the view would be everything the user sees and interacts with on the front end (e.g. user clicking an 'add to cart' button).

Following the user event, input is relayed to the controller, which acts as an intermediary between the front and back ends by handling the control logic in order to manipulate data in the model (e.g. receives the input from 'add to cart' click and notifies model to 'add item'. Finally, the model updates to reflect the item being added, which is in turn relayed to the view whereby the user can view the shopping list item as added to the cart.

MVC

Focusing on the controller part specifically, I utilized Sinatra to facilitate the requests/responses between my project's front and back ends. Sinatra is a small-scale, lightweight framework that allowed me to create a RESTful Ruby-based API. The routes that determine and direct how client-side user input is going to access and manipulate server-side data that gets returned as iterable json objects reside in the application_controller.rb file. Here, API endpoints are established and model methods can be invoked and executed on class models to return specific data.

The first and most broadly encompassing json object I wanted to return for my project was a nested collection of trip data including bookings and users. Because I created my database as having a many-to-many relationship between Trips and Users, joined by a UsersTrips table, I set up my first endpoint as follows:

get '/trips' do
trips = Trip.show_recently_active_trips.limit(10)
trips.to_json(only: %i[title budget start_date end_date img id], include: {
users_trips: { include: :user }
})
end

Here, the API is constructed and can be pinged via the /trips endpoint. When fetching data from the front end, a get request can be made to this endpoint in order to communicate with the backend and receive a response... and what facilitates this cycle is the controller, and, what facilitates this same front and back end interaction in the case of my project, is Sinatra. Sinatra sets up the API endpoints that manipulate the data and inform the db of what it needs to update. My trips endpoint is calling a .show_recently_active_trips and .limit method that I constructed in the Trip model to manipulate my Trip class and return the objects in particular structure and order.

Then, having my data organized as I like it, I can use .to_json to parse the data into an iterable json object that is compatible with JavaScript on the front end. Included in the .to_json are the only: and included: keywords that help to further filter and structure the content of the json object. I chose to only: return the title, budget, start_date, end_date, img, and id keys of my trip object, while include allows me to add additional nested object data because of the relational associations between Trips and Users on the backend.

So, following the Trip data, I can include the join table users_trips, and through users_trips, I can include each user that corresponds to a users_trip. Effectively, this single get request returns all of my individual trips in the database, each trips' userstrips bookings on record, and all of the users attached to each trip via their userstrip. The nesting will log in the console as follows:

Many Trip json objects
Code snippet

have many userstrips json objects that contain a single user each

UsersTrip json Objects:
Code snippet

User Objects
Code snippet

and therefore give a Trips object access to multiple userstrips and users.

Through Sinatra deployment in my project, I was able to set up endpoints in an API that facilitated user input requests that manipulated date on the backend and subsequently returned that updated data to the front end. As the controller in MVC, Sinatra performed a vital function in facilitating interactions between client and server sides and allowed me to create an app that renders trips wherein users can sign up, view details, and create new trips. In utilizing Sinatra, I was able to better experience and thereby understand the importance of MVC as a fundamental programming concept.

Sources:

  1. https://developer.mozilla.org/en-US/docs/Glossary/MVC

Top comments (0)