1. 什么是MVC架构
MVC是模型(model)-视图(view)-控制器(controller)的缩写。MVC是一个框架模式,它强制性的使应用程序的输入、处理和输出分开。其中,
(1)模型用来模拟应用所管理的真实世界的事物。
(2)视图是应用中展示给用户看的那一部分,通常被称为表示层。
(3)控制器是应用的真正大脑。它决定了应用怎样和系统交互、控制着那些数据可以从模型中获得,以及视图中的那一部分用来表示这些数据。
2. 什么是对象-关系映射(ORM),Rails所采用的ORM层是什么?
答:对象-关系映射(ORM)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术,比如java系列的Hibernate就是一个轻量级ORM框架。
Rails所采用的ORM层是一个名为ActivityRecord的对象-关系映射库。
3. 如何创建一个rails应用程序demo。
答:
(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,要求在浏览器窗口中输入按回车后,页面显示“This is my first action.”
答:
(1)新建一个rails的程序,取名helloDemo。
(2)在目录app/controllers/下新建一个rb文件,命名为test_controller.rb。
打开test_controller.rb文件,输入如下代码:
(3) 在目录app/views/ 下新建一个目录为test, 并在该目录下新建一个first_action.html.erb的文件。
打开first_action.html.erb文件,输入以下代码并保存:
(4) 重启server,然后运行该Rails程序,在浏览器中输入,后显示
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> |
运行结果如下:
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> |
运行结果如下:
9. 在控制器test中新增一个action(second_action),实现它与first_action之间的互相跳转。
答:
(1) 首先在目录app/controller/test_controller.rb文件中新增一个second_action方法。
(2) 然后,在views/test/目录中新增一个action命名为”second_action.html.erb”。
打开文件,输入代码:
(3) 最后,重启server,运行程序。
运行结果如下:
点击链接跳转至second_action
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> |
运行结果如下:
《Web敏捷开发之道》(5-13)
=====================================================================
1. 创建rails应用程序depot,后台使用mysql数据库,创建数据库depot_development,depot_production,depot_test,给出配置应用程序数据库方法。
答:
(1) 在项目的目录中使用”rails --database=mysql depot”命令创建一个使用mysql的应用程序,程序命名为depot。
(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'”
(2) 使用迁移脚本创建表products。
继续输入命令:“ruby script/generate scaffold product title:string description:text image_url:string “
(3) 执行迁移任务的命令:” depot> rake db:migrate”
3. 创建“货品维护”应用,要求可以实现货品的增加、删除、修改、查看功能答:
答:按照题目2的步骤进行后,
(1)本地主机启动一个web服务器:” depot> ruby script/server”
(2)打开一个浏览器,输入URL” ”
(3)单击链接“New product”按钮,增加产品
(4)点击“create”按钮,新的货品会成功创建,如果你点击 Back 链接,你就会看到新的货品出现在列表中。
(5)点击商品后面的”Destroy”超链接,则跳转如下:
4. 用迁移脚本给products表增加1列,为(price(decimal,初始值为0,只保存2位小数))
答:
(1) 使用迁移脚本给Products表增加1列,迁移脚本为:“rails generate migration add_price_to_product price:decimal”。
(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)再次运行数据迁移。
(4)修改相关view,增加与字段price相关的内容,在model目录的product.rb文件里添加属性:price
(5)刷新页面,即可看见新添加的自动
(6)点击“New Product”超链接,可以看到。
5. 增加对页面输入的验证,title,description,image_url不能为空,title的长度不能大于50,如果输入出错,在页面反应出来
答:Ruby2提供的较验方法的API位置:
其中大概许多校验的辅助方法简化操作有:
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文档,检验一个字段的长度的方法如下:
此外,在Rail3版本中,还可以使用validate(*attributes)方法
在这儿我们使用第一个方法,打开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,页面显示结果如下:
6. 通过使用布局实现《Web敏捷开发之道》P93的图7.3页面,在点击图书封面图片时,也调用add_to_cart这个action。
提示:用link_to和image_tag,这两个标签的具体使用方法请查阅
答: 我们可以将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) %> |
替换后,在单击图片即可出现如下页面,表明
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> |
刷新页面,即可发现访问次数在不断增加,
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,重新载入页面,弹出如下页面:
然后我们再次刷新页面,点击“Add to cart”按钮,此时只有左边的购物车的区域发生变化,增加了一个lineItem,而没有重定向到index.html页面,图片如下:
这样就成功实现了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命名
(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命令:
《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中求一个字符串所包含的字符数的函数是什么?如求的字符长度
答: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。
9. Rails中如何实现表间的一对一、一对多、多对多的关联
答: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的声明。
(2) 一对多的关联:父对象(它在逻辑上包含一组子对象)应该使用has_many来声明与子对象的关联,子对象则应该用belongs_to来声明与父对象的关联。如下图,由于line_items表包含外键,所以,LineItem对象就应该包含belongs_to声明,而在一对多的关系中“一”的那方(比如这儿一个订单对多个订单项,这儿订单就是关系中“一”的那方)中使用has_many声明
(3) 多对多的关联:多对多关联是对称的——两个模型类都用has_and_belongs_to_many来声明自己与对方的关系。
10. 如何实现仅在创建记录时校验某个数据库字段ip_address的值是否是ip地址?
答:根据查看Rails的API得,
在该方法的可选项上有一个’:on’参数,可以指定指定校验进行的时机,可选的值有:save(默认值)、:create和:update,在本题中我们使用”:create”
而判断一个IP地址的正则表达式如下:
因而,该题代码如下:
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””。该段代码的显示结果如下,
我们也可以使用rails提供的form_for和form_tag方法来生成模板,其中根据ROR的guildes(网站)可得
以上传一个图片为例子。
(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)运行程序,在地址栏输入,单击“浏览”上传图片时显示如下,
单击“上传文件”后,页面显示如下,
同时,查看项目根目录下的public文件,发现长传成功的hello.jpg文件
文件的下载。
文件的下载有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 Options: · · · · |
注意选项” :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) 运行程序,在地址栏输入得到,如下画面,然后点击”download”按钮,即显示下载该文件的对话框。
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> |