REST示例exercise

简介:
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,如需转载请自行联系原作者
目录
相关文章
|
3天前
|
XML JSON API
Understanding RESTful API and Web Services: Key Differences and Use Cases
在现代软件开发中,RESTful API和Web服务均用于实现系统间通信,但各有特点。RESTful API遵循REST原则,主要使用HTTP/HTTPS协议,数据格式多为JSON或XML,适用于无状态通信;而Web服务包括SOAP和REST,常用于基于网络的API,采用标准化方法如WSDL或OpenAPI。理解两者区别有助于选择适合应用需求的解决方案,构建高效、可扩展的应用程序。
|
4月前
|
XML JSON API
Title: Empowering E-commerce with the Product Details Upload API Interface
Title: Empowering E-commerce with the Product Details Upload API Interface
|
7月前
|
XML 缓存 API
【Azure API 管理】使用APIM进行XML内容读取时遇见的诡异错误 Expression evaluation failed. Object reference not set to an instance of an object.
【Azure API 管理】使用APIM进行XML内容读取时遇见的诡异错误 Expression evaluation failed. Object reference not set to an instance of an object.
|
IDE Java API
REST Assured API Automation Testing Ⅰ - Getting Started
REST Assured API Automation Testing Ⅰ - Getting Started
REST Assured API Automation Testing Ⅰ - Getting Started
|
缓存 中间件 API
【laravel】 Unable to prepare route [api/user] for serialization. Uses Closure.
【laravel】 Unable to prepare route [api/user] for serialization. Uses Closure.
642 0
【laravel】 Unable to prepare route [api/user] for serialization. Uses Closure.
simple rxjava code programming style
simple rxjava code programming style package zhangphil.rx; import android.
909 0
|
JSON API 网络架构
使用GraphHttpClient调用Microsoft Graph接口 - POST
博客地址:http://blog.csdn.net/FoxDave 本篇接上一讲,我们继续看如何通过GraphHttpClient创建一个Office 365的组,需要使用POST请求。
957 0
|
数据库 网络架构

热门文章

最新文章