27 August 2014

前言

在学校胡乱的学了一个月Rails,期间忙着自己的毕设、别人的毕设。工作后,边抄边猜,边搜边试,5个月也就这么过去了。一般的开发任务,自己想想找找,再问问,也能搞定。但总这样不是个办法,决定再过一遍的Rails相关的知识。

ActiveSupport

ActionSupport是一组类库,是Ruby语言的一种扩展。大体的扩展如下:

  • 通用扩展: Ruby对象-编程语言无关形式(比如xml,json,yaml): to_jsonto_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_forend之间的代码块会得到表单构建器(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_forlink_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.. endexpire_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中提供了帮助方法:

  1. RecordTagHelper: content_tag_for 为对象生成容器标签(Html中的容器标签) ; div_for
  2. 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。

  1. AtomFeedHelper atom_feed-简化生成Atom Feed的过程。
  2. BenchmarkHelper-基准测试辅助类。benchmark-计算模板中代码块的执行时间
  3. CacheHelper- 缓存视图片段,代码块。
  4. CaptureHelper capture将视图中的一段代码赋值给一个变量。content_for用标记符表示一段代码。
  5. DateHelper 日历辅助方法
  6. 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