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

suffering · 2015年07月15日 · 最后由 henryzhangios 回复于 2021年10月15日 · 8719 次阅读
本帖已被管理员设置为精华贴

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

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/

赞,ActiveSupport 好火。哈哈

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

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

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