web开发敏捷之道笔记
前言
在学校胡乱的学了一个月Rails,期间忙着自己的毕设、别人的毕设。工作后,边抄边猜,边搜边试,5个月也就这么过去了。一般的开发任务,自己想想找找,再问问,也能搞定。但总这样不是个办法,决定再过一遍的Rails相关的知识。
ActiveSupport
ActionSupport是一组类库,是Ruby语言的一种扩展。大体的扩展如下:
- 通用扩展: Ruby对象-编程语言无关形式(比如xml,json,yaml):
to_json
和to_yaml
。 - blank?方法
- 枚举和数组:web应用经常要处理对象集合, 比如表格之类的。
group_by
方法:将集合进行分组,对集合中每个对象调用代码块,返回hash-包含数组。index_by
: 接受集合作为参数,并将其转换为hash- sum 对集合进行加总,对代码返回值进行累加。
- many?: 判断集合中的元素是否都多于一个;
to_sentence
,in_groups_of
- first,last,second,third,fourth以及fifth。
- 字符串扩展: at, form, to, first, last,
starts_with
,ends_with
,is_utf8?
- 时间和日期的扩展: Time.now,to_date,to_s等等,
- Date对象的有用方法:today,tomorrow,current之类
- 符号扩展:
posts.group_by {|post| post.author_id}
简写成posts.group_by(&:author_id)
- with_options:允许一次指定共同的选项,主要用在路由中。
问题: 在irb中require ‘activesupport‘时说找不到相应的方法, 难道是RVM和bundle处理的结果? 仔细阅读enviroment.rb
等文件后,并在irb中使用require 'active_support/all'
后,成功加载了类库。
ActiveController
控制器为action设置了环境,存在很多可以获取URL/请求中的信息的方法: action_name
, cookies, headers, params, request(方法较多的对象), response(HTTP 响应对象,请求处理过程中,Rails会填充该对象), session, logger对象
一次一请求: render
, redirect_to
, send_xxx
。大部分action为视图设置上下文环境。render方法是Rails渲染机制核心所在,render渲染填充响应体。
send_data
: 发送二进制流给客户端。send_file
: 将指定的文件发送给客户端。
重定向 - 浏览器处理 - URL变化,redirect_to
: 简单强大的重定向机制。
redirect_to :action => ..., options...
redirect_to path # path为某一具体的URL - action内部 - url_for
redirect_to :back
cookies - 类hash对象,封装了cookie协议,其值必须是字符串。存在默认附加的信息: 有效期、有效路径,域名等。
cookie检测:浏览器禁用cookie ,解决步骤:两个快速重定向,第一次访问站点和session建立。
session - 类hash结构,与cookie不同的是,其可包含任意可序列化的对象。session存储的位置:1. 作为cookie存储 2. 存放在服务器端(将session id保存在cookie中,服务器通过id索引session数据)
session对象的一些限制:
- 可序列化对象,I/O对象否决
- 大小限制,大数据的对象不要
- 频繁变化对象不要,将频繁变化的对象发在数据库中,然后在session中应用这些数据
- 关键/机密信息 - 存入数据库,session中引用
基于cookied的session的好处,可以自清理。顶层控制器的申明指定cookie名。 session的存储策略存在很多中,不过,要熟练的运用,还需要学习和理解。
memcached策略适合大流量的网站。分析应用程序的特性、进行充分的基准测试,然后进行性能优化,起步是从最简单的开始。10000个并发会话。session 也需要考虑过期和清除的问题,具体的实现,与所选的session存储机制有关。
Flash - Action之间通信,临时存储空间,hash结构,仅在下次请求中有效。常见用途: 将错误消息传递下去,视图中有显示flash的块结构。 常见变量: flash[:notice]
,flash.now[:notice]
,flash.keep(:notice)
过滤器(before,after,around):可以访问request和response对象。前置过滤器用来权限验证和重定向,后置过滤器用作应答修改(替换或压缩),around过滤器保持action完整的上下文 - 计算action耗费的时间。代码块实现的around过滤器:
around_filter do |controller,action|
started = Time.now
action.call # 这里是显式调用,效果和yield类似
elapsed = Time.now - started
controller.logger.info "#{controller.action_name} took #{elapsed} seconds"
过滤器存在三种实现方法: private私有方法,代码块,实现了filter的对象。与前置/后置过滤器的链式方式不同的是,环绕过滤器是嵌套执行的。 skip_filter可以用来忽略所有过滤器。
Rails自带校验机制 - verify三方面 : 范围(:only,:except)、条件(:flash, :method, :params, :session, :xhr)、动作(失败时的动作 - :add_flash, :add_headers, :redirect_to, :render)
缓存: caches_page
, caches_action
, expire_page
- 显式失效和隐式失效(使用sweeper观察器,cache_sweeper), expires_in
, stale?, fresh_when
缓存存储体制: session数据很少,缓存片段内容较多,需要仔细处理。
GET问题: button_to
, 设计原则 - 将危险的操作放在POST请求中,使用button_to和表单
ActionView
关于模板的三个问题: 在哪里(render),运行环境(控制器的实例变量,controller变量及其公共方法),内容(各种模板,builder,erb,coffescript等)
模板可以直接访问控制的中的flash,headers,params, request,response和session对象。注: 处理信息的逻辑应当放在控制器中。
- builder可以用来生成任意xml文本,支持namespace,xml entity, processing instructions,xml notation。
- erb 模板:
config.action_view.erb_trim_mode
选项的”%”值, sanitize(存)和h(取)方法, 绑定上下文-binding
视图中存在很多用于处理日期,数字和文本格式化的方法。button_to
生成的是表单。
表单处理
Rails的表单支持:表单模块对应于数据库资源,使用form_for
,不与数据表对应的使用form_tag
。
form_for
的使用示例:
视图中的form_for
,位于form_for
和end
之间的代码块会得到表单构建器(FormBuilder类,ActionView中的form_helper.rb):
<% form_for :user do |form| %>
...
<% end %>
对应的控制器中的方法:
def new
@user = User.new
end
def create
@user = User.new(params[:user])
...
end
如果模型对象的变量名和类名不匹配,可以传递form_for
第二个可选的参数: <% form_for :user, @account_holder do |form| %>
form_for
中可接受的hash选项有:url
和:html
,:url
的输入与url_for
和link_to
方法类似,:html
则为表单添加HTML属性。:multipart => true
提交multipart表单数据,用于上传文件。
form_for
表单之间,可以存在的元素有text_field
,hidden_field
, password_field
, text_area
, select
,这些都是input元素,input用作值的提交。这些方法的签名如下:
form.text_field(:attribute, options)
form.hidden_field(:attribute, options)
form.password_field(:attribute, options)
form.text_area(:attribute, options)
form.radio_button(:attribute, tag_value, options)
form.check_box(:attribute, options, on_value, off_value)
form.select(:attribute, choices, options, html_options)
form.date_select(:attribute, options)
form.datetime_select(:attribute, options)
select_date, select_day , select_month, select_year, select_xxx
应用程序通常需要根据数据库信息来构建选择列表,如下展示两种做法:
<%=
# 第一种做法
@users = User.find(:all, :order => "name").map { |u| [u.name, u.id] }
form.select :name , @users
# 第二种做法
@users = User.find(:all, :order => "name")
form.collection_select :name, @users, :id , :name
%>
直接使用text_field
,而不是form.text_field
,这在由多个ActiveRecord对象共同组成的表单中很有用。fields_for
弥补form_for
不足,
form_for
方法的问题: 同时做了两件事 - 上下文环境和表单所需的form标签和属性。注意,生成的HTML的表单的命名方式。
多个模型保存到数据库中使用的方法是事务和异常处理,简单的例子如下:
def create
@product = Product.new(params[:product])
@details = Detail.new params[:details]
Product.transaction do
@product.save!
@details.product = @product
@details.save!
redirect_to :action => :show, :id => @product
end
rescue ActiveRecord::RecordInvaild => e
@details.valid? # 即使products失败了,也要强制检查其错误
render :new
end
通过继承ActionView中FormBuilder,从而实现自定义的表单。css样式描述了组件的基本外观,而不考虑组件的具体行为,即文本框也可以使用按钮的样式。
HTTP协议中,文件作为特殊的POST消息(multipart/form-data)上传到服务器。上传文件的消息是有表单生成的,type="file"
的input标签。上传文件的处理,有些复杂,需要理解。
布局和组件
layout申明:
- layout “standard”, except: [ :rss , :atom ]
- layout nil - 禁用布局
- layout :determine_layout - 动态决定布局模板
- action中可自由选择布局模板
普通模板在布局模板之前渲染,普通模板中可访问的数据在布局模板中也可访问。
cache.. end
和 expire_fragment
方法,在控制器中编写过多的expire_fragment
不好,可以使用sweeper来处理。
web 2.0 ,XHR以及多种可视化的效果,可视化效果是ajax的表。xhr请求,生成js返回浏览器执行,进行页面DOM元素的处理。
模板处理器
实现模板处理器的两个条件:
- 接受代表视图对象的参数
- render方法,两个参数: 模板正文和本地hash变量
ActiveRecord
Active Record理念:理性看待SQL语句,面向对象不是绝对好的,遗留的SQL语句。
注: 深入理解ActiveRecord,果然还是深入理解SQL这门语言的。
establish_connection()方法 - 到数据库的连接依然是存在的,框架抽象其实现,不代表不存在; 表、类、字段、和属性(ORM);属性id以及相互关系;创建、读取、更新和删除操作,回调(callback)和事务(transaction)。
经验: 可以在Rails console中尝试模型, 即所谓的,一开始就与模型进行交互的敏捷开发模式。
- 模型的关联性:一对一(belongs_to/has_one),一对多(belongs_to/has_many)和多对多(has_and_belongs_to_many/has_and_belongs_to_many,中间连接)。
- 模型声明:has_one,has_many,belongs_to。
- Active Record,动态生成sql语句-SQL注入的问题。
new,create,find方法(find_by_sql),where(like子句),流式API调用。
数据库事务(数据库):回滚rollback,ACID。
Active Record中验证数据
- length选项比较复杂:
validates :content, length: {
minimum: 300,
maximum: 400,
tokenizer: lambda { |str| str.scan(/\w+/) },
too_short: "must have at least %{count} words",
too_long: "must have at most %{count} words"
}
numericality验证也很复杂。
在模型中显示出错信息。
回调函数: 创建、保存、更新、删除、验证、读取时执行。 主要进行预设置和
更好的SQL语句执行的方式。
scope: 条件查询, 特定域条件多个条件查询。
find_by_sql: 定制查询的简单方式, 抽象是存在缺陷的。
ids 获取数据库表的主键; exists 接受多个参数; 数据库的计算: count 、 average、minimum、maximum。
分发请求是通过Action Pack,主要由路由定义其行为,Filter可以在控制器处理之前或之后改变请求.
一些filter方法: before_filter
, after_filter
,skip_before_filter
, skip_after_filter
钩子方法
Session/Flash: 可以通过session或者flash hash来横跨多个请求保存数据.
session :off, only: :action, :session_secure => true
嵌入HTML的Ruby代码编写。
HTML 由三部分组成:模板,局部视图和布局
模板: Builder模板,ERB模板,JS模板。
Builder模板比ERB模板需要更多的编程,用来生成XML文档,用作SOAP。
将模板编译成方法??,然后渲染视图。
局部视图,将渲染分成多个任意管理的代码片段。
<%= render "shared/footer" %>
<%= render partial: "product" %>
等价与 <%= render partial: "product", locals: {product: @product} %>
局部视图的as选项:指定在局部视图中使用的变量名; object选项:指定在局部视图中使用的对象; partial选项指定视图中使用的模板。
布局
用来控制Rails控制器动作的页面整体结构
。Rails程序,多个布局,应用程序布局。
局部视图可以使用自己的布局,布局也通过erb定义,yield方法
和代码块
。
Active View中提供了帮助方法:
- RecordTagHelper:
content_tag_for
为对象生成容器标签(Html中的容器标签) ;div_for
- AssetTagHelper: 帮助方法生成链接到静态资源文件HTML,图片、JS、样式表和Feed等。register_javascript_expansion-插件使用,注册JS。register_stylesheet_expansion-注册样式表。auto_discovery_link_tag-link标签。image_tag、image_url使用image_path。javascript_include_tag
javascript_include_tag
引入app/assets/javascripts目录下的js文件。:all表示所有的js文件,多个文件合成一个,减少http连接数。
stylesheet_link_tag
返回特定资源样式表的标签,类似javascript_include_tag
。stylesheet_path、stylesheet_url。
- AtomFeedHelper atom_feed-简化生成Atom Feed的过程。
- BenchmarkHelper-基准测试辅助类。benchmark-计算模板中代码块的执行时间
- CacheHelper- 缓存视图片段,代码块。
- CaptureHelper capture将视图中的一段代码赋值给一个变量。content_for用标记符表示一段代码。
- DateHelper 日历辅助方法
- FormHelper 表单处理方法,代替标准的HTML元素,简化模型的处理过程。点击提交或submit,params对象。
form_for
方法: check_box,fields_for,file_field,hidden_field,label,password_field,radio_button,text_area等等。看起来,更为推荐的是simple_form
。
Rails的Action View中提供了很多的辅助的方法。
视图本地化
通过I18n.locale = :xx,可以设置视图的本地化操作,本地化渲染模板.
Cookies管理: 键值对, value, path, domain, expires, secure. Redis数据库
控制器中的所有实例变量以及headers, request, response, params, session, controller都可以视图中访问.
h函数: 用来解决HTML转义的问题,防止html注入的问题。
多种构建模板,js模板(效果,替换,查找选择), xml模板, erb模板等.
辅助类: 表单和链接非常的重要.
链接: link_to
, image_tag
, mail_to
, stylesheet_link_tag
表单: form_tag
, text_field
, hidden_field
, password_field
, file_field
布局和视图渲染
控制器创建HTTP响应: render, redirect_to, head。需要注意的是,动作只能响应一次。
多约定少配置,默认的视图渲染就是典型。但是,Rails中的配置,也是需要深入了解的事情。
部署和安全性
程序安全性
sql注入
Order.find(params[:id]) - find方法会提供响应的方法清理
部署
尽早部署: “编程-测试-提交-部署”节奏,获取部署经验,学习数据迁移工具
capistrano部署:
$ capify .
[add] writing './Capfile' # Cap的Rakefile
[add] writing './config/deploy.rb' # 部署所用的方法,可以存在config/deploy文件,设置不同的部署环境
[done] capified!
生成的deploy.rb文件:
set :application, "set your application name here"
set :repository, "set your repository location here"
# set :scm, :git # You can set :scm explicitly or Capistrano will make an intelligent guess based on known version control directory names
# Or: `accurev`, `bzr`, `cvs`, `darcs`, `git`, `mercurial`, `perforce`, `subversion` or `none`
role :web, "your web-server here" # Your HTTP server, Apache/etc
role :app, "your app-server here" # This may be the same as your `Web` server
role :db, "your primary db-server here", :primary => true # This is where Rails migrations will run
role :db, "your slave db-server here"
# if you want to clean up old releases on each deploy uncomment this:
# after "deploy:restart", "deploy:cleanup"
# if you're still using the script/reaper helper you will need
# these http://github.com/rails/irs_process_scripts
# If you are using Passenger mod_rails uncomment this:
# namespace :deploy do
# task :start do ; end
# task :stop do ; end
# task :restart, :roles => :app, :except => { :no_release => true } do
# run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
# end
# end
常用的一些Cap命令:
cap deploy # 部署, 若config/deploy/目录下存在环境文件,则使用`cap deploy xxx`
cap deploy:rollback # 回滚
维护就是各种查看日志,以及使用Rails控制台(console)。
日志分卷:
config.logger = Logger.new(config.log_path, 10, 10.megabytes) # 按大小分卷
config.logger = Logger.new(config.log_path, 'daily') # 按时间分卷
config.logger = SyslogLogger.new # 将日志设置为系统日志
Session的清理:
基于cookie的session,Rails会自动清理,其它的都要手动处理。
完成部署后,需要仔细的维护应用程序,仔细观察和监控,找出应用的瓶颈。
后记
偶然,发现在公司的application.html.erb中有一个在样式表下的img标签:
到正式的网站上,在Firebug中搜了一下,看到img被一个链接包裹,链接的地址是百度统计的。原来统计可以通过设置图片标签?!学习了。
傲娇的使用Disqus