Gem Rails 项目 Gem 依赖的社交关系网络关系图及其生成

suffering · 2015年07月15日 · 最后由 adamshen 回复于 2015年09月12日 · 6388 次阅读
本帖已被设为精华帖!

自己做的小玩具, 以及做小玩具的思路:).

WHAT

Rails 4.2.1 初始化项目 gem 依赖关系图:

原图 (上传至 ruby-china.org 的图会被压缩): http://gemrelationship.zhuboliu.me/gem_relationship.png (帖出来后才发现这张图神似欧亚大陆, 连澳大利亚都有~)

WHY?

在阅读 Rails 源码时, 我脑子里总是冒出这样的念头:"卧操! 这是什么鬼!?这方法是哪里来的!? 自定义的!? 来自父类!? 来自 include/extend 的模块!? 来自第三方 gem!? 哪一个?". 如果是项目内引用定义的, 倒是很好查找. 但是, 如果是来自第三方项目, 那来自哪些? 这样的阅读体验无疑让人很痛苦.

换个思路, 用社交关系网络的概念来生成图, 让各种依赖关系一目了然.

IDEAS

rails 依赖许多 gem. 现在, rails 的主要模块本身就被拆成了数个独立的 gem. 我们可以理解为 rails 依赖这些 gem. 同时, 这些 gem 又依赖于其他的 gem. 拨出萝卜带出泥, 不胜期烦.

从社交关系的角度来看问题, 我们作如下两点设定:

  1. 一个项目是一个社交圈
  2. 项目中运行的每一个 gem, 即为圈子内的一个独立的用户.
  3. 如果 gemA 依赖于 gemB , 即 gemA 关注了 gemB, 换而言之 gemA 是 gemB 的粉丝. 即画一个 gemA 指向 gemB 的有向箭头指向.

最终生成的图, 通过看线条与箭头, 我们可以清楚地看到哪些 Gem 受欢迎 (大批的粉丝).

有了思路, 接下来就要考虑如何实现的问题了.

HOW?

  1. 用户 (gem) 列表数据, 即图内的节点.
  2. 用户 (gem) 关系数据, 即节点间的有向线段.
  3. 数据可视化方案. 即展示方案, 或者说绘图方案. 错综复杂的关系如何布局与绘图非常重要.

问题 1 和问题 2 很简单, 项目内通过bundle install生成的Gemfile.lock提供了所有的节点与关系.

This is important: the Gemfile.lock makes your application a single package of both your own code and the third-party code it ran the last time you know for sure that everything worked. Specifying exact versions of the third-party code you depend on in your Gemfile would not provide the same guarantee, because gems usually declare a range of versions for their dependencies.

问题 3, 通过网络搜索, 发现Graphviz(Graph Visualization Software) 可以很好地解决这个问题.

取数据, 生成 Graphviz 有效的文件

创建一个 Rails 原生项目, 不添加任何其他 Gem.

cd to/your/path
# rails 4.2.1
rails new rgraph
cd graph
bundle install
touch graph.rb
# edit graph.rb

分析Gemfile.lock文件, 生成gem_relationshop.gv文件.

# rails/app/root/path/graph.rb
src_box = []
File.open('gem_relationship.gv', 'w') do |t|
  t.puts "digraph G {"
  File.open("Gemfile.lock").each do |n|
    # 通过缩进来判断指向关系.
    n =~ /^( {2,})([a-zA-Z0-9_-]+)/
    if $1 and $1.length == 4
      @src = $2
      src_box << @src
    elsif $1 and $1.length == 6
      @target = $2
      puts @src
      t.puts "  \"#{@src}\" -> \"#{@target}\""
    else
    end
  end
  t.puts "}"
end

运行 script 生成源文件:

ruby graph.rb

运行 graphviz 相关命令生成图:

# 系统内必须装好 Graphviz.
dot -Tpng -o ./gem_relationship.png ./gem_relationship.gv
# open gem_relationship.png

Done!

再来一个 web 版的吧.

google 的结果是arborjs勉强可以干这事儿, 而且arborjs语法类似Graphviz的语法. 使用源码中的 demo,复制一份 json 文件, 将生成的.gv的主内容帖进, hack 一下, 测试, 发布:

在线演示地址: http://gemrelationship.zhuboliu.me 在线站点支持节点拖曳. 支持在线编辑, 可以考虑直接在项目要目录下生成的文件帖到右边的框内, 实时生成关系图.

如果你需要使用此演示站的功能生成自己的 ruby 项目, 请确保项目使用bundlerGemfile.lock存在.

站点实时生成关系图的数据的代码略有不同, 如下:

css_box = %W{NAVY BLUE AQUA TEAL OLIVE GREEN LIME ORANGE RED MAROON FUCHSIA PURPLE BLACK GRAY SILVER}
src_box = []
File.open('gem_relationship.gv', 'w') do |t|
  # t.puts "digraph G {"
  File.open("Gemfile.lock").each do |n|
    # 通过缩进来判断指向关系.
    n =~ /^( {2,})([a-zA-Z0-9_-]+)/
    if $1 and $1.length == 4
      @src = $2
      src_box << @src
    elsif $1 and $1.length == 6
      @target = $2
      puts @src
      # t.puts "  \"#{@src}\" -> \"#{@target}\""
      t.puts "#{@src} -> #{@target}"
    else
    end
  end
  # t.puts "}"
  # 随机生成色彩.
  src_box.each do |s|
    t.puts "#{s} {color:#{css_box.shuffle.first}}"
  end
end

注意:不要帖入大型项目的关系数据, 节点太多, 结果会很难看.

看图说话

railtie, activesupport, activepack, rack是真正的大 V, 粉丝无数, 若要阅读 Rails 源码, 从了解它们开始吧.

附上一张个人的小项目 (引入第三方 gem 若干) 的图: 原图地址: http://gemrelationship.zhuboliu.me/color.png

后续开发意见征集

  • 有必要做成一个 gem 吗? 添加 command line, 在 ruby 项目下运行即直接生成相应关系图.
  • 或者, 做成 engine, 在开发模式下, 随时可能通过链接进入到一个页面看到指向关系图? 类似http://localhost:3000/rails/info的功能.
  • 在前两者的基础上做些 option 定制, 可以手动选定中心结点? 或者为结点加上链接, 可直接点到相应 gem 的官方文档页去?
  • 其他

请有好的意见 @suffering, 谢谢.

参考资料

http://bundler.io/v1.7/rationale.html#checking-your-code-into-version-control http://www.graphviz.org/ http://arborjs.org/ http://arborjs.org/docs/barnes-hut http://getspringy.com/

共收到 20 条回复

赞,ActiveSupport 好火。哈哈

#1 楼 @reyesyang , ActiveSupport 就是一大堆扩展, 连 KernelObject都没放过. 很多时候, Rails 的源码里蹦出来一个不知根脚的方法都是出自 ActiveSupport.

太 nice

bundle viz 吧,还带 group 信息...

bundle viz是个好东西啊, 都不知道有这个命令. 愚蠢地重造了轮子了.

这个 coooooool

@suffering 挺好,bundle viz 在我 rails 项目下面会报错, NoMethodError: undefined method `runtime_dependencies' for nil:NilClass, 试了一下,在 middleman 项目下挺好的。

#5 楼 @suffering 赞一个,千万不要因为造了轮子而觉得不好,要知道,不是每一个人都拥有造轮子的能力,造轮子的过程本身就是学习!

轮子还是要造的,F1 赛车用的轮子跟空客 A380 的轮子能是一样的东西么

#9 楼 @suffering 我也一直对关系图生成比较感兴趣,请问 Graphviz 是只要你提供必要的数据,节点布局跟线条那些都是它负责的吗?开发者只需要保证输入的数据是吧?

#11 楼 @martin91 ,是的. 节点线条都是它负责. 当然, 你也可以自己选择控制每一个节点. 包括节点的形状, 线条的颜色, 粗细, 虚线还是实线, 有无箭头等等. 这个帖子里的没有作任何相关的设定. 只声明了节点与关系. Graphviz 自动用默认设置生成了整张图.

#11 楼 @martin91 , 这里有一个 Graphviz 的 ruby gem 工具: https://github.com/glejeune/Ruby-Graphviz, 有兴趣可以研究一下.

#12 楼 @suffering 好的,谢谢,明白了

可以参考 java, groovy 界的 gradle, 可以将所有的依赖生成一个 dependency tree, 一目了然~

#5 楼 @suffering 造轮子,说明有研究精神。现在拿来主义居多,造轮子的少了

ios 端 mark

自己项目跑了一下 bundle viz ,关系多得无法直视啊!~~

已经眼花

确实麻烦,我一般用 RubyMine 直接 Go to define

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册