DEV Community

Chukwuma Anyadike
Chukwuma Anyadike

Posted on • Edited on

The CRUD on the Backend of Active Record/Sinatra

I told you that CRUD will return in Active Record (refer to my other blog about The SQL to CRUD: The Use of Structured Query Language to Create, Read, Update, and Destroy). The time is now and CRUD is back. Today I will talk about CRUD in the context of Active Record and Sinatra.

Again, a table of muscles will be used, since it lends itself well to creating a database. Let us follow CRUD in Active Record to the letter.

First we will create a table by creating a migration. Migrations are created in order to create or modify a table. I will not go into too much detail about migrations because that is another topic of discussion.

rake db:create_migration NAME=create_muscles
Enter fullscreen mode Exit fullscreen mode

Once we create our migration a file is created which is appropriately named and timestamped (20230203201213_create_muscles.rb). The following code is added.

class CreateMuscles < ActiveRecord::Migration[6.1]
  def change
    create_table :muscles do |t|
      t.string :name
      t.string :origin
      t.string :insertion 
      t.string :action 
      t.string :innervation 
      t.string :blood_supply
      t.string :url
      t.integer :bodypart_id
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Note that this class Create Muscles inherits methods from ActiveRecord::Migration. It creates a table with the columns name, origin, insertion, action, innervation, blood_supply, url, and bodypart_id. All these are strings except bodypart_id which is an integer which refers to the primary key in another table called body_parts which is created in the same aforementioned way.

class CreateBodyparts < ActiveRecord::Migration[6.1]
  def change
    create_table :bodyparts do |t|
      t.string :name 
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

body_parts has a one to many relationship with muscles. See the ERD (entity relationship diagram) below.

one to many relationship

Speaking of that, let us finish the migration with this command.

rake db:migrate
Enter fullscreen mode Exit fullscreen mode

Now our schema is created which looks like this.

ActiveRecord::Schema.define(version: 2023_02_03_202632) do

  create_table "bodyparts", force: :cascade do |t|
    t.string "name"
  end

  create_table "muscles", force: :cascade do |t|
    t.string "name"
    t.string "origin"
    t.string "insertion"
    t.string "action"
    t.string "innervation"
    t.string "blood_supply"
    t.string "url"
    t.integer "bodypart_id"
  end

end
Enter fullscreen mode Exit fullscreen mode

Now we create the models. The models are classes which inherit methods from ActiveRecord::Base. These classes correspond to tables. Instances which correspond to rows can and will be created from these classes. Look at the models below for Bodypart and Muscle. These are in separate files but I display them in one code block for the reader. Since each model inherits methods there is very little code to write but the relationships are clearly defined to allow the tables to relate to each other. It is like real life. Body parts have many muscles and a muscle typically belongs to one body part.

class Bodypart < ActiveRecord::Base
    has_many :muscles
end

class Muscle < ActiveRecord::Base
    belongs_to :bodypart
end
Enter fullscreen mode Exit fullscreen mode

Now that our database is set up, I can finally talk about CRUD methods used to manipulate databases.

Create:

Now that we have our models established we can talk about the "C" in CRUD, create.

The new method creates a new instance while the save method saves an instance to the database. It is important to be aware that it is not enough to create a new instance but that instance has to be saved (persisted) to the data base. Lets take a generic model called User which has attributes name and job.

User.new(name: "Goku", job: "Saiyan with a level of over 9000")
User.name = "Goku"
User.job = "Saiyan with a level of over 9000"
Enter fullscreen mode Exit fullscreen mode

This creates an instance of Goku but does not persist him to the database. It means that the data exists but has not been added to the table. It's like a ball player with no team.

User.save
Enter fullscreen mode Exit fullscreen mode

This adds Goku to the database.

Goku

Now, this is all well and good, but there is an easier way to do this. The create method can be used. It combines the new and save methods into one. I demonstrate this using the create method to create an instance of muscle and persist it to the muscles database as demonstrated below.

Muscle.create(
 name: "deltoid",
 origin: "lateral one-third of the clavicle, acromion, the lower lip of the crest of the spine of the scapula",
 insertion: "deltoid tuberosity of the humerus",
 action: "abducts arm; anterior fibers flex & medially rotate the arm; posterior fibers extend & laterally rotate the arm",
 innervation: "axillary nerve (C5,6)",
 blood_supply: "posterior circumflex humeral a.",
 url:
  "https://cdn-aolkg.nitrocdn.com/JEsNUzsMoDdLqhSXkopLhNFWnBniacqf/assets/images/optimized/rev-5038be8/wp-content/uploads/2014/02/deltoids-muscle-attachments.jpg",
 bodypart_id: 264
)
=> #<Muscle:0x0000564b01685f40
 id: 415,
 name: "deltoid",
 origin: "lateral one-third of the clavicle, acromion, the lower lip of the crest of the spine of the scapula",
 insertion: "deltoid tuberosity of the humerus",
 action: "abducts arm; anterior fibers flex & medially rotate the arm; posterior fibers extend & laterally rotate the arm",
 innervation: "axillary nerve (C5,6)",
 blood_supply: "posterior circumflex humeral a.",
 url:
  "https://cdn-aolkg.nitrocdn.com/JEsNUzsMoDdLqhSXkopLhNFWnBniacqf/assets/images/optimized/rev-5038be8/wp-content/uploads/2014/02/deltoids-muscle-attachments.jpg",
 bodypart_id: 264>
Enter fullscreen mode Exit fullscreen mode

We have just created a muscle and added it to the database. Note that an id is automatically generated, it is autoincrementing.

Read:

Now that we can create rows of data in a table it is imperative that we can access this data. This is the "R" is CRUD which is read. There are many methods here but I will highlight the commonly used ones. Let us use the Muscle model. Think of the database as an array of instances (technically the data from the instances) stored in a table.

  • Muscle.all returns a collection with all muscles.
  • Muscle.first returns the first muscle.
  • Muscle.find(3) returns the muscle with an id number of 3.
  • Muscle.find_by(name: "deltoid") finds a muscle with the name of deltoid. This can be used for any attribute within that class.
  • Muscle.where(bodypart_id: 5, action: "flex the knee").order(name: :desc) finds all muscles located in a body part with the identifier of 5 which flex the knee in descending order by name.

Update:

We can read and find data in a table now. Let us talk about the "U" in CRUD, update. We can update a single record by attribute(s) using the update method.

deltoid = Muscle.find_by(name: "deltoid")
deltoid.update(bodypart_id: 250)
Enter fullscreen mode Exit fullscreen mode

We can also update several records at the same time using the update_all method.

User.update_all(job: "Super Saiyans with level > 9000")
Enter fullscreen mode Exit fullscreen mode

Delete:

The "D" is CRUD is delete/destroy. The methods are straightforward. For a single record we can use the destroy method. For multiple records we can use the destroy_by or destroy_all method.

biceps = Muscle.find_by(name: "biceps brachii")
biceps.destroy

Muscle.destroy_by(name: "biceps brachii") //deletes all muscles named "biceps brachii"

Muscle.destroy_all //deletes all muscles
Enter fullscreen mode Exit fullscreen mode

Now we can perform CRUD methods on our records in the database. With the addition of Sinatra we can create a backend with our records which can be accessed from the front end. We can use CRUD methods to do this. Here is a quick overview. Note that below we are using and HTTP method combined with a relative path to make changes to the backend server. The combination of an HTTP method and a relative path is called a route.

Create: the use of post

    post '/muscles' do
      new_muscle = Muscle.create(
        name: params[:name], 
        origin: params[:origin], 
        insertion: params[:insertion], 
        action: params[:action], 
        innervation: params[:innervation], 
        blood_supply: params[:blood_supply], 
        url: params[:url],
        bodypart_id: params[:bodypart_id].to_i
        )
      new_muscle.to_json
    end
Enter fullscreen mode Exit fullscreen mode

This post method involves creating (create method) a new instance of muscle in the class Muscle and adds it to the database. Its value is assigned to new_muscle. A unique id is assigned to it. Its attributes are assigned values from the params hash (sent by the frontend). It then converts it to JSON format (serialization) and persists it to the backend with a relative path of /muscles using the to_json method.

Read: the use of get

    get '/muscles' do
      muscles = Muscle.all.order(:name)
      muscles.to_json
    end
Enter fullscreen mode Exit fullscreen mode

This get method involves accessing all (all method) of the muscles in the table muscles and converting it to an array of instances in alphabetical order (using the order method) by the attribute name. This array is assigned to the variable muscles. It then converts it to JSON format (serialization) and persists it to the backend with a relative path of /muscles using the to_json method.

Update: the use of patch

    patch '/muscles/:id' do
      updated_muscle = Muscle.find_by(id: params[:id])
      updated_muscle.update(
        origin: params[:origin], 
        insertion: params[:insertion], 
        action: params[:action], 
        innervation: params[:innervation], 
        blood_supply: params[:blood_supply]
      )
      updated_muscle.to_json
    end
Enter fullscreen mode Exit fullscreen mode

This patch method involves finding an instance of Muscle with the id value from the params hash. The update method is used to update that instance of muscle with by assigning attributes values from params. The value of this instance is assigned to updated_muscle. It then converts it to JSON format (serialization) and persists it to the backend with a relative path of /muscles using the to_json method.

Delete: the use of destroy

    delete '/muscles/:id' do
      muscle = Muscle.find(params[:id])
      muscle.destroy
      muscle.to_json
    end
Enter fullscreen mode Exit fullscreen mode

This delete method involves finding an instance of Muscle with the id value from the params hash. This instance is assigned to muscle. It is subsequently deleted from the database using the destroy method and serialized using to_json. It is deleted from the backend.

Now we can use CRUD methods to create and manipulate both the database and the server. Now with a functional React frontend I have created an interactive muscle anatomy chart which looks like this.

Muscle anatomy

This the power of CRUD combined with a Ruby based Active Record/Sinatra backend and a React frontend. Thank you. The CRUD prequel will come soon to bring an end to the trilogy.

Top comments (0)