Ruby on Rails is a modern web application framework.
Ruby SQLite3
$ rails new blog
Start the webserver to run at http://localhost:3000 by default
$ bin/rails server
app
- has the controllers, models, views, helpers, mailers, channels, jobs and assetsbin
- contains the rails script that starts your app and has other scripts (used to setup, update, deploy, run your app)config
- your application’s routes, databases, etc.db
- contains your current database schema and database migrationsGemfile
, Gemfile.lock
- specify what your gem dependencies are; files are used by Bundlerlib
- extended modules for your applicationlog
- application log filespackage.json
- specify what npm dependencies are needed for your Rails app; file is used by Yarnpublic
- static files and compiled assetsRakefile
- file that locates and loads tasks that can be run from the command line; tasks are defined throughout
components of Rails. Do not modify the Rakefile, instead add tasks through the lib/tasks
dirtest
- unit tests, fixturestmp
- temporary files (cache, pid files)vendor
- third-party code (e.g. vendored gems).ruby-version
- the default Ruby versionTo generate models or resources, you can either generate individual models, resources or do this with scaffolding to setup a lot for you
rails generate model MyModelName myfieldname:text # Will generate a model in your ‘models’ dir and a migration file in ‘db/migrate/ dir
rails generate controller MyControllerName MyActionName
rails generate resource MyResourceRoute name:my_table # Will generate the above model, migration file, as well as controller in controllers
dir and ‘resource’ route in ‘routes.rb’ file
rails generate scaffold Micropost context: text user_id: integer # generates the above, but in controller file, also adds a lot more methods (e.g. GET, POST, PATCH, DELETE)
Shortcut: rails g
is the shortcut for rails generate
Note: rails destroy
(aka rails d
) does the opposite of generate
and will undo.
To manually create items, we need to create a new controller by running the ‘controller’ generator and telling it we want a controller named ‘Welcome’ with an action called ‘index’
$ bin/rails generate controller Welcome index
create app/controllers/welcome_controller.rb
route get 'welcome/index'
invoke erb
create app/views/welcome
create app/views/welcome/index.html.erb
invoke test_unit
create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/welcome.coffee
invoke scss
create app/assets/stylesheets/welcome.scss
We generated:
app/controllers/welcome_controller.rb
app/views/welcome/index.html.erb
Open the config/routes.rb
file and add root 'welcome#index'
This tells Rails to map requests to the root of the application to the welcome controller’s index
action and
get welcome/index
so that requests go to http://localhost:3000/welcome/index
i.e. GET /welcome/index/
returns to welcome#index
, which is our template in /app/view/welcome/welcome.index.erb
Rails provides a resources method which can be used to declare a standard REST resource.
You need to add the ‘article’ resource to the config/routes.rb
file.
Under bin/rails routes
or bin/rake routes
(depending on version), you should see every REST endpoint now
$bin/rake routes
Prefix Verb URI Pattern Controller#Action
welcome_index GET /welcome/index(.:format) welcome#index
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / ### Generate Controller for Articles
When we visit http://localhost:3000/articles
, we get an error with uninitialized constant ArticlesController
You can generate with:
$bin/rails generate controller articles
create app/controllers/articles_controller.rb
invoke erb
create app/views/articles
invoke test_unit
create test/controllers/articles_controller_test.rb
invoke helper
create app/helpers/articles_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/articles.coffee
invoke scss
create app/assets/stylesheets/articles.scss
We then created the app/controllers/articles
In app/controllers/articles_controller.rb
, we need to define a method for new
class ArticlesController < ApplicationController
def new
end
end
When you’re in your app directory, you can get into a console to debug issues:
$rails console
Post.first
Post.first.comments
Post.first.comments.create! body: 'Say something funny!'
If you want to run without changing any data, use sandbox: rails console --sandbox
If you want to run the cli for your database, run: rails dbconsole
Rake is Ruby Make
, a standalone Ruby utility that replaces UNIX make
. We have a Rakefile
and a .rake
files
to build up a list of tasks. In Rails, Rake is used for common administration tasks, especialy sophisticated ones that
build off of each other.
To see a list of Rake tasks available to you, run rake --tasks
. Each task has a description.
rake --tasks
You want to make sure that only valid data is saved to your database. Check if valid with .valid?
Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false
These methods trigger validations:
The bang versions (e.g. save!
) raise an exception if the record is invalid. The non-bang versions don’t
so be careful using those.
Some methods do not trigger validations, including:
You can create associations between data models. Say we have a User
Model, each potentially connected to
many Micropost
models. The key attributes here are has_many
and belongs_to
.
#app/models/user.rb
class User < ApplicationRecord
has_many :microposts
end
#app/models/micropost.rb
class Micropost < ApplicationRecord
belongs_to :user
validates :content, length: { maximum: 140 }
end
Models inhert from other models using <
. Here’s an example:
#app/models/micropost.rb
class Micropost < ApplicationRecord
end
The Micropost
Model inherits from ApplicationRecord
, which then inherits from the base class ActiveRecord::Base
Model
Controllers also inherit from other controllers using <
, similar to Models. Here’s an example:
#app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
end
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
end
The MicropostsController
Controller inherits from ApplicationController
, which then inherits from the base class ActionController::Base
Some of the functionality that is inherited from the base class ActionController::Base
includes the ability to
manipulate model objects, filter inbound HTTP requests, and render views as HTML.
Since all Rails controllers inherit from ApplicationController
, rules defined in the Application controller are
automatically applied to every action in the application.
To run tests, do rails test
ActiveRecord is the M (model) in MVC. ActiveRecord helps with the creation and use of objects whose data requires persistent storage to a database. ActiveRecord is an implementation of the Active Record pattern, which is a description of an Object Relational Mapping system.
In the book ‘Patterns of Enterprise Application Architecture’, Active Record pattern says that objects carry both persistent data and behavior which operates on that data. Active Record takes the opinion that ensuring data access logic as part of the object will educate users of that object on how to read and write to a database.
save
and destroy
are wrapped in a transactionTransaction Code
For this example, we take money from will and pass to laura only if neither withdrawal
or deposit
raises an exception.
Exceptions will force a rollback that returns the database to the state before the transaction began.
However, objects will NOT have their instance data returned to their pre-transactional state.
ActiveRecord::Base.transaction do
will.withdrawal(100)
laura.deposit(100)
end
Exception Handling
There’s a couple ways to make sure that your transaction worked OR is rolled back correctly.
after_*
callbacksExceptions thrown within a transaction block will be propagated (after triggering the ROLLBACK), so be ready to
catch those in your application code. An example of an exception is the ActiveRecord::Rollback
exception, which
will trigger a ROLLBACK when raised, but not be re-raised by the transaction block.
Nested Transactions
transaction
calls can be nested. By deafult, all database statements in the nested transaction block should
become part of the parent transaction. That means even if you have a raise ActiveRecord::Rollback
exception inside
a nested block, it might not issue a rollback if the parent transaction does not see it. In order to avoid that,
you can pass requires_new: true
to enable sub-transactions (so that the sub-transaction can be rolled back, but
not roll back the parent transaction).
So in short, requires_new: true
basically creates a savepoint.
Active Record emulates nested transactions using savepoints on MySQL and PostgreSQL. See details about rollbacks here: https://dev.mysql.com/doc/refman/5.7/en/savepoint.html
Note: On MySQL, do NOT use Data Definition Language (DDL) operations in nested transaction blocks that are
emulated with savepoints (i.e. do not execute statements like CREATE TABLE
inside blocks). MySQL automatically
releases all savepoints on executing a DDL operation.
There are two types of callbacks associated with committing and rolling back transactions.
These callbacks are used for interacting with other systems since you will be guaranteed that the callback is only
executed when the database is in a permanent state. An example of this is after_commit
is a good place to
put in a hook to clear a cache since clearing it from within a transaction could trigger the cache to be
regenerated before the database is updated. After committing will ensure your cache is up to date.
after_commit
- this callback is called on every record saved or destroyed within a transaction immediately
after the transaction is committedafter_rollback
- this callback is called on every record saved or destroyed within a transaction immediately
after the transaction OR savepoint is rolled back.