static code analysis for ruby
Post on 12-Sep-2014
4.433 views
DESCRIPTION
This is my presentation on Shanghai on Rails at 3.20. It introduces static code analysis for ruby, tells you a pattern to analysis ruby code and gives you some examples that how rails_best_practices analysis rails codes.TRANSCRIPT
Static Code Analysis for Ruby
Richard HuangEkohe
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
有没有对 Ruby 代码进行过静态分析?
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
案例
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
有没有看过张文钿的演讲稿 Rails Best Practices ?
http://www.slideshare.net/ihower/rails-best-practices
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
有没有使用过 rails_best_practices gem?http://github.com/flyerhzm/rails_best_practices
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
静态分析 VS 动态分析• 整体 VS 局部• 粗略 VS 精确
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
静态分析什么?源程序?
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
抽象语法树 (AST)assign
a
*
+
*
b bc c
• a = b * c + b * c
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
s-expression s(:class, :Student, nil, s(:scope, s(:block, s(:call, nil, :attr_accessor, s(:arglist, s(:lit, :name)) ), s(:defn, :initialize, s(:args, :name), s(:scope, s(:block, s(:iasgn, :@name, s(:lvar, :name)) ) ) ))))
class Student attr_accessor :name
def initialize(name) @name = name endend
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
s-expressions(:defn, :output, s(:args, :arg1, :arg2), s(:scope, s(:block, s(:if, s(:call, s(:lvar, :arg1), :==, s(:arglist, s(:str, "hello")) ), s(:call, nil, :print, s(:arglist, s(:lvar, :arg1), s(:lvar, :arg2)) ), s(:call, nil, :print, s(:arglist, s(:lvar, :arg1)) ) ) ) ))
def output(arg1, arg2) if arg1 == 'hello' print arg1, arg2 else print arg1 endend
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
s-expression 的特点• 类树(继承数组)• 结构清晰• 方便解析
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
ruby 源代码 s-expression
ruby_parser
ruby2ruby
如何生成 s-expression
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
如何解析 s-expression ?• Visitor Pattern
• 优点:– 数据结构与访问类分离– 方便地增加访问类
• 缺点:– 增加数据类型很麻烦– 访问类只能处理当前结点
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
rails_best_practices 是如何做静态代码分析的?
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
rails_best_practices
rails_best_practices
结果输出
代码检查类代码解析器
配置源代码
5. 代码检查2. 生成检查类集合
3. 分析源代码4. 生成 s-
expression
1. 读取配置
6. 显示检查结果
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
代码解析• 解析顺序
model files
migration filescontroller files
helper filesview files
……
migration files
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
代码解析
erb files
haml files
ruby files
src
precompiled ruby_parser s-expression
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
代码检查s-expression
class
defn
call
Check
UseModelAssociationCheck
MoveFinderToNamedScopeCheck
if UseScopeAccessCheck
LawOfDemeterCheck
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
代码检查• Visitor Pattern 扩展
– 不仅关注结点的类型
– 而且关注结点所在的文件
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
rails_best_practices 做静态代码分析的一些思路
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
ReplaceInstanceVariableWithLocalVariableCheck
# app/views/posts/_post.html.erb<%= @post.title %><%= @post.body %>
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
ReplaceInstanceVariableWithLocalVariableCheck
# app/views/posts/_post.html.erb<%= @post.title %>
在 partial文件中任何类型为 ivar的结点都违反
ivar s(:call, s(:ivar, :@post), :title, s(:arglist))
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
MoveFinderToNamedScopeCheck
class PostsController < ApplicationController def index @published_posts = Post.find(:all, :limit => 10, :order
=> ‘created_at desc’) endend
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
MoveFinderToNamedScopeCheck Post.find(:all, :limit => 10, :order => ‘created_at desc’)
在 controller文件中 find, all, first, last方法带类型为 hash的参数的都违反
constfind,all,
first,last
hashs(:call, s(:const, :Post), :find, s(:arglist, s(:lit, :all), s(:hash, s(:lit, :limit), s(:lit, 10), s(:lit, :order), s(:str, "created_at desc")) ))
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
AddModelVirtualAttributeCheck
class UsersController < ApplicationController def create @user = User.new(params[:user]) @user.first_name = params[:full_name].split(‘ ‘, 2).first @user.last_name = params[:full_name].split(‘ ‘, 2).last @user.save endend
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
[email protected]_name = params[:full_name].split(‘ ‘, 2).firstattrassign
@user.last_name = params[:full_name].split(‘ ‘, 2).lastattrassign
@user.save
对于一个 variable,如果接收到两条不同的赋值消息,但参数的 "首部分 "是相同的,那么它就违反
mesage arguments whose message is []
mesage arguments whose message is []
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
LawOfDemeterCheck
class Invoice < ActiveRecord::Base belongs_to :userend
<%= @invoice.user.name %>
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
LawOfDemeterCheck
class Invoice < ActiveRecord::Base belongs_to :userend
<%= @invoice.user.name %>app/models/*.rb 必须首先被解析
如果 variable的名字和model 的 class name匹配,同时调用的消息名和 association名字相同,则违反
message
class name
association name
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
AlwaysAddDbIndexCheck
class CreateComments < ActiveRecord::Migration def self.up create_table :comments do |t| t.string :content t.integer :post_id t.integer :user_id end endend
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
AlwaysAddDbIndexCheckcreate_table :comments do |t| t.string :content t.integer :post_id t.integer :user_idendadd_index :comments, :post_id
db/migrations/*.rb 必须被解析两次所有 create_table中创建的外键没有出现在 add_index的参数中,那么它就违反
table name
integer with _idcolumn with _id and integerreferences
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
AlwaysAddDbIndexCheck
What about?
[[:comments, :post_id], [:comments, :user_id]].each do |args|
add_index *argsend
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
AlwaysAddDbIndexCheck
Ruby2Ruby.new.process(node)
模拟 add_index,动态处理Ruby is Powerful!
def add_index(*args) table_name, column_names = *args table_name = table_name.to_s
# call AlwaysAddDbIndexCheck methodend
静态代码分析代码检查代码重构
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development
Q&A
Thank you
Website: http://www.huangzhimin.com
Github: http://github.com/flyerhzm
www.ekohe.comWeb Development & Graphic DesignChina Ruby on Rails Development - Rails Consulting - Rails Services - Merb - Offshore Web Development