This is a truly minimalist Rails application which has every thing in one
file. Rails is a one-person framework which allows you move fast from zero to
one. This takes the "quickly prototype" aspect of that previous statement and
puts it on steroids. Now, you can test that small idea you had without
dealing with everything rails new comes with. If you're like me, you're
thinking multiple things at once at this point. Microservices can now be
easily spun up and deaded with concepts like this. And… (my favourite)… it
may be possible to run Rails within shared hosting, if you can execute a ruby
script. Lastly, this shows you all it takes to get something on-screen using
Rails. Note that this is far from a secure application. You should never
NEVER do this in production. Use this as a learning tool, or a minimalist's
beginning; but build on it to use Rails in the way which best fits your
paradigm.
Caution
Never run OneFileRails, as is, in production.
In this example, I create a file with a simple landing page, and a posts resource. Posts here only has the index action which simply renders its heading; but you can see how this could be easily built into the full resource.
Running one-file-rails is simple.
- Clone
- Run
rackup -p [PORT]- You should have the
rackupgem installed
- You should have the
require "bundler/inline"Bring in all the gems we need.
This is made possible by bundler-inline.
Think of this the same way you would your Gemfile. This section can be as verbose as that if you wish
gemfile(true) do
source "https://rubygems.org"
gem "rails", "~> 8"
gem "puma"
gem "sqlite3"
endRails has grown over the years into a huge framework. Its convention brings in a lot of best practices on what the framework thinks your app may need. With OneFileRails you can choose to only bring in the parts of Rails you would need.
Here, I'm building a simple M (active_record), V (action_view), C
(action_controller) application. If you're building something which does not
have a view — say a service which only speaks JSON, and you can handle your
JSON templating in your own way — you don't need the V bit.
require "active_record/railtie"
require "action_controller/railtie"
require "action_view/railtie"I'd like to store my Ms in a database. For simplicity, I'm using SQLite3.
This simply specifies the location of the database we would be using. If this
was MySQL or PostgreSQL, this would be the connection string without the
protocol. For other data management systems, this would be the identifier
string to your schema instance.
database = "development.sqlite3"Ever wondered what happens when you run :point-up: this command when you're starting a new application? Well, here's some barebones version of oit. We create the database, connect to it, and ensure the schema is loaded. Now in main Rails, there's the migrate step which would take your migration files and create a schema out of it. That also manages our schema; giving us all that goodness of rolling back changes to our schema structure. In this setup, we do not have such goodness. We must ensure that our schema is up to date by ourselves. We still have the beauty of specifying the schema using the DSL Rails provides; so the regression is not at old-school PHP yet.
ENV["DATABASE_URL"] = "sqlite3://#{database}"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: database)
ActiveRecord::Base.logger = Logger.new($stdout)
ActiveRecord::Schema.define do
create_table :posts, force: true do |t|
t.string :title
t.text :body
t.timestamps
end
endNow we have our "infra" setup, we can define our application. Look how little we need to get up and running! Now, compared to main rails, there are configs for three different environments, and special initializers for specific tooling we may need. In OneFileRails, we have one file, one environment, one configuration. For now, all requests are local requests. If you're taking OneFileRails to production, ensure you remove that. OneFileRails is also based on Rails 8.0; which means you have all the nice things of Rails 8 with OneFileRails.
class OneFileRails < Rails::Application
config.root = __dir__
config.consider_all_requests_local = true
config.secret_key_base = "b05c717ee236c33644094b2f32daf4a27a4f335adbec083c3be786cff353cb71bcbcc064036ffcf29ba5e35eb3ee60cbaf55fc3b66569ec47c90a7485d9a8172"
config.load_defaults 8.0
config.eager_load = true
routes.append do
root to: "welcome#index"
resources :posts
end
endThis section should be readily recognizable to modern web developers. But to
flog a dead horse — and because more content is good content — this section
defines the handlers behind what you see in the routes.append block above.
For simplicity, the response is from an inline rendering. How main Rails
handles this is to write the templated response in some other file, and use a
templating engine (most preferably ERB) to render this file to the response
format. Remember that what matters is the response sent through the HTTP
connection; it can be anything. In OneFileRails you have the flexibility to
design your responses in the best way you deem fit; free from the templating
engines, free from the view hierarchies, and other factors you may deem
constricting.
class WelcomeController < ActionController::Base
def index
render inline: <<-HTML
<h1>Welcome#index</h1>
HTML
end
endOur Post model without any model-specific logic.
class Post < ActiveRecord::Base; endThis bit is the most exciting to me because this here is technically the data
layer in the application. There has been too much talk about ORM vs.
DataMapper, entities vs. models, and some other vs. battles which you're never
the one pushing the buttons or holding the controller (no pun intended). Now,
you can be free from all that noise and design your data layer the way you deem
fit. Notice that main Rails uses ApplicationRecord as a catch-all for all
model definitions; and that's conveniently missing here. With this, there's now
the possibility of having an ApplicationRecord if you deem fit, but also the
possibility of having a SomeRecord, SomeMapper, or SomeRepository as base
class.
class PostsController < ActionController::Base
def index
render inline: <<-HTML
<h1>Posts</h1>
HTML
end
endWith all that setup, we then initialize our application and run it as a Rack application. Sinatra folks would appreciate this, because this is how most Sinatra apps end up being run.
OneFileRails.initialize!
run OneFileRails- Prototype ideas quicker
- Learn Rails internals
- Re-pattern the framework
- Event-driven,
- bounded-contexts,
- modular-monolith,
- fun-follows-function,
- …are all possible
- Cherrypick ideas
…basically invert everything in the previous section