Rails学习小结

 

1. 什么是MVC架构 html

MVC是模型(model)-视图(view)-控制器(controller)的缩写。MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。其中, java

(1)模型用来模拟应用所管理的真实世界的事物。 mysql

(2)视图是应用中展现给用户看的那一部分,一般被称为表示层。 web

(3)控制器是应用的真正大脑。它决定了应用怎样和系统交互、控制着那些数据能够从模型中得到,以及视图中的那一部分用来表示这些数据。 正则表达式

clip_image002

2. 什么是对象-关系映射(ORM),Rails所采用的ORM层是什么? sql

答:对象-关系映射(ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,好比java系列的Hibernate就是一个轻量级ORM框架。 数据库

Rails所采用的ORM层是一个名为ActivityRecord的对象-关系映射库。 编程

3. 如何建立一个rails应用程序demo json

答: api

(1) 使用RedRails工具建立一个rails程序demo的方法:

a) 单击File -> New -> 选择Rails Project

b) 输入Project name后按下”Finish”键便可

(2) 在命令行下建立一个Rails应用

a) 建立D:\work目录,在 DOS下进入此目录,而后执行命令“rails demo”,就获得一个Rails项目。

b) 在DOS下进入 D:\work\demo目录,执行命令“ruby script/server”来启动来启动 WEB服务器。

4. 实现demo的第一个action,要求在浏览器窗口中输入http://localhost:3000/test/first_action按回车后,页面显示“This is my first action.

答:

(1)新建一个rails的程序,取名helloDemo。

(2)在目录app/controllers/下新建一个rb文件,命名为test_controller.rb。

clip_image004

打开test_controller.rb文件,输入以下代码:

clip_image006

(3) 在目录app/views/ 下新建一个目录为test, 并在该目录下新建一个first_action.html.erb的文件。

clip_image008

打开first_action.html.erb文件,输入如下代码并保存:

clip_image010

(4) 重启server,而后运行该Rails程序,在浏览器中输入http://localhost:3000/test/first_action,后显示

clip_image012

5. ERB文件中如何插入ruby代码?

答: ERb 模板,其实是Ruby 内建的、用于生成HTML的工具,一般用于生成HTML 页面。最简单的ERb 模板就是一个普通的HTML 文件。ERB文件中插入ruby代码的方法有:

(1) 使用<% %>:不会返回运算结果。

(2) 使用<%= %>:<%= 和%> 之间的代码会被求值,而后用to_s() 方法将结果转换成字符串,最后将这个字符串显示在结果页面上。

6. ERB文件里用ruby代码实现一个循环。

答:程序代码和运行结果以下

<html>

<head>

<title>Hello, Rails!</title>

</head>

<body>

<% str="hello world\n\n" %>

<% a=1 %>

<% while a<4 %>

<h2><%= str %></h2>

<% a=a+1 %>

<% end %>

</body>

</html>

运行结果以下:

clip_image014

7. ERB的<%= %>序列末尾加上/去掉减号(把%>变成-%>),页面显示有什么区别?

答:这里的减号是在告诉ERb ,不要把紧随其后的空格和空行放进HTML 输出结果中。

8. 在demo添加代码,让第4题的页面显示当前时间,格式为“2010-05-18 10:43:45

答:程序代码和运行结果以下

<html>

<head>

<title>Hello, Rails!</title>

</head>

<body>

The time

<% @time = Time.now.strftime("%Y-%m-%d %H:%M:%S") %>

is <%= @time %>

</body>

</html>

运行结果以下:

clip_image016

9. 在控制器test中新增一个action(second_action),实现它与first_action之间的互相跳转。

答:

(1) 首先在目录app/controller/test_controller.rb文件中新增一个second_action方法。

clip_image018

(2) 而后,在views/test/目录中新增一个action命名为”second_action.html.erb”。

clip_image020

打开文件,输入代码:

clip_image022

(3) 最后,重启server,运行程序。

运行结果以下:

clip_image024

点击连接跳转至second_action

clip_image026

10. 调用下列方法会返回一个列表,其中包含当前目录的全部文件:

Dir.glob(‘*’)

要求在页面中用 <ul>显示该文件列表。

答:新增一个action命名为exer3_action,并输入以下代码

<html>

<head>

<title>第3周做业,题10</title>

</head>

<body>

<%dirArr=Dir.glob('*') %>

<ul>

<%for element in dirArr %>

<li>

<%= element -%>

</li>

<%end %>

</ul>

</body>

</html>

运行结果以下:

clip_image028

《Web敏捷开发之道》(5-13)

=====================================================================

1. 建立rails应用程序depot,后台使用mysql数据库,建立数据库depot_development,depot_production,depot_test,给出配置应用程序数据库方法。

答:

(1) 在项目的目录中使用”rails --database=mysql depot”命令建立一个使用mysql的应用程序,程序命名为depot。

clip_image030

(2)打开项目的目录” 安装路径\depot\config\”目录下的’database.yum’配置文件,就发现项目自动配置的程序数据库。

# MySQL. Versions 4.1 and 5.0 are recommended.

#

# Install the MySQL driver:

# gem install mysql

# On Mac OS X:

# sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql

# On Mac OS X Leopard:

# sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config

# This sets the ARCHFLAGS environment variable to your native architecture

# On Windows:

# gem install mysql

# Choose the win32 build.

# Install MySQL and put its /bin directory on your path.

#

# And be sure to use new-style password hashing:

# http://dev.mysql.com/doc/refman/5.0/en/old-client.html

development:

adapter: mysql

encoding: utf8

database: depot_development

pool: 5

username: root

password: icyi4cy

host: localhost

# Warning: The database defined as "test" will be erased and

# re-generated from your development database when you run "rake".

# Do not set this db to the same as development or production.

test:

adapter: mysql

encoding: utf8

database: depot_test

pool: 5

username: root

password: icyi4cy

host: localhost

production:

adapter: mysql

encoding: utf8

database: depot_production

pool: 5

username: root

password: icyi4cy

host: localhost

2. 用迁移脚本建立数据库表products,表有3列,分别为(title(string,长度<=50),description(text),image_url(string)),给出迁移脚本代码和建立方式

答:

(1) 使用Rails来建立数据库的命令“depot> rake db:create RAILS_ENV='development'”

clip_image032

(2) 使用迁移脚本建立表products。

继续输入命令:“ruby script/generate scaffold product title:string description:text image_url:string “

clip_image034

(3) 执行迁移任务的命令:” depot> rake db:migrate”

clip_image036

3. 建立“货品维护”应用,要求能够实现货品的增长、删除、修改、查看功能答:

答:按照题目2的步骤进行后,

(1)本地主机启动一个web服务器:” depot> ruby script/server”

clip_image038

(2)打开一个浏览器,输入URL” http://localhost:3000/products

clip_image040

(3)单击连接“New product”按钮,增长产品

clip_image042

(4)点击“create”按钮,新的货品会成功建立,若是你点击 Back 连接,你就会看到新的货品出如今列表中。

clip_image044

clip_image046

(5)点击商品后面的”Destroy”超连接,则跳转以下:

clip_image048

4. 用迁移脚本给products表增长1列,为(price(decimal,初始值为0,只保存2位小数))

答:

(1) 使用迁移脚本给Products表增长1列,迁移脚本为:“rails generate migration add_price_to_product price:decimal”。

clip_image050

(2)打开新生成的文件“db/migrate/20080514090912_add_title_body_to_post.rb“并以下修改。

class AddPriceToProduct < ActiveRecord::Migration

def self.up

add_column :products, :price, :decimal,

:precision => 8,:scale => 2,:default => 0

end

def self.down

remove_colum :products, :price

end

end

(3)再次运行数据迁移。

clip_image052

(4)修改相关view,增长与字段price相关的内容,在model目录的product.rb文件里添加属性:price

clip_image054

(5)刷新页面,便可看见新添加的自动

clip_image056

(6)点击“New Product”超连接,能够看到。

clip_image058 clip_image060

5. 增长对页面输入的验证,title,description,image_url不能为空,title的长度不能大于50,若是输入出错,在页面反应出来

答:Ruby2提供的较验方法的API位置:

clip_image062

其中大概许多校验的辅助方法简化操做有:

validates_acceptance_of 确认表单的checkbox是否被标记

validates_associated 确认关联对象,每一个关联对象的nil?法被调用

validates_confirmation_of 确认字段和指定字段的值是否有一样的内容

validates_each 确认块内的每一个属性

validates_exclusion_of 确认指定的属性值不会出如今一个特定的可枚举对象中

validates_format_of 确认指定的属性值是否匹配由正则表达式提供的正确格式

validates_inclusion_of 确认指定的属性值是不是一个特定的可枚举对象中的一个有效值

validates_length_of 肯定指定的属性值是不是匹配约束的长度 validates_length_of为其别名

validates_numericality_of 确认一个属性值是不是数字

validates_presence_of 确认指定属性值是否为空

validates_uniqueness_of 肯定指定的属性在系统中是否惟一

根据API文档,检验一个字段的长度的方法以下:

clip_image064

此外,在Rail3版本中,还可使用validate(*attributes)方法

clip_image066

在这儿咱们使用第一个方法,打开models目录下的product.rb文件,输入以下信息:

class Product < ActiveRecord::Base

attr_accessible :description, :image_url, :title, :price

# 检查指定字段存在、而且值不为空

validates_presence_of :title, :description, :image_url

# 数值合法性检查

validates_numericality_of :price

# 确保每样货品都有一个独一无二的名称(title)

validates_uniqueness_of :title

# 确保title的长度不大于50

validates_length_of :title, :maximum => 50

# 验证输入的图片地址URL合法性

validates_format_of :image_url,

:with => %r{\.(gif|jpg|png)$}i,

:message => 'must be a URL for GIF, JPG or PNG image.(gif|jpg|png)'

end

从新刷新页面,输入长度大于50的Title,页面显示结果以下:

clip_image068

6. 经过使用布局实现《Web敏捷开发之道》P93的图7.3页面,在点击图书封面图片时,也调用add_to_cart这个action

提示:用link_to和image_tag,这两个标签的具体使用方法请查阅http://api.rubyonrails.org

答: 咱们能够将depot \app\views\store\ index.html.erb文件下的这条语句

<%= image_tag(product.image_url) %>

替换成以下语句:

<%=link_to image_tag(product.image_url),:action=>:add_to_cart,:id => product,:method => post %>

可是因为并无add_to_cart这个action存在,所以咱们就用如下语句替换:

<%=link_to image_tag(product.image_url),

line_items_path(product_id: product) %>

替换后,在单击图片便可出现以下页面,代表clip_image070

7. 在以上建立的应用中使用session存储某页面被访问的次数,并在页面上显示出来。

答:

在depot\app\controllers\store_ controller文件中增长以下代码:

class StoreController < ApplicationController

def index

@products = Product.order(:title)

if session[:count].nil?

session[:count]=1

else

session[:count] +=1

end

end

end

而后再depot\app\views\store\index.html.erb文件中增长以下语句:

<h1>browse counts: <%= session[:count]%> -Your Pragmatic Catalog </h1>

刷新页面,便可发现访问次数在不断增长,

clip_image072

clip_image074

8. Rails应用中flash的做用是什么?flash里的内容在何时有效?如何在页面中显示flash里的内容?

答:Rails应用中的flash的结构和Hash很相似,咱们能够在处理请求的过程当中把任何东西放进去,也就是说,flash通常都是用于收集错误信息的。

flash的生命周期为一个session,即在同一个session的下一个请求中,你可使用flash的内容,而后这些内容就会被自动删除。

使用flash方法能够访问flash中的内容,例如:

flash[:notice] = “Invalid product”

9. 参照第9章内容,当用户点击“Add to Cart”按钮时,只更新局部页面,如插入一段html代码到页面的某个div

答:

想要达到Ajax的效果,即点击“Add to Cart”连接,看到只有div购物车信息被更新,同时浏览器却不会有任何刷新页面的迹象。

原先咱们是使用button_to()来建立咱们的“Add to Cart”连接的,其实,button_to()方法就是生成了HTML的<form>标记。原先咱们的调用方法

<%= button_to 'Add to Cart', line_items_path(product_id: product) %>

会生成这样的相似HTML代码

<form method="post" action="/store/carts/1"class="button-to">

<input type="submit" value="Add to Cart" />

</form>

 

咱们首先在改代码后添加一段“, remote: true”,以下

<% if notice %>

<p id="notice"><%= notice %></p>

<% end %>

<h1>Your Pragmatic Catalog</h1>

<% @products.each do |product| %>

<div class="entry">

<%= image_tag(product.image_url) %>

<h3><%= product.title %></h3>

<p><%= sanitize(product.description) %></p>

<div class="price_line">

<span class="price"><%= number_to_currency(product.price) %></span>

<%= button_to 'Add to Cart', line_items_path(product_id: product), remote: true %>

</div>

</div>

<% end %>

首先是,咱们要当咱们要请求javascrip时中止creat这个action向index页面重定向。这样咱们要给respond_to()方法增长一个请求,告诉它咱们想要一个.js文件的应答。所以,修改app/controllers/line_items_controller.rb文件的creat方法:

def create

@cart = current_cart

product = Product.find(params[:product_id])

@line_item = @cart.line_items.build

@line_item.product = product

@line_item = @cart.add_product(product.id)

respond_to do |format|

if @line_item.save

format.html { redirect_to @line_item.cart,

notice: 'Line item was successfully created.' }

format.js

format.json { render json: @line_item,

status: :created, location: @line_item }

else

format.html { render action: "new" }

format.json { render json: @line_item.errors,

status: :unprocessable_entity }

end

end

end

而后,咱们在depot_l/app/views/line_items/目录下新建一个“create.js.erb”文件,输入以下代码:

$('#cart').html("<%=j render @cart %>");

先清空购物车Cart,从新载入页面,弹出以下页面:

clip_image076

而后咱们再次刷新页面,点击“Add to cart”按钮,此时只有左边的购物车的区域发生变化,增长了一个lineItem,而没有重定向到index.html页面,图片以下:

clip_image078

这样就成功实现了Ajax效果。

10. 模型的单元测试和控制器的功能测试如何进行,征对应用中的某个model和某个controller,各举一个例子

答:

(1) 模型Product的单元测试

在前面咱们定义Product的model的时候,定义了以下方法,用来检验数据:

class Product < ActiveRecord::Base

attr_accessible :description, :image_url, :title, :price

validates_presence_of :title, :description, :image_url

validates_numericality_of :price

validates_uniqueness_of :title

validates_length_of :title, :maximum => 50

validates_format_of :image_url,

:with => %r{\.(gif|jpg|png)$}i,

:message => 'must be a URL for GIF, JPG or PNG image.(gif|jpg|png)'

end

知道这些校验逻辑确实起做用了就得靠测试,在咱们用脚手架生成Product的时候,Rails已经自动为咱们生成了一套test框架. 首先,若是建立一个货品却不给它设置任何属性,咱们但愿它不能经过校验,而且每一个字段都应该有对应的错误信息。

(a)于是咱们打开depot\test\unit目录下的product_test.rb文件并编辑:

require 'test_helper'

class ProductTest < ActiveSupport::TestCase

# test "the truth" do

# assert true

# end

test "product attributes must not be empty" do

product = Product.new

assert product.invalid?

assert product.errors[:title].any?

assert product.errors[:description].any?

assert product.errors[:price].any?

assert product.errors[:image_url].any?

end

end

(b)输入rake test:units命名

clip_image080

(2) 控制器Controller的功能测试。

控制器负责控制用户界面的展现。它们接收进入的web请求(一般是用户的输入),与模型对象进行交互以得到应用程序的状态,而后找到合适的视图显示给用户。因此,当对控制器进行测试时,咱们必须确保必定的请求可以获得合适的应答.

在这儿,咱们的Product模型已经测试过了,于是只须要对controller进行测试。打开文件/test/functional/store_controller_test.rb。增长四段代码:

require 'test_helper'

class StoreControllerTest < ActionController::TestCase

test "should get index" do

get :index

assert_response :success

assert_select '#columns #side a', minimum: 4

assert_select '#main .entry', 3

assert_select 'h3', 'Programming Ruby 1.9'

assert_select '.price', /\$[,\d]+\.\d\d/

end

end

输入rake test:functionals命令:

clip_image082

《Web敏捷开发之道》(14-19)

=====================================================================

1. 如何将Rails自己固化到程序里?

答:为了确保程序始终能够获得正确的Rails版本。一种方法是直接将Rails代码固化(freeze)在应用程序的目录中,这样一来,Rails库就和应用程序代码一道保存在版本控制系统中了。

其方法以下,只要输入下列命令便可:

depot>rake rails:freeze:gems

这个命令会在幕后将最新版本的Rails库拷贝到vendor/rails目录下——当应用程序启动时,Rails会首先到这里来寻找本身须要的库,而后再寻找全系统共享的版本。若是在固化以后但愿取消绑定、继续使用系统共享的Rails版本,能够直接把vendor/rails目录删掉,也能够执行下列命令:

depot>rake rails:unfreeze

2. 当运行应用程式时,怎样指定运行时环境?

答:编写代码、测试和工做环境下的运行,在这三个阶段,开发者的须要有很大差别。

◆在编码阶段,你但愿看到更多的日志、可以当即加载修改过的代码、直观的错误提示,等等。

◆在测试阶段,你就须要一个与世隔绝的环境,这样测试才具备可重复性。

◆在真实运行时,系统须要最优化的效率,而且不该该让用户看到错误。

为了支持这些不一样的需求,Rails引入了运行时环境的概念。每一个运行时环境都有本身的一组配置参数。当运行应用程序时,你就能够指定运行时环境。譬如说,若是你使用rails server来运行,能够加上-e选项:

depot> rails server -e development # the default if -e omitted

depot> rails server -e test

depot> rails server -e production

3. rails中求一个字符串所包含的字符数的函数是什么?如求clip_image084的字符长度

答:Multibyte库并无将Ruby内建的字符串类库替换成可以处理Unicode的版本,而是义了一个名叫Chars的新类型。这个类定义了与内建的String类相同的方法,惟一区别是这些方法都可以处理多种字符编码方式。

使用多字节字符串的规则很是简单:但凡须要处理UTF-8编码的字符串时,都应该首先将这些字符串转换成Chars对象。Multibyte库给String类加上了chars方法,让这个转换变得易如反掌。 方法以下:

E:\RubyOnRailProj\depot>rails console

Loading development environment (Rails 3.2.11)

irb(main):001:0> name = "G\303\274nter"

=> "Günter"

irb(main):002:0> name.mb_chars.length

=> 6

4. 如何使用迁移任务修改字段名、字段类型、给表添加索引?

答:(1) 咱们能够经过在用迁移任务时使用rename_column()方法来修改字段名,好比将原来的e_name更名为customer_email,代码以下:

class RenameEmailColumn < ActiveRecord::Migration

def change

rename_column :orders, :e_mail, :customer_email

end

end

此时,由于更名操做是可逆的,所以可使用change()方法,而不须要使用up()和down()方法。

(2) 咱们能够经过在用迁移任务时change_column()方法来修改字段类型。此时注意最好使用self.up()和self.down()方法,由于可能类型之间的转换不是可逆转的。而且,要注意类型转换异常。例如,咱们将原来为integer类型的order字段改变成String类型,代码以下:

class ChangeOrderTypeToString < ActiveRecord::Migration

def self.up

change_column :orders, :order_type, :string, :null => false

end

def self.down

raise ActiveRecord::IrreversibleMigration

end

end

(3) 咱们能够经过在用迁移任务时用add_index()方法给表添加索引。例如,当数据库中有不少订单数据时,根据顾客名字搜索订单数据就会变得很慢。此时,就应该给这张表加上索引了,方法以下:

class AddCustomerNameIndexToOrders < ActiveRecord::Migration

def change

add_index :orders, :name

end

end

5. 如何经过model向数据库中添加一条记录。

答: 咱们可使用面向对象的方法经过model向数据库中添加一条记录。好比,咱们只要调用Order.new()方法,就能够建立一个Order对象,它表明着orders表中的一条记录;随后咱们能够填充该对象各个属性(对应于数据库中的字段)的值;最后调用该对象的save()方法,就能够将它存入数据库。代码以下:

an_order = Order.new

an_order.name = "Dave Thomas"

an_order.email = "dave@example.com"

an_order.address = "123 Main St"

an_order.pay_type = "check"

an_order.save

或者使用如下方法:

an_order = Order.new(

name: "Dave Thomas",

email: "dave@example.com",

address: "123 Main St",

pay_type: "check")

an_order.save

6.用Order.find方法实现下列sql语句”select * from orders where id=2 and name like ‘a%’ order by id DESC

答:代码以下,

orders=Order.find(:all,

:conditions => ["id = '2'" and "name like ?",'a'+"%"],

:order => "id DESC")

7. 用ActiveRecord的方法实现order(id:integer, amount:integer)表里面id大于3小于100的全部记录的amoun字段的值的和。

答:代码以下,

total= Order.sum :id, :conditions => "id > 3 and id <100"

8. 使用ActiveRecord定义外键的命名约定是什么?

答: ActiveRecord的命名约定:外键字段的名字应该以被引用的目标表名为基础,将其转换为单数形式,并加上_id后缀。外键字段使用了单数形式与_id后缀,也就是说它的名字必定与目标表名不一样。

好比,在下图表示,数据库中一个订单(orders)有零个或多个订单项(line_items),它们分别映射的ActiveRecord模型类为Order和LineItem,其中在line_items表中引用orders表所使用的外键字段名就应该是order_id。

clip_image086

9. Rails中如何实现表间的一对1、一对多、多对多的关联

答:ActiveRecord 支持三种表间关联:一对一(one-to-one)、一对多(one-to-many)、多对多(many-to-many)。你须要在模型类中加上声明以便指明使用哪一种关联:has_one、has_many、belongs_to或has_and_belongs_to_many。

(1) 一对一的关联:实现方式是在其中任意一张表里保存外键字段,引用另外一张表里的记录。以下图,在Invoices表中有一个orders表的外键,此时须要在有外键的Invoice模型类中使用belongs_to :order的声明,同时在被引用的Order模型类中使用has_one :invoice的声明。

clip_image088

(2) 一对多的关联:父对象(它在逻辑上包含一组子对象)应该使用has_many来声明与子对象的关联,子对象则应该用belongs_to来声明与父对象的关联。以下图,因为line_items表包含外键,因此,LineItem对象就应该包含belongs_to声明,而在一对多的关系中“一”的那方(好比这儿一个订单对多个订单项,这儿订单就是关系中“一”的那方)中使用has_many声明

clip_image090

(3) 多对多的关联:多对多关联是对称的——两个模型类都用has_and_belongs_to_many来声明本身与对方的关系。

clip_image092

10. 如何实现仅在建立记录时校验某个数据库字段ip_address的值是不是ip地址

答:根据查看Rails的API得,

clip_image094

在该方法的可选项上有一个’:on’参数,能够指定指定校验进行的时机,可选的值有:save(默认值)、:create和:update,在本题中咱们使用”:create”

clip_image096

而判断一个IP地址的正则表达式以下:

clip_image098

于是,该题代码以下:

class Demo < ActiveRecord::Base

validates_format_of :ip_address,

:on => :create,

:with => %r{((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}

(2[0-4]\d|25[0-5]|[01]?\d\d?)$}i

end

《Web敏捷开发之道》(20-25)

=====================================================================

1. 将外部URL跟内部应用程序对应起来的文件是什么?

答:config/routes.rb文件创建了一个映射关系,将外部的URL与内部的应用程序链接起来。

2. 对于controller为a, action为b的方法,为其定义路由,映射url为’a/cdef’,post请求

答:根据路由定义规则,代码以下。

ActionController::Routing::Routes.draw do |map|

map.connect 'a/cdef' ,

:conditions => { :method => :post },

:controller => "a" ,

:action => "b"

end

3. 应用程序routes.rb文件中map.reource :books, :collection=>{:latest=>:get}和map.resource :book, :member=>{:hide=>:put,:relase=>:put},它们添加的路由规则是什么?有什么区别?

答:map.reource :books, :collection=>{:latest=>:get}的路由规则为新增一个名叫latest的action,并用HTTP GET方法来访问它,它适用于books资源。

map.resource :book, :member=>{:hide=>:put,:relase=>:put}的路由规则为:为资源book单独创建一个action,将book标记为“隐藏”或“发布”。

4. 使用render(:action => :index)会不会调用index方法,为何?

答: 不会调用index方法,只会渲染index的默认模板。

由于,render(:action =>action_name)方法用于渲染当前控制器中指定的action所对应的模板。调用render(:action =>action_name)并不会调用后一个action方法,只是把模板渲染出来。

5. 编写程序,要求:页面上有一个上传按钮和一个下载按钮,实现文件上传和下载。

答: 在HTTP 协议中,文件是做为一种特殊的POST 消息上传到服务器的,其消息类型是multipart/form-data,便是由表单生成的。你能够在表单内部放置一个或多个带有type="file"属性的<input>标签,在浏览器上显示时,该标签会容许用户选择一个文件;当这样的表单被提交到服务器时,文件内容就会与其余表单数据一道被送回服务器。

一个基本的上传按钮的代码以下,

<%=start_form_tag ({:action=>"upload"},:multipart=>true )%>

<input id="upload" name="upload" type="file" />

<%=end_form_tag%>

其中<input type="file" />的标签即是提供上传的标签,设置的“:multipart=>true”,编译后的代码即是“enctype=”multipart/form-data””。该段代码的显示结果以下,

clip_image100

咱们也可使用rails提供的form_for和form_tag方法来生成模板,其中根据ROR的guildes(网站http://guides.rubyonrails.org/form_helpers.html#uploading-files)可得

clip_image102

以上传一个图片为例子。

(1)首先,建立一个表来保存上传的数据,使用scaffold,假设咱们是要在Product表上的img字段上,上传图片数据。

rails generate scaffold Product title:string img:string

这样在db/migrate文件中就能够找到XXX_create_products.rb文件,打开文件获得,

class CreateProducts < ActiveRecord::Migration

def change

create_table :products do |t|

t.string :title

t.string :img

t.timestamps

end

end

end

(2)在控制器ProductsController文件中,建立一个名为uploadImg的控制器,并添加以下代码。

def uploadImg

end

(3)在app/view/products文件中添加一个名为uploadImg.html.erb的文件,添加的代码以下,

<% if notice %>

<p id="notice"><%=notice %></p>

<% end%>

<h1>Upload </h1>

<%= form_tag "/products/upload",:multipart => true do%>

<%= file_field_tag(:img,:size=>"40")%>

<%= submit_tag("上传文件")%>

<% end%>

(4)在控制器ProductsController文件中再添加upload方法,代码以下。

def upload

image=params[:img]

content_size=image.size

file_data=image.read

filetype=image.content_type

@filename=image.original_filename

File.open("#{Rails.root}/public/"+@filename,"wb"){|f| f.write(file_data)}

flash[:notice]="file:"+@filename+"upload success"

render :action=>"uploadImg"

end

其中的“image=params[:img]”是得到上传的文件对象。另外,代码“file_data=image.read”一旦获取上传文件二进制数据,就能够经过IO流将这些数据写入到服务器的文件中。

(5)最后再修改config文件下的routes文件中的代码,以下。

UploadDemo::Application.routes.draw do

match 'products/uploadImg' => 'products#uploadImg'

match 'products/upload' => 'products#upload'

root :to=>"products#uploadImg"

resources :products

end

(6)运行程序,在地址栏输入http://localhost:3000/products/uploadImg,单击“浏览”上传图片时显示以下,

clip_image104

单击“上传文件”后,页面显示以下,

clip_image106

同时,查看项目根目录下的public文件,发现长传成功的hello.jpg文件

clip_image108

文件的下载。

文件的下载有send_file和send_data两种方法,在本题中使用send_data的方法。send_data的API以下,

send_data(data, options = {})

Sends the given binary data to the browser. This method is similar to render :text => data, but also allows you to specify whether the browser should display the response as a file attachment (i.e. in a download dialog) or as inline data. You may also set the content type, the apparent file name, and other things.

Options:

· :filename - suggests a filename for the browser to use.

· :type - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify either a string or a symbol for a registered type register with Mime::Type.register, for example :json If omitted, type will be guessed from the file extension specified in :filename. If no content type is registered for the extension, default type 'application/octet-stream' will be used.

· :disposition - specifies whether the file will be shown inline or downloaded. Valid values are 'inline' and 'attachment' (default).

· :status - specifies the status code to send with the response. Defaults to 200.

注意选项” :disposition”的值有” inline”和” attachment”,分别表示是直接显示方法下载仍是使用附件的形式下载。

(1)修改app/view/products目录下的index.html.erb文件,增长一个button按钮。修改的代码以下,

<h1>Listing products</h1>

<table>

<tr>

<th>Title</th>

<th>Img</th>

<th></th>

<th></th>

<th></th>

</tr>

<% @products.each do |product| %>

<tr>

<td><%= product.title %></td>

<td><%= product.img %></td>

<td><%= link_to 'Show', product %></td>

<td><%= link_to 'Edit', edit_product_path(product) %></td>

<td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td>

<td><%= button_to 'download', :action=>"downSendData",:filename=>"#{product.img}.jpg" %></td>

</tr>

<% end %>

</table>

<br />

<%= link_to 'New Product', new_product_path %>

(2)在控制器文件中添加downSendData方法,代码以下,

def downSendData

io=File.open("#{Rails.root}/public/"+params[:filename])

io.binmode

send_data(io.read,:filename=>params[:filename],:type=>"image.jpg" ,:disposition=>"attachment")

io.close

end

(3)在config目录下的routes文件中添加以下的代码。

UploadDemo::Application.routes.draw do

match 'products/uploadImg' => 'products#uploadImg'

match 'products/upload' => 'products#upload'

match 'products/downSendData' => 'products#downSendData' root :to=>"products#uploadImg"

resources :products

end

(4) 运行程序,在地址栏输入http://localhost:3000/products/获得,以下画面,而后点击”download”按钮,即显示下载该文件的对话框。

clip_image110

6.信息存储到session应该注意什么?

答: 将信息存储到session应该注意,

(1) 可以放入session的对象是有限制的,通常而言,session中的对象必须是可序列化的。

(2) 不要把拥有大量数据的对象放进session。能够把它们放进数据库,而后在session中引用这些数据。

(3) 不要把常常变化的对象放进session。常常变化的数据应该放进数据库,而后从session中引用这些数据

(4) 不要把关键信息单独放进session关键信息应该保存在数据库中,而后从session中引用这些数据。

7. 如何实现将session数据保存到应用程序所使用的数据库中。

答:ActionController::Base中的session_store属性值,能够指定session的存储机制。

将session数据保存到应用程序所使用的数据库中,可设置属性

session_store = :active_record_store。

而后使用rake 任务建立sessions表。

depot> rake db:sessions:create

再运行rake db:migrate就能够实际建立这张表。

8. 编码实现采用ActiveRecord存储的过时session数据(最近2天没有更新的)

答: 采用ActiveRecord存储session数据,利用sessions数据库表中的updated_at字段,而后执行SQL就能够删除全部在大于2天中没有更新的session数据,剩下存储的就是最近两天没有更新的sessoin数据,SQL代码以下,

delete from sessions where now() - updated_at > 3600*24*2;

9. 使用前置过滤器实现以下内容:定义一个authorize方法用于身份验证,若是当前session中没有登陆用户信息,就重定向到登陆界面。

答:前置过滤器会在action以前被调用,后置过滤器则在action以后调用。Rails会针对这两种过滤器分别维护一个链表。在执行action以前,控制器会首先执行前置链表中的全部过滤器;在action执行完毕以后再执行后置链表中的全部过滤器。在本题中,程序代码以下,

class ApplicationController < ActionController::Base

before_filter :authorize

protected

def authorize

unless User.find_by_id(session[:user_id])

redirect_to login_url, notice: "Please log in"

end

end

end

10. 编程实现带选项分组的列表选择

答: 在选择列表中分组,这项功能并不经常使用,但却很是强大:你能够用这种方式来给列表中的选项加上标题。

完整的列表能够看做是一个包含多个分组的数组,每一个分组又是一个对象,由“分组名称”与“子选项集合”两部分组成。在本次编程中,咱们准备了包含“发货选项”的一个列表,并按照“交付速度”将其分组。

第一步:在辅助模块中,咱们定义了一个与数据库无关的模型类Shipping来表明“发货选项”,并用一个类来表明“一组发货选项”。而后,咱们在代码中初始化了三个分组。

class Shipping

ShippingOption = Struct.new(:id, :name)

class ShippingType

attr_reader :type_name, :options

def initialize(name)

@type_name = name

@options = []

end

def <<(option)

@options << option

end

end

ground = ShippingType.new("SLOW" )

ground << ShippingOption.new(100, "Ground Parcel" )

ground << ShippingOption.new(101, "Media Mail" )

regular = ShippingType.new("MEDIUM" )

regular << ShippingOption.new(200, "Airmail" )

regular << ShippingOption.new(201, "Certified Mail" )

priority = ShippingType.new("FAST" )

priority << ShippingOption.new(300, "Priority" )

priority << ShippingOption.new(301, "Express" )

OPTIONS = [ ground, regular, priority ]

end

第二步:在视图中建立一个选择列表控件,以便显示辅助模块提供的列表。这里没有一个高级的包装方 法 可 以 方 便 地 创 建<select>标 签 并 用 分 好 组 的 标 签 填 充 , 所 以 我 们 只 得 改 用option_groups_from_collection_for_select()方法。该方法的参数包括一个分组的集合、用于获取分组信息与选项信息的访问方法名,以及模型对象的当前值。咱们把这些放在一个<select>标签的内部,该标签的名字是模型对象名和属性名的组合。

<label for="order_shipping_option" >Shipping: </label>

<select name="order[shipping_option]" id="order_shipping_option" >

<%=

option_groups_from_collection_for_select(Shipping::OPTIONS,

:options, :type_name, # <- groups

:id,:name, # <- items

@order.shipping_option)

%>

</select>

相关文章
相关标签/搜索