Rails为支持REST开发提供的3个工具:
1. map.resources
map.connect '', :controller=>'home', :action=>'welcome'
map.connect '/post/:id', :controller=>'post', :action=>'show'
map.connect '/weather/:year/:month/:date', :controller=>'weather', :action=>'archive'
但是用这种方式在使用指定路由方式的时候,会给Rails开发人员增加许多重复性的劳动:
在视图中会这么使用代码:
link_to 'Home', :controller=>'home', :action=>'welcome'
link_to 'Show Post', :controller=>'post', :action=>'show', :id=>@post
link_to 'Weather Last Christmas',
:controller=>'weather', :action=>'archive', :year=>'2006', :month=>'12', :date=>'25'
重复性就是出现在手动指定controller, action,以及类似id的这些参数上。那么采用下面的路由方式会带来很大的改观:
map.home '', :controller=>'home',:action=>'welcome'
map.post '/post/:id', :controller=>'post', :action=>'show'
map.weather_archive '/weather/:yaer/:month/:date', :controller=>'weather', :action=>'archive'
这样创建了路由之后,Rails会自动提供两个新的URL方法:{named_route}_path和{named_route}.url例如map.home路由相应的这两个方法即为home_path和home_url, 开发人员可以通过这两个方法来生成指定路由的URL。二者的区别是:home_url方法生成的是绝对路径,包括主机地址和请求地址(例如[url]http://localhost:3000/[/url])而home_path生成的是相对路径(/welcome),因此,使用命名路由后,之前的rhtml里可以写做:
link_to 'Home', home_path
link_to 'Show Post', post_path(@post)
link_to 'Weather Last Christmas', weather_archive_path('2006', '12', '25')
虽然命名路由功能非常强大,但是对于REST开发来说还是显得力不从心,加入每个资源都拥有7个CRUD方法的话,就不得不创建大量的命名路由,不过rails提供了一个新的路由方法:map.resources, 创建REST方式的路由时,它可以提供类似于脚手架(scaffolding)的功能。举例来说:
map.resources :exercises
Rails会根据控制器中的方法自动创建所有对应的路由, 以及相应生成的URL方法:
7个CRUD方法:
  Action HTTP method URL 生成URL的方法
1 index GET /exercises exercises_path
2 show GET /exercises/1 exercises(:id)
3 new GET /exercises/new new_exercise_path
4 edit GET /exercises/1;edit edit_exercise_path(:id)
5 create POST /exercises exercises_path
6 update PUT /exercises/1 exercises_path
7 destroy DELETE /exercises/1 exercises_path(:id)
2. respond_to
在非REST应用中,我们看到的方法可能如下:
def index
@exercises = Exercise.find(:all)
end
默认情况下,这个方法会自动显示相应的index模板文件(index.rhtml),但是如果改为使用REST, 就能够以多种形式表现来自同一资源的数据,为了实现这点,在index方法中添加respond_to方法的调用:
def index
@exercises = Exercise.find(:all)
respond_to do |format|
format.html
format.xml {render :xml => @exercises.to_xml}
end
添加的respond_to 方法,会根据HTTP请求的头信息来返回相应的模板。因此如果用户发送普通的web请求,server返回HTML格式信息,如果是XML请求,server会以XML格式返回exercises对象。
3. scaffold_resource
scaffold_resource的脚手架的语法规则:
script/generate scaffold_resource ModelName [field:type ]…
--------------------------------------------
创建Exercise_app
1. rails exercise
2. .script/generate scaffold_resource exercise name:string user_id:integer
#用scaffold_resource创建了exercise类及其字段之后,rails会自动在route.rb中添加map.resources :exercises代码,并且会生成相应的migration.在index方法中, respond_to会根据头信息中的请求方式来确定返回的内容,Accept: text/html返回html形式的内容, Accept: text/xml返回xml形式的内容,但是如果发出/exercises.xml的GET请求,即使头信息为Accept: text/html也会返回xml模板,这样可以解决RSS的问题(RSS会发出类似/exercises.xml的GET请求,但是头信息为Accept:text/html)。
---------------------------------------------
使用restful_authentication插件,下载插件拷贝到vender/plugins目录下.
关于restful_authentication插件的详细说明,见里面的readme
---
3..script/generate authenticated user sessions
第一个参数指定了在注册时创建的模型对象(user或者account),在创建模型的同时,还会创建一个基本的,包含create方法的控制器
第二个参数指定了session会话控制器名称。该控制器用来处理站点中的登录和注销功能。
4. 配置路由config/route.rb
ActionController::Routing::Routes.draw do |map| 
  map.resources :exercises
 map.home '', :controller=>'sessions', :action=>'new'      #默认首页路由(删除public下的index.html文件), [url]http://localhost:3000/[/url] 
  map.resources :users, :sessions                                       
  map.signup '/signup', :controller=>'users', :action=>'new'
  map.login '/login', :controller=>'sessions', :action=>'new' 
  map.logout '/logout', :controller=>'sessions', :action=>'destory'

end

5.将users_controller,sessions_controller中的"include AuthenticatedSystem"代码剪切到application_controller中,如果想支持"自动登录"功能,需要在application_controller中添加before_filter :login_from_cookie
6.将sessions视图下的new.rhtml文件第一行代码session_path改成sessions_path,此时打开[url]http://localhost:3000/[/url]就会出现默认首页(登录页面)
7.在layout目录下,删除由scaffold_resource生成的模板文件,新建application.rhtml来改变一下外观,代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"[url]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/url]"> 
<html> 
  <head> 
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> 
    <title><%=@title || "Exercisr"%></title> 
    <link rel="stylesheet" type="text/css" href="[url]http://yui.yahooapis.com/2.2.2/build/reset-fonts-grids/reset-fonts-grids.css[/url]"> 
    <%= stylesheet_link_tag 'styles' %> 
    <%= javascript_include_tag :defaults %> 
  </head> 
  <body> 
    <div id="doc2" class="yui-t2"> 
      <div id="hd" class="box grad blue"> 
        <%= image_tag 'grad_black.png'%> 
        <h1 id="masthead"><%= link_to "Exercisr", home_path%></h1> 
      </div> 
      <div id="bd"> 
        <div id="yui-main"> 
          <div class="yui-b"> 
            <%= yield%> 
          </div> 
        </div> 
        <%if logged_in?%> 
          <div class="yui-b sidebar"> 
            <ul> 
              <li><%=link_to 'Exercises', exercises_path%></li> 
              <li><%# link_to 'Workouts', workouts_path%></li> 
              <li><%# link_to 'Goals', goals_path%></li> 
              <li><%# link_to 'Logout', logout_path%></li> 
            </ul> 
          </div>
<%else%> 
            <div class="yui-b sidebar"> 
            <ul> 
              <li><%= link_to 'signup', signup_path%></li> 
            </ul> 
          </div>

        <%end%> 
      </div> 
      <div id="ft" class="box grad blue"><%= image_tag 'grad_white.png'%></div> 
    </div> 
  </body> 
</html>
------下面的步骤用来设计页面导航
在sessions_controller中实现了3个方法中的2个-- create和destory,并且在这两个方法中都调用了redirect_back_or_default方法,现在redirect_back_or_default方法按照默认方式返回登录页面(即使已经登录),而我们现在需要让其返回到本系统的首页。
应该创建一个漂亮的,具有交互性的首页,能够为用户提供演示数据、教程等内容。但是现在我们创建一个静态页面,显示一些欢迎信息即可。同时在session模板中添加相应的模板代码。
8.在session_controller中添加一个welcome的action
def welcome
end
在welcome.rhtml模板中添加:
<h1>Welcome to Exercisr</h1> 
<h3>A RESTful place to keep track of your workouts</h3>
之后在/config/routes.rb中添加一个新的命名路由(注意路由顺序):
ActionController::Routing::Routes.draw do |map| 
  map.resources :exercises
map.home '', :controller=>'sessions', :action=>'new' 
  map.resources :users, :sessions 
  map.welcome '/welcome', :controller=>'sessions', :action=>'welcome' 
  map.signup '/signup', :controller=>'users', :action=>'new' 
  map.login '/login', :controller=>'sessions', :action=>'new' 
  map.logout '/logout', :controller=>'sessions', :action=>'destory'

end

最后修改sessions_controller中默认的跳转页面,使得create方法跳转到新创建的welcome页面,destroy返回到登录页面
def create 
...
  redirect_back_or_default(welcome_path) 
...   
end
#############
def destroy 
... 
    redirect_back_or_default(login_path) 
end
当用户注册之后,也应该跳转到welcome页面,所以修改user_controller的create方法:
def create 
        redirect_back_or_default(welcome_path) 
end
现在打开[url]http://localhost:3000/signup[/url]注册一个新帐户,测试一下刚设计的功能。
-------------------
创建模型关联
Exercise类:
class Exercise < ActiveRecord::Base 
  belongs_to :user 
  validates_presence_of :name 
  validates_uniqueness_of :name, :scope=>:user_id #一个用户不能输入两个相同的运动名 
end
User类:
class User < ActiveRecord::Base 
  # ... 
  has_many :exercises, :dependent => :destroy, :order=>'name asc'
#... 
end
修改Exercise控制器的作用域
之前对每个资源都生成了基本的CRUD操作,这些代码处于全局(Global)作用域中。需要降低代码的作用域。意思是:
譬如在对exercises生成的基本操作中,index这个action的代码是@exercises=Exercise.find(:all),而现在需要的是在用户登录之后只寻找跟登录后的user相关联的exercises,也就是要将@exercises=Exercise.find(:all)写成@exercises=current_user.exercises.build.修改exercise_controller代码:
将其中的Exercise类的操作都换成current_user.exercises,将new方法换成build方法,具体代码如下:
class ExercisesController < ApplicationController 
  before_filter :login_required 
  # GET /exercises 
  # GET /exercises.xml 
  def index 
    @exercises =current_user.exercises.find(:all)
respond_to do |format| 
      format.html # index.rhtml 
      format.xml  { render :xml => @exercises.to_xml } 
    end 
  end
# GET /exercises/1 
  # GET /exercises/1.xml 
  def show 
    @exercise =current_user.exercises.find(params[:id])
respond_to do |format| 
      format.html # show.rhtml 
      format.xml  { render :xml => @exercise.to_xml } 
    end 
  end
# GET /exercises/new 
  def new 
    @exercise = current_user.exercises.build 
  end
# GET /exercises/1;edit 
  def edit 
    @exercise = current_user.exercises.find(params[:id]) 
  end
# POST /exercises 
  # POST /exercises.xml 
  def create 
    @exercise = current_user.exercises.build(params[:exercise])
respond_to do |format| 
      if @exercise.save 
        flash[:notice] = 'Exercise was successfully created.' 
        format.html { redirect_to exercise_url(@exercise) } 
        format.xml  { head :created, :location => exercise_url(@exercise) } 
      else 
        format.html { render :action => "new" } 
        format.xml  { render :xml => @exercise.errors.to_xml } 
      end 
    end 
  end
# PUT /exercises/1 
  # PUT /exercises/1.xml 
  def update 
    @exercise = current_user.exercises.find(params[:id])
respond_to do |format| 
      if @exercise.update_attributes(params[:exercise]) 
        flash[:notice] = 'Exercise was successfully updated.' 
        format.html { redirect_to exercise_url(@exercise) } 
        format.xml  { head :ok } 
      else 
        format.html { render :action => "edit" } 
        format.xml  { render :xml => @exercise.errors.to_xml } 
      end 
    end 
  end
# DELETE /exercises/1 
  # DELETE /exercises/1.xml 
  def destroy 
    @exercise = current_user.exercises.find(params[:id]) 
    @exercise.destroy
respond_to do |format| 
      format.html { redirect_to exercises_url } 
      format.xml  { head :ok } 
    end 
  end 
end
-------------
利用局部模板:
1.在创建或者修改exercise一项时,可以提取出来一个局部模板。在views/exercises下新建一个_form.rhtml文件,代码如下:
<p> 
  <label for="exercise-name">Name</label><br /> 
   <%= f.text_field :name %> 
</p>
<p> 
   <%= submit_tag "Save" %> 
</p>
在new方法和edit视图中调用这个局部模板:
new.rhtml代码:
<h1>New exercise</h1>
<%= error_messages_for :exercise %>
<% form_for(:exercise, :url => exercises_path) do |f| %> 
<%= render :partial=>"form", :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Back', exercises_path %>
edit.rhtml代码:
<h1>Editing exercise</h1>
<%= error_messages_for :exercise %>
<% form_for(:exercise, :url => exercise_path(@exercise), :html => { :method => :put }) do |f| %> 
  <%=render :partial=>'form', :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Show', exercise_path(@exercise) %> | 
<%= link_to 'Back', exercises_path %>
把new模板的内容可以放到index页面中,直接可以在index中添加新的运动。修改后的index.rhtml:
<%# 介绍性的提示信息%>
<h1>Exercises</h1> 
<p>On this page you can create and manage the exercises that you use in your workouts.</p> 
<p>You can also view reports on your progress for each exercises</p>
<table id="exercise_details"> 
  <tr> 
    <th>Name</th> 
  </tr> 
<%= render :partial=>'exercise', :collection=>@exercises%> 
</table>
<br /><br /> 
<h1>Add a New Exercise</h1> 
<div id="add_exercise"> 
  <% form_for(:exercise, :url=>exercises_path, :html=>{:id=>'new_exercise'}) do |f|%> 
      <%=render :partial=>'form', :locals=>{:f=>f}%> 
        <%end%> 
  </div>
---------
workout资源
用来记录用户在某一天锻炼的完成程度,该资源中,不仅应该包括进行锻炼的日期,还包括一个用来描述锻炼类型(上身、腹部或者双臂)的可选文本字段,具体实现过程:
1.ruby script/generate scaffold_resource workout date:date label:string user_id:integer
2.rake db:migrate
模型关系:
user.rb: has_many :workouts
workout.rb: belongs_to :user, :dependent => :destroy
修改控制器workout_controller作用域:
添加before_filter=>:login_required
修改7个方法中的@workout表达式,特别注意的是new和create中间对new方法的调用要改成build:
class WorkoutsController < ApplicationController 
  before_filter :login_required 
  # GET /workouts 
  # GET /workouts.xml 
  def index 
    @workouts = current_user.workouts.find(:all)
respond_to do |format| 
      format.html # index.rhtml 
      format.xml  { render :xml => @workouts.to_xml } 
    end 
  end
# GET /workouts/1 
  # GET /workouts/1.xml 
  def show 
   @workout = current_user.workouts.find(params[:id])
respond_to do |format| 
      format.html # show.rhtml 
      format.xml  { render :xml => @workout.to_xml } 
    end 
  end
# GET /workouts/new 
  def new 
    @workout = current_user.workouts.build 
  end
# GET /workouts/1;edit 
  def edit 
    @workout = current_user.workouts.find(params[:id]) 
  end
# POST /workouts 
  # POST /workouts.xml 
  def create 
    @workout = current_user.workouts.build(params[:workout])
respond_to do |format| 
      if @workout.save 
        flash[:notice] = 'Workout was successfully created.' 
        format.html { redirect_to workout_url(@workout) } 
        format.xml  { head :created, :location => workout_url(@workout) } 
      else 
        format.html { render :action => "new" } 
        format.xml  { render :xml => @workout.errors.to_xml } 
      end 
    end 
  end
# PUT /workouts/1 
  # PUT /workouts/1.xml 
  def update 
   @workout = current_user.workouts.find(params[:id])
respond_to do |format| 
      if @workout.update_attributes(params[:workout]) 
        flash[:notice] = 'Workout was successfully updated.' 
        format.html { redirect_to workout_url(@workout) } 
        format.xml  { head :ok } 
      else 
        format.html { render :action => "edit" } 
        format.xml  { render :xml => @workout.errors.to_xml } 
      end 
    end 
  end
# DELETE /workouts/1 
  # DELETE /workouts/1.xml 
  def destroy 
    @workout = current_user.workouts.find(params[:id]) 
    @workout.destroy
respond_to do |format| 
      format.html { redirect_to workouts_url } 
      format.xml  { head :ok } 
    end 
  end 
end
修改视图:
1.修改workout的index.rhtml页面:
<h1>Listing workouts</h1>
<table> 
  <tr> 
    <th>Date</th> 
    <th>Label</th>
 
  </tr> 
<%= render :partial=>'workout', :collection=>@workouts %> 
</table>
<br />
<h1>Add a New Workout</h1> 
<div id="add_workout"> 
  <%form_for(:workout, :url=>workouts_path, :html=>{:id=>'new_workout'}) do |f|%> 
    <%= render :partial=>'form', :locals=>{:f=>f}%> 
      <%end%> 
</div>
2._workout.rhtml局部模板:
<tr> 
  <td><%=h workout.date.to_s(:long) %></td> 
  <td><%=h workout.label %></td>
<td><%= link_to image_tag("display.gif", {:title=>"View Workout Details"}),workout_path(workout)%></td> 
  <td><%= link_to image_tag("edit_photo.gif", {:title=>"Edit Workout Date/label"}), edit_workout_path(workout) %></td> 
  <td><%= link_to image_tag("delete_photo.gif", {:title=>"Delete Workout"}), workout_path(workout), :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
3.new.rhtml:
<h1>New workout</h1>
<%= error_messages_for :workout %>
<% form_for(:workout, :url => workouts_path) do |f| %> 
<%= render :partial=>'form', :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Back', workouts_path %>
4.edit.rhtml
<h1>Editing workout</h1>
<%= error_messages_for :workout %>
<% form_for(:workout, :url => workout_path(@workout), :html => { :method => :put }) do |f| %> 
  <%= render :partial=>'form', :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Show', workout_path(@workout) %> | 
<%= link_to 'Back', workouts_path %>
5._form.rhtml局部模板:
<p> 
   <b>Date</b><br /> 
   <%= f.date_select :date %> 
</p>
<p> 
   <b>Label</b><br /> 
   <%= f.text_field :label %> 
</p>
<p> 
   <%= submit_tag "Save" %> 
</p>
-----------------
记录锻炼过程中的数据:
在创建完每次的workout对象之后,需要记录如下信息:
1.本次锻炼进行了哪几项运动。(exercises)
2.每项运动完成了几组()
3.每次运动使用的重量或者负荷(resistance)
4.每组运动进行的次数(repitition)
按照这些需求,我们应该新建这样一张表,表中每一行必须表示一组运动,其中记录的数据包括:workoutID(外键,用来查询关联的workout)、exerciseID(外键,用来查询关联的exercise)、该组运动使用的重量以及该组包含的运动次数。
创建这个资源,名字叫做activities(活动).
过程:
1.ruby script/generate scaffold_resource activity workout_id:integer exercise_id:integer resistance:integer repetitions:integer
2.rake db:migrate
3.删除生成的layout文件
---4.创建模型之间的关系:
class Activity < ActiveRecord::Base 
  belongs_to :exercise 
  belongs_to :workout 
  validates_presence_of :resistance, :repetitions
 
end
class Workout < ActiveRecord::Base 
  belongs_to :user 
  has_many :activities, :dependent=>:destroy 
  has_many :exercises, :through=>:activities
 
  validates_presence_of :date 
end
class User < ActiveRecord::Base 
  # Virtual attribute for the unencrypted password 
  has_many :exercises, :dependent => :destroy, :order=>'name asc' 
  has_many :workouts, :dependent => :destroy 
  has_many :activities, :through=>:workouts
...
end
修改activity路由
ActionController::Routing::Routes.draw do |map| 
# map.resources :activities
  map.resources :workouts do |workout| 
    workout.resources :activities 
  end
...
这是一个嵌套式路由的写法,简单的来说,先要得到workouts的资源,然后根据workouts得到activities资源,如果不然没有意义。
修改activities_controller
跟以前一样,在activities_controller里首先要加上before_filter :login_required,确保只有用户登录之后访问控制器中的方法,其次,由于使用了嵌套路由,所以在限制查询范围方面需要进行一些修改。在之前的控制器中,我们将查询范围限定为当前用户,而现在对于嵌套资源来说,需要在父资源中进行查询。
所以还得首先得出父资源的实例变量值。所以定义一个protected方法,然后在控制器开始加上before_filter :find_workout
这个protected的find_workout方法写在activities_controller的最下面:
protected 
def find_workout 
  @workout= current_user.workouts.find(params[:workout_id]) 
end
然后再在最上面写上before_filter :find_workout,这样就得到了实例变量@workout
在其他的方法中做出修改如下:
class ActivitiesController < ApplicationController 
  before_filter :login_required 
  before_filter :find_workout 
  # GET /activities 
  # GET /activities.xml 
  def index 
    @activities = @workout.activities.find(:all) 
    respond_to do |format| 
      format.html # index.rhtml 
      format.xml  { render :xml => @activities.to_xml } 
    end 
  end
# GET /activities/1 
  # GET /activities/1.xml 
  def show 
   @activity = @workout.activities.find(params[:id])
respond_to do |format| 
      format.html # show.rhtml 
      format.xml  { render :xml => @activity.to_xml } 
    end 
  end
# GET /activities/new 
  def new 
    @activity = @workout.activities.build 
  end
# GET /activities/1;edit 
  def edit 
   @activity = @workout.activities.find(params[:id]) 
  end
# POST /activities 
  # POST /activities.xml 
 
def create 
  @activity = @workout.activities.build(params[:activity])
respond_to do |format| 
    if @activity.save 
      flash[:notice] = 'Activity was successfully created.' 
      format.html { redirect_to workout_path(@workout) } 
      format.xml  { head :created, :location => activity_url(@workout,@activity) } 
    else 
      format.html { render :action => "new" } 
      format.xml  { render :xml => @activity.errors.to_xml } 
    end 
  end 
end
# PUT /activities/1 
  # PUT /activities/1.xml 
 
def update 
  @activity = @workout.activities.find(params[:id])
respond_to do |format| 
     if @activity.update_attributes(params[:activity]) 
       flash[:notice] = 'Activity was successfully updated.' 
       format.html { redirect_to workout_path(@workout) } 
       format.xml  { head :ok } 
     else 
       format.html { render :action => "edit" } 
       format.xml  { render :xml => @activity.errors.to_xml } 
     end 
   end 
end
# DELETE /activities/1 
  # DELETE /activities/1.xml 
  def destroy 
   @activity = @workout.activities.find(params[:id]) 
    @activity.destroy
respond_to do |format| 
      format.html { redirect_to activities_url } 
      format.xml  { head :ok } 
    end 
  end 
  protected 
  def find_workout 
    @workout= current_user.workouts.find(params[:workout_id]) 
  end
end
修改activity视图模板:
这里修改activity模板跟以前的类似,有一点就是因为activity资源必须是以workout资源为前提的,所以在参数传递时要加上@workout,下面首先来创建一个_activity.rhtml模板:
<tr> 
    <td><%=h activity.exercise.name %></td>
    <td><%=h activity.resistance %></td>
    <td><%=h activity.repetitions %></td>
    <td><%= link_to image_tag("edit_photo.gif", {:title=>"Edit Exercise"}), edit_activity_path(@workout,activity) %></td> 
    <td><%= link_to image_tag("delete_photo.gif"), activity_path(@workout,activity), :confirm => 'Are you sure?', :method => :delete %></td> 
  </tr>
然后创建一个_form.rhtml模板:
<p> 
   <%= f.collection_select :exercise_id, current_user.exercises.find(:all), :id, :name, :prompt=>"Select an Exercise" %> 
</p>
<p>One set of <%= f.text_field :repetitions %>with <%= f.text_field :resistance%>pounds of resistance. 
</p>
<p> 
   <%= submit_tag "Save" %> 
</p>
修改edit.rhtml模板:
<h1>Editing activity</h1>
<%= error_messages_for :activity %>
<% form_for(:activity, :url => activity_path(@workout,@activity), :html => { :method => :put }) do |f| %> 
    <%= render :partial=>'form', :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Back', activities_path(@workout) %>
修改index视图模板:
<h1>Listing activities</h1>
<table> 
  <tr> 
    <th>Exercise</th> 
    <th>Resistance</th> 
    <th>Repetitions</th> 
  </tr>
  <%= render :partial=>'activity', :collection=>@activities%>
</table>
<br />
<%= link_to 'New activity', new_activity_path(@workout) %>
修改new.rhtml模板:
<h1>New activity</h1>
<%= error_messages_for :activity %>
<% form_for(:activity, :url => activities_path) do |f| %> 
  <%= render :partial=>'form', :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Back', activities_path(@workout) %>
最后,修改show.rhtml模板:
<p> 
  <b>Exercise:</b> 
  <%=h @activity.exercise %> 
</p>
<p> 
  <b>Resistance:</b> 
  <%=h @activity.resistance %> 
</p>
<p> 
  <b>Repetitions:</b> 
  <%=h @activity.repetitions %> 
</p>
<%= link_to 'Edit', edit_activity_path(@workout,@activity) %> | 
<%= link_to 'Back', activities_path(@workout) %>
修改workout控制器的show方法:
def show 
    @workout = current_user.workouts.find(params[:id]) 
    @activities= @workout.activities.find(:all, :include=>:exercise) 
    respond_to do |format| 
      format.html # show.rhtml 
      format.xml  { render :xml => @workout.to_xml } 
    end 
  end
修改workout/show.rhtml模板:
<h1><%= h @workout.label%>Workout on <%= h @workout.date.to_s(:long)%></h1> 
  <table> 
    <tr><th>Exercise</th><th>Reps</th><th>Resistance</th></tr> 
<%=render :partial=>'activities/activity', :collection=>@activities%> 
    </table>
  <h3>Add Exercise to this workout</h3> 
<%form_for(:activity, :url=>activities_path(@workout)) do |f|%> 
  <%= render :partial=>'activities/form', :locals=>{:f=>f}%> 
  <%end%> 
    <%=link_to "Back", workouts_path%>
---改进添加Activity的表单
如果在选择运动时发现运动名称不在select表单中,后面需要加入一个文本框,用来添加运动名称:
先在activity的_form.rhtml模板中增加一个文本框,用来输入要添加的运动名称:
<p> 
  <%= f.collection_select :exercise_id, current_user.exercises.find(:all), :id, :name, :prompt=>"Select an Exercise" %> 
  or add a new exercise: 
<%= f.text_field :new_exercise_name%>
 
</p>
<p>One set of <%= f.text_field :repetitions %>with <%= f.text_field :resistance%>pounds of resistance. 
</p>
<p> 
  <%= submit_tag "Save" %> 
</p>
这里将文本框内容传递给了:new_exercise_name,在activity模型类中创建一个虚拟属性new_exercise_name
class Activity < ActiveRecord::Base 
  belongs_to :exercise 
  belongs_to :workout 
  validates_presence_of :resistance, :repetitions 
  attr_accessor :new_exercise_name

  before_save :create_exercise_if_submitted 
  def create_exercise_if_submitted 
    create_exercise(:user_id=>workout.user_id, 
      :name=>new_exercise_name) unless new_exercise_name.blank? 
  end 
end
--------跟踪锻炼目标:
下面创建一个资源,用来跟踪锻炼目标(体重、血糖等)。该资源属性包括要跟踪的目标名称,要达到的目标,以及该目标上次锻炼后的结果(以便计算当前与目标之间的差距),确定属性后,创建goal资源:
ruby script/generate scaffold_resource goal name:string value:decimal last:decimal user_id:integer
我们还需要记录在当前尚未实现目标时的数据。例如,加入用户的目标是跟踪自己的体重,那么可能希望每周都记录一下自己的体重,以便查看是否有变化,我们将这个资源命名为result,包含以下几个属性:该对象关联的目标(goal对象),本次记录的时间,以及记录的数据。
ruby script/generate scaffold_resource result goal_id:integer date:date value:decimal
rake db:migrate
删除生成的layout
---
修改模型类:
class Goal< ActiveRecord::Base 
  belongs_to :user 
  has_many :results, :dependent => :destroy 
  validates_presence_of :value
 
end
class Result < ActiveRecord::Base 
  belongs_to :goal 
  validates_presence_of :date, :value 
end
class User < ActiveRecord::Base 
... 
  has_many :goals
...
end
创建嵌套路由:
ActionController::Routing::Routes.draw do |map| 
  # map.resources :results
  # map.resources :goals
#map.resources :activities
map.resources :goals do |goal| 
    goal.resources :results 
  end
...
配置控制器:
class GoalsController < ApplicationController 
  before_filter :login_required 
  # GET /goals 
  # GET /goals.xml 
  def index 
    @goals = current_user.goals.find(:all)
respond_to do |format| 
      format.html # index.rhtml 
      format.xml  { render :xml => @goals.to_xml } 
    end 
  end
# GET /goals/1 
  # GET /goals/1.xml 
  def show 
    @goal =  current_user.goals.find(params[:id]) 
    @results=@goal.results.find(:all, :order=>'date desc')
respond_to do |format| 
      format.html # show.rhtml 
      format.xml  { render :xml => @goal.to_xml } 
    end 
  end
# GET /goals/new 
  def new 
    @goal =  current_user.goals.build 
  end
# GET /goals/1;edit 
  def edit 
    @goal =  current_user.goals.find(params[:id]) 
  end
# POST /goals 
  # POST /goals.xml 
  def create 
    @goal =  current_user.goals.build(params[:goal])
respond_to do |format| 
      if @goal.save 
        flash[:notice] = 'Goal was successfully created.' 
        format.html { redirect_to goal_url(@goal) } 
        format.xml  { head :created, :location => goal_url(@goal) } 
      else 
        format.html { render :action => "new" } 
        format.xml  { render :xml => @goal.errors.to_xml } 
      end 
    end 
  end
# PUT /goals/1 
  # PUT /goals/1.xml 
  def update 
    @goal =  current_user.goals.find(params[:id])
respond_to do |format| 
      if @goal.update_attributes(params[:goal]) 
        flash[:notice] = 'Goal was successfully updated.' 
        format.html { redirect_to goal_url(@goal) } 
        format.xml  { head :ok } 
      else 
        format.html { render :action => "edit" } 
        format.xml  { render :xml => @goal.errors.to_xml } 
      end 
    end 
  end
# DELETE /goals/1 
  # DELETE /goals/1.xml 
  def destroy 
    @goal =  current_user.goals.find(params[:id]) 
    @goal.destroy
respond_to do |format| 
      format.html { redirect_to goals_url } 
      format.xml  { head :ok } 
    end 
  end 
end
由于result是一个嵌套资源,所以对于Results控制器来说,需要按照前面修改activities控制器的方法来修改:
class ResultsController < ApplicationController 
  # GET /results 
  # GET /results.xml 
  before_filter :login_required 
  before_filter :find_goal 
  def index 
    @results = @goal.results.find(:all)
respond_to do |format| 
      format.html # index.rhtml 
      format.xml  { render :xml => @results.to_xml } 
    end 
  end
# GET /results/1 
  # GET /results/1.xml 
  def show 
    @result = @goal.results.find(params[:id])
respond_to do |format| 
      format.html # show.rhtml 
      format.xml  { render :xml => @result.to_xml } 
    end 
  end
# GET /results/new 
  def new 
    @result = @goal.results.build 
  end
# GET /results/1;edit 
  def edit 
    @result = @goal.results.find(params[:id]) 
  end
# POST /results 
  # POST /results.xml 
  def create 
    @result = @goal.results.build(params[:result])
respond_to do |format| 
      if @result.save 
        flash[:notice] = 'Result was successfully created.' 
        format.html { redirect_to goal_url(@goal) } 
        format.xml  { head :created, :location => result_url(@result) } 
      else 
        format.html { render :action => "new" } 
        format.xml  { render :xml => @result.errors.to_xml } 
      end 
    end 
  end
# PUT /results/1 
  # PUT /results/1.xml 
  def update 
    @result = @goal.results.find(params[:id])
respond_to do |format| 
      if @result.update_attributes(params[:result]) 
        flash[:notice] = 'Result was successfully updated.' 
       format.html { redirect_to goal_url(@goal) } 
        format.xml  { head :ok } 
      else 
        format.html { render :action => "edit" } 
        format.xml  { render :xml => @result.errors.to_xml } 
      end 
    end 
  end
# DELETE /results/1 
  # DELETE /results/1.xml 
  def destroy 
    @result = @goal.results.find(params[:id]) 
    @result.destroy
respond_to do |format| 
      format.html { redirect_to goal_url(@goal) } 
      format.xml  { head :ok } 
    end 
  end 
  protected 
  def find_goal 
    @goal=current_user.goals.find(params[:goal_id]) 
  end
 
end
配置视图:
首先在layout/appliaction.rhtml中去掉<li><%# link_to 'Goals', goals_path%></li>中的#
1.goal视图:
这里的修改于之前的workout模板的修改基本一致。
新建_form.rhtml
<p> 
    <label for="goal-name">Name of the Goal:</label><br /> 
    <%= f.text_field :name %> 
  </p>
<p> 
    <label for="goal-value">Goal to Reach:</label><br /> 
    <%= f.text_field :value %> 
  </p>
<p> 
    <label for="goal-last">Current Result:</label><br /> 
    <%= f.text_field :last %> 
  </p>
<p> <%= submit_tag "Save" %></p>
在edit和new中调用此局部模板:
edit.rhtml
<h1>Editing goal</h1>
<%= error_messages_for :goal %>
<% form_for(:goal, :url => goal_path(@goal), :html => { :method => :put }) do |f| %> 
  <%= render :partial=>'form', :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Show', goal_path(@goal) %> | 
<%= link_to 'Back', goals_path %>
new.rhtml
<h1>New goal</h1>
<%= error_messages_for :goal %>
<% form_for(:goal, :url => goals_path) do |f| %> 
<%= render :partial=>"form", :locals=>{:f=>f}%> 
<% end %>
<%= link_to 'Back', goals_path %>
新建_goal.rhtml模板:
<tr> 
    <td><%=h goal.name %></td> 
    <td></td>
    <td><%= link_to image_tag("display.gif", {:title=>'View Report'}), goal_path(goal) %></td> 
    <td><%= link_to image_tag('edit_photo.gif',{:title=>"Edit Goal Details"}), edit_goal_path(goal) %></td> 
    <td><%= link_to image_tag("delete_photo.gif"), goal_path(goal), :confirm => 'Are you sure?', :method => :delete %></td> 
  </tr>
最后在index页面(goals/index.rhtml)中添加显示两个局部模板的代码:
<h1>Listing goals</h1>
<table> 
  <tr> 
    <th>Name</th> 
  </tr>

<%=render :partial=>"goal", :collection=>@goals%>

</table>

<br /> 
<h1>Add a New Goal</h1> 
<div id="add_goal"> 
  <% form_for(:goal, :url=>goals_path, :html=>{:id=>'new_goal'}) do |f|%> 
    <%= render :partial=>'form', :locals=>{:f=>f}%> 
    <%end%> 
</div>
2.result视图
同样,我们首先创建两个局部模板文件,然后在视图模板中调用这两个局部模板。其次,由于result是一个嵌套资源,所以还需要向所有相关的命名路由传递@goals变量
_form.rhtml:
<p>
  <label for="">Date</label><br />
  <%= f.date_select :date %>
</p>

<p>
  <label for="">Value</label><br />
  <%= f.text_field :value %>
</p>

<p>
  <%= submit_tag "Save" %>
</p>

_result.rhtml
<tr>
  <td><%=h result.date.to_s(:long) %></td>
  <td><%=h result.value %></td>

<td><%= link_to image_tag("edit_photo.gif", {:title=>"Edit Result Details"}), edit_result_path(@goal,result) %></td>
  <td><%= link_to image_tag("delete_photo.gif",{:title=>"Delete Result"}), result_path(@goal,result), :confirm => 'Are you sure?', :method => :delete %></td>
</tr>

new.rhtml
<h1>New result</h1>

<%= error_messages_for :result %>

<% form_for(:result, :url => results_path(@goal)) do |f| %>
 <%= render :partial=>'form', :locals=>{:f=>f}%>
<% end %>

<%= link_to 'Back', results_path(@goal) %>

edit.rhtml
<h1>Editing result</h1>

<%= error_messages_for :result %>

<% form_for(:result, :url => result_path(@goal,@result), :html => { :method => :put }) do |f| %>
  <%= render :partial=>"form", :locals=>{:f=>f}%>
<% end %>

<%= link_to 'Back to Goal', goal_path(@goal) %>

index.rhtml
<h1>Listing results for<%= h @goal.name%></h1>

<table>
  <tr>

<th>Date</th>
    <th>Value</th>
  </tr>

<%= render partial=> 'result', :collection=>@results%>

</table>

<br />
<%= link_to "Back to Goal", goal_path(@goal)%>

最后,修改goal资源的show模板:
<h1>Results for <%= h @goal.name%></h1>
<table>
  <tr><th>Date</th><th>Value</th></tr>
<%= render :partial=>'results/result', :collection=>@results%>
</table>

<h3>Record New Result for this Goal</h3>
<% form_for :result, :url=>results_path(@goal) do |f|%>
  <%= render :partial=>'results/form', :locals=>{:f=>f}%>
  <%end%>
  <%= link_to 'Back', workouts_path%>

按照前面的设计,还需要存储最近一次锻炼结果的数据。在这里要借助rails强大的回调函数,可以解决这个问题。打开models/results.rb添加after_create回调函数来设置相关goal资源的last属性。
class Result < ActiveRecord::Base
  belongs_to :goal
  validates_presence_of :date, :value
  after_create :update_last_result
  def update_last_result
    goal.last=value
    goal.save
  end

end

本文转自 fsjoy1983 51CTO博客,原文链接:http://blog.51cto.com/fsjoy/99346,如需转载请自行联系原作者

REST示例exercise相关推荐

  1. 《Qt图形界面编程入门》课后习题全解

    代码地址 https://github.com/duganlx/QT 说明 以下答案并非来自官方,而是本人自己实现的,若有啥问题,欢迎讨论(^-^) 所基于的书籍为<Qt图形界面编程入门> ...

  2. 台大郭彦甫-Matlab软件学习课堂exercise示例(第二讲)

    台大郭彦甫-Matlab软件学习课堂exercise示例 (仅供参考) 第二讲 基本操作与矩阵输入 (P6 exercise) >> cos(((1+2+3+4)^3/5)^(1/2))a ...

  3. 新SQL Server 2016示例数据库

    背景 (Background) We have all learned to love and hate the trusty Bike shop database. Almost every dem ...

  4. 示例填充图片_用示例解释洪水填充算法

    示例填充图片 什么是洪水填充? (What is Flood Fill?) Flood fill is an algorithm mainly used to determine a bounded ...

  5. 小白也能看懂的seaborn入门示例

    作者:奔雷手,目前是名在校学生,当前主要在学习机器学习,也在做机器学习方面的助教,相对还是比较了解初学者学习过程的需求和问题,希望通过这个专栏能够广结好友,共同成长. 编辑:王老湿 3.  4.  5 ...

  6. Deep Learning Exercise: Linear Regression

    Deep Learning Exercise: Linear Regression 简介 最简单的二元线性回归,参考斯坦福大学教学网http://openclassroom.stanford.edu/ ...

  7. vcfab算法示例_用示例解释贪婪算法

    vcfab算法示例 什么是贪心算法? (What is a greedy algorithm?) You may have heard about a lot of algorithmic desig ...

  8. Fuzzing101:Exercise 1 - Xpdf 翻译+解题

    注:查看全文请关注作者,或点击前往:Fuzzing101:Exercise 1 - Xpdf 翻译+解题 Fuzzing101:Exercise 1 - Xpdf 翻译+解题 题目部分翻译 题目原文: ...

  9. python serial.write_Python serial.write方法代码示例

    本文整理汇总了Python中serial.write方法的典型用法代码示例.如果您正苦于以下问题:Python serial.write方法的具体用法?Python serial.write怎么用?P ...

最新文章

  1. ab测试nginx Nginx性能优化
  2. 5个常用Java代码混淆器 助你保护你的代码
  3. 首个内河无人驾驶数据集公布!清华大学等高校联合AI公司开发
  4. FPGA的设计艺术(14)使用函数和任务提升逻辑的可重用性
  5. python画三维立体图-Python+matplotlib绘制三维图形5个精选案例
  6. 开源如此火热,但研究表明该领域已不再增长
  7. java进阶08 GUI图形界面
  8. neo4j CQL语句
  9. HihoCoder - 1175 拓扑排序·二
  10. .NET Core WebApi中实现多态数据绑定
  11. bzoj1012: [JSOI2008]最大数maxnumber [单调队列]
  12. .net知识和学习方法系列(前言)
  13. 怎样用计算机打出seeyouagain,怎么唱好seeyouagain说唱
  14. ElasticSearch服务器的搭建与使用
  15. 网站一键分享到新浪微博QQ空间腾讯微博
  16. 注解unchecked的原因_详解java中的5个基本注解
  17. 【MySQL技术内幕】34-lock与latch
  18. 数据结构笔记(王道考研) 第八章:排序
  19. 推送原理解析 极光推送使用详解
  20. 三大中值定理及简单例题

热门文章

  1. Ubuntu1804 配置静态IP
  2. C语言指针 *p++和*++p及++*p的区别
  3. 双网卡一个连接外网一个连接内网的处理方法
  4. 文章分类标签数据库设计
  5. XYOJ1255: 寻找最大数X(按数的一个一个元素输出)
  6. 浅谈大数据技术之实战足球盘口分析的方法与思路(二)
  7. 贤胜足球分析系统 v2.4.4 大小球测试版 怎么用
  8. eclipse护眼豆沙绿
  9. java哲学家就餐_哲学家就餐问题的解决方案(三种)
  10. 机器学习笔记 - 学习朴素贝叶斯概念及应用