This repository contains a complete RESTful Rails-api along with a clean documentation which walks developers through all construction steps. If you wish to start with ruby on rails, this repository is a great resource for you.
- Homebrew is for downloading and installing open source apps on mac. It is an open source package manager.
- RVM and rbenv are version managers for ruby. They make it possible for you to have projects which work with different versions of ruby.
- RubyGems is a package manager for installing plugins and libraries for use with ruby rails.
- A RubyGem or Gem is a ruby code packed for easy distribution. RubyGems.org is a public gem repository.
- Rails is a gem which manages.
- Bundler is a gem that helps your rails app to load the right ruby gems.
- Rake is a gem which will make tasks.
- for installation steps on ubuntu visit here
- install homebrew
- install rbenv via homebrew
- install ruby-build via homebrew
- put command
eval "$(rbenv init -)"
in ~/.bash_profile - install latest version of ruby: find the latest version on ruby-lang.org and install it via this command:
rbenv install 2.2.3
- enter this command:
rbenv rehash
- change global ruby version by this command:
rbenv global 2.2.3
gem list
gives the current locally installed gems, to update all gems do:gem update --system
- install bundler gem:
gem install bundler
and then:rbenv rehash
- install rails gem:
gem install rails --no-ri --no-rdoc
(these options forces gem not to download documentations which can take a lot of space) and then:rbenv rehash
brew install mysql
at the end it will give you a command that make mysql launch at startup.- you can always manually do:
msyql.server start/stop/status
- login to mysql:
mysql -u <user> -p
- set password for root:
mysqladmin -u <user> password
then it will ask you to enter password for this user. - to make rails able to talk to mysql you need mysql2 gem: gem install mysql2
- (for mac users) to make sublime open with subl command in terminal enter this command:
ln -s /Applications/Sublime\ Text.app/Contents/SharedSupport/bin/subl /usr/local/bin/
Navigate to the directory you want to create the project inside it. Create a new rails web application:
$ rails new <name> [-d mysql]
Create a new Rails-api:
$ rails-api new <name> [-d mysql]
(rails-api is a gem, install it if you don’t already have it)
Bundler is used to install ruby gems for our rails application. To install a gem, add its name to the Gemfile
, like:
gem 'bcrypt'
Now, navigating to the root of your app and do:
$ bundle install
If at some point some command did not work for your project you need to put bundle exec
at front of it so that the command is executed in the context of the specific bundle of gems your project has.
show databases;
create database <name>;
use <name>;
queries …
Create new user for your application and grant appropriate privileges to it:
grant all privileges on <dbname>.* to '<new_username>'@'localhost' identified by '<password>'
Configure database by editing config/database.yml
Navigate to root of your app:
$ rails server
or
rails s
Navigate to root of your app:
$ rails generate
It will guide you through generating whatever type of entity you need.
app/controllers/concerns
and app/models/concerns
are for common code that can be shared throughout all controllers or models.
app/helpers
contain ruby code that helps us with our views.
####### Skip if you are not using rails template engine
- Insert ruby code in views:
<% code %>
- Print value of an expression:
<%= code %>
- Drop variables in strings:
"the beginning ... #{var} ... the end"
- Generating links using rails helpers:
<%= stylesheet_link_tag('application', :media => 'all') %>
<%= javascript_include_tag('application') %>
<%= image_tag('logo.png') %>
<%= link_to(name, target) %>
target
could be rails hash:
{:controller => 'x', :action => 'y', :page => 't', …}
You can create as much html.erb
layouts as you wish in views/layouts
. Then put <%= yield %>
inside a view to specify where you want html fragments to be rendered. In the controller class you need to specify layout("<name_without_html.erb>")
. The default layout is called application
.
You can exclude similar html fragments from full templates and put them in a Partial file. Partials have "_"
at the beginning of their name. The below code is used to yield a partial within a full template:
<%= render(:partial => “name_or_path”, :locals => {:varname => value, …}) %>
Rails provide a very strong set of text, number, date and more helpers. Just search for whatever you want to do first, it is likely that a function already exists for that. You can also create custom helper functions to use in the views. There are also sanitize helpers. Consider using them. They are absolutely necessary.
Put all asset files in app/assets
for rails to be able to perform optimization processes for production environment. In development environment, rails directly uses asset files in app/assets
. In production however, it will optimize all asset files and put them in public/assets
. Different kind of assets are kept in different folders under app/assets
. In all of them there is a manifest file called application
. The comments at the beginning of these files are the manifest and are used as input to the optimization process. If you wish to process asset files of a particular kind in a certain order, you must explicitly define this order. To do that, vefore //= require_tree .
add //= require <asset_name>
By default, each controller action renders a template in views/<controller_name>/<action_name>.html.erb
. To overwrite this default behaviour, you can put render command in controller actions:
render(:template => 'path/to/the/view/template')
The template being rendered has access to all instance variables of the action which are those with @
at the beginning.
redirect_to(:controller => 'x', :action => 'y')
or
redirect_to("google.com")
Request parameters can be accessed using:
params[:page]
Rails 4 has a very strong way of passing parameters when instantiating model objects in controllers called Mass Assignment. You can create a safe parameter hash and pass it to model new
or create
methods using the require
and permit
methods:
params.require(:user).permit(:username, :email, :password)
string.to_i
to convert to integervariable.inspect
for debugging
$ rake -T
lists all available rake tasks.$ rake <taskname>
runs the task.$ rake <taskname> RAILS_ENV=development
runs the task only within a particular environment.
$ rails generate migration CamleCaseName
creates a migration in db/migrate
.
In each migration ruby class, put an up
and a down
method. These methods must be in the mirror order of each other.
In up
method u can do:
create_table :cards do |t|
# id column is generated by default
t.column('<name>', :type, options)
# or:
t.<type>('<name>', options)
t.references(:student)
# this adds a column for foreign key with name 'student_id'
t.timestamps(:null => false)
# this adds created_at and updated_at columns
end
Available types are:
binary
,boolean
,date
,datetime
,decimal
,float
,integer
,string
,text
,time
.
Available options:
:limit => size
:default => value
:null => true/false
:precision => number
(for decimal type only):scale => number
(for decimal type only)
In down method u can do:
drop_table(:users)
To see the schema and migration status:
$ rake db:migrate:status
To make all migrations up:
$ rake db:migrate
To make all migrations down:
$ rake db:migrate VERSION=0
To go to a particular version of schema:
$ rake db:migrate VERSION=number
There is also:
$ rake db:migrate:up/down/redo VERSION=number
There are many other migration methods like create_table
and drop_table
out there.
It is a good practice to add index on all foreign key columns and those columns which are used frequently to look up for a row of data:
add_index(:table_name, 'column_name')
Cross column index:
add_index(:table_name, [‘column1_name’, ‘column2_name’])
If you have a typo in your migration code it will cause the migration run to abort and you will get stuck in a state where you can not either go up or down. In such states just comment those executed lines of the broken method and try running the migration method again.
$ rails generate model CamleCaseName
Presistable models (entities) must inherit from ActiveRecord
class.
We do not need to define attributes in our models. By default, rails create methods for accessing the model's attributes based on the model's migration file.
[Complete association reference] (guides.rubyonrails.org/association_basics.html)
On the model class of the owner side (Student):
has_one(:card)
On the model class of the other side (Card):
belongs_to(:student)
You also need to add foreign key to the belongs_to
side in the migration file:
...
t.references(:student)
...
When you set student.card
or card.student
, rails automatically saves the relationship in the database. Also if you set any of these to nil you will break the relationship.
On the model class of the owner side (Course):
has_many(:projects)
On the model class of the other side (Project):
belongs_to(:course)
You also need to add foreign key to the belongs_to
side in the migration file:
...
t.references(:course)
...
Then you can do things like:
course.projects
course.projects << p
course.projects = [p1, p2, p3]
course.projects.destroy(p1) # destroys completely
course.projects.delete(p1) # deletes from the list of projects
course.projects.clear
course.projects.empty?
course.projects.size
If you perform any modification on course.projects
, rails will instantly sync the database accordingly.
On the model class of one side (Student):
has_and_belongs_to_many(:courses)
On the model class of the other side (Course):
has_and_belongs_to_many(:students)
has_and_belongs_to_many
can also take additional parameters to configure the relationship:
:join_table => 'table_name'
:class_name => 'ActualClassNameInCaseYouChangeTheRelationshipKey'
:foreign_key => 'column_name_in_table_for_actual_class'
We have to generate a migration to create join table for this relationship. Rails naming convention for the name of join table is:
first_table + _ + second_table
(in alphabetical order)
The migration file should look like this:
def up
create_table :courses_students, :id => false do |t|
t.references(:student)
t.references(:course)
end
add_index(:courses_students, ['student_id', 'course_id'])
end
def down
drop_table(:courses_students)
end
Again, If you perform any modification on objects in either side of such relationship, rails will instantly sync the database accordingly.
Generate models for the base class and all of its subclasses. Then, put the following lines in the migration file for the super class:
...
t.references(:<super_ + name of superclass>, :polymorphic => true)
...
add_index(:<name of table>, ['<super_ + name of superclass>_id', '<super_ + name of superclass>_type'], {:name => 'a name'})
This makes an id
and a type
column in the table of the base class.
In the model for superclass add:
belongs_to(:<super_ + name of superclass>, :polymorphic => true)
In the models for subclasses add:
has_one(:<name of superclass>, :as => :<super_ + name of superclass>)
or
has_many(:<name of superclass>s, :as => :<super_ + name of superclass>)
m = ModelName.new
m = ModelName.new(hash to initialize attributes)
m.new_record?
# has it been saved in the database?
m.save
m = ModelName.create(hash to initialize attributes)
# this will call save itself after instantiation
m = ModelName.find(id)
# returns error if not found
m = ModelName.find_by_<column_name>(value)
# returns nil if not found or the first record matching the search
ModelName.all
ModelName.first
ModelName.last
ModelName.where(hash)
ModelName.where([“where clause with ?s in the middle”, val1, val2, …])
# where, order, offset, and limit are query methods available to ActiveRecord class.
Named Scopes are to make use of the above four methods but in a nicer way. For that, just add scopes to a Model:
scope(:<name>, -> (params) {where(…)})
and then we use it like:
ModelName.<scope_name>
m = ModelName.find(id)
# you can change and then save
m.update_attributes(hash)
# this will change and save all at once
m.destroy
Rails gives a very strong command line interface to work with models directly. To do that, navigate to the root of your application and do:
$ rails console [environment_name (development is default)]
Rails has some methods that adds validation to the models. The signature of almost all of these methods is:
function_name(:attribute_to_check, {hash of options})
List of validation functions:
validates_presence_of
# options -> :message
validates_length_of
# options -> :is, :minimum, :maximum, :within, :in, :wrong_length, :too_short, :too_long
validates_numericality_of
# options -> :equal_to, :greater_than, :less_than, :greater_than_or_equal_to, :less_than_or_equal_to, :odd, :even, :only_integer, :message
validates_inclusion_of
# options -> :in, :message
validates_exclusion_of
# options -> :in, :message
validates_format_of
# options -> :with, :message
validates_uniqueness_of
# options -> :case_sensitive, :scope, :message
validates_acceptance_of
# options -> :accept, :message
validates_confirmation_of
# options -> :message
validates_associated(:association_name_to_check)
Global Options we can use on almost all of the above:
:allow_blank
:allow_nil
:on => :create/:update/:save(default)
:if => :method_name
# a boolean returner function, indicating whether or not the object should be validated
There is another way to write validations called Sexy Validation:
validates(:attribute_to_check,
:presence => {}, # or a boolean
:numericality => {}, # or a boolean
:length => {},
:format => {},
:inclusion => {},
:exclusion => {},
:acceptance => {},
:uniqueness => {},
:confirmation => {})
Call .valid?
on objects to check their validity. Call .errors
on objects to see its errors. You can also add your own custom validation methods to the model:
def :name_for_this_validation_method
# check validation criteria
# finally do:
errors.add(:attribute or :base, "msg")
end
And use them on the model like:
validate :method_name
# you can also add :on or :if options here
To set a cookies and session variables:
cookies[:username] = "john"
session[:username] => "john"
cookies[:username] = {:value => "", :expires => 1.week.from_now}
Session data can be stored in file, database, or we can use cookies to store them. Since we are sending information to the user's local machine we need a mechanism to check whether or not they are changed by anyone other than us. Rails use the secret key stored in config/initializers/secret_token.rb
to validate the integrity of signed cookies. $ rake secret
will produce new key for you which you can replace the old one with.
The configuration to make rails use cookies for storing sessions is stored in config/initializers/session_store.rb
To see all routes of your app, do:
$ rake routes
Below is the standard list of RESTful routes:
| URL | HTTP Method | Controller Action | Description |
|----------------------|-------------|-------------------|------------------------------------|
| /subjects | GET | index | Show all items |
| /subjects/new | GET | new | Show new form |
| /subjects | POST | create | Create an item |
| /subjects/:id | GET | show | Show item with :id |
| /subjects/:id/edit | GET | edit | Show edit form for item with :id |
| /subjects/:id | PATCH | update | Update item with :id |
| /subjects/:id/delete | GET | delete | Show delete form for item with :id |
| /subjects/:id | DELETE | destroy | Delete item with :id |
You can find routing configuration file in: config/routes.rb
First, you need to set permission for ajax calls from other domains. You need to get this gem installed:
$ gem install rack-cors
Add this line to gemfile:
gem 'rack-cors', :require => 'rack/cors'
Navigate to the root of your project and do:
$ bundle install
Add this code to config/application.rb
:
config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :options]
end
end
Then you need to add routes for your RESTful api: Read this blog
namespace :api, :defaults => {:format => :json} do
resources(:users, :only => [:create])
end
Then in controller actions you render json responses:
render(:json => json_object, :status => 200)
# or
render(:nothing => true, :status => 200)
To customize the serialization of your models into json, include this in your gemfile:
gem 'active_model_serializers'
Do:
$ bundle install
Now put the following code in config/initializers/active_model_serializers.rb
:
ActiveSupport.on_load(:active_model_serializers) do
# This prevents generating root nodes in json responses
ActiveModel::Serializer.root = false
ActiveModel::ArraySerializer.root = false
end
If you are working on a Rails-api, serializers are not loaded by default. Therefore, you need to put the following line in your application_controller:
include ActionController::Serialization
Now generate and customize serializers for those models you need to serialize:
$ rails generate serializer User
Search for more details on the Internet for the things you can customize with serializers.
Install bcrypt gem. Add a column named password_digest
on your users table. Then simply add has_secure_password
in the User model. What it does, is it adds a virtual attribute named password
to that model. It also adds validation to check if password
and password_confirmation
are present and then whether or not they match. So what you need to do is setting plain password on a new user object and call save. It will encrypt this password and save it in password_digest
.
Then to check whether or not a user can be authenticated with a given password, find the user and call .authenticate("given password")
. This will either return false or the user object itself.
If the authentication is successful, you need to mark the user as authenticated. You can do this by putting the logged in status in user's session.
If your application is only a rails api, you need to generate a Json Web Token instead of cookie session and send it back to user. To create this token, follow the instructions here.
To check if user has logged in when they request for an action, first, add this method to ApplicationController:
private
def confirm_logged_in
if <token is valid>
return true
else
render(:nothing => true, :status => 401)
return false
end
end
Now you have to add this line to all controllers to check if the user has logged in before an actions is actually run:
before_action(:confirm_logged_in, {:except => [:action1, :action2])
# or
before_action(:confirm_logged_in, {:only => [:action3, :action4]})
# or the combination of both :only and :except