从修复 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
我是先去看语法:http://www.w3cschool.cc/ruby/ruby-tutorial.html
w3cschool的语法虽然有一些已经过时了,但不影响了解ruby,看的过程中就跟着例子上手实践了。花了两天时间~接下来就要选择是做一个小程序?还是找个一个现成的去修改代码修改bug。我选择了后者,原因就是因为在testerhome里玩的时候发现了一个bug。这里请没有框架使用经验的新手不要选择后者,自己先做一些像计算器之类的小程序。因为一般的现成的程序中,会使用一些框架,里面蕴含了框架的思想,如果不理解这个思想是找不到北的。
bug标题:testerhome论坛,社区帖子列表的分页数量不正确
bug描述:进入“社区”菜单,下方的分页栏,其页数是100,但点击100页后,里面没有数据。这里的分页数量不是根据实际数据生成的,是错误的。
截图:

修复bug过程
要想修复这个bug,首先要有源码,还要知道论坛的架构。混坛子里的肯定都知道了,源码在@lihuazhang群主的github中就有,而论坛是使用的rubychina的源码,架构是rails on ruby.
参考:http://www.cnblogs.com/likeyu/archive/2012/02/25/2367379.html
环境搭建什么的就不赘述了,就从bug分析开始吧
- 这个bug的原因不难想象,程序中计算分页的代码有问题。我们要追到这部分代码,就要先去找页面。那接下来就观察一下工程结构

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

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

从中不难看出topics就是页面所在了,展开topics文件夹,找可能页面去观察代码,最终将页面定位到index.html.erb。问题页面定位到了,我们就开始观察代码吧
<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就不能放过,同时也为社区贡献点东西,只是改完这个没法真实测试,这个需要@lihuazhang群主帮忙了,话说我这古董技术,到现在对git还不熟。另外,在没有看到rubychina那位仁兄写的rubychina实践之前,不知道管理员怎么进,就去学了mongodb,好不容易学会了,去数据库里看到user里有一个admin,但密码是加密的,咋办,,,只好自己注册了一个用户,然后将这个用户的加密密码替换了admin的密码,结果发现仍然访问不了后台管理,最后才找到那篇文章,豁然开朗。这次解决问题的过程,其中有很多都是靠着经验猜出来的,对与rails框架我还不熟悉,通过这次解决问题已经看清一点论轮廓了。希望能帮助到那些想学习开发的新手,同时也欢迎ruby大神给点建议!
最后的最后希望,testerhome能越来越好~各位tester事业生活一帆风顺~好不容易有时间写点东西,又有点忙了,请新秀群不要踢我啊
从修复 testerhome(rubychina)网站的一个 bug 学习 ruby&rails on ruby的更多相关文章
- 趣图:快下班了,剩一个bug,修复一下再走
趣图:当我给老板展示我修复了那个 bug 时 趣图:当我以为这是最后一个Bug时……
- 用nopcomerce3.8版本的同行注意了,前2天发布3.8正式版后,作者收到一些BuG,作者修复后重新提供了一个源代码包下载.
用nopcomerce3.8版本的同行注意了,前2天发布3.8正式版后,作者收到一些BuG,作者修复后重新提供了一个源代码包下载地址,不是github上的那个链接.去作者官网论坛我那个链接地址,或关注 ...
- 印象最深的一个bug——排查修复问题事件BEX引发的谷歌浏览器闪退崩溃异常
前言 最近,我们部门负责项目运维的小王频频接到甲方的反馈,运行的项目使用谷歌浏览器登录后,每次点击处理2秒后,浏览器自动闪退崩溃.小王同学折腾了一个星期,还没找到问题的原因.甲方客户都把问题反馈给项目 ...
- (四)一个bug的生命周期
Bug的属性 Bug重现环境 这个应该是我们重现BUG的一个前提,如果没有这个前提,我们可能会无法重现问题,或者根本就无从下手. • 操作系统 这个是一般软件运行的一大前提,基本上所有的软件都依赖于操 ...
- Win10系统菜单打不开问题的解决,难道是Win10的一个Bug ?
Win10左下角菜单打不开,好痛苦,点击右下角的时间也没反应,各种不爽,折磨了我好几天,重装又不忍心,实在费劲,一堆开发环境要安装,上网找了很多方法都不适用.今天偶然解决了,仔细想了下,难道是Win1 ...
- 你可能不知道的 NaN 以及 underscore 1.8.3 _.isNaN 的一个 BUG
这篇文章并不在我的 underscore 源码解读计划中,直到 @pod4g 同学回复了我的 issue(详见 https://github.com/hanzichi/underscore-analy ...
- 修复ext4日志(jbd2)bug( Ext4 文件系统有以下 Bug)
from:http://blog.donghao.org/2013/03/20/%E4%BF%AE%E5%A4%8Dext4%E6%97%A5%E5%BF%97%EF%BC%88jbd2%EF%BC% ...
- 有人向我反馈了一个bug
我是一个前端开发者,但我想这个故事对任何开发者都会引起共鸣的有人向你反馈了一个 bug. “26 楼会议室的灯亮着.它需要被熄灭.”bug 的备注里写道“你应该能在 5 分钟内搞定,只要按一下开关就好 ...
- qt widget设置Qt::FramelessWindowHint和Qt::WA_TranslucentBackground, 会出现一个bug: 在最小化后还原时界面停止刷新
qt widget设置Qt::FramelessWindowHint和Qt::WA_TranslucentBackground, 会出现一个bug: 在最小化后还原时界面停止刷新 Widget wit ...
随机推荐
- C#对XML、JSON等格式的解析
C#对XML.JSON等格式的解析 一.C#对XML格式数据的解析 1.用XMLDocument来解析 XmlDocument xmlDocument = new XmlDocument(); xml ...
- 关于NRW算法(Quorum算法)
在分布式系统中,冗余数据是保证可靠性的手段,因此冗余数据的一致性维护就非常重要.一般而言,一个写操作必须要对所有的冗余数据都更新完成了,才能称为成功结束.比如一份数据在5台设备上有冗余,因为不知道读数 ...
- eclipse import的项目报autowired cannot be resolved to a type的错误
eclipse报autowired cannot be resolved to a type的错误,一般情况是依赖的JDK或者jar包有问题,检查build path可以排查文件,我今天遇到这个情况, ...
- python中functools.wraps装饰器的作用
functools.wraps装饰器用于显示被包裹的函数的名称 import functools def node(func): #@functools.wraps(func) def wrapped ...
- poj 2796 Feel Good 单调栈区间问题
Feel Good 题意:给你一个非负整数数组,定义某个区间的参考值为:区间所有元素的和*区间最小元素.求该数组中的最大参考值以及对应的区间. 比如说有6个数3 1 6 4 5 2 最大参考值为6,4 ...
- 编译报错GLIBCXX_3.4.15 clock_gettime@@GLIBC_2.2
GLIBCXX_3.4.15 升级gcc,g++编译器 clock_gettime@@GLIBC_2.2 链接库时加-lrt
- Oracle索引扫描
Oracle索引扫描:先通过index查找到索引的值,并根据索引的值对应的rowid值(对于非唯一索引可能返回多个rowid值)直接从表中得到具体的数据.一个rowid唯一的表示一行数据,该行对应的数 ...
- 利用Multipeer Connectivity框架进行WiFi传输-b
什么是Multipeer Connectivity? 在iOS7中,引入了一个全新的框架——Multipeer Connectivity(多点连接).利用Multipeer Connectivity框 ...
- JAVA入门第二季(mooc-笔记)
相关信息 /** * @subject <学习与创业>作业1 * @author 信管1142班 201411671210 赖俊杰 * @className <JAVA入门第二季&g ...
- word-wrap同word-break的区别(转)
本文列举了兼容 IE 和 FF 的换行 CSS 推荐样式,详细介绍了word-wrap同word-break的区别. 兼容 IE 和 FF 的换行 CSS 推荐样式 最好的方式是 word-wrap: ...