DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Remember the environment!
edensongjbs
edensongjbs

Posted on • Updated on

Remember the environment!

How ignoring my Rails environment got me into trouble

A couple days into learning Rails, I was coming off after a frustrating week of managing tedious Sinatra syntax errors, malfunctioning gems, and inexplicably failing Capybara tests. I kept hearing that β€œRails is magic” and I was very ready to embrace some of that β€œmagic” and offload a lot of the manual and human error prone repetition of Sinatra to a framework that does much of the heavy lifting. However, my first experience with Rails left me wanting to pull out my beard. In fairness to Rails, and as with most coding issues, this was an easily resolved problem if only I had known where to look.

The assignment: I was given a partially built basic web application to track student enrollment, and I was tasked with building out some additional functionality and make a simple change to a table in the database, namely whether the student was currently "active" or "inactive." I needed to add an additional table column, and I decided to do this by modifying the original CreateStudents migration to create a new, larger table from scratch, rather than creating a new migration just to create the additional column in the existing table.

So I rolled back and re-migrated with the original migration file that I just modified. After seeding my data, I verified that the new attribute was showing up in my newly created student instances.

=> #<Student id: 1, first_name: "Daenerys", last_name: "Targaryen", active: false, created_at: "2020-06-20 21:42:23", updated_at: "2020-06-20 21:42:23">

...and my table schema...

ActiveRecord::Schema.define(version: 1) do
create_table "students", force: :cascade do |t|
t.string "first_name"
t.string "last_name"
t.boolean "active", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end

All good! However, when I went to run my capybara tests, I was failing. Capybara was claiming that the attribute and associated methods didn’t exist!!

 ActionView::Template::Error:
   undefined method 'active' for #<Student:0x00007fac65ac9108>  
Enter fullscreen mode Exit fullscreen mode

I had literally just confirmed that it was there! I used byebug to peer inside the test run, and to my astonishment, saw that the attribute was indeed missing from the Student instance in question...

<Student id: 1, first_name: "Daenerys", last_name: "Targaryen", created_at: "2020-06-21 00:08:53", updated_at: "2020-06-21 00:08:53">

So, what was happening? The answer lies in the way that Rails handles environments.

I felt fairly comfortable with the concept of an environment in Ruby and understood the utility of the environment.rb to help centralize all our require statements and wire up all our far flung classes and methods. I had seen references to the existence of different environments (test, development, production) within ActiveRecord and Rails, but hadn’t yet spent the time to truly understand what they are and how they work. I knew that the Capybara tests were somehow using a different set of data than I was, but hadn’t really thought through the mechanics of how this was accomplished.

It turns out that the tests operate in the capybara β€œtest” environment and my own data seeding, testing, and manipulation exist in the β€œdevelopment" environment. So, while I had modified my table in the β€œdevelopment” environment (rails treats this as default, if RAILS_ENV is not explicitly defined in the shell command), I had not properly rolled back and rerun the updated migration in the β€œtest” environment. Thus, the capybara tests were still using the old table schema.

bearded guy facepalm

ActiveRecord/Rails further complicates matters since there is only a single schema.rb for the project and it does not account for discrepancies between the databases for different environments, nor does it reflect the state of one environment in particular, but instead seems to update to reflect the most recent migration task.

So, what is the purpose of the environment? From what I've learned thus far, Rails tries to preempt our needs and define some default behaviors that would likely be useful in each stage of the project development cycle. One such aspect of this behavior is to separate the databases for each of the stages. The "test" database is populated according to the specific tests and purged between test runs, for instance.

There are far more environment implications beyond managing the project’s database, and each environment includes is own set of configuration settings and gems. Each of the predefined environments in Rails (you can define your own as well) is optimized for its set purpose. The "production" environment is optimized for server speed while the "development" environment is optimized for easy modification, allowing us to make changes to our app files without having to stop and restart our server. Pretty useful!

Some developers have shared their own approaches to using and creating some custom Rails environments, and I'll link to some of those below. Additionally, Rails Guides provides a ton of information when it comes to configuring components within each environment. In fact, Rails provides some very useful info about the purpose of each environment and component configuration as comments within the environment files themselves (inside the config/environments directory).

While I’m just at the beginning of my Rails journey and certainly no expert on the topic, I do want to urge you to take a minute to understand the implications of environments on your databases, and not make the same mistake that I did. It was a real headscratcher/beard-puller and I’m hoping I can help save a fellow Rails newcomer some precious debugging time on their next project.

The takeaway: if you’re running tests in the β€œtest” environment and have made any changes to your database, remember to add a RAILS_ENV=”test” to your rake/rails db: commands!

Resources
Ruby on Rails Guides: Configuring Rails Applications
Beyond the default Rails environments - Signal v Noise
Use of Ruby on Rails environments - Enrico Teotti
Creating Staging and other environments in Rails - Josef Strzibny

Top comments (0)

🌚 Life is too short to browse without dark mode