编写程序求e的近似值一年365天共有多少个星期,还余几天

GitHub 漫游指南
By (微博、知乎、GitHub、SegmentFault: @)
微信公众号
我的GitHub主页上写着加入的时间——Joined on Nov 8, 2010,那时才大一,在那之后的那么长的日子里我都没有登录过。也许是因为我学的不是计算机,到了今天——,我才发现这其实是程序员的社交网站。
过去,曾经有很长的一些时间我试过在GitHub上连击,也试着去了解别人是如何用好这个工具的。当然粉丝在GitHub上也是很重要的。
在这里,我会试着将我在GitHub上学到的东西一一分享出来。
我与GitHub的故事
在我大四找工作的时候,试图去寻找一份硬件、物联网相关的工作(ps: 专业是电子信息工程)。尽管简历上写得满满的各种经历、经验,然而并没有卵用。跑了几场校园招聘会后,十份简历(ps: 事先已经有心里准备)一个也没有投出去——因为学校直接被拒。我对霸面什么的一点兴趣都没有,千里马需要伯乐。后来,我加入了所在的公司,当然这是后话了。
这是一个残酷的世界,在学生时代,如果你长得不帅不高的话,那么多数的附加技能都是白搭(ps: 通常富的是看不到这篇文章的)。在工作时期,如果你上家没有名气,那么将会影响你下一份工作的待遇。而,很多东西却可以改变这些,GitHub就是其中一个。
注册GitHub的时候大概是大一的时候,我熟悉的时候已经是大四了,现在已经毕业一年了。在过去的近两年里,我试着以几个维度在GitHub上创建项目:
快速上手框架来实战,即demo
重构别人的代码
创建自己可用的框架
快速构建大型应用
构建通用的框架
GitHub与收获
先说说与技能无关的收获吧,毕业设计做的是一个《》,考虑到我们专业老师没有这方面知识,答辩时会带来问题,尽量往这方面靠拢。当我毕业后,这个项目已经有过百个star了,这样易上手的东西还是比较受欢迎的(ps: 不过这种硬件相关的项目通常受限于GitHub上硬件开发工程师比较少的困扰)。
毕业后一个月收到PACKT出版社的邮件(ps: 他们是在github上找到我的),内容是关于Review一本书籍,即在《》中提到的《Learning Internet of Things》。作为一个四级没过的“物联网专家”,去审阅一本英文的物联网书籍。。。
当然,后来是审阅完了,书上有我的英文简介。
Phodal Huang Introduction
一个月前,收到MANNING出版社的邮件(ps: 也是在github上),关于Review一本书籍的目录,并提出建议。
也因此带来了其他更多的东西,当然不是这里的主题。在这里,我们就不讨论各种骚扰邮件,或者中文合作。从没有想象过,我也可以在英语世界有一片小天地。
这些告诉我们,GitHub上找一个你擅长的主题,那么会有很多人找上你的。
GitHub与成长
过去写过一篇《》的文章,现在只想说三点:
更多的测试
更多的、更多的、更多的测试
没有测试的项目是很扯淡的,除非你的项目只有一个函数,然后那个函数返回Hello,World。
如果你的项目代码有上千行,如果你能保证测试覆盖率可以达到95%以的话,那么我想你的项目不会有太复杂的函数。假使有这样的函数,那么它也是被测试覆盖住的。
如果你在用心做这个项目,那么你看到代码写得不好也会试着改进,即重构。当有了一些,你的技能会不断提升。你开始会试着接触更多的东西,如stub,如mock,如fakeserver。
有一天,你会发现你离不开测试。
然后就会相信: 那些没有写测试的项目都是在耍流氓
为什么你应该深入GitHub
上面我们说的都是我们可以收获到的东西,我们开始尝试就意味着我们知道它可能给我们带来好处。上面已经提到很多可以提升自己的例子了,这里再说说其他的。
我们可以从中获取到不同的知识、内容、信息。每个人都可以从别人的代码中学习,当我们需要构建一个库的时候,我们可以在上面寻找不同的库和代码来实现我们的功能。如当我在实现一个库的时候,我会在GitHub上找到相应的组件:
Promise 支持
Class类(ps:没有一个好的类使用的方式)
Template 一个简单的模板引擎
Router 用来控制页面的路由
Ajax 基本的Ajax Get/Post请求
获得一份工作
越来越多的人因为GitHub获得工作,因为他们的做的东西正好符合一些公司的要求。那么,这些公司在寻找代码的时候,就会试着邀请他们。
因而,在GitHub寻找合适的候选人,已经是一种趋势。
如果我们想创造出更好、强大地框架时,那么认识更多的人可能会带来更多的帮助。有时候会同上面那一点一样的效果
创建开源项目
人们出于不同的目的来创建开源项目,可不论目的是什么,过程都是一样的。
首先,我们需要为我们的项目取一个名字。
然后,为我们的开源项目选择一个合适的 LICENSE
然后再去创建项目
取一个好的名字
取名字,从来就不是一件容易的事。
因此,我就长话短说,一般就是取一个有意义的名字,当然没有意义也没有任何问题。
通常而言,如果自己计划有一系列的开源项目,那么我们可以保持一定的命令规则。
挑选好 LICENSE
在二十世纪而七十年代末和八十年代初,为了防止自己的软件被竞争对手所使用,大多数厂家停止分发其软件源代码,并开始使用版权和限制性软件许可证,来限制或者禁止软件源代码的复制或再分配。随后,Richard Matthew Stallman(Richard Matthew Stallman)发起了自由软件运动,他开创了 Copyleft 的概念:使用版权法的原则来保护使用、修改和分发自由软件的权利,并且是描述这些术语的自由软件许可证的主要作者。最为人所称道的是GPL(被z广泛使用的自由软件协议)。
(PS:关于自由软件及 RMS 的更多信息、历史,可以阅读《若为自由故:自由软件之父 - 理查德 斯托曼传》)
随后,便诞生了开源软件的概念,开源的要求比自由软件宽松一些。迄今发布的自由软件源代码都是开源软件,而并非所有的开源软件都是自由软件。这是因为不同的许可(协议)赋予用户不同的权利,如 GPL 协议强制要求开源修改过源码的代码,而宽松一点的 MIT 则不会有这种要求。
如下是不同开源许可证的市场占有率及使用情况。
License 使用情况
又比如,在我们看到的一些外版书籍上,如果拥有代码。那么作者一般就会在前言或者类似的位置里,指明书中代码的版权所属。如:也许你需要在自己的程序或文档中用到本书的代码,但除非大篇幅地使用,否则不必与我们联系取得授权。例如,用本书中的几段代码编写程序无需请求许可,blabla。
于是,选择一个合理的 LICENSE,就变成了一个有趣的话题。为此,笔者做了一个如何进行开源协议选型的流程图:
简单地来说,这些 License 之间是一些权利的区别,如当你把代码放置到公有领域,就意味着任何人可以修改,并且不需要标明出注;可如果你想要别人标明出处及作者,你就需要 MIT 协议;而你希望别人闭源的话,那么你就需要 MPL 协议等等。
那么,下面让我们简单地介绍一下不同的几个协议。
WTFPL(Do What The Fuck You Want To Public License,中文译名:你他妈的想干嘛就干嘛公共许可证)是一种不太常用的、极度放任的自由软件许可证。它的条款基本等同于贡献到公有领域。
这就意味着,对于拿到这些代码的其他人,他们想怎么修改就可以怎么修改。
由于 GPL 的传染性,便意味着,他人引用我们的代码时,其所写的代码也需要使用 GPL 开源。即:GPL 是有 “传染性” 的 “病毒” ,因为 GPL 条款规定演绎作品也必须是 GPL 的。
而如果我们只针对的是,他人可以使用库,而不开源,则可以用 LGPL。但是修改库则不适用。
因此,一般而言,我使用的是 MIT 协议。至少我保留了一个署名权,即你可以修改我的代码,但是在 LICENSE 里必须加上我的名字。
选用 MIT 特别有意思,特别是在最近几年里,发生过:
等等。这告诫了我们,如果你不想要有这种经历,那么就不要用 MIT 了。
Creative Commons
是的,当我写 Markdown 的时候,考虑到未来会以纸质书的形式出现,便会使用 CC-BY-NC-ND 协议:
CC -& Creative Commons
BY -& 署名(英语:Attribution,by)
NC -& 非商业性使用(英语:NonCommercial)
ND -& 禁止演绎(英语:NoDerivs)。
即,任何人可以使用我写的电子书来自由复制、散布、展示及演出,但是不得用于商业用途(作者本人可以)。它可以随意地放在他的博客上,他的各个文章里。但是必须标明出自,并且不得改变、转变或更改本作品。
如果你不介意的话,你可以使用公有领域(Public Domain)。可是这样一来,万一有一天,别人直接拿的作品出书,你就骂爹了。
Git基本知识与GitHub使用
从一般开发者的角度来看,git有以下功能:
从服务器上克隆数据库(包括代码和版本信息)到单机上。
在自己的机器上创建分支,修改代码。
在单机上自己创建的分支上提交代码。
在单机上合并分支。
新建一个分支,把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。
生成补丁(patch),把补丁发送给主开发者。
看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。
一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。
从主开发者的角度(假设主开发者不用开发代码)看,git有以下功能:
查看邮件或者通过其它方式查看一般开发者的提交状态。
打上补丁,解决冲突(可以自己解决,也可以要求开发者之间解决以后再重新提交,如果是开源项目,还要决定哪些补丁有用,哪些不用)。
向公共服务器提交结果,然后通知所有开发人员。
如果是第一次使用Git,你需要设置署名和邮箱:
$ git config --global user.name &用户名&
$ git config --global user.email &电子邮箱&
将代码仓库clone到本地,其实就是将代码复制到你的机器里,并交由Git来管理:
$ git clone :someone/symfony-docs-chs.git
你可以修改复制到本地的代码了(symfony-docs-chs项目里都是rst格式的文档)。当你觉得完成了一定的工作量,想做个阶段性的提交:
向这个本地的代码仓库添加当前目录的所有改动:
$ git add .
或者只是添加某个文件:
$ git add -p
我们可以输入
$git status
来看现在的状态,如下图是添加之前的:
Before add
下面是添加之后 的
可以看到状态的变化是从黄色到绿色,即unstage到add。
Wiki百科上是这么说的
GitHub 是一个共享虚拟主机服务,用于存放使用Git版本控制的软件代码和内容项目。它由GitHub公司(曾称Logical Awesome)的开发者Chris Wanstrath、PJ Hyett和Tom Preston-Werner 使用Ruby on Rails编写而成。
当然让我们看看官方的介绍:
GitHub is the best place to share code with friends, co-workers, classmates, and complete strangers. Over eight million people use GitHub to build amazing things together.
它还是什么?
管理配置文件
管理代码片段
托管编程环境
等等。看上去像是大餐,但是你还需要了解点什么?
版本管理与软件部署
jQuery[^jQuery]在发布版本2.1.3,一共有152个commit。我们可以看到如下的提交信息:
Ajax: Always use script injection in globalEval … bbdfbb4
Effects: Reintroduce use of requestAnimationFrame … 72119e0
Effects: Improve raf logic … 708764f
Build: Move test to appropriate module fbdbb6f
Build: Update commitplease dev dependency
GitHub与Git
Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理。在推出后,Git在其它项目中也取得了很大成功,尤其是在Ruby社区中。目前,包括Rubinius、Merb和Bitcoin在内的很多知名项目都使用了Git。Git同样可以被诸如Capistrano和Vlad the Deployer这样的部署工具所使用。
GitHub可以托管各种git库,并提供一个web界面,但与其它像 SourceForge或Google Code这样的服务不同,GitHub的独特卖点在于从另外一个项目进行分支的简易性。为一个项目贡献代码非常简单:首先点击项目站点的“fork”的按钮,然后将代码检出并将修改加入到刚才分出的代码库中,最后通过内建的“pull request”机制向项目负责人申请代码合并。已经有人将GitHub称为代码玩家的MySpace。
在GitHub创建项目
接着,我们试试在上面创建一个项目:
GitHub Roam
就会有下面的提醒:
GitHub Roam
它提供多种方式的创建方法:
…or create a new repository on the command line
echo &# github-roam& && README.md
git add README.md
git commit -m &first commit&
git remote add origin :phodal/github-roam.git
git push -u origin master
…or push an existing repository from the command line
git remote add origin :phodal/github-roam.git
git push -u origin master
如果你完成了上面的步骤之后,那么我想你想知道你需要怎样的项目。
GitHub流行项目分析
之前曾经分析过一些GitHub的用户行为,现在我们先来说说GitHub上的Star吧。(截止: 日23时。)
free-programming books
angular.js
JavaScript
JavaScript
JavaScript
上面列出来的是前5的,看看大于1万个stars的项目的分布,一共有82个:
JavaScript
库和框架: 如jQuery
系统: 如Linux、hhvm、docker
配置集: 如dotfiles
辅助工具: 如oh-my-zsh
工具: 如Homewbrew和Bower
资料收集: 如free programming books,You-Dont-Know-JS,Font-Awesome
其他:简历如Resume
Pull Request
除了创建项目之外,我们也可以创建Pull Request来做贡献。
我的第一个PR
我的第一个PR是给一个小的Node的CoAP相关的库的Pull Request。原因比较简单,是因为它的README.md写错了,导致我无法办法进行下一步。
const dgram
= require('dgram')
, coapPacket
= require('coap-packet')
= require('coap-packet')
很简单,却又很有用的步骤,另外一个也是:
cat && END
$0: error: module ngx_pagespeed requires the pagespeed optimization library.
-Look in obj/autoconf.err for more details.
+Look in objs/autoconf.err for more details.
CLA即Contributor License Agreement,在为一些大的组织、机构提交Pull Request的时候,可能需要签署这个协议。他们会在你的Pull Request里问你,只有你到他们的网站去注册并同意协议才会接受你的PR。
以下是我为Google提交的一个PR
Google CLA
以及Eclipse的一个PR
Eclipse CLA
他们都要求我签署CLA。
构建 GitHub 项目
如何用好 GitHub
如何用好 GitHub,并实践一些敏捷软件开发是一个很有意思的事情.我们可以在上面做很多事情,从测试到CI,再到自动部署.
敏捷软件开发
显然我是在扯淡,这和敏捷软件开发没有什么关系。不过我也不知道瀑布流是怎样的。说说我所知道的一个项目的组成吧:
看板式管理应用程序(如trello,简单地说就是管理软件功能)
CI(持续集成)
测试覆盖率
代码质量(code smell)
对于一个不是远程的团队(如只有一个人的项目) 来说,Trello、Jenkin、Jira不是必需的:
你存在,我深深的脑海里
当只有一个人的时候,你只需要明确知道自己想要什么就够了。我们还需要的是CI、测试,以来提升代码的质量。
通常我们都会找Document,如果没有的话,你会找什么?看源代码,还是看测试?
it(&specifying response when you need it&, function (done) {
var doneFn = jasmine.createSpy(&success&);
lettuce.get('/some/cool/url', function (result) {
expect(result).toEqual(&awesome response&);
expect(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url');
expect(doneFn).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({
&status&: 200,
&contentType&: 'text/plain',
&responseText&: 'awesome response'
上面的测试用例,清清楚楚地写明了用法,虽然写得有点扯。
等等,测试是用来干什么的。那么,先说说我为什么会想去写测试吧:
我不希望每次做完一个个新功能的时候,再手动地去测试一个个功能。(自动化测试)
我不希望在重构的时候发现破坏了原来的功能,而我还一无所知。
我不敢push代码,因为我没有把握。
虽然,我不是TDD的死忠,测试的目的是保证功能正常,TDD没法让我们写出质量更高的代码。但是有时TDD是不错的,可以让我们写出逻辑更简单地代码。
也许你已经知道了Selenium、Jasmine、Cucumber等等的框架,看到过类似于下面的测试
? specifying response when you need it
? specifying html when you need it
? should be post to some where
? respects instanceof
? inherits methods (also super)
? extend methods
? should be able fadein elements
? should be able fadeout elements
看上去似乎每个测试都很小,不过补完每一个测试之后我们就得到了测试覆盖率
lettuce.js
98.58% (209 / 212)
82.98%(78 / 94)
100.00% (54 / 54)
98.58% (209 / 212)
本地测试都通过了,于是我们添加了Travis-CI来跑我们的测试
虽然node.js不算是一门语言,但是因为我们用的node,下面的是一个简单的.travis.yml示例:
language: node_js
notifications:
email: false
before_install: npm install -g grunt-cli
install: npm install
after_success: CODECLIMATE_REPO_TOKEN=fc37deb0de70aa2a3ccfffbb0ab codeclimate &
我们把这些集成到README.md之后,就有了之前那张图。
CI对于一个开发者在不同城市开发同一项目上来说是很重要的,这意味着当你添加的部分功能有测试覆盖的时候,项目代码会更加强壮。
像jslint这类的工具,只能保证代码在语法上是正确的,但是不能保证你写了一堆bad smell的代码。
过长的函数
Code Climate是一个与github集成的工具,我们不仅仅可以看到测试覆盖率,还有代码质量。
先看看上面的ajax类:
Lettuce.get = function (url, callback) {
Lettuce.send(url, 'GET', callback);
Lettuce.send = function (url, method, callback, data) {
data = data || null;
var request = new XMLHttpRequest();
if (callback instanceof Function) {
request.onreadystatechange = function () {
if (request.readyState === 4 && (request.status === 200 || request.status === 0)) {
callback(request.responseText);
request.open(method, url, true);
if (data instanceof Object) {
data = JSON.stringify(data);
request.setRequestHeader('Content-Type', 'application/json');
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.send(data);
在在出现了一堆问题
Missing “use strict” statement. (Line 2)
Missing “use strict” statement. (Line 14)
‘Lettuce’ is not defined. (Line 5)
而这些都是小问题啦,有时可能会有
Similar code found in two :expression_statement nodes (mass = 86)
这就意味着我们可以对上面的代码进行重构,他们是重复的代码。
模块分离与测试
在之前说到
奋斗了近半个月后,将fork的代码读懂、重构、升级版本、调整,添加新功能、添加测试、添加CI、添加分享之后,终于almost finish。
今天就来说说是怎样做的。
以之前造的为例,里面有:
代码质量(Code Climate)
CI状态(Travis CI)
测试覆盖率(96%)
自动化测试(npm test)
按照来说,我们还需要有:
代码模块化
在SkillTree的源码里,大致分为三部分:
namespace函数: 顾名思义
Calculator也就是TalentTree,主要负责解析、生成url,头像,依赖等等
Skill 主要是tips部分。
而这一些都在一个js里,对于一个库来说,是一件好事,但是对于一个项目来说,并非如此。
依赖的库有
好在Knockout可以用Require.js进行管理,于是,使用了Require.js进行管理:
&script type=&text/javascript& data-main=&app/scripts/main.js& src=&app/lib/require.js&&&/script&
main.js配置如下:
require.config({
baseUrl: 'app',
jquery: 'lib/jquery',
json: 'lib/json',
text: 'lib/text'
require(['scripts/ko-bindings']);
require(['lib/knockout', 'scripts/TalentTree', 'json!data/web.json'], function(ko, TalentTree, TalentData) {
'use strict';
var vm = new TalentTree(TalentData);
ko.applyBindings(vm);
text、json插件主要是用于处理web.json,即用json来处理技能,于是不同的类到了不同的js文件。
|____Book.js
|____Doc.js
|____ko-bindings.js
|____Link.js
|____main.js
|____Skill.js
|____TalentTree.js
|____Utils.js
加上了后来的推荐阅读书籍等等。而Book和Link都是继承自Doc。
define(['scripts/Doc'], function(Doc) {
'use strict';
function Book(_e) {
Doc.apply(this, arguments);
Book.prototype = new Doc();
return Book;
而这里便是后面对其进行重构的内容。Doc类则是Skillock中类的一个缩影
define([], function() {
'use strict';
var Doc = function (_e) {
var e = _e || {};
var self = this;
self.label = e.label || (e.url || 'Learn more');
self.url = e.url || 'javascript:void(0)';
return Doc;
或者说这是一个AMD的Class应该有的样子。考虑到this的隐性绑定,作者用了self=this来避免这个问题。最后Return了这个对象,我们在调用的就需要new一个。大部分在代码中返回的都是对象,除了在Utils类里面返回的是函数:
getSkillsByHash: getSkillsByHash,
getSkillById: getSkillById,
prettyJoin: prettyJoin
当然函数也是一个对象。
自动化测试
一直习惯用Travis CI,于是也继续用Travis Ci,.travis.yml配置如下所示:
language: node_js
notifications:
email: false
- gh-pages
使用gh-pages的原因是,我们一push代码的时候,就可以自动测试、部署等等,好处一堆堆的。
接着我们需要在package.json里面添加脚本
&scripts&: {
&test&: &mocha&
这样当我们push代码的时候便会自动跑所有的测试。因为mocha的主要配置是用mocha.opts,所以我们还需要配置一下mocha.opts
--reporter spec
最后的test/spec是指定测试的目录。
JSLint定义了一组编码约定,这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验,并以一条年代久远的编程原则 作为宗旨:能做并不意味着应该做。JSLint会对它认为有的编码实践加标志,另外还会指出哪些是明显的错误,从而促使你养成好的 JavaScript编码习惯。
当我们的js写得不合理的时候,这时测试就无法通过:
A constructor name should start with an uppercase letter.
Strings must use singlequote.
这是一种驱动写出更规范js的方法。
Mocha 是一个优秀的JS测试框架,支持TDD/BDD,结合 should.js/expect/chai/better-assert,能轻松构建各种风格的测试用例。
最后的效果如下所示:
? should return book label & url
? should return link label & url
简单地看一下Book的测试:
/* global describe, it */
var requirejs = require(&requirejs&);
var assert = require(&assert&);
var should = require(&should&);
requirejs.config({
baseUrl: 'app/',
nodeRequire: require
describe('Book,Link', function () {
var Book, Link;
before(function (done) {
requirejs(['scripts/Book'、], function (Book_Class) {
Book = Book_Class;
describe('Book Test', function () {
it('should return book label & url', function () {
var book_name = 'Head First HTML与CSS';
var url = '';
var books = {
label: book_name,
var _book = new Book(books);
_book.label.should.equal(book_name);
_book.url.should.equal(url);
因为我们用require.js来管理浏览器端,在后台写测试来测试的时候,我们也需要用他来管理我们的依赖,这也就是为什么这个测试这么长的原因,多数情况下一个测试类似于这样子的。(用Jasmine似乎会是一个更好的主意,但是用习惯Jasmine了)
describe('Book Test', function () {
it('should return book label & url', function () {
var book_name = 'Head First HTML与CSS';
var url = '';
var books = {
label: book_name,
var _book = new Book(books);
_book.label.should.equal(book_name);
_book.url.should.equal(url);
最后的断言,也算是测试的核心,保证测试是有用的。
代码质量与重构
当你写了一大堆代码,你没有意识到里面有一大堆重复。
当你写了一大堆测试,却不知道覆盖率有多少。
这就是个问题了,于是偶然间看到了一个叫code climate的网站。
Code Climate
Code Climate consolidates the results from a suite of static analysis tools into a single, real-time report, giving your team the information it needs to identify hotspots, evaluate new approaches, and improve code quality.
Code Climate整合一组静态分析工具的结果到一个单一的,实时的报告,让您的团队需要识别热点,探讨新的方法,提高代码质量的信息。
简单地来说:
对我们的代码评分
找出代码中的坏味道
于是,我们先来了个例子
lib/coap/coap_request_handler.js
lib/coap/coap_result_helper.js
lib/coap/coap_server.js
lib/database/db_factory.js
lib/database/iot_db.js
lib/database/mongodb_helper.js
lib/database/sqlite_helper.js
lib/rest/rest_helper.js
lib/rest/rest_server.js
lib/url_handler.js
分享得到的最后的结果是:
[Coverage][1]
代码的坏味道
于是我们就打开lib/database/sqlite_helper.js,因为其中有两个坏味道
Similar code found in two :expression_statement nodes (mass = 86)
在代码的 lib/database/sqlite_helper.js:58…61 & &
SQLiteHelper.prototype.deleteData = function (url, callback) {
'use strict';
var sql_command = &DELETE FROM
& + config.table_name + &
where & + URLHandler.getKeyFromURL(url) + &=& + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
lib/database/sqlite_helper.js:64…67 & &
SQLiteHelper.prototype.getData = function (url, callback) {
'use strict';
var sql_command = &SELECT * FROM
& + config.table_name + &
where & + URLHandler.getKeyFromURL(url) + &=& + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
只是这是之前修改过的重复。。
原来的代码是这样的
SQLiteHelper.prototype.postData = function (block, callback) {
'use strict';
var db = new sqlite3.Database(config.db_name);
var str = this.parseData(config.keys);
var string = this.parseData(block);
var sql_command = &insert or replace into & + config.table_name + & (& + str + &) VALUES (& + string + &);&;
db.all(sql_command, function (err) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
callback();
SQLiteHelper.prototype.deleteData = function (url, callback) {
'use strict';
var db = new sqlite3.Database(config.db_name);
var sql_command = &DELETE FROM
& + config.table_name + &
where & + URLHandler.getKeyFromURL(url) + &=& + URLHandler.getValueFromURL(url);
db.all(sql_command, function (err) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
callback();
SQLiteHelper.prototype.getData = function (url, callback) {
'use strict';
var db = new sqlite3.Database(config.db_name);
var sql_command = &SELECT * FROM
& + config.table_name + &
where & + URLHandler.getKeyFromURL(url) + &=& + URLHandler.getValueFromURL(url);
db.all(sql_command, function (err, rows) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
callback(JSON.stringify(rows));
说的也是大量的重复,重构完的代码
SQLiteHelper.prototype.basic = function(sql, db_callback){
'use strict';
var db = new sqlite3.Database(config.db_name);
db.all(sql, function (err, rows) {
SQLiteHelper.prototype.errorHandler(err);
db.close();
db_callback(JSON.stringify(rows));
SQLiteHelper.prototype.postData = function (block, callback) {
'use strict';
var str = this.parseData(config.keys);
var string = this.parseData(block);
var sql_command = &insert or replace into & + config.table_name + & (& + str + &) VALUES (& + string + &);&;
SQLiteHelper.prototype.basic(sql_command, callback);
SQLiteHelper.prototype.deleteData = function (url, callback) {
'use strict';
var sql_command = &DELETE FROM
& + config.table_name + &
where & + URLHandler.getKeyFromURL(url) + &=& + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
SQLiteHelper.prototype.getData = function (url, callback) {
'use strict';
var sql_command = &SELECT * FROM
& + config.table_name + &
where & + URLHandler.getKeyFromURL(url) + &=& + URLHandler.getValueFromURL(url);
SQLiteHelper.prototype.basic(sql_command, callback);
重构完后的代码比原来还长,这似乎是个问题~~
Git 提交信息及几种不同的规范
受 Growth 3.0 开发的影响,最近更新文章的频率会有所降低。今天,让我们来谈谈一个好的 Git、SVN 提交信息是怎样规范出来的。
在团队协作中,使用版本管理工具 Git、SVN 几乎都是这个行业的标准。当我们提交代码的时候,需要编写提交信息(commit message)。
而提交信息的主要用途是:告诉这个项目的人,这次代码提交里做了些什么。如,我更新了 React Native Elements 的版本,那么它就可以是:[T] upgrade react native elements。对应的我修改的代码就是:package.json 和 yarn.lock 中的文件。一般来说,建议小步提交,即按自己的 Tasking 步骤来的提交,每一小步都有对应的提交信息。这样做的主要目的是:防止一次修改中,修改过多的文件,导致后期修改、维护、撤销等等困难。
而对于不同的团队来说,都会遵循一定的规范,本文主要会介绍以下几种写法:
开源库写法
那么,先从我习惯的做法说起。
在我的第一个项目里,我们使用 Jira 作为看板工具,Bamboo 作为持续集成服务器,并采用结对编程的方式进行。
在 Jira 里每一个功能卡都有对应的卡号,而 Bamboo 支持使用 Jira 的任务卡号关联的功能。即在持续构建服务器上示例对应的任务卡号,即相应的提交人。
因此,这个时候我们的规范稍微有一些特别:
[任务卡号] xx & xx: do something
比如:[PHODAL-0001] ladohp & phodal: update documents,解释如下:
PHODAL-0001,业务的任务卡号,它可以帮我们找到某个业务修改的原因,即点出相应 bug 的来源
ladohp & phodal ,结对编程的两个人的名字,后者(phodal)一般是写代码的人,出于礼貌就放在后面了。由于 Git 的提交人只显示一个,所以写上两个的名字。当提交的人不在时,就可以问另外一个人修改的原因。
update documents,我们做了什么事情
缺点:而对于采用看板的团队来说,并不存在任务卡号这种东西,因此就需要一种额外的作法。
对于我来说,我则习惯这种的写法:
[任务分类] 主要修改组件(可选):修改内容
示例 1,[T] tabs: add icons 。其中的 T 表示这是一个技术卡,tabs 表示修改的是 Tabs,add icons 则表示添加了图标。
示例 2,[SkillTree] detail: add link data。其中的 SkillTree 表示修改的是技能树 Tab 下的内容,detail 则表示修改的是详情页,add link data 则表示是添加了技能的数据
这样做的主要原因是,它可以轻松也帮我** filter 出相应业务的内容**。
缺点:要这样做需要团队达到一致,因此付出一些额外的成本。
开源应用、开源库写法
与我们日常工作稍有不同的是:工作中的 Release 计划一般都是事先安排好的,不需要一些 CHANGELOG 什么的。而开源应用、开源库需要有对应的 CHANELOG,则添加了什么功能、修改了什么等等。毕竟有很多东西是由社区来维护的。
因此,这里以做得比较好的开源项目 Angular 中为例展示。Angular 团队建议采用以下的形式:
&type&(&scope&): &subject&
&BLANK LINE&
&BLANK LINE&
诸如:docs(changelog): update change log to beta.5 中:
docs 则对应修改的类型
changelog 则是影响的范围
subject 则是对应做的事件
对应的类型有:
build: 影响构建系统或外部依赖关系的更改(示例范围:gulp,broccoli,npm)
ci: 更改我们的持续集成文件和脚本(示例范围:Travis,Circle,BrowserStack,SauceLabs)
docs: 仅文档更改
feat: 一个新功能
fix: 修复错误
perf: 改进性能的代码更改
refactor: 代码更改,既不修复错误也不添加功能
style: 不影响代码含义的变化(空白,格式化,缺少分号等)
test: 添加缺失测试或更正现有测试
同时还对应了 20+ 的 Scope,可以说这种提交比上面的提交更有挑战。
(以上的 10 个类型,感谢 Google Translate 提供的快速翻译支持)
而这样做的优点是,它可以轻松地生成一个 CHANGELOG。与些同时还有一个名为 Conventional Commits 的规范,建议采用相似的形式。
创建项目文档
我们需要为我们的项目创建一个文档,通常我们可以将核心代码以外的东西都称为文档:
通常这个会在项目的最上方会有一个项目的简介,如下图所示:
GitHub Project Introduction
README通常会显示在GitHub项目的下面,如下图所示:
GitHub README
通常一个好的README会让你立马对项目产生兴趣。
如下面的内容是React项目的简介:
React README
下面的内容写清楚了他们的用途:
Just the UI: Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it’s easy to try it out on a small feature in an existing project.
Virtual DOM: React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using .
Data flow: React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding.
通常在这个README里,还会有:
运行的平台
如何参与贡献
官方首页与在线文档
很多开源项目都会有自己的网站,并在上面有一个文档,而有的则会放在。
Read the Docs 托管文档,让文档可以被全文搜索和更易查找。您可以导入您使用任何常用的版本控制系统管理的文档,包括 Mercurial、Git、Subversion 和 Bazaar。 我们支持 webhooks,因此可以在您提交代码时自动构建文档。并且同样也支持版本功能,因此您可以构建来自您代码仓库中某个标签或分支的文档。查看完整的功能列表 。
在一个开源项目中,良好和专业的文档是相当重要的,有时他可能会比软件还会重要。因为如果一个开源项目好用的话,多数人可能不会去查看软件的代码。这就意味着,多数时候他在和你的文档打交道。文档一般会有:API 文档、 配置文档、帮助文档、用户手册、教程等等
写文档的软件有很多,如Markdown、Doxygen、Docbook等等。
一个简单上手的示例非常重要,特别是通常我们是在为着某个目的而去使用一个开源项目的是时候,我们希望能马上使用到我们的项目中。
你希望看到的是,你打开浏览器,输入下面的代码,然后It Works:
var HelloMessage = React.createClass({
render: function() {
return &div&Hello {this.props.name}&/div&;
React.render(
&HelloMessage name=&John& /&,
document.getElementById('container')
而不是需要繁琐的步骤才能进行下一步。
改善 GitHub 项目代码质量:重构
或许你应该知道了,重构是怎样的,你也知道重构能带来什么。在我刚开始学重构和设计模式的时候,我需要去找一些好的示例,以便于我更好的学习。有时候不得不创造一些更好的场景,来实现这些功能。
有一天,我发现当我需要我一次又一次地重复讲述某些内容,于是我就计划着把这些应该掌握的技能放到GitHub上,也就有了 计划。
每个程序员都不可避免地是一个Coder,一个没有掌握好技能的Coder,算不上是手工艺人,但是是手工人。
艺,需要有创造性的方法。
为什么重构?
为了更好的代码。
在经历了一年多的工作之后,我平时的主要工作就是修Bug。刚开始的时候觉得无聊,后来才发现修Bug需要更好的技术。有时候你可能要面对着一坨一坨的代码,有时候你可能要花几天的时间去阅读代码。而,你重写那几十代码可能只会花上你不到一天的时间。但是如果你没办法理解当时为什么这么做,你的修改只会带来更多的bug。修Bug,更多的是维护代码。还是前人总结的那句话对:
写代码容易,读代码难。
假设我们写这些代码只要半天,而别人读起来要一天。为什么不试着用一天的时候去写这些代码,让别人花半天或者更少的时间来理解。
如果你的代码已经上线,虽然是一坨坨的。但是不要轻易尝试,没有测试的重构。
从前端开始的原因在于,写得一坨坨且最不容易测试的代码都在前端。
让我们来看看我们的第一个训练,相当有挑战性。
重构uMarkdown
代码及setup请见github:
uMarkdown是一个用于将Markdown转化为HTML的库。代码看上去就像一个很典型的过程代码:
/* code */
while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
str = str.replace(stra[0], '&code&\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '&br/&').replace(/\ /gm, '&') + '&/code&\n');
/* headlines */
while ((stra = micromarkdown.regexobject.headline.exec(str)) !== null) {
count = stra[1].length;
str = str.replace(stra[0], '&h' + count + '&' + stra[2] + '&/h' + count + '&' + '\n');
/* mail */
while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
str = str.replace(stra[0], '&a href=&mailto:' + stra[1] + '&&' + stra[1] + '&/a&');
选这个做重构的开始,不仅仅是因为之前在写的时候进行了很多的重构。而且它更适合于,重构到设计模式的理论。让我们在重构完之后,给作者进行pull request吧。
Markdown的解析过程,有点类似于Pipe and Filters模式(架构模式)。
Filter即我们在代码中看到的正规表达式集:
regexobject: {
headline: /^(\#{1,6})([^\#\n]+)$/m,
code: /\s\`\`\`\n?([^`]+)\`\`\`/g
他会匹配对应的Markdown类型,随后进行替换和处理。而``str```,就是管理口的输入和输出。
接着,我们就可以对其进行简单的重构。
(ps: 推荐用WebStrom来做重构,自带重构功能)
作为一个示例,我们先提出codeHandler方法,即将上面的
/* code */
while ((stra = micromarkdown.regexobject.code.exec(str)) !== null) {
str = str.replace(stra[0], '&code&\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '&br/&').replace(/\ /gm, '&') + '&/code&\n');
提取方法成
codeFilter: function (str, stra) {
return str.replace(stra[0], '&code&\n' + micromarkdown.htmlEncode(stra[1]).replace(/\n/gm, '&br/&').replace(/\ /gm, '&') + '&/code&\n');
while语句就成了
while ((stra = regexobject.code.exec(str)) !== null) {
str = this.codeFilter(str, stra);
然后,运行所有的测试。
grunt test
同理我们就可以mail、headline等方法进行重构。接着就会变成类似于下面的代码,
/* code */
while ((execStr = regExpObject.code.exec(str)) !== null) {
str = codeHandler(str, execStr);
/* headlines */
while ((execStr = regExpObject.headline.exec(str)) !== null) {
str = headlineHandler(str, execStr);
/* lists */
while ((execStr = regExpObject.lists.exec(str)) !== null) {
str = listHandler(str, execStr);
/* tables */
while ((execStr = regExpObject.tables.exec(str)) !== null) {
str = tableHandler(str, execStr, strict);
然后你也看到了,上面有一堆重复的代码,接着让我们用JavaScript的奇技淫巧,即apply方法,把上面的重复代码变成。
['code', 'headline', 'lists', 'tables', 'links', 'mail', 'url', 'smlinks', 'hr'].forEach(function (type) {
while ((stra = regexobject[type].exec(str)) !== null) {
str = that[(type + 'Handler')].apply(that, [stra, str, strict]);
进行测试,blabla,都是过的。
? should parse h1~h3
? should parse link
? should special link
? should parse font style
? should parse code
? should parse ul list
? should parse ul table
? should return correctly class name
快来试试吧,
是时候讨论这个Refactor利器了,最初看到这个重构的过程是从ThoughtWorks郑大晔校开始的,只是之前对于Java的另外一个编辑器Eclipse的坏感。。这些在目前已经不是很重要了,试试这个公司里面应用广泛的编辑器。
Intellij Idea重构
开发的流程大致就是这样子的,测试先行算是推荐的。
编写测试-&功能代码-&修改测试-&重构
上次在和buddy聊天的时候,才知道测试在功能简单的时候是后行的,在功能复杂不知道怎么下手的时候是先行的。
开始之前请原谅我对于Java语言的一些无知,然后,看一下我写的Main函数:
package com.phodal.
public class Main {
public static void main(String[] args) {
int c=new Cal().add(1,2);
int d=new Cal2().sub(2,1);
System.out.println(&Hello,s&);
System.out.println(c);
System.out.println(d);
代码写得还好(自我感觉),先不管Cal和Cal2两个类。大部分都能看懂,除了c,d不知道他们表达的是什么意思,于是。
快捷键:Shift+F6
作用:重命名
把光标丢到int c中的c,按下shift+f6,输入result_add
把光标移到int d中的d,按下shift+f6,输入result_sub
package com.phodal.
public class Main {
public static void main(String[] args) {
int result_add=new Cal().add(1,2);
int result_sub=new Cal2().sub(2,1);
System.out.println(&Hello,s&);
System.out.println(result_add);
System.out.println(result_sub);
Extract Method
快捷键:alt+command+m
作用:扩展方法
选中System.out.println(result_add);
按下alt+command+m
在弹出的窗口中输入mprint
public static void main(String[] args) {
int result_add=new Cal().add(1,2);
int result_sub=new Cal2().sub(2,1);
System.out.println(&Hello,s&);
mprint(result_add);
mprint(result_sub);
private static void mprint(int result_sub) {
System.out.println(result_sub);
似乎我们不应该这样对待System.out.println,那么让我们内联回去
Inline Method
快捷键:alt+command+n
作用:内联方法
选中main中的mprint
alt+command+n
选中Inline all invocations and remove the method(2 occurrences) 点确定
然后我们等于什么也没有做了~~:
public static void main(String[] args) {
int result_add=new Cal().add(1,2);
int result_sub=new Cal2().sub(2,1);
System.out.println(&Hello,s&);
System.out.println(result_add);
System.out.println(result_sub);
似乎这个例子不是很好,但是够用来说明了。
Pull Members Up
开始之前让我们先看看Cal2类:
public class Cal2 extends Cal {
public int sub(int a,int b){
return a-b;
以及Cal2的父类Cal
public class Cal {
public int add(int a,int b){
return a+b;
最后的结果,就是将Cal2类中的sub方法,提到父类:
public class Cal {
public int add(int a,int b){
return a+b;
public int sub(int a,int b){
return a-b;
而我们所要做的就是鼠标右键
重构之以查询取代临时变量
Windows/Linux: 木有
或者: Shift+alt+command+T 再选择 Replace Temp with Query
鼠标: Refactor | Replace Temp with Query
过多的临时变量会让我们写出更长的函数,函数不应该太多,以便使功能单一。这也是重构的另外的目的所在,只有函数专注于其功能,才会更容易读懂。
以书中的代码为例
import java.lang.S
public class replaceTemp {
public void count() {
double basePrice = _quantity * _itemP
if (basePrice & 1000) {
return basePrice * 0.95;
return basePrice * 0.98;
选中basePrice很愉快地拿鼠标点上面的重构
Replace Temp With Query
import java.lang.S
public class replaceTemp {
public void count() {
if (basePrice() & 1000) {
return basePrice() * 0.95;
return basePrice() * 0.98;
private double basePrice() {
return _quantity * _itemP
而实际上我们也可以
_quantity * _itemPrice
对其进行Extrace Method
选择basePrice再Inline Method
Intellij IDEA重构
在Intellij IDEA的文档中对此是这样的例子
public class replaceTemp {
public void method() {
String str = &str&;
String aString = returnString().concat(str);
System.out.println(aString);
接着我们选中aString,再打开重构菜单,或者
Command+Alt+Shift+T 再选中Replace Temp with Query
便会有下面的结果:
import java.lang.S
public class replaceTemp {
public void method() {
String str = &str&;
System.out.println(aString(str));
private String aString(String str) {
return returnString().concat(str);
改善 GitHub 项目代码质量:测试
虽然接触的TDD时间不算短,然而真正在实践TDD上的时候少之又少。除去怎么教人TDD,就是与人结对编程时的switch,或许是受限于当前的开发流程。
偶然间在开发一个物联网相关的开源项目——的时候,重拾了这个过程。不得不说提到的一点是,在我们的开发流程中测试是由相关功能开发人员写的,有时候测试是一种很具挑战性的工作。久而久之,为自己的开源项目写测试变成一种自然而然的事。有时没有测试,反而变得没有安全感。
一次测试驱动开发
之前正在重写一个的服务端,主要便是结合CoAP、MQTT、HTTP等协议构成一个物联网的云服务。现在,主要的任务是集中于协议与授权。由于,不同协议间的授权是不一样的,最开始的时候我先写了一个http put授权的功能,而在起先的时候是如何测试的呢?
curl --user root:root -X PUT -d '{ &dream&: 1 }' -H &Content-Type: application/json& http://localhost:8899/topics/test
我只要顺利在request中看有无req.headers.authorization,我便可以继续往下,接着给个判断。毕竟,我们对HTTP协议还是蛮清楚的。
if (!req.headers.authorization) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm=&Secure Area&');
return res.end('Unauthorized');
可是除了HTTP协议,还有MQTT和CoAP。对于MQTT协议来说,那还算好,毕竟自带授权,如:
mosquitto_pub -u root -P root -h localhost -d -t lettuce -m &Hello, MQTT. This is my first message.&
便可以让我们简单地完成这个功能,然而有的协议是没有这样的功能如CoAP协议中是用Option来进行授权的。现在的工具如libcoap只能有如下的简单功能
coap-client -m get coap://127.0.0.1:5683/topics/zero -T
于是,先写了个测试脚本来验证功能。
= require('coap');
var request
= coap.request;
var req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'});
req.setHeader(&Accept&, &application/json&);
req.setOption('Block2',
[new Buffer('phodal'), new Buffer('phodal')]);
req.end();
写完测试脚本后发现不对了,这个不应该是测试的代码吗? 于是将其放到了spec中,接着发现了上面的全部功能的实现过程为什么不用TDD实现呢?
测试驱动开发是一个很“古老”的程序开发方法,然而由于国内的开发流程的问题——即开发人员负责功能的测试,导致这么好的一项技术没有在国内推广。
测试驱动开发的主要过程是:
先写功能的测试
实现功能代码
提交代码(commit -& 保证功能正常)
重构功能代码
而对于这样的一个物联网项目来说,我已经有了几个有利的前提:
已经有了原型
通常在我的理解下,TDD是可有可无的。既然我知道了我要实现的大部分功能,而且我也知道如何实现。与此同时,对Code Smell也保持着警惕、要保证功能被测试覆盖。那么,总的来说TDD带来的价值并不大。
然而,在当前这种情况下,我知道我想要的功能,但是我并不理解其深层次的功能。我需要花费大量的时候来理解,它为什么是这样的,需要先有一些脚本来知道它是怎么工作的。TDD变显得很有价值,换句话来说,在现有的情况下,TDD对于我们不了解的一些事情,可以驱动出更多的开发。毕竟在我们完成测试脚本之后,我们也会发现这些测试脚本成为了代码的一部分。
在这种理想的情况下,我们为什么不TDD呢?
轻量级网站测试TWill
twill was initially designed for testing Web sites, although since then people have also figured out that it’s good for browsing unsuspecting Web sites.
之所以说轻量的原因是他是拿命令行测试的,还有DSL,还有Python。
除此之外,还可以拿它做压力测试,这种压力测试和一般的不一样。可以模拟整个过程,比如同时有多少人登陆你的网站。
不过,它有一个限制是没有JavaScript。
看了一下源码,大概原理就是用requests下载html,接着用lxml解析html,比较有意思的是内嵌了一个DSL。
这是一个Python的库。
pip install twill
Twill 登陆测试
1.启动我们的应用。
2.进入twill shell
-= Welcome to twill! =-
current page:
*empty page*
3.打开网页
&& go http://127.0.0.1:5000/login
==& at http://127.0.0.1:5000/login
current page: http://127.0.0.1:5000/login
4.显示表单
&& showforms
## ## __Name__________________ __Type___ __ID________ __Value__________________
csrf_token
csrf_token
##5005bdfe2fbf450 ...
current page: http://127.0.0.1:5000/login
5.填充表单
formclear 1
fv 1 email
fv 1 password test
6.修改action
formaction 1 http://127.0.0.1:5000/login
7.提交表单
Note: submit is using submit button: name=&login&, value=&登入&
current page: http://127.0.0.1:5000/
发现重定向到首页了。
Twill 测试脚本
当然我们也可以用脚本直接来测试login.twill:
go http://127.0.0.1:5000/login
formclear 1
fv 1 email
fv 1 password test
formaction 1 http://127.0.0.1:5000/login
go http://127.0.0.1:5000/logout
twill-sh login.twill
&& EXECUTING FILE login.twill
AT LINE: login.twill:0
==& at http://127.0.0.1:5000/login
AT LINE: login.twill:2
## ## __Name__________________ __Type___ __ID________ __Value__________________
csrf_token
csrf_token
##7a000b612fef39aceab5ca54 ...
AT LINE: login.twill:3
AT LINE: login.twill:4
AT LINE: login.twill:5
AT LINE: login.twill:6
Setting action for form
(&Element form at 0x10e7cbb50&,) to
('http://127.0.0.1:5000/login',)
AT LINE: login.twill:7
Note: submit is using submit button: name=&login&, value=&登入&
AT LINE: login.twill:9
==& at http://127.0.0.1:5000/login
1 of 1 files SUCCEEDED.
一个成功的测试诞生了。
Fake Server
实践了一下怎么用sinon去fake server,还没用respondWith,于是写一下。
这里需要用到sinon框架来测试。
当我们fetch的时候,我们就可以返回我们想要fake的结果。
var data = {&id&:1,&name&:&Rice&,&type&:&Good&,&price&:12,&quantity&:1,&description&:&Made in China&};
beforeEach(function() {
this.server = sinon.fakeServer.create();
this.rices = new Rices();
this.server.respondWith(
&http://localhost:8080/all/rice&,
{&Content-Type&: &application/json&},
JSON.stringify(data)
于是在 afterEach 的时候,我们需要恢复这个server。
afterEach(function() {
this.server.restore();
接着写一个jasmine测试来测试
describe(&Collection Test&, function() {
it(&should get data from the url&, function() {
this.rices.fetch();
this.server.respond();
var result = JSON.parse(JSON.stringify(this.rices.models[0]));
expect(result[&id&])
.toEqual(1);
expect(result[&price&])
.toEqual(12);
expect(result)
.toEqual(data);
除了擅长编写 md 电子书来攒 star,我还写了一系列的开源软件,也掌握了一些项目运营的技巧。
开源并不是你把软件、README 写好就行了,还有详细的文档、示例程序等等。
开源也不是你的项目好了,就会有一堆人参与进来。
开源还要你帮助别人解决 Bug,……。
人们做事都是有原因的,即动机。再举例一下,如果你的项目不够火,别人都没听过,那么写到简历上可能没啥用。
Marketing First
开源需要一些营销的技巧,这些技巧可以帮你吸引关注。举个简单的例子,司徒正美的
框架出身得很早,也 MV* 方面也做得很不错,但是在 marketing 上就……。以至于国内的很多前端,都不了解这个框架,要不今天在国内可能就是 AVRR 四大框架了。
Vue 不是因为好用,而一下子火了。这一点我印象特别深,当时在 GitHub Trending 上看到了这个项目,发现它还不能很好地 work。
而如文章 《》所说,项目刚开始的时候作者做了一系列的营销计划:
HackerNews
Reddit /r/javascript
The DailyJS blog
JavaScript Weekly
Maintain a project Twitter account(维护项目的 Vue 账户)
除此,文中还提到了一篇文章《》 。
这一点相当的有意思,如果你的想法好的话,那么大家都会肯定,点下链接,为你来个 star。那么,你就获得更好的动力去做这件事。项目也在开头的时候,获得了相当多的关注。而如果大家觉得你的项目没有新意的话,那么你懂的~。
除此,还有一种可能是,你的 ID 不够 fancy,即你在社区的影响上比较少。此时,就需要一点点慢慢积累人气了。当你积累了一些人气,你就能和松本行弘一样,在创建 mRuby 的时候就有 1000+ 的 star。并且,在社区上还有一些相关的文章介绍,各个头条也由他的粉丝发了上去。如,一年多以前,我创建了
当时,是为了给自己做一个基于 GitHub 云笔记的工具,在完成度到一定程度的时候。我在我的微信公从号上发了相关的介绍,第二天就有 100+ 的 star 了,还接收至最一些鼓舞的话语。对应于国内则有:
开发者头条
不成器的微博
所以,你觉得呢?
编写一个好的 README
在一个开源项目里,README 是最重要的内容。它快速地介绍了这个项目,并决定了它能不能吸引用户:
这个项目做什么?
它解决了什么问题
它有什么特性 — hello, world 示例
这个项目做什么——一句话文案
GitHub 的 Description 是我们在 Hacking News、GitHub Trneding 等等,第一时间看到的介绍。也是我们能快速介绍给别人的东西,如下图所示:
GitHub Trending
这一句话,必须简单明了也介绍,它是干什么的。
如 Angular 的一句话方案是:One framework. Mobile & desktop.
而 React 是:A declarative, efficient, and flexible JavaScript library for building user interfaces.
Vue 则是:A progressive, incrementally-adoptable JavaScript framework for building UI on the web.
它解决了什么问题
上面的一句话描述,它不能很好地说明,它能解决什么问题。
如下是今天在 GitHub Trending 上榜的 RPC 项目的简介:
Most machines on internet communicate with each other via TCP/IP. However TCP/IP only guarantees reliable data transmissions, we need to abstract more to build services:
RPC 开源项目
以上便是这个项目能解决的问题,不过这个项目能解决的问题倒是比较长,哈哈哈。
它有什么特性
当我们有 A、B、C 几个不同的框架的时候,作为一个开发人员,就需要对比他们的特性,。如下是 Go 语言实现的 MQTT 示例:
GO MQTT 示例
这个项目只支持的 Qos 级别为 0。如果我们需要的级别是 1,那么就不能用这个项目了。
又比如 lodash 项目:
Lodash makes JavaScript easier by taking the hassle out of working with arrays, numbers, objects, strings, etc. Lodash’s modular methods are great for:
Iterating arrays, objects, & strings
Manipulating & testing values
Creating composite functions
你会怎么写?脸皮够厚的话,可以直接写一下,与其它项目的对比,blabla:
对比其它项目
当然了,这种事不能太过,要不是会招来一堆黑。
安装及hello, world 示例
在我们看完了上面的介绍之后,紧接着接一个 hello, world 的示例。在运行 hello, world 之前,我们可能需要一些额外的安装工作,如:
npm install koa
如 Koa 的示例:
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx =& {
ctx.body = 'Hello Koa';
app.listen(3000);
作为一个程序员,你应该懂得它的重要性。
好在这里的安装工作只有两步,而不是:
Lan 安装过程
对于那些需要复杂的安装过程的软件,应该简化安装过程,如提供 Docker 镜像,或者直接提供一个可运行的 Demo 环境。以免用户在看完 README 之后,直接放弃了使用该库。
好了,依一个开发人员的角度,如果上面的步骤一切顺利的话,接下来,便是使用这个开源项目来完成我们的功能。这个时候,我们开始转移注意力到文档上了。
由于,之前在某一个项目,经历过一个第三方 API 文档的大坑——文档中只罗列了 API 的用法。如下 Intellij Idea 生成的结构图:
文档中上,罗列了每个函数,以及每个函数需要的参数。我使用 Intellij Idea 直接反编译 jar 包,看到的信息都比文档多多了。文档上,没有任务示例,甚至连怎么初始化这个库的代码都没有。
对于一个复杂的开源项目来说,文档上要写明安装、编译、配置等等的过程。如下图所示:
Python Social Auth 文档
并且在我们发布包的时候,就要不断地去重复这个过程——如果你使用了自动化测试,那么这个过程便自动完成了。
如果我们的项目使用起来相当的简单,那么我们就可以只写一些示例代码即可。
并且,我们可以将文档直接入到代码里。它可以有效地减少文档不同步,带来的一些问题。
Lodash 示例
上图是使用了 jsdoc 的 Lodash 示例。
除了上面的示例,我们还可以录制一些视频,写一些文章说明项目的思考、架构等等。
更多的示例程序
示例代码本身也是文档的一部分,不要问我为什么~~。
反正,除了一个 hello, world,你还要有各种场景下的示例:
没有这么多示例,敢说自己是好用的开源项目?
编写技术文章、书籍
到目前为止,我们做了一系列 markdown 相关的工作,却也还没有结束。要知道只有真正写过一系列开源项目的人,才能体会到什么是 markdown 程序员~~。
官方文档,一般要以比较正式的口吻来描述过程,这种写法相当的压抑。如果要用轻松诙谐的口气,那么就可以写一系列的技术文章。假如这是一个前端框架,那么我们可以介绍它如何与某个后端框架配合使用;又或者是,它与某个框架的对比等等。
好了,一切顺利了,那么下一步就是吸引更多的人参与到项目上来了。
鼓励、吸引贡献者
要吸引其它开发人员来到你的项目,不是一件容易的事。
你需要不断地鼓励他/她们,并适时地帮他/她们解决问题,以避免他/她们在提 pull request 的过程中放弃了。这一点特别的有意思,当有一个开发人员发现了项目中的 bug,那么他/她会尝试去解决这个问题。与此同时,他/她还会为你的项目带来 pull request,但是在这个过程中,因为测试等等的问题,可能会阻碍他的 PR。这个时候,就需要我们主要去提示/教他们怎么做,又或者是帮他/她们解决完剩下的问题。那么,下次他/她提一个 PR 的时候,他/她就能解决问题了。
这一点可以在 README,以及介绍上体现:
Feel free to contribute!
哪怕只是一个错误字的 PR,那么你也可以 merge,啊哈哈~。然后,就有人帮你宣传了,『我给 xxx 项目一个 PR 了』。刚毕业的时候,我也是从这种类型的 PR 做起的~~。
开源项目维护
Git 与 GitHub 工具推荐
Git 命令行增强
diff so fancy 截图
$ sudo apt-get install git-extras
Mac OS X with Homebrew
$ brew install git-extras
$ git-summary
: github-roam
repo age : 2 years, 7 months
Fengda HUANG
Fengda Huang
Phodal HUANG
Phodal Huang
yangpei3720
WangXiaolong
Intellij IDEA
由于日常用的开发工是 Intellij IDEA 企业版,所以就有点依赖于这个工具了。最常用的功能便是:修复 Bug 时,对于文件修改的追溯。了解某行代码修改的原因,对应的修改人等等。
Intellij IDEA
Git、GitHub桌面增强
SourceTree
SourceTree 方便用来查看一些非我写的项目,寻找其中的一些问题。个中缘由便是:Intelli IDEA 刚打开某个项目的时候,需要花费大量的时间 index,只可惜现有的 SourceTree 客户端都需要登录 Atlassian 账户了。
gitflow 分支合并、查看
SourceTree 截图
GitHub Desktop
GitHub Desktop
********************************************************************************
********************************************************************************
No githug directory found, do you wish to create one? [yn]
Welcome to Githug!
Name: init
Difficulty: *
A new directory, `git_hug`, initialize an empty repository in it.
$ githug play
********************************************************************************
********************************************************************************
Congratulations, you have solved the level!
Name: config
Difficulty: *
Set up your git name and email, this is important so that your commits can be identified.
#2: config
#4: commit
#6: clone_to_folder
#7: ignore
#8: include
#9: status
#10: number_of_files_committed
#12: rm_cached
#13: stash
#14: rename
#15: restructure
Gource 历史
GitHub用户分析
如何分析用户的数据是一个有趣的问题,特别是当我们有大量的数据的时候。除了matlab,我们还可以用numpy+matplotlib
数据可以在这边寻找到
最后效果图
要解析的json文件位于data/-0.json,大小6.6M,显然我们可能需要用每次只读一行的策略,这足以解释为什么诸如sublime打开的时候很慢,而现在我们只需要里面的json数据中的创建时间。。
==, 这个文件代表什么?
日零时到一时,用户在github上的操作,这里的用户指的是很多。。一共有4814条数据,从commit、create到issues都有。
import json
for line in open(jsonfile):
line = f.readline()
然后再解析json
import dateutil.parser
lin = json.loads(line)
date = dateutil.parser.parse(lin[&created_at&])
这里用到了dateutil,因为新鲜出炉的数据是string需要转换为dateutil,再到数据放到数组里头。最后有就有了parse_data
def parse_data(jsonfile):
f = open(jsonfile, &r&)
dataarray = []
datacount = 0
for line in open(jsonfile):
line = f.readline()
lin = json.loads(line)
date = dateutil.parser.parse(lin[&created_at&])
datacount += 1
dataarray.append(date.minute)
minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
return minuteswithcount
下面这句代码就是将上面的解析为
minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
这样的数组以便于解析
[(0, 92), (1, 67), (2, 86), (3, 73), (4, 76), (5, 67), (6, 61), (7, 71), (8, 62), (9, 71), (10, 70), (11, 79), (12, 62), (13, 67), (14, 76), (15, 67), (16, 74), (17, 48), (18, 78), (19, 73), (20, 89), (21, 62), (22, 74), (23, 61), (24, 71), (25, 49), (26, 59), (27, 59), (28, 58), (29, 74), (30, 69), (31, 59), (32, 89), (33, 67), (34, 66), (35, 77), (36, 64), (37, 71), (38, 75), (39, 66), (40, 62), (41, 77), (42, 82), (43, 95), (44, 77), (45, 65), (46, 59), (47, 60), (48, 54), (49, 66), (50, 74), (51, 61), (52, 71), (53, 90), (54, 64), (55, 67), (56, 67), (57, 55), (58, 68), (59, 91)]
Matplotlib
开始之前需要安装``matplotlib
sudo pip install matplotlib
然后引入这个库
import matplotlib.pyplot as plt
如上面的那个结果,只需要
plt.figure(figsize=(8,4))
plt.plot(x, y,label = files)
plt.legend()
plt.show()
最后代码可见
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
import dateutil.parser
import numpy as np
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
def parse_data(jsonfile):
f = open(jsonfile, &r&)
dataarray = []
datacount = 0
for line in open(jsonfile):
line = f.readline()
lin = json.loads(line)
date = dateutil.parser.parse(lin[&created_at&])
datacount += 1
dataarray.append(date.minute)
minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
return minuteswithcount
def draw_date(files):
mwcs = parse_data(files)
for mwc in mwcs:
x.append(mwc[0])
y.append(mwc[1])
plt.figure(figsize=(8,4))
plt.plot(x, y,label = files)
plt.legend()
plt.show()
draw_date(&data/-0.json&)
继上篇之后,我们就可以分析用户的每周提交情况,以得出用户的真正的工具效率,每个程序员的工作时间可能是不一样的,如
Phodal Huang’s Report
这是我的每周情况,显然如果把星期六移到前面的话,随着工作时间的增长,在github上的使用在下降,作为一个
a fulltime hacker who works best in the evening (around 8 pm).
不过这个是osrc的分析结果。
python github 每周情况分析
看一张分析后的结果
Feb Results
结果正好与我的情况相反?似乎图上是这么说的,但是数据上是这样的情况。
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
├── -0.json
└── -0.json
我们获取是每天晚上0点时的情况,至于为什么是0点,我想这里的数据量可能会比较少。除去1月1号的情况,就是上面的结果,在只有一周的情况时,总会以为因为在国内那时是假期,但是总觉得不是很靠谱,国内的程序员虽然很多,会在github上活跃的可能没有那么多,直至列出每一周的数据时。
, 1, 1, 12897,
, 1, 1, 12940,
, 1, 12555
Python 数据分析
重写了一个新的方法用于计算提交数,直至后面才意识到其实我们可以算行数就够了,但是方法上有点hack
def get_minutes_counts_with_id(jsonfile):
datacount, dataarray = handle_json(jsonfile)
minuteswithcount = [(x, dataarray.count(x)) for x in set(dataarray)]
return minuteswithcount
def handle_json(jsonfile):
f = open(jsonfile, &r&)
dataarray = []
datacount = 0
for line in open(jsonfile):
line = f.readline()
lin = json.loads(line)
date = dateutil.parser.parse(lin[&created_at&])
datacount += 1
dataarray.append(date.minute)
return datacount, dataarray
def get_minutes_count_num(jsonfile):
datacount, dataarray = handle_json(jsonfile)
return datacount
def get_month_total():
:rtype : object
monthdaycount = []
for i in range(1, 20):
if i & 10:
filename = 'data/' + i.__str__() + '-0.json'
filename = 'data/2014-02-' + i.__str__() + '-0.json'
monthdaycount.append(get_minutes_count_num(filename))
return monthdaycount
接着我们需要去遍历每个结果,后面的后面会发现这个效率真的是太低了,为什么木有多线程?
Python Matplotlib图表
让我们的matplotlib来做这些图表的工作
if __name__ == '__main__':
results = pd.get_month_total()
print results
plt.figure(figsize=(8, 4))
plt.plot(results.__getslice__(0, 7), label=&first week&)
plt.plot(results.__getslice__(7, 14), label=&second week&)
plt.plot(results.__getslice__(14, 21), label=&third week&)
plt.legend()
plt.show()
蓝色的是第一周,绿色的是第二周,红色的是第三周就有了上面的结果。
我们还需要优化方法,以及多线程的支持。
让我们分析之前的程序,然后再想办法做出优化。网上看到一篇文章讲的就是分析这部分内容的。
存储到数据库中
我们创建了一个名为userdata.db的数据库文件,然后创建了一个表,里面有owner,language,eventtype,name url
def init_db():
conn = sqlite3.connect('userdata.db')
c = conn.cursor()
c.execute('''CREATE TABLE userinfo (owner text, language text, eventtype text, name text, url text)''')
接着我们就可以查询数据,这里从结果讲起。
def get_count(username):
userinfo = []
condition = 'select * from userinfo where owener = \'' + str(username) + '\''
for zero in c.execute(condition):
count += 1
userinfo.append(zero)
return count, userinfo
当我查询gmszone的时候,也就是我自己就会有如下的结果
(u'gmszone', u'ForkEvent', u'RESUME', u'TeX', u'/gmszone/RESUME')
(u'gmszone', u'WatchEvent', u'iot-dashboard', u'JavaScript', u'/gmszone/iot-dashboard')
(u'gmszone', u'PushEvent', u'wechat-wordpress', u'Ruby', u'/gmszone/wechat-wordpress')
(u'gmszone', u'WatchEvent', u'iot', u'JavaScript', u'/gmszone/iot')
(u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'/gmszone/iot-doc')
(u'gmszone', u'CreateEvent', u'iot-doc', u'None', u'/gmszone/iot-doc')
(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'/gmszone/iot-doc')
(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'/gmszone/iot-doc')
(u'gmszone', u'PushEvent', u'iot-doc', u'TeX', u'/gmszone/iot-doc')
一共有109个事件,有Watch,Create,Push,Fork还有其他的, 项目主要有iot,RESUME,iot-dashboard,wechat-wordpress, 接着就是语言了,Tex,Javascript,Ruby,接着就是项目的url了。
值得注意的是。
-rw-r--r--
1 fdhuang staff 905M Apr 12 14:59 userdata.db
这个数据库文件有905M,不过查询结果相当让人满意,至少相对于原来的结果来说。
Python自带了对SQLite3的支持,然而我们还需要安装SQLite3
brew install sqlite3
sudo port install sqlite3
或者是Ubuntu的
sudo apt-get install sqlite3
openSUSE自然就是
sudo zypper install sqlite3
不过,用yast2也很不错,不是么。。
需要注意的是这里是需要python2.7,起源于对gzip的上下文管理器的支持问题
def handle_gzip_file(filename):
userinfo = []
with gzip.GzipFile(filename) as f:
events = [line.decode(&utf-8&, errors=&ignore&) for line in f]
for n, line in enumerate(events):
event = json.loads(line)
actor = event[&actor&]
attrs = event.get(&actor_attributes&, {})
if actor is None or attrs.get(&type&) != &User&:
key = actor.lower()
repo = event.get(&repository&, {})
info = str(repo.get(&owner&)), str(repo.get(&language&)), str(event[&type&]), str(repo.get(&name&)), str(
repo.get(&url&))
userinfo.append(info)
return userinfo
def build_db_with_gzip():
conn = sqlite3.connect('userdata.db')
c = conn.cursor()
year = 2014
for day in range(1,31):
date_re = re.compile(r&([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz&)
fn_template = os.path.join(&march&,
&{year}-{month:02d}-{day:02d}-{n}.json.gz&)
kwargs = {&year&: year, &month&: month, &day&: day, &n&: &*&}
filenames = glob.glob(fn_template.format(**kwargs))
for filename in filenames:
c.executemany('INSERT INTO userinfo VALUES (?,?,?,?,?)', handle_gzip_file(filename))
executemany可以插入多条数据,对于我们的数据来说,一小时的文件大概有五六千个会符合我们上面的安装,也就是有actor又有type才是我们需要记录的数据,我们只需要统计用户的那些事件,而非全部的事件。
我们需要去遍历文件,然后找到合适的部分,这里只是要找到的全部事件,而光这些数据的gz文件就有1.26G,同上面那些解压为json文件显得不合适,只能用遍历来处理。
这里参考了osrc项目中的写法,或者说直接复制过来。
首先是正规匹配
date_re = re.compile(r&([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]+)\.json.gz&)
不过主要的还是在于glob.glob
glob是python自己带的一个文件操作相关模块,用它可以查找符合自己目的的文件,就类似于Windows下的文件搜索,支持通配符操作。
这里也就用上了gzip.GzipFile又一个不错的东西。
最后代码可以见
更好的方案?
查询用户事件总数
import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
pipe = pipe = r.pipeline()
pipe.zscore('osrc:user',&gmszone&)
pipe.execute()
系统返回了227.0,试试别人。
&&& pipe.zscore('osrc:user',&dfm&)
&redis.client.StrictPipeline object at 0x104fa7f50&
&&& pipe.execute()
看看主要是在哪一天提交的
&&& pipe.hgetall('osrc:user:gmszone:day')
&redis.client.StrictPipeline object at 0x104fa7f50&
&&& pipe.execute()
[{'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}]
结果大致如下图所示:
看看主要的事件是?
&&& pipe.zrevrange(&osrc:user:gmszone:event&.format(&gmszone&), 0, -1,withscores=True)
&redis.client.StrictPipeline object at 0x104fa7f50&
&&& pipe.execute()
[[('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)]]
Main Event
蓝色的就是push事件,黄色的是create等等。
到这里我们算是知道了OSRC的数据库部分是如何工作的。
Redis 查询
主要代码如下所示
def get_vector(user, pipe=None):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
no_pipe = False
if pipe is None:
pipe = pipe = r.pipeline()
no_pipe = True
user = user.lower()
pipe.zscore(get_format(&user&), user)
pipe.hgetall(get_format(&user:{0}:day&.format(user)))
pipe.zrevrange(get_format(&user:{0}:event&.format(user)), 0, -1,
withscores=True)
pipe.zcard(get_format(&user:{0}:contribution&.format(user)))
pipe.zcard(get_format(&user:{0}:connection&.format(user)))
pipe.zcard(get_format(&user:{0}:repo&.format(user)))
pipe.zcard(get_format(&user:{0}:lang&.format(user)))
pipe.zrevrange(get_format(&user:{0}:lang&.format(user)), 0, -1,
withscores=True)
if no_pipe:
return pipe.execute()
结果在上一篇中显示出来了,也就是
[227.0, {'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}, [('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)], 0, 0, 0, 11, [('CSS', 74.0), ('JavaScript', 60.0), ('Ruby', 12.0), ('TeX', 6.0), ('Python', 6.0), ('Java', 5.0), ('C++', 5.0), ('Assembly', 5.0), ('C', 3.0), ('Emacs Lisp', 2.0), ('Arduino', 2.0)]]
有意思的是在这里生成了和自己相近的人
['alesdokshanin', 'hjiawei', 'andrewreedy', 'christj6', '1995eaton']
osrc最有意思的一部分莫过于flann,当然说的也是系统后台的设计的一个很关键及有意思的部分。
邻近算法与相似用户
邻近算法是在这个分析过程中一个很有意思的东西。
邻近算法,或者说K最近邻(kNN,k-NearestNeighbor)分类算法可以说是整个数据挖掘分类技术中最简单的方法了。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用她最接近的k个邻居来代表。
换句话说,我们需要一些样本来当作我们的分析资料,这里东西用到的就是我们之前的。
[227.0, {'1': '51', '0': '41', '3': '17', '2': '34', '5': '28', '4': '22', '6': '34'}, [('PushEvent', 154.0), ('CreateEvent', 41.0), ('WatchEvent', 18.0), ('GollumEvent', 8.0), ('MemberEvent', 3.0), ('ForkEvent', 2.0), ('ReleaseEvent', 1.0)], 0, 0, 0, 11, [('CSS', 74.0), ('JavaScript', 60.0), ('Ruby', 12.0), ('TeX', 6.0), ('Python', 6.0), ('Java', 5.0), ('C++', 5.0), ('Assembly', 5.0), ('C', 3.0), ('Emacs Lisp', 2.0), ('Arduino', 2.0)]]
在代码中是构建了一个points.h5的文件来分析每个用户的points,之后再记录到hdf5文件中。
[ 0...2246696
0...0969163
这里分析到用户的大部分行为,再找到与其行为相近的用户,主要的行为有下面这些:
每星期的情况
事件的类型
贡献的数量,连接以及语言
最多的语言
osrc中用于解析的代码
def parse_vector(results):
points = np.zeros(nvector)
total = int(results[0])
points[0] = 1.0 / (total + 1)
# Week means.
for k, v in results[1].iteritems():
points[1 + int(k)] = float(v) / total
# Event types.
for k, v in results[2]:
points[n + evttypes.index(k)] = float(v) / total
# Number of contributions, connections and languages.
n += nevts
points[n] = 1.0 / (float(results[3]) + 1)
points[n + 1] = 1.0 / (float(results[4]) + 1)
points[n + 2] = 1.0 / (float(results[5]) + 1)
points[n + 3] = 1.0 / (float(results[6]) + 1)
# Top languages.
for k, v in results[7]:
if k in langs:
points[n + langs.index(k)] = float(v) / total
# Unknown language.
points[-1] = float(v) / total
return points
这样也就返回我们需要的点数,然后我们可以用get_points来获取这些
def get_points(usernames):
r = redis.StrictRedis(host='localhost', port=6379, db=0)
pipe = r.pipeline()
results = get_vector(usernames)
points = np.zeros([len(usernames), nvector])
points = parse_vector(results)
return points
就会得到我们的相应的数据,接着找找和自己邻近的,看看结果。
真看不出来两者有什么相似的地方 。。。。
如何在GitHub“寻找灵感(fork)”
重造轮子是重新创造一个已有的或是已被其他人优化的基本方法。
最近萌发了一个想法写游戏引擎,之前想着做一个JavaScript前端框架。看看,这个思路是怎么来的。
Lettuce构建过程
Lettuce是一个简约的移动开发框架。
故事的出发点是这样的:写了很多代码,用的都是框架,最后不知道收获什么了?事实也是如此,当自己做了一些项目之后,发现最后什么也没有收获到。于是,就想着做一个框架。
有这样的几个前提
为什么我只需要jQuery里的选择器、Ajax要引入那么重的库呢?
为什么我只需要一个Template,却想着用Mustache
为什么我需要一个Rout

我要回帖

更多关于 编写程序 求1 3 5 7 的文章

 

随机推荐