从修复 testerhome(rubychina)网站的一个 bug 学习 ruby&rails on ruby

前言

testerhome: http://testerhome.com/topics/1480

对于一个差点脱离前沿技术人,想要学习ruby,就意味着要放弃熟悉的操作系统windows,熟悉的ide-eclipse,更重要的是要从java这种重量级编译型语言的编程思想强行转换为ruby这种轻量级解释执行语言编程思想。这个过程远比我想象的要难,总是抱着以前以java编程思想,通吃vb,C#的傲慢,虽然js还算可以,但js的编程思想与java也是很像的,经过这次打击让我意识到我掉队了。但仍有一部分经验可以帮助我入手新的技术,这篇文章一方面是警醒自己,另一方面也希望能将这次的学习过程分享给想要学习一门语言的同学。

正文

至于为什么选择ruby,中间的选择过程也费了一些时日,主要是在python和ruby之间比较,放弃python的重要理由是python的语法令我找不到北,还有那每个包下的_init_.py文件,请原谅我思想过于老化,无论如何都接受不了。而ruby,给我感觉就比较优雅易理解,虽然社区和资源不如python。过程曲折,就不赘述了。

看清现状

要学习一门新技术,首先要知道自己会什么,新技术涉及到了什么。

linux&shell脚本 ruby最好还是在linux下学习开发吧,我在windows下虽然也搭建成功了,但装了例如 devkit 我也是醉了。
开发工具 工欲善事,必先利器。其实我是被eclipse惯的,实在不想像原来学习java一样,从文本编辑器开始,编译执行。。。我选择了rubymine,是jetbrain出的,基于IntelliJ IDEA。其强大自不必说了。

对我来说上面这两条满足就够了,至于编程经验什么的,我认为如果没有编程经验,反而还好一些,这样不会被以前的思想干扰。

看清需要什么之后,就去熟悉他们吧,下载安装blablabla....

学习方法

万事具备之后,就要考虑应该怎么学习ruby,是去买本书?找网络教学视频找资料?。。。无论哪种方法,要选择合适自己的。对于有编程经验的人,我建议先去看语法,练上几个小例子,深层次的不需要太多考虑。然后就实践,无论是自己编写一个小程序还是去修改bug都可以。没有编程经验的人,这里就不好办了,按照通常的做法,需要学习面向对象编程思想、数据结构、网络通信以及其他一些必备的IT技能。当然这里不是说要按部就班的去学,如果对自己有信心可以直接从视频教程、ruby书籍开始学习。

贴一个ruby的视频教程:http://edu.51cto.com/lesson/id-10162.html

  1. 我是先去看语法:http://www.w3cschool.cc/ruby/ruby-tutorial.html
    w3cschool的语法虽然有一些已经过时了,但不影响了解ruby,看的过程中就跟着例子上手实践了。花了两天时间~

  2. 接下来就要选择是做一个小程序?还是找个一个现成的去修改代码修改bug。我选择了后者,原因就是因为在testerhome里玩的时候发现了一个bug。这里请没有框架使用经验的新手不要选择后者,自己先做一些像计算器之类的小程序。因为一般的现成的程序中,会使用一些框架,里面蕴含了框架的思想,如果不理解这个思想是找不到北的。

bug标题:testerhome论坛,社区帖子列表的分页数量不正确
bug描述:进入“社区”菜单,下方的分页栏,其页数是100,但点击100页后,里面没有数据。这里的分页数量不是根据实际数据生成的,是错误的。
截图:

修复bug过程

要想修复这个bug,首先要有源码,还要知道论坛的架构。混坛子里的肯定都知道了, ,而论坛是使用的rubychina的源码,架构是rails on ruby.

参考:http://www.cnblogs.com/likeyu/archive/2012/02/25/2367379.html

环境搭建什么的就不赘述了,就从bug分析开始吧

  1. 这个bug的原因不难想象,程序中计算分页的代码有问题。我们要追到这部分代码,就要先去找页面。那接下来就观察一下工程结构

    接下来就是我的初步猜测
  • app 论坛业务所在
  • bin 忽略,二进制对业务无影响
  • config 一些配置
  • db 数据库相关
  • doc 里面是readme,看一眼有没有有用的,忽略
  • faye_server 不知道是什么,实在找不到的时候才去研究它
  • lib 引用的库,忽略
  • log 日志忽略
  • misc 存放了一些资源,忽略
  • public 里面放了404.html等公用的错误页、跳转页什么的,忽略
  • script 就放了rails文件,忽略
  • spec 不知道什么,rubymine将其颜色设置为灰色,看来是没什么影响忽略
  1. 有了上面的分析,那么入手点就找到了,我们关注app中的内容,那么就展开看看吧

    从这个目录结构中,如果对web框架有所熟悉,一定会立刻找到切入点,views文件夹,因为视图就是表现层,说白了就是组织web页面,展开后观察一下[社区列表]这个页面在哪里

    从中不难看出topics就是页面所在了,展开topics文件夹,找可能页面去观察代码,最终将页面定位到index.html.erb。

  2. 问题页面定位到了,我们就开始观察代码吧

<div class="content">
  <div class="box box_gray">
    <div id="node_info">
      <%= render "topics/node_info", node: @node %>
    </div>
    <div class="topics">
      <% if @topics.current_page == 1 %>
        <% cache(@suggest_topics) do %>
          <%= render partial: "topics/topic", collection: @suggest_topics, locals: { suggest: true } %>
        <% end %>
      <% end %>
      <%= render partial: "topics/topic", collection: @topics, locals: { suggest: false } %>
    </div>
    <%= will_paginate @topics, inner_window: 2 %>
  </div>
 ......

注意这一行<%= will_paginate @topics, inner_window: 2 %>,说的很清楚,will_paginate分页。好了,找到了逻辑处理部分的代码,本来按照我老旧的思路,去找will_paginate方法,结果发现这个方法并不是业务逻辑处理,只是一个rails通用方法,那么谁才是业务逻辑处理呢,如果使用过web框架的就会想到mvc,rails也是如此。好,这时候还记得目录结构吧,上面app目录下有一个controllers文件夹,业务处理就在这里了,从中很容易猜到topics_controller.rb。来看下代码

# coding: utf-8
class TopicsController < ApplicationController
  load_and_authorize_resource only: [:new, :edit, :create, :update, :destroy,
                                     :favorite, :unfavorite, :follow, :unfollow, :suggest, :unsuggest]
  caches_action :feed, :node_feed, expires_in: 1.hours

  def index
    @suggest_topics = Topic.without_hide_nodes.suggest.limit(3)
    suggest_topic_ids = @suggest_topics.map(&:id)

    @topics = Topic.last_actived.without_hide_nodes.where(:_id.nin => suggest_topic_ids) #去数据库查询有多少帖子
    @topics = @topics.fields_for_list.includes(:user) #这部不太明白,但不影响分析
    @topics = @topics.paginate(page: params[:page], per_page: 15, total_entries: 1500)#这个方法就是计算分页了,可以从参数看出每页15个帖子,但total_entries这个参数却给了一个固定值1500,那么也就是说分100页。。。。bug就在这里

    set_seo_meta '', "#{Setting.app_name}#{t("menu.topics")}"
  end
  ... ...#代码多,就看上面这部分吧

上面代码中的注释就是我对代码的分析。我们继续跟到@topics.paginate方法中,

# coding: utf-8
require 'will_paginate'
require 'will_paginate/collection'

module Mongoid
  module WillPaginate
    extend ActiveSupport::Concern

    def paginate(options = {})
      options = base_options options

      ::WillPaginate::Collection.create(options[:page], options[:per_page]) do |pager| #从这里读到意思是根据传入的分页参数创建一个分页集合然后遍历每页,将每页的帖子展示
        items_count = options[:total_entries] || self.count #这块就需要了解ruby的语法了,||类似于三目运算,意思是若`options[:total_entries]!=nil`则`items_count=options[:total_entries]`,若`options[:total_entries]=nil`,则`items_count=self.count',转换为业务说法就是,若传入的总贴数为nil则计算数据库中查询出的帖子数量,这里显然逻辑错误。无论何时都应当使用数据库中查询出的帖子数量
        fill_pager_with self.skip(options[:offset]).limit(options[:per_page]), items_count, pager
      end
    end
  ......

问题定位清楚了,就开始想怎么改,已经说过了,无论何时都应当使用数据库中查询出的帖子数量。按照这个思路,我们先以调试模式启动testerhome这个web应用,启动后就可以访问本地调试环境的testerhome了

在调试之前,我们需要取得管理员权限,具体的方法可以查看http://www.cnblogs.com/likeyu/archive/2012/02/25/2367379.html这位同学写的。
我们进入http://localhost:3000/cpanel添加分类和节点,这样才能发帖。
接下来我们就要动代码了,首先我们在app/controllers/topics_controller.rb文件中,将代码修改为如下:

# coding: utf-8
class TopicsController < ApplicationController
  load_and_authorize_resource only: [:new, :edit, :create, :update, :destroy,
                                     :favorite, :unfavorite, :follow, :unfollow, :suggest, :unsuggest]
  caches_action :feed, :node_feed, expires_in: 1.hours

  def index
    @suggest_topics = Topic.without_hide_nodes.suggest.limit(3)
    suggest_topic_ids = @suggest_topics.map(&:id)

    @topics = Topic.last_actived.without_hide_nodes.where(:_id.nin => suggest_topic_ids) #去数据库查询有多少帖子
    @topics = @topics.fields_for_list.includes(:user) #这部不太明白,但不影响分析
    @topics = @topics.paginate(page: params[:page], per_page: 15)#去掉total_entries:1500这个参数,不能写死帖子总数

    set_seo_meta '', "#{Setting.app_name}#{t("menu.topics")}"
  end
  ... ...#代码多,就看上面这部分吧

同时要将app/models/mongoid/will_paginate.rb文件中的代码修改如下:

# coding: utf-8
require 'will_paginate'
require 'will_paginate/collection'

module Mongoid
  module WillPaginate
    extend ActiveSupport::Concern

    def paginate(options = {})
      options = base_options options

      ::WillPaginate::Collection.create(options[:page], options[:per_page]) do |pager| #从这里读到意思是根据传入的分页参数创建一个分页集合然后遍历每页,将每页的帖子展示
        items_count = self.count #不需要total_entries参数,我们使用从数据库中查询出的帖子数目
        fill_pager_with self.skip(options[:offset]).limit(options[:per_page]), items_count, pager
      end
    end
  ......

代码修改完成,接着就去浏览器中访问testerhome,进入社区帖子列表,新建16个帖子,因为我们要看出效果,需要至少2页。造完数据后,我们可以看到如下结果

从中可以看到当前的分页已经正常了,16条数据,分了2页,同时也试试点击第2页,看看是否显示正常。

至此这个bug就修复了。

最后

之所以想要修复这个bug,是因为我们是testerhome,那么看到bug就不能放过,同时也为社区贡献点东西,只是改完这个没法真实测试, ,话说我这古董技术,到现在对git还不熟。另外,在没有看到rubychina那位仁兄写的rubychina实践之前,不知道管理员怎么进,就去学了mongodb,好不容易学会了,去数据库里看到user里有一个admin,但密码是加密的,咋办,,,只好自己注册了一个用户,然后将这个用户的加密密码替换了admin的密码,结果发现仍然访问不了后台管理,最后才找到那篇文章,豁然开朗。这次解决问题的过程,其中有很多都是靠着经验猜出来的,对与rails框架我还不熟悉,通过这次解决问题已经看清一点论轮廓了。希望能帮助到那些想学习开发的新手,同时也欢迎ruby大神给点建议!

最后的最后希望,testerhome能越来越好~各位tester事业生活一帆风顺~好不容易有时间写点东西,又有点忙了,请新秀群不要踢我啊 :scream:,我要学的东西还有很多,以后有时间写东西一定会写~ :smirk:

你可能感兴趣的:(Rails)