Learning more about Ruby on Rails ActiveRecord

Danny Lee
Danny Lee
May 16, 2020 · 7 min read
https://upload.wikimedia.org/wikipedia/commons/c/c3/Kayagumplayer2.jpg

Before starting at the Flatiron school last year, I had never heard of Ruby or Rails, and the friendliness and human-readable format was strange coming from a background that was scared off from computer science by the C language.

After finishing up in January, I found myself returning to the same coding patterns I learned in school, but I felt I was missing out on a lot. Soon after joining LinkedIn to start my job hunt I signed up for the free premium month (mostly for the InMail credits), but I was soon drawn to the LinkedIn Learning section. There’s tons of good courses for everything from programming, interviewing and career insights, project management, software and business skills. In particular I found a course by Kevin Skoglund very useful in taking a deeper dive into Ruby on Rail’s ActiveRecord and highly recommend his courses at Lynda.com or LinkedIn Learning.

Being Efficient with Database Calls

One of the key takeaways I got from Kevin’s course was that the way in which we write our code dictates how ActiveRecord interacts with the database. When working on projects at Flatiron School, my focus was on learning basic concepts, rather than efficiency or performance. Those concepts weren’t even on the radar.

However, the more I apply for jobs and talk to startup founders, I realize understanding the challenges of scaling is paramount in being able to contribute usefully to the conversation that goes into something like the product roadmap.

For example: If I have a portfolio project running Rails and accessing a database on localhost with a Class of objects named Gayageum and I seed it with 100 instances.

https://media.giphy.com/media/l0ErPUsWJ2X6TvXj2/giphy.gif

The way I would change a common attribute (e.g. ‘color’) in every instance of Gayageum is by, first, getting all my Gayageum objects, assigning them to a variable. Then, using an enumerator (like each or map ) to iterate through each item, and setting the attribute to the new value. Let’s do this in Rails console.

2.6.1 :001 > allMyGayageum = Gayageum.all
(0.7ms) SELECT sqlite_version(*)
Gayageum Load (0.2ms) SELECT "gayageums".* FROM "gayageums" LIMIT ? [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Gayageum id: 101, name: "bgjqqwgpjq", color: "indigo", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 102, name: "ydswvodwcv", color: "blue", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 103, name: "carywjthmi", color: "yellow", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 104, name: "yvbdhsfdci", color: "ivory", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 105, name: "fmpdnjyzkk", color: "azure", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 106, name: "fquiretgrl", color: "fuchsia", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 107, name: "sscjdjmttb", color: "tan", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 108, name: "gmnsdbdadc", color: "fuchsia", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 109, name: "kjicjaqnur", color: "orchid", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 110, name: "ggkmxwtvis", color: "lime", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, ...]>

If you’ll notice, there’s a SQL log statement right after my command which is SELECTing records from the database. Later, this SQL debug logging feature will be useful. It will enable us to see the difference between different ActiveRecord methods and the SQL they generate for you. You can turn this on by entering (more info here) in the console:

ActiveRecord::Base.logger = Logger.new(STDOUT)

It will return the instance of the Logger object:

2.6.1 :007 > ActiveRecord::Base.logger = Logger.new(STDOUT) => #<Logger:0x00007fbf1da96788 @level=0, @progname=nil, @default_formatter=#<Logger::Formatter:0x00007fbf1da96738 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x00007fbf1da965f8 @shift_period_suffix=nil, @shift_size=nil, @shift_age=nil, @filename=nil, @dev=#<IO:/dev/ttys008>, @mon_mutex=#<Thread::Mutex:0x00007fbf1da96530>, @mon_mutex_owner_object_id=70229406561020, @mon_owner=nil, @mon_count=0>>

Now that we’re all set, we can use this variable, allMyGayageum to change our colors. Remember, our variable contains all of our Gayageum. So, I can go ahead and use the each method to loop through our array and change each instance of Gayageum to a ‘silver’ color.

2.6.1 :008 > allMyGayageum.each do |singleGayageum| singleGayageum.color = "silver" end
D, [2020-05-16T17:46:38.182836 #24167] DEBUG -- : Gayageum Load (1.2ms) SELECT "gayageums".* FROM "gayageums"
D, [2020-05-16T17:46:38.183813 #24167] DEBUG -- : ↳ (irb):8:in `irb_binding'
=> [#<Gayageum id: 101, name: "bgjqqwgpjq", color: "silver", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 102, name: "ydswvodwcv", color: "silver", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 103, name: "carywjthmi", color: "silver", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">
...[95 deleted lines]...21:34:57">, #<Gayageum id: 199, name: "csmnabwxmp", color: "silver", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">, #<Gayageum id: 200, name: "aycbvxlsdq", color: "silver", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 21:34:57">]

The call that ActiveRecord makes to the database in this case is SELECT, which retrieves the data for each object. This is an important point to make because the Active Record uses Object Relational Mapping (ORM) which stores objects in memory created from data that is stored in the database. So, we are not just changing the attribute of color to ‘silver’, we are getting the entire list of objects, creating an instance in memory, and then manipulating them.

This might seem okay with 100 objects, it takes but an instant. However, like Kevin repeats many times in many different examples, what if there were 100,000 or even 1,000,000 Gayageum. Do you think there would be a performance hit? You can bet your bottom dollar there will be.

We haven’t even saved the changes to the database. When we do that, we see we have more database calls:

2.6.1 :008 > allMyGayageum.each do |singleGayageum| singleGayageum.save end
(0.1ms) begin transaction
Gayageum Update (0.7ms) UPDATE "gayageums" SET "color" = ?, "updated_at" = ? WHERE "gayageums"."id" = ? [["color", "silver"], ["updated_at", "2020-05-16 22:09:17.145282"], ["id", 101]]
(1.3ms) commit transaction
(0.1ms) begin transaction
...[99 deleted transactions]...
=> [#<Gayageum id: 101, name: "bgjqqwgpjq", color: "silver", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 22:09:17">,
...[99 deleted updates]...

The same goes for if we used the update method, each transaction is made up of a retrieval and update:

2.6.1 :004 > allMyGayageum.each do |g| g.update(:color => "gold") end
(0.1ms) begin transaction
Gayageum Update (0.7ms) UPDATE "gayageums" SET "color" = ?, "updated_at" = ? WHERE "gayageums"."id" = ? [["color", "gold"], ["updated_at", "2020-05-16 22:07:34.881708"], ["id", 101]]
(2.1ms) commit transaction
...[99 deleted transactions]...

Each one taking roughly 2.2 ms, with a 100,000 records this would be roughly 3.67 minutes, a million records 36+ minutes. I don’t even know how these kind of real world situations are dealt with yet, but I do see the value in using a different method Kevin suggested, a class method that sends a single SQL statement to update the records, without instantiating each instance.

2.6.1 :010 > Gayageum.update_all(:color => "platinum")
Gayageum Update All (4.4ms) UPDATE "gayageums" SET "color" = ? [["color", "platinum"]]
=> 100
2.6.1 :011 > Gayageum.last
Gayageum Load (0.4ms) SELECT "gayageums".* FROM "gayageums" ORDER BY "gayageums"."id" DESC LIMIT ? [["LIMIT", 1]]
=> #<Gayageum id: 200, name: "aycbvxlsdq", color: "platinum", created_at: "2020-05-16 21:34:57", updated_at: "2020-05-16 22:09:17">
2.6.1 :012 >

The time this took was 4.4ms to change all the records. Amazing!

The disadvantage of this method is that because I don’t have an instance of each object, I can’t access, manipulate or validate the objects. So, this is one of those cases where knowing what each method does and the advantages and disadvantages of each is important.

There are other Class methods available for destroying or delete objects, using an argument of an id:

Gayageum.destroy(1) Gayageum.delete([7, 17]) Gayageum.destroy_all

The Struggles of Learning — Epilogue

I’ve always struggled to learn my whole life, and even though I was always considered pretty smart (specialized high schools, high PSAT/SAT, IQ tests) I’ve failed a considerable amount of times (dropped out of specialized high school, failed out of multiple colleges, poor social skills) and went through periods of deep depression and frustration with the annoying contradiction of being able to grasp new concepts, but failing to retain or build on learnings.

My struggles continue to this day, but I’ve better learned to grasp at my strengths and find coping mechanisms to help me better my understanding. There’s a theory about learning which says that we can better retain what we learn if we are repeatedly exposed to it, the forgetting curve. I’ve learned to not knock myself down because I learn something and then forget it, or when I can’t understand something. Whereas, in the past I would get frustrated and give up, now I am more forgiving and willing to learn a little bit, let it simmer and then go back to it later and learn a little more, rinse, and repeat.

This dual pronged strategy of being 1) gracious towards my self and my weaknesses and 2) being persistent and not saying ‘its too difficult’ or ‘too much’, but accepting that things take time, some more than for others. And I’m okay with being one of those that takes a while to get things and really understand them and be able to recall them.

Google has helped a lot and keeping copious notes and git gists of commands and syntaxes that I often need but forget. The amazing world of cloud/computers we have today is a godsend to people like me. For those of you reading my blogs and understanding where I’m coming from, my best advice is to keep pushing, keep your chin up and sometime soon, everything will fall into place, or it wont, and that’s fine too.

Lastly, I highly recommend Kevin Skoglund’s courses. His in-depth dives into Ruby (especially, but also git and other languages) are really helpful in seeing all the possibilities using Rails and ActiveRecord. Like I’ve said before, when the only tool you have is a hammer everything looks like a nail — know your tools well and you’re golden my friend!

https://media.giphy.com/media/3o7qDDpvuvTiITUsdW/giphy.gif

The Startup

Get smarter at building your thing. Join The Startup’s +788K followers.

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Danny Lee

Written by

Danny Lee

a nyc based coding enthusiast, handyman, farmer at-heart, artist and future surfer

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +788K followers.

Danny Lee

Written by

Danny Lee

a nyc based coding enthusiast, handyman, farmer at-heart, artist and future surfer

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +788K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store