I'm loving Ruby on Rails. If you're not aware of it, it's a framework for developing web sites. It's built around a scripting language called Ruby and is highly opinionated. This means, there's a Rails (Ruby on Rails) way to do things, and it makes your life easier if you forego what you know and learn the Rails way instead.
I'm a Comp Sci grad who has learned quite a few languages at different stages, none of them to expert level. Starting to learn Rails has been a real brain freeze because there's so much going on under the covers that I can't see and I have to trust that the framework is doing the right thing while I'm only touching the 'high level' stuff.
Here's an example. What if I'm building a web site that, in part, deals with a list of cars. If you know something about database normalisation, you'll know you should deal with marques (car brands, eg. Toyota) as their own thing. Then you can attach a car brand to a car model (Toyota Camry). So let's create a list of car brands (marques) in a web application using Rails. Sorry if you're a Windows user, this code is from my Mac.
For future reference, these are the software versions I'm using:
~
▶ cd code
~/code
▶ cd Rails
~/code/Rails
▶ ruby -v
ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e) [arm64-darwin22]
~/code/Rails
▶ rails -v
Rails 7.0.4.2
~/code/Rails
▶ sqlite3
SQLite version 3.39.5 2022-10-14 20:58:05
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> ^D
~/code/Rails
▶
Rails does all the heavy lifting for you. This is a long listing so feel free to scroll and read. Create a Rails web site like this.
~/code/Rails
▶ rails new Cars
create
create README.md
create Rakefile
create .ruby-version
create config.ru
create .gitignore
create .gitattributes
create Gemfile
run git init from "."
Initialized empty Git repository in /Users/prawnhead/Code/rails/Cars/.git/
create app
create app/assets/config/manifest.js
create app/assets/stylesheets/application.css
create app/channels/application_cable/channel.rb
create app/channels/application_cable/connection.rb
create app/controllers/application_controller.rb
create app/helpers/application_helper.rb
create app/jobs/application_job.rb
create app/mailers/application_mailer.rb
create app/models/application_record.rb
create app/views/layouts/application.html.erb
create app/views/layouts/mailer.html.erb
create app/views/layouts/mailer.text.erb
create app/assets/images
create app/assets/images/.keep
create app/controllers/concerns/.keep
create app/models/concerns/.keep
create bin
create bin/rails
create bin/rake
create bin/setup
create config
create config/routes.rb
create config/application.rb
create config/environment.rb
create config/cable.yml
create config/puma.rb
create config/storage.yml
create config/environments
create config/environments/development.rb
create config/environments/production.rb
create config/environments/test.rb
create config/initializers
create config/initializers/assets.rb
create config/initializers/content_security_policy.rb
create config/initializers/cors.rb
create config/initializers/filter_parameter_logging.rb
create config/initializers/inflections.rb
create config/initializers/new_framework_defaults_7_0.rb
create config/initializers/permissions_policy.rb
create config/locales
create config/locales/en.yml
create config/master.key
append .gitignore
create config/boot.rb
create config/database.yml
create db
create db/seeds.rb
create lib
create lib/tasks
create lib/tasks/.keep
create lib/assets
create lib/assets/.keep
create log
create log/.keep
create public
create public/404.html
create public/422.html
create public/500.html
create public/apple-touch-icon-precomposed.png
create public/apple-touch-icon.png
create public/favicon.ico
create public/robots.txt
create tmp
create tmp/.keep
create tmp/pids
create tmp/pids/.keep
create tmp/cache
create tmp/cache/assets
create vendor
create vendor/.keep
create test/fixtures/files
create test/fixtures/files/.keep
create test/controllers
create test/controllers/.keep
create test/mailers
create test/mailers/.keep
create test/models
create test/models/.keep
create test/helpers
create test/helpers/.keep
create test/integration
create test/integration/.keep
create test/channels/application_cable/connection_test.rb
create test/test_helper.rb
create test/system
create test/system/.keep
create test/application_system_test_case.rb
create storage
create storage/.keep
create tmp/storage
create tmp/storage/.keep
remove config/initializers/cors.rb
remove config/initializers/new_framework_defaults_7_0.rb
run bundle install
Fetching gem metadata from https://rubygems.org/...........
Resolving dependencies...
Using rake 13.0.6
Fetching concurrent-ruby 1.2.2
Fetching minitest 5.18.0
Using builder 3.2.4
Using racc 1.6.2
Using nio4r 2.5.8
Using websocket-extensions 0.1.5
Using marcel 1.0.2
Using mini_mime 1.1.2
Using date 3.3.3
Using timeout 0.3.2
Using erubi 1.12.0
Using bundler 2.3.26
Fetching rack 2.2.6.4
Using bindex 0.8.1
Using crass 1.0.6
Using regexp_parser 2.7.0
Using public_suffix 5.0.1
Using matrix 0.4.2
Fetching msgpack 1.7.0
Using zeitwerk 2.6.7
Using rexml 3.2.5
Using rubyzip 2.3.2
Using websocket 1.2.9
Using io-console 0.6.0
Using method_source 1.0.0
Using websocket-driver 0.7.5
Using net-protocol 0.2.1
Using puma 5.6.5
Fetching addressable 2.8.2
Using thor 1.2.1
Fetching sqlite3 1.6.2 (arm64-darwin)
Using nokogiri 1.14.2 (arm64-darwin)
Using net-pop 0.1.2
Using net-smtp 0.3.3
Fetching reline 0.3.3
Using net-imap 0.3.4
Fetching selenium-webdriver 4.8.6
Installing rack 2.2.6.4
Installing msgpack 1.7.0 with native extensions
Installing minitest 5.18.0
Fetching loofah 2.20.0
Installing addressable 2.8.2
Installing reline 0.3.3
Using xpath 3.2.0
Using mail 2.8.1
Fetching rack-test 2.1.0
Installing concurrent-ruby 1.2.2
Fetching irb 1.6.3
Using i18n 1.12.0
Using tzinfo 2.0.6
Using sprockets 4.2.0
Fetching activesupport 7.0.4.3
Installing loofah 2.20.0
Using rails-html-sanitizer 1.5.0
Installing irb 1.6.3
Fetching debug 1.7.2
Installing rack-test 2.1.0
Using capybara 3.38.0
Installing activesupport 7.0.4.3
Fetching activemodel 7.0.4.3
Using rails-dom-testing 2.0.3
Using globalid 1.1.0
Fetching actionview 7.0.4.3
Fetching activejob 7.0.4.3
Installing debug 1.7.2 with native extensions
Installing activejob 7.0.4.3
Installing activemodel 7.0.4.3
Fetching activerecord 7.0.4.3
Installing actionview 7.0.4.3
Using jbuilder 2.11.5
Fetching actionpack 7.0.4.3
Installing activerecord 7.0.4.3
Installing actionpack 7.0.4.3
Fetching railties 7.0.4.3
Using sprockets-rails 3.4.2
Fetching activestorage 7.0.4.3
Fetching actioncable 7.0.4.3
Fetching actionmailer 7.0.4.3
Installing actioncable 7.0.4.3
Installing actionmailer 7.0.4.3
Installing sqlite3 1.6.2 (arm64-darwin)
Installing activestorage 7.0.4.3
Installing railties 7.0.4.3
Fetching actionmailbox 7.0.4.3
Fetching actiontext 7.0.4.3
Installing actionmailbox 7.0.4.3
Using importmap-rails 1.1.5
Using web-console 4.2.0
Using stimulus-rails 1.2.1
Fetching turbo-rails 1.4.0
Installing actiontext 7.0.4.3
Fetching rails 7.0.4.3
Installing rails 7.0.4.3
Installing turbo-rails 1.4.0
Installing selenium-webdriver 4.8.6
Using webdrivers 5.2.0
Using bootsnap 1.16.0
Bundle complete! 15 Gemfile dependencies, 72 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
run bundle binstubs bundler
rails importmap:install
Add Importmap include tags in application layout
insert app/views/layouts/application.html.erb
Create application.js module as entrypoint
create app/javascript/application.js
Use vendor/javascript for downloaded pins
create vendor/javascript
create vendor/javascript/.keep
Ensure JavaScript files are in the Sprocket manifest
append app/assets/config/manifest.js
Configure importmap paths in config/importmap.rb
create config/importmap.rb
Copying binstub
create bin/importmap
rails turbo:install stimulus:install
Import Turbo
append app/javascript/application.js
Pin Turbo
append config/importmap.rb
Run turbo:install:redis to switch on Redis and use it in development for turbo streams
Create controllers directory
create app/javascript/controllers
create app/javascript/controllers/index.js
create app/javascript/controllers/application.js
create app/javascript/controllers/hello_controller.js
Import Stimulus controllers
append app/javascript/application.js
Pin Stimulus
Appending: pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true"
append config/importmap.rb
Appending: pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
append config/importmap.rb
Pin all controllers
Appending: pin_all_from "app/javascript/controllers", under: "controllers"
append config/importmap.rb
~/code/Rails
▶
Then move into the directory you just created:
~/code/Rails
▶ cd Cars
code/Rails/Cars main ✗ ◒
▶ ls
Gemfile Rakefile config lib storage vendor
Gemfile.lock app config.ru log test
README.md bin db public tmp
code/Rails/Cars main ✗ ◒
▶
Then run that web site:
code/Rails/Cars main ✗ ◒
▶ rails server
=> Booting Puma
=> Rails 7.0.4.3 application starting in development
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.1.3-p185) ("Birdie's Version")
* Min threads: 5
* Max threads: 5
* Environment: development
* PID: 77089
* Listening on http://127.0.0.1:3000
* Listening on http://[::1]:3000
Use Ctrl-C to stop
In the following code, we'll create our first model in the web application, the Marque (car brand) and we'll define it as having only a name, which is a string.
code/Rails/Cars main ✗ ◒
▶ rails generate scaffold Marques name:string
[WARNING] The model name 'Marques' was recognized as a plural, using the singular 'Marque' instead. Override with --force-plural or setup custom inflection rules for this noun before running the generator.
invoke active_record
create db/migrate/20230402030625_create_marques.rb
create app/models/marque.rb
invoke test_unit
create test/models/marque_test.rb
create test/fixtures/marques.yml
invoke resource_route
route resources :marques
invoke scaffold_controller
create app/controllers/marques_controller.rb
invoke erb
create app/views/marques
create app/views/marques/index.html.erb
create app/views/marques/edit.html.erb
create app/views/marques/show.html.erb
create app/views/marques/new.html.erb
create app/views/marques/_form.html.erb
create app/views/marques/_marque.html.erb
invoke resource_route
invoke test_unit
create test/controllers/marques_controller_test.rb
create test/system/marques_test.rb
invoke helper
create app/helpers/marques_helper.rb
invoke test_unit
invoke jbuilder
create app/views/marques/index.json.jbuilder
create app/views/marques/show.json.jbuilder
create app/views/marques/_marque.json.jbuilder
code/Rails/Cars main ✗ ◒
▶
Again, we run the Rails server, updating the database first, then open the URL (http://127.0.0.1:3000/marques) to see what it does:
Using the functionality already created we can CRUD (create, read, update, delete) a list of marques.
It doesn't take too much imagination to see we can create a list of Models too, so that for each model you need to select a marque (manufacturer) for each one. And so it goes. A whole AirBnB web site is only a million keystrokes away!
That is all