I like working with Rails because I can easily and quickly create an application that works and show it to the world or just my friends. However, there are types of applications that do not need such a large framework as Rails and all its functionalities.
It may happen that our application needs only M (Model) out of the whole MVC pattern Model-Controller. Is it worth starting a project in Rails if we know that the V-C (View-Controller) part will not be needed?
It’s good to know that Active Record , Active Model, Action Pack and Action View, which are responsible for MVC, can be used independently outside Rails. This allows us to create a simple Ruby application that has a database connection and develop it without the unnecessary code and libraries that we would get in a package by running the rails new command.
I have described step by step how to achieve this and you can find the whole code on GitHub. The link is at the bottom of this article.
Structure
To start our project, we don't need much. Let's start by creating a Gemfile
where we add the gems we need to work on the application, along with the version of Ruby we will be using.
cat Gemfile
source 'https://rubygems.org'
ruby '2.7.2'
An optional README.md
file is to describe how our application works and how to continue working on it, both for ourselves and other developers who will want to develop the project with us in the future.
cat README.md
TO DO: Delete this and the text above, and describe your app
app
directory with application.rb
file, which will be responsible for configuration and loading libraries and files we will be adding in our application. Remember to run bundle install
to generate the Gemfile.lock
. The structure of our application at this stage should look like this:
tree
.
├── Gemfile
├── Gemfile.lock
├── README.md
└── app
└── application.rb
Database
With such a structure prepared, we can consider which database engine to choose and configure. For this article, I chose PostgresQL, with which I have the most experience. It can also be MySQL or SQlite3, or any other engine working with Active Record. When choosing technology, it is good to be guided by the purpose of the application, what it will be used for and what its purpose will be.
For a quick and simple database configuration, I used docker and docker-compose. I don't want to elaborate on the configuration of these tools, their pros and cons, but if you've never used docker before then I'd refer you to the official documentation for Docker and Docker Compose for more information.
version: '3.7'
services:
postgresql:
image: postgres:12.0-alpine
ports:
- 5432:5432
environment:
- PGDATA=/postgresql
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
volumes:
- db-volume:/postgresql
volumes:
db-volume:
We will also need to add to our Gemfile
and to our application.rb
file
require 'pg'
module Application
class Error < StandardError; end
end
Standalone Migrations, Rake
The next step in configuring our application is to add the standalone_migrations
and rake
gems, which will allow us to manage our migrations just like in Rails and gain access to rake db: commands.
- Update
Gemfile
with the necessary gems, and do a bundle install
gem 'standalone_migrations'
gem 'rake'
gem 'dotenv'
- Let's add a
Rakefile
to our project in the root directory, where we will load dotenv
and standalone_migrations
that we added earlier
require 'dotenv'
Dotenv.load
require 'standalone_migrations'
StandaloneMigrations::Tasks.load_tasks
With the Rakefile
configured this way, we can check if our rake
is working by using the rake -T
command, which should return a list of available commands in our application.

- Before
rake db:create
, we still need to have a configuration file in our project to connect to the Postgres instance. To do this, we need to create a db directory along with a config.yml
file that should look like the one below:
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch('MAX_THREADS') { 5 } %>
database: <%= ENV.fetch('DATABASE_NAME') %>
username: <%= ENV.fetch('DATABASE_USER') %>
password: <%= ENV.fetch('DATABASE_PASSWORD') %>
host: <%= ENV.fetch('DATABASE_HOST') %>
port: <%= ENV.fetch('DATABASE_PORT') %>
development:
<<: *default
test:
<<: *default
staging:
<<: *default
production:
<<: *default
As you can see, I used environment variables to configure the connection to our Postgres, where we will keep sensitive data that should not be in the repository. For this I used the previously added gem dotenv
, which was also added in the Rakefile
along with standalone_migrations
. If we are using Git to manage version control of our application, let’s remember to add a .gitignore
file where we will disable the possibility of tracking the .env
file from our project.
and add an.env
file containing the correctly configured ENV
DATABASE_NAME="development"
DATABASE_USER="postgres"
DATABASE_PASSWORD="postgres"
DATABASE_HOST="localhost"
DATABASE_PORT="5432"
-
At this stage, we should be able to run the rake db:create
command which will create the database

-
Let's try adding a new migration via rake db:new_migration name=
, where we create a posts
table with a :title
column

class CreatePosts < ActiveRecord::Migration\[6.0]
def change
create_table :posts do |t|
t.string :title
end
end
end

You should notice that the db/migrate
directory was automatically added and schema.rb
was created after successful migration. Currently, our project structure looks as follows:
tree
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── .gitignore
├── .env.example
├── app
│ └── application.rb
├── db
│ ├── config.yml
│ ├── migrate
│ │ └── 20210504135128_create_posts.rb
│ └── schema.rb
└── docker-compose.yml
Active Record
The last but not least, another step in creating our application is to add activerecord
and its configuration. For this, we will need to update our Gemfile with 3 more gems:
gem 'activerecord'
gem 'erb'
gem 'yaml'
Why we add erb
and ymal
is explained below in the comments. The entire active_record
configuration will be in the app/application.rb
file.
Let's go through what happens here, one by one:
ENV['ENVIRONMENT'] ||= 'development'
require 'pg'
require 'active_record'
require 'dotenv'
require 'yaml'
require 'erb'
Dotenv.load(".env.#{ENV.fetch('ENVIRONMENT')}.local", ".env.#{ENV.fetch('ENVIRONMENT')}", '.env')
def db_configuration
db_configuration_file_path = File.join(File.expand_path('..', __dir__), 'db', 'config.yml')
db_configuration_result = ERB.new(File.read(db_configuration_file_path)).result
YAML.safe_load(db_configuration_result, aliases: true)
end
ActiveRecord::Base.establish_connection(db_configuration[ENV['ENVIRONMENT']])
module Application
class Error < StandardError; end
end
We already have the configurations, so we can add the Post model in our ruby app.
`├── app`
`│ └── models`
`│ └── post.rb`
app/models/post.rb
class Post < ActiveRecord::Base;end
and remember to load the file in application.rb
require 'app/models/post'
Also, remember to add require 'app/runner'
to app/application.rb
If we want to add new files in our application, services, more models, we need to load them in application.rb
.
SUMMARY
Currently, our ruby application is ready to continue. We have configured:
- database connection,
- Active Record,
- Standalone migrations with rake
As you can see, it is not always necessary to use rails new
. This way we avoid unnecessary code in our application that is not used. We have more control over the development of our application. We can add more libraries and business logic over time. We can use such configured application to create a crawler or scraper, connect to external API from which we will retrieve information and store in our own database or load files and extract interesting information from them. I wish you good luck with the further development of your own applications!
BONUS
Our application also needs to be started somehow. We can do it in several ways, for example from the terminal. We can create an exe/app
file that will load our application logic from the 'app/application'
file and run our application through the Runner
service added in the app directory.
require 'bundler/setup'
$LOAD_PATH.unshift File.expand_path('..', __dir__)
require 'app/application'
Runner.start
class Runner
def self.start
puts 'Start'
end
end

Also remember to add require 'app/runner'
to app/application.rb
Code can be found on GitHub:
- https://github.com/dwatek/simple_ruby_app
Read More
GraphQL Ruby. What about performance?
Rails and Other Means of Transport
Rails Development with TMUX, Vim, Fzf + Ripgrep