File structure:
- app/
- app/controllers
- app/models
- app/views
- app/helpers
- app/assets
- config/
- db/ : schema and migrations
- doc/
- lib/ : load files with
require
- lib/task/
- log/
- public/
- bin/
- test/
- tmp/
- vendor/assets
Command
# generate
rg model person
rg controller people
# open console
rc
rc production # with production environment
rc --sandbox # sandbox mode
# open server
rs -p 4000 -e production # set port and environment
# new rails project
bin/rails new my_app --database=mysql # create MyApp project in ./my_app
# open db console
dbconsole
# destroy
start and application settings
application.rb
has timezone setting.
other settings
In config/initializers
.
$ rails c
$ Loading development environment (Rails 3.2.8)
$ > "Business".singularize => "Busines" #
$ > "moose".pluralize => "mooses" #
Model and migration
create model
rails g model event name:string description:text is_public:boolean capacity:integer
apply db changes
bin/rake db:migrate
modify model
rails g migration add_status_to_events
# in the migration
class AddStatusToEvents < ActiveRecord::Migration
def change
add_column :events, :status, :string
end
end
validation of info
class Event < ActiveRecord::Base
validates_presence_of :name
end
CRUD
match route
put as the last line, match path “/foo/bar” to Controller foo Action bar
match ':controller(/:action(/:id(.:format)))', :via => :all
end
list all
The instance variable “@foo” will be passed to view to be used.
def index
@events = Event.all
end
new action
def new
@event = Event.new
end
new view
<%= form_for @event, :url => { :controller => 'events', :action => 'create' } do |f| %>
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :description, "Description" %>
<%= f.text_area :description %>
<%= f.submit "Create" %>
<% end %>
It will send parameters to create Action by params (a Hash)
def create
@event = Event.new(event_params)
# add input validation, so can try again
if @event.save
redirect_to :action => :index
else
# go to the template instead of calling action, information is saved
render :action => :new
end
flash[:notice] = "event was successfully created"
end
private
# to check the params, filter to have `params[:event][:name]`
# and `params[:event][:description]`
def event_params
params.require(:event).permit(:name, :description)
end
show individual record
If go to http://localhost:3000/events/show/1', Rails will call show Action and set
params[:id] = 1`
def show
@event = Event.find(params[:id])
# set page title
@page_title = @event.name
end
edit record
def edit
@event = Event.find(params[:id])
end
Then edit view
<%= form_for @event, :url => { :controller => 'events', :action => 'update', :id => @event } do |f| %>
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :description, "Description" %>
<%= f.text_area :description %>
<%= f.submit "Update" %>
<% end %>
Then update Action
def update
if @event.update(event_params)
redirect_to :action => :show, :id => @event
else
render :action => :edit
end
flash[:notice] = "event was successfully updated"
end
destroy record
def destroy
@event = Event.find(params[:id])
@event.destroy
flash[:alert] = "event was successfully deleted"
redirect_to :action => :index
end
Page layout
Default: views/layouts/application.html
add flash notice before yield
in default layout
<p style="color: green"><%= flash[:notice] %></p>
<p style="color: red"><%= flash[:alert] %></p>
Partial template
The name starts with a _
like _form.html.erb
# _form
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :description, "Description" %>
<%= f.text_area :description %>
# new view
<%= form_for @event, :url => { :controller => 'events', :action => 'create' } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= f.submit "Create" %>
<% end %>
# edit view
<%= form_for @event, :url => { :controller => 'events', :action => 'update', :id => @event } do |f| %>
<%= render :partial => 'form', :locals => { :f => f } %>
<%= f.submit "Update" %>
<% end %>
before action
DRY
class EventsController < ApplicationController
before_action :set_event, :only => [ :show, :edit, :update, :destroy]
# ....
private
def set_event
@event = Event.find(params[:id])
end
multi pages
- add
gem "kaminari"
to Gemfile bundle install
add to index Action
def index
@events = Event.page(params[:page]).per(5)
end
edit index view
<%= paginate @events %>
RESTful
Helper | GET | POST | PATCH/PUT | DELETE |
---|---|---|---|---|
event_path(@event) | /events/1 show action | /events/1 update action | /events/1 destroy action | |
events_path | /events index action | /events create action | ||
edit_event_path(@event) | /events/1/edit edit action | |||
new_event_path | /events/new new action |
show
,new
,edit
,destroy
are operations on single element.index
andcreate
are operations on setsevent_path(@event)
need parameters, decideshow
,update
ordestroy
based on HTTP verbevents_path
doesn’t need parameters, decideindex
orcreate
based on HTTP verb.
## add to routes.rb
resources :events
modify CRUD
# show
<%= link_to "Show", event_path(event) %>
# edit
<%= link_to 'Edit', edit_event_path(event) %>
# delete
<%= button_to 'Delete', event_path(event), :method => :delete, :data => { :confirm => "Are you sure?" } %>
# back to index
<p><%= link_to 'Back to index', events_path %></p>
# new view: send to
<%= form_for @event, :url => events_path do |f| %>
# edit view: send to
<%= form_for @event, :url => event_path(@event), :method => :patch do |f| %>
# event controller
# create
redirect_to events_url
# update
redirect_to event_url(@event)