-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
419 lines (340 loc) · 47.4 KB
/
atom.xml
File metadata and controls
419 lines (340 loc) · 47.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Gavin's Blog]]></title>
<subtitle><![CDATA[Programming (cdr (life))]]></subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://blog.ligan.me/"/>
<updated>2015-02-10T14:11:43.649Z</updated>
<id>http://blog.ligan.me/</id>
<author>
<name><![CDATA[Gavin]]></name>
<email><![CDATA[gavin.li1986@gmail.com]]></email>
</author>
<generator uri="http://zespia.tw/hexo/">Hexo</generator>
<entry>
<title><![CDATA[极客公园招人啦]]></title>
<link href="http://blog.ligan.me/2015/02/10/join-us/"/>
<id>http://blog.ligan.me/2015/02/10/join-us/</id>
<published>2015-02-10T12:04:06.000Z</published>
<updated>2015-02-10T14:10:06.000Z</updated>
<content type="html"><![CDATA[<h2 id="极客公园是什么?">极客公园是什么?</h2>
<p><a href="http://www.geekpark.net/" target="_blank" rel="external">极客公园</a>是创新者的大本营, 是具有丰富线下活动, 强大内容能力的创业创新机构.</p>
<p>我们由一群年轻的极客组成, 开放, 平等, 自由, 热爱互联网, 喜欢折腾各种新鲜的事物, 理想主义, 希望用产品思维和创新精神改变世界.</p>
<h2 id="技术团队">技术团队</h2>
<p>我们的工程师团队崇尚工程师文化, 热爱编程, 追求效率, 喜欢分享, 鼓励开源, 对任何新鲜有意思的技术都不拒绝.</p>
<p>我们的工程师团队是公园的资源部门, 主要负责网站的开发维护, app 的开发, 及各种效率工具. 如果你喜欢做产品, 今年还有一个神秘项目极客实验室可供你大展拳脚.</p>
<p>团队目前还很小, 但是会保持工程师的骄傲, 只有真正喜欢技术的人才是我们的伙伴.</p>
<h2 id="我们能提供的">我们能提供的</h2>
<ul>
<li>不需要坐班,对上班时间没有限制, 只要你能完成工作. 完全的 remote 暂时还没能支持, 但是后续会努力争取.</li>
<li>herman miller的椅子,员工大保健? 我们都没有…但我们的工作环境还算舒适, 逼格勉强还有, 无论你喜欢坐着躺着还是站着都能满足你.</li>
<li>薪资是我最讨厌谈的, 但是出于对你的尊重还是要说出来.动辄30多k的高薪我们虽然很难到, 但是10几k还是能达到的, 具体还需要根据你的情况来定(如果真的够优秀我可以用2个headcount去和老板谈). 另外我们有补充医疗保险, 公积金也是按照工资全额的12%来上.</li>
<li>各种新鲜的评测产品可以经常玩到.</li>
<li>员工食堂, 零食饮料, 雾霾天的口罩, 闲下来还可以弹弹钢琴, 玩玩游戏(x1, ps4两大主机). 科学上网的 vpn 这些小福利也是必不可少的.</li>
<li>定期的outing, 今年刚刚从三亚回来, 下次应该会去更好的地方.</li>
<li>各种聪(dou)明(bi)又靠谱的同事(处女座挺多的你不要害怕).</li>
</ul>
<h2 id="我们希望你">我们希望你</h2>
<p>我们不要求工作年限, 不要求学历, 对经验也没有要求, 但是有一点不能缺少.</p>
<p><strong>工程师气质</strong></p>
<ul>
<li>知道问题从哪里可以找到答案, 而不是问一些 RTFM 的问题.</li>
<li>靠键盘解决问题, 而不是打嘴炮.</li>
<li>有产品思维但是不对别人的工作妄加评判.</li>
<li>善于学习, 乐于分享.</li>
</ul>
<h2 id="职位需求">职位需求</h2>
<ul>
<li><strong>前端工程师:</strong> jquery, bootstrap, angularjs, yeoman, gulp/grunt, nodejs, scss, slim</li>
<li><strong>后端工程师:</strong> ruby, rails, memcached, redis, pg</li>
<li><strong>iOS工程师</strong></li>
<li><strong>web设计师</strong></li>
<li><strong>产品经理助理</strong></li>
</ul>
<p>以上职位有一项或多项符合者.</p>
<h2 id="联系方式">联系方式</h2>
<p>简历投送至: <a href="mailto:hr@geekpark.net">hr@geekpark.net</a></p>
<p>技术岗位在这里填写<a href="http://www.mikecrm.com/f.php?t=qffauG" target="_blank" rel="external">表单</a></p>
<p>QQ:25192109<br>微信: gavin1986</p>
<p>公司地址: <a href="http://goo.gl/maps/7H5lD" target="_blank" rel="external">北京市朝阳区酒仙桥路4号751 DPark 正东集团院内 C8座105室 </a></p>
<h2 id="无图无真相">无图无真相</h2>
<p><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job0.jpg" alt="办公环境1"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job1.jpg" alt="办公环境2"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job2.jpg" alt="不限时的 xbox , ps4 和钢琴"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job3.jpg" alt="来到公园后走上人生的巅峰"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job4.jpg" alt="随时随地的交流机会(左为 google doodle 负责人 ryan)"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job5.jpg" alt="各种奇怪的小伙伴"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job6.JPG" alt="欢迎加入极客公园"><br><img src="http://7jppss.com2.z0.glb.qiniucdn.com/job7.jpg?imageView2/2/w/500" alt="瞅啥瞅,快投简历"></p>
]]></content>
<summary type="html">
<![CDATA[极客公园招人啦. 工程师/产品经理/web 设计师]]>
</summary>
<category term="招聘" scheme="http://blog.ligan.me/tags/%E6%8B%9B%E8%81%98/"/>
<category term="闲聊" scheme="http://blog.ligan.me/categories/%E9%97%B2%E8%81%8A/"/>
</entry>
<entry>
<title><![CDATA[一个简单的评论过滤系统的实现]]></title>
<link href="http://blog.ligan.me/2015/01/13/a-simple-comments-spam-system/"/>
<id>http://blog.ligan.me/2015/01/13/a-simple-comments-spam-system/</id>
<published>2015-01-13T15:28:32.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<p>最近公园网站开启匿名评论之后, 垃圾评论就一直是一个让人头疼的问题. 关键词过滤及限制评论数很容易被找到针对的办法, 发垃圾评论的人会不停的通过空格, 符号等来绕过关键词的过滤. 对于一个每天评论不过百的网站来说, 上 <a href="http://en.wikipedia.org/wiki/Natural_language_processing" target="_blank" rel="external">NLP</a> 又太重了(其实是不懂).</p>
<h2 id="通过语句相似度过滤垃圾评论">通过语句相似度过滤垃圾评论</h2>
<p>后来我想到公园网站的垃圾评论大部分内容都是几个垃圾评论的变种, 内容的相似度都比较高. 所以我只需要维护一个包含这几个内容的列表, 并计算语句的相似度, 屏蔽掉相似度较高的评论就可以暂时达到过滤垃圾评论的目的.</p>
<p>研究如何计算过程中我找到了2种方式, <a href="http://en.wikipedia.org/wiki/Levenshtein_distance" target="_blank" rel="external">Levenshtein distance</a> 以及 <a href="http://www.catalysoft.com/articles/StrikeAMatch.html" target="_blank" rel="external">How to Strike a Match</a> . 最后我使用了后者计算相似度的方式实现了一个简单的评论过滤系统.</p>
<h2 id="代码实现">代码实现</h2>
<p><a href="https://github.com/threedaymonk/text" target="_blank" rel="external">text</a>这个gem 同时提供了以上两种算法. 有兴趣的可以看一下源码中两种算法的具体实现.</p>
<p>Comment 中验证评论是否合法</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Comment</span> <span class="inheritance">< <span class="parent">ActiveRecord::Base</span></span></span></div><div class="line"> ...</div><div class="line"> validate <span class="symbol">:check_spam</span></div><div class="line"> ...</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">def</span> </span>check_spam</div><div class="line"> <span class="comment">#遍历检查 spam 中条目与评论的相似度, 如果大于设定比例则添加 error, 验证失败</span></div><div class="line"> <span class="constant">Spam</span>.all.each <span class="keyword">do</span> |spam|</div><div class="line"> <span class="keyword">if</span> <span class="constant">Text::WhiteSimilarity</span>.new.similarity(body, spam.body) > (spam.similarity || <span class="number">0</span>.<span class="number">9</span>)</div><div class="line"> errors.add(<span class="symbol">:body</span>, <span class="string">"内容不合法"</span>)</div><div class="line"> <span class="keyword">return</span></div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="comment"># 对于 spam 中没有收录的模板或漏网之鱼检查评论与最近的3条评论的相似度</span></div><div class="line"> <span class="comment"># 如果相似度大于95%则过滤, 一般垃圾评论都是连续刷评论的.</span></div><div class="line"> new_record? <span class="keyword">and</span> <span class="constant">Comment</span>.last(<span class="number">3</span>).each <span class="keyword">do</span> |comment|</div><div class="line"> <span class="keyword">if</span> <span class="constant">Text::WhiteSimilarity</span>.new.similarity(body, comment.body) > <span class="number">0</span>.<span class="number">95</span></div><div class="line"> errors.add(<span class="symbol">:body</span>, <span class="string">"内容不合法"</span>)</div><div class="line"> <span class="keyword">return</span></div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="keyword">end</span> </div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<p>加上这层防护之后, 应该可以暂时安心了.</p>
]]></content>
<summary type="html">
<![CDATA[网站开启匿名评论之后, 垃圾评论就一直是一个让人头疼的问题. 关键词过滤及限制评论数很容易被找到针对的办法, 上 NLP 又太重. 最后使用了语句的相似度检查来完成一个简单的评论过滤系统.]]>
</summary>
<category term="rails" scheme="http://blog.ligan.me/tags/rails/"/>
<category term="ruby" scheme="http://blog.ligan.me/tags/ruby/"/>
<category term="垃圾评论" scheme="http://blog.ligan.me/tags/%E5%9E%83%E5%9C%BE%E8%AF%84%E8%AE%BA/"/>
<category term="Rails" scheme="http://blog.ligan.me/categories/Rails/"/>
</entry>
<entry>
<title><![CDATA[CSRF and protect_from_forgery in Rails 4]]></title>
<link href="http://blog.ligan.me/2014/12/08/CSRF-and-protect-from-forgery-in-rails-4/"/>
<id>http://blog.ligan.me/2014/12/08/CSRF-and-protect-from-forgery-in-rails-4/</id>
<published>2014-12-07T16:20:31.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<p>最近的一个项目<a href="http://innoawards.geekpark.net/" target="_blank" rel="external">2014中国互联网创新评选</a>频繁出现恼人的 InvalidAuthenticityToken 错误, 引发了我对 Rails 中 protect_from_forgery 的思考, 本文介绍 CSRF 攻击以及在 Rails 中的保护方式, 什么情况下应该使用那种策略来防CSRF.</p>
<h2 id="CSRF">CSRF</h2>
<p>CSRF(Cross-site request forgery) 跨站请求伪造, 是一种比较常见的入侵方式. 顾名思义, 就是伪造成某个受信任的用户去向网站发送请求, 来达到某些目的. 比如伪造成你向网站提交一个修改密码的表单, 将你的密码改为攻击者设定的某个密码, 这样在不知不觉中你的密码就被修改了, 而假设攻击者同时也知道你的用户名, 这样攻击者盗取了你的帐号. </p>
<p>CSRF 一般是怎么发生的呢? </p>
<p>一般我们登录网站后, 都会和网站建立起一个一对一的 session 或 cookie 联系. 你接下来所发出的请求, 会带有你特有的一个身份信息, 服务器会识别出哪个用户执行的操作, 这样接下来你进行其它的操作时, 比如发帖, 转账等就不需要再次进行登录. 而 CSRF 所做的, 就是<strong>通过你的浏览器</strong>, 利用你和服务器间这特有的识别信息, 来通过你向服务器发出某个请求. 这样服务器认为这个操作是由你发出的, 从而”遵循你的意志”, 执行接到的命令.</p>
<p>比如在 A 网站有一个表单, 你已经登录了 A 网站, 我在我的 B 网站写了一段 js 代码, 内容就是按照 A 网站的表单的格式向 A 网站提交一份表单, 表单的 value 是我自己定义的. 然后我只需要引导你访问 B 网站, 在你访问 B 网站期间, 这段 js 代码被你的浏览器所执行, 然后向 A 网站提交了表单, 这样我就成功利用了你的身份你的浏览器向 A 网站发出了请求. 而由于请求是由你的浏览器发出,可以利用你本地的 cookie 和 session 信息, 使其看起来就好像你发出的, 所以 A 网站的服务器也认为这个请求是来自你的合法请求. 这样不知不觉间, 你就执行了一个并非你本意的操作. 而一次 CSRF 攻击就成功了.</p>
<p>这种攻击原理简单, 实现也不难, 但是绝大多数 web 框架都已经对这种攻击有所防范, 但遗憾的是互联网上依然有很多网站对这种攻击毫无防范.</p>
<h2 id="Rails_中对_CSRF_的防范">Rails 中对 CSRF 的防范</h2>
<p>Rails 中会在 html 页面生成时给每个表单添加一个隐藏的 authenticate_token 属性, 这个 token 在你每次登录网站时生成, 过期时间于 session 相同. 在你每次提交一个表单时都要带着这个 token, 并在 controller 中, 首先通过 <code>protect_from_forgery</code> 这个方法对 token 进行验证, 如果 token 验证失败的话, 你所执行的 post 操作就会失败. </p>
<p>这样攻击者因为无法拿到你的 token, 所以就无法伪造整个表单. 前面所说的攻击方式中所提交的表单因为 token 错误, 而无法继续执行, 这样就达到了防范 CSRF 攻击的目的. 我们一般在 rails 的 layout 中会看到这样的代码.</p>
<figure class="highlight slim"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">doctype html</div><div class="line">html lang=session[:locale]</div><div class="line"> head</div><div class="line"> title</div><div class="line"> = (content_for?(:title) ? <span class="string">"#{yield(:title)} | "</span> : <span class="string">''</span>) + t(:sitename)</div><div class="line"> <span class="keyword">...</span></div><div class="line"> = csrf_meta_tags</div><div class="line"> <span class="keyword">...</span></div><div class="line"> body class=(controller_name + <span class="string">' '</span> + controller_name + <span class="string">'_'</span> + action_name)</div><div class="line"> = render <span class="string">'share/header'</span></div><div class="line"> = yield</div><div class="line"> = render <span class="string">'share/footer'</span></div><div class="line"> = javascript_include_tag <span class="string">'application'</span></div></pre></td></tr></table></figure>
<p>其中<code>csrf_meta_tags</code>所做的就是生成这个 token</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="title">meta</span> <span class="attribute">content</span>=<span class="value">"authenticity_token"</span> <span class="attribute">name</span>=<span class="value">"csrf-param"</span>></span></div><div class="line"><span class="tag"><<span class="title">meta</span> <span class="attribute">content</span>=<span class="value">"c2tB7nB1S0s7nQD2++sjv3pT/bKtb+4GIrEfcnn/tWw="</span> <span class="attribute">name</span>=<span class="value">"csrf-token"</span>></span></div></pre></td></tr></table></figure>
<p>rails 会默认在表单中使用这个 token参数和 form 一起传过来.</p>
<h2 id="protect_from_forgery"><code>protect_from_forgery</code></h2>
<p>token 传过来之后, rails 的 controller 会对这个 token 的合法性进行验证</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ApplicationController</span> <span class="inheritance">< <span class="parent">ActionController::Base</span></span></span></div><div class="line"> <span class="comment"># Prevent CSRF attacks by raising an exception.</span></div><div class="line"> <span class="comment"># For APIs, you may want to use :null_session instead.</span></div><div class="line"> protect_from_forgery</div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<p>在 controller 中, <code>protect_from_forgery</code> 所做的就是验证这个 token 是否正确, 可以在后面加入<code>:with</code>参数指定处理方式, 如<code>protect_from_forgery with: :null_session</code>, rails 提供了三种处理方式</p>
<h3 id=":exception"><code>:exception</code></h3>
<p>这个是 rails4 的默认处理方式, 在 token 验证失败时抛出一个 InvalidAuthenticityToken 异常, 如果没有捕获的话进程终止, 客户端会收到服务器返回的 500错误. 这样所提交的伪造请求就无法继续执行到具体业务步骤, 达到了防范的目的.</p>
<h3 id=":null_session"><code>:null_session</code></h3>
<p>这曾是 rails3 的默认处理方式, token 验证失败时并不抛出异常, 而是使用一个空的 session 来执行下面的业务处理. 因为我们一般会把大部分的用户识别信息都保存在 session 中, 而在业务执行时也会在 session 中查找相关的用户信息. 所以当使用一个空 session 来提交表单时候, 应用因为从 session 中无法找到用户的相关信息, 从而无法伪造成该用户来进行操作.</p>
<h3 id=":reset_session"><code>:reset_session</code></h3>
<p>完全的重置这个 session. 这个不怎么用到, 虽然也可以达到防范的目的, 但是用户的 session 会被完全清空, 会需要重新进行登录, 一般使用 :null_session 就够了.</p>
<h3 id="另外还有一个skip_before_action_:verify_authenticity_token">另外还有一个<code>skip_before_action :verify_authenticity_token</code></h3>
<p>以上的3种策略之外, 还有一种情况, 就是完全跳过对 csrf_token 的验证. 也就是放弃对 csrf 的保护. 在你完全知道自己在做什么的情况下, 慎用!</p>
<h2 id="什么情况下应该选用那种策略来应对">什么情况下应该选用那种策略来应对</h2>
<p>知道了有以上的几种策略, 就需要考虑什么时候适合用什么策略来应对. 其实这才是写本文的初衷, 前面废话比较多, 其实对 rails 和 csrf 有所了解的人直接跳到这里看就够了, 2333…</p>
<h3 id="with:_:exception"><code>with: :exception</code></h3>
<p>如开始所说的, 在这个项目中 (<a href="http://innoawards.geekpark.net/vote" target="_blank" rel="external">2014中国互联网创新评选</a>), 一开始被<code>InvalidAuthenticityToken</code> 频繁骚扰让我很痛苦, 差点想把 csrf 改成 <code>:null_session</code> 方式. 但是在这个项目的评选中, 观众是完全匿名的, 我们只针对投票的 IP 地址的投票频率进行了限制, 如果使用<code>:null_session</code>的方式, 即使空的 session 也依然是可以投票的. 这样 csrf 的防范作用就没有了. </p>
<p>厂商在拉票时一般都会推送一篇文章给粉丝们号召他们去投票, 如果没有做验证的话, 厂商只需要在推送的文章甚至在自己的主页中加入一段提交这个投票表单的 js 代码既可在浏览这个网页的用户不知情的情况下进行投票. 因为票会从用户的浏览器投出, 也就做到了 ‘分布式投票’ 那么针对 IP 地址的限制就几乎没有什么作用了. 也就无法杜绝刷票现象.</p>
<p>所以最终这个项目我没有更改 csrf 的防护方式, 而是在代码中对<code>InvalidAuthenticityToken</code> 进行了处理</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">rescue_from <span class="constant">ActionController::InvalidAuthenticityToken</span> <span class="keyword">do</span> |exception|</div><div class="line"> render_422(exception)</div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<h3 id="with:_:null_session"><code>with: :null_session</code></h3>
<p>在做上一个项目的同时, 手中还有另一个项目(<a href="http://gif.geekpark.net/cn" target="_blank" rel="external">2015极客公园创新大会</a>)<br>在这个项目中, 只有一个下订单购票的表单, 用户同样是不需要登录就能提交的. 正常的用户一般不会直接进行购票, 而是浏览页面, 查看大会的特色, 嘉宾等等. 当他们觉得足够有吸引力, 终于下定决心要购票的时候, 页面生成的 session 以及 token 可能已经过期了. 这个使用如果依然使用<code>exception</code>的方式进行处理就会提示错误, 需要用户重新刷新页面后再次提交订单. 对用户下订单的流畅度造成了影响, 体验下降. 而且以买票来说, 未付款的订单是无效的, 不像投票一样会影响计票准确性.</p>
<p>所以最终在这个项目中我使用了<code>:null_session</code>的方式.</p>
<p>另外, 在 rails 中的一些 api 接口, 由于验证是通过其它方式进行的, rails 推荐的也是使用<code>:null_session</code>这种方式.</p>
<h3 id=":reset_session-1"><code>:reset_session</code></h3>
<p>这个用的比较少, 使用情况参照<code>:null_session</code></p>
]]></content>
<summary type="html">
<![CDATA[最近的一个项目频繁出现恼人的 InvalidAuthenticityToken 错误, 引发了我对 Rails 中 protect_from_forgery 的思考, 本文介绍 CSRF 攻击以及在 Rails 中的保护方式, 什么情况下应该使用那种策略来防CSRF.]]>
</summary>
<category term="rails" scheme="http://blog.ligan.me/tags/rails/"/>
<category term="ruby" scheme="http://blog.ligan.me/tags/ruby/"/>
<category term="Rails" scheme="http://blog.ligan.me/categories/Rails/"/>
</entry>
<entry>
<title><![CDATA[使用oh-my-zsh内置插件省略bundle exec]]></title>
<link href="http://blog.ligan.me/2014/03/02/shi-yong-oh-my-zshnei-zhi-cha-jian-sheng-lue-bundle-exec/"/>
<id>http://blog.ligan.me/2014/03/02/shi-yong-oh-my-zshnei-zhi-cha-jian-sheng-lue-bundle-exec/</id>
<published>2014-03-02T04:09:23.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<h2 id="bundle_exec">bundle exec</h2>
<p>在使用bundler管理的项目中,为了统一使用环境,经常要使用<code>bundle exec</code>来执行项目中的各种命令.</p>
<p>比如在某些项目中使用的是rake 0.9而系统中安装有rake 10时候如果不加上<code>bundle exec</code>就会提示错误.这时候就需要执行</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">bundle <span class="keyword">exec</span> rake <span class="keyword">my</span>:task</div></pre></td></tr></table></figure>
<p>以前使用rvm时可以通过每个项目不同的gemset来统一项目中不同版本的gem.换了rbenv之后完全依靠bunder来管理gem之后每次执行<code>bundle exec</code>就有点麻烦了.</p>
<p>有一些工具可以代替<code>bundle exec</code>,比如<a href="https://github.com/gma/bundler-exec" target="_blank" rel="external">bundler-exec</a></p>
<p>但是如果正好使用oh-my-zsh的话,则有一个更简单的方法</p>
<h2 id="oh-my-zsh">oh-my-zsh</h2>
<p>oh-my-zsh现在已经几乎成为了标配,这里不做介绍,如果你没有使用它,可以到这里查看<a href="https://github.com/robbyrussell/oh-my-zsh" target="_blank" rel="external">oh-my-zsh</a></p>
<p>只需要在<code>.zshrc</code>中的找到这一行</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="attribute">plugins</span>=<span class="string">(git bundler)</span></div></pre></td></tr></table></figure>
<p>plugins中加入bundler,绝大部分情况下就不需要再手动敲上<code>bundle exec</code>了.</p>
<p><a href="https://github.com/robbyrussell/oh-my-zsh/blob/master/plugins/bundler/bundler.plugin.zsh#L9" target="_blank" rel="external">源码看这里</a></p>
]]></content>
<summary type="html">
<![CDATA[在oh-my-zsh中省略输入烦人的 bundle exec.]]>
</summary>
<category term="ruby" scheme="http://blog.ligan.me/tags/ruby/"/>
<category term="bundler" scheme="http://blog.ligan.me/tags/bundler/"/>
<category term="Ruby" scheme="http://blog.ligan.me/categories/Ruby/"/>
</entry>
<entry>
<title><![CDATA[rspec中使用feature spec进行用户/验收测试]]></title>
<link href="http://blog.ligan.me/2013/09/08/rspec-feature-ce-shi-jian-jie/"/>
<id>http://blog.ligan.me/2013/09/08/rspec-feature-ce-shi-jian-jie/</id>
<published>2013-09-08T07:55:00.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<h2 id="feature_spec与request_spec的区别">feature spec与request spec的区别</h2>
<p>rspec和capybara在ruby程序员中很多人都不陌生了.在2.0版本以后的capybara中,新加入了feature spec的写法.在rspec中默认使用spec/feature,而不再使用spec/request.</p>
<p>feature spec在测试中来说是比较高等级的测试.大概相当于集成测试或者更高级别的用户/验收测试.一般是模拟用户的实际操作和应用外部的请求来执行测试.简单来说就是模拟浏览器中的动作,期待应用正确的反应.</p>
<p>为什么2.0之后的capybara要做出这样的改动呢?</p>
<p>因为一般的来说capybara是模拟浏览器中的动作,这种测试是相当于模拟一个用户在用户界面中的操作,是基于usercase的.而以前的request spec则还是没有脱离应用层面(发送http请求,比较相应结果),比如在以前的capybara的dsl中,使用’get’来访问页面,而现在则改成了’visit’.后者’访问’这个动作,比’get’这个http请求更接近于用户的行为.</p>
<p>所以使用feature spec相对来说更符合behavior的思想,也带来更好的阅读性.</p>
<h2 id="rails中使用feature_spec">rails中使用feature spec</h2>
<p>本文以rails为例,来介绍feature spec的使用.</p>
<p>首先安装rspec和capybara,在gemfile中加入</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">group <span class="symbol">:development</span>, <span class="symbol">:test</span> <span class="keyword">do</span></div><div class="line"> gem <span class="string">'rspec-rails'</span></div><div class="line"><span class="keyword">end</span></div><div class="line"></div><div class="line">group <span class="symbol">:test</span> <span class="keyword">do</span></div><div class="line"> gem <span class="string">'capybara'</span></div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<p>之后执行</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">bundle <span class="keyword">install</span></div></pre></td></tr></table></figure>
<p>再之后执行</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag">rails</span> <span class="tag">generate</span> <span class="tag">rspec</span><span class="pseudo">:install</span></div></pre></td></tr></table></figure>
<p>rspec和capybara就安装完毕了.</p>
<p>rails中的generator默认生成的集成测试会在request/xxxx_spec.rb.默认是不支持capybara的,我们如果要在其中使用capybara还要在spec_helper中的configure中加入<code>config.include Capybara::DSL</code></p>
<p>而我们要使用的feature/xxxx_spec.rb目前尚无法由rails自动生成,但是不用做任何设置就可以支持capybara的DSL.</p>
<p>所以我们手动建立spec/feature文件夹,在其中手动创建xxx_spec.rb的测试文件.</p>
<h2 id="feature_spec写法">feature spec写法</h2>
<p>在feature spec中,我们使用<code>feature-scenario</code> 来替代之前rspec的<code>describe - it</code>模式<br>注意两者千万不要混写成<code>feature-it</code>或者<code>describe-scenario</code>这种不伦不类的风格..</p>
<p>另外与describe不同,feature下面是不允许再套feature的.</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">describe <span class="string">'Static pages'</span> <span class="keyword">do</span></div><div class="line"> describe <span class="string">'Home page'</span> <span class="keyword">do</span></div><div class="line"> it <span class="string">"should have the content 'Sample App'"</span> <span class="keyword">do</span></div><div class="line"> visit <span class="string">'/static_pages/home'</span></div><div class="line"> expect(page).to have_content(<span class="string">'Sample App'</span>)</div><div class="line"> <span class="keyword">end</span></div><div class="line"> <span class="keyword">end</span></div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<p>这样是可以的,但是把describe换成feature则会报错.</p>
<p>feature spec的测试中,我们一般按照待测的功能点来划分用例,即每一个feature.而每一个scenario对应一条user case.这一点和测试人员写测试用例的方法很相似.比如下面的2条测试用例</p>
<table>
<thead>
<tr>
<th>功能点</th>
<th>用户输入</th>
<th style="text-align:center">期望结果</th>
</tr>
</thead>
<tbody>
<tr>
<td>用户登录</td>
<td>用户输入正确的用户名和密码</td>
<td style="text-align:center">登录成功</td>
</tr>
<tr>
<td></td>
<td>用户输入错误的用户名和密码</td>
<td style="text-align:center">登录失败</td>
</tr>
</tbody>
</table>
<p>使用feature spec的方式写出的测试代码如下.</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">require</span> <span class="string">"spec_helper"</span></div><div class="line"></div><div class="line">feature <span class="string">'Login'</span> <span class="keyword">do</span></div><div class="line"> scenario <span class="string">'User enter right account'</span> <span class="keyword">do</span></div><div class="line"> visit <span class="string">'/login'</span></div><div class="line"> fill_in <span class="string">'Name'</span>, <span class="symbol">:with</span> => <span class="string">'username'</span></div><div class="line"> fill_in <span class="string">'Password'</span>, <span class="symbol">:with</span> => <span class="string">'right'</span></div><div class="line"> click_button <span class="string">'login'</span></div><div class="line"></div><div class="line"> expect(page).to have_text(<span class="string">'welcome back,username'</span>)</div><div class="line"> <span class="keyword">end</span></div><div class="line"></div><div class="line"> scenario <span class="string">'User enter wrong account'</span> <span class="keyword">do</span></div><div class="line"> visit <span class="string">'/login'</span></div><div class="line"> fill_in <span class="string">'Name'</span>, <span class="symbol">with:</span> <span class="string">'username'</span></div><div class="line"> fill_in <span class="string">'Password'</span>, <span class="symbol">with:</span> <span class="string">'wrong'</span></div><div class="line"> click_button <span class="string">'login'</span></div><div class="line"></div><div class="line"> expect(page).to have_text(<span class="string">'Wrong password,please try again!'</span>)</div><div class="line"> <span class="keyword">end</span></div><div class="line"><span class="keyword">end</span></div></pre></td></tr></table></figure>
<p>feature spec的简单介绍就写到这里,希望大家喜欢这种方式.</p>
]]></content>
<summary type="html">
<![CDATA[rspec和capybara在ruby程序员中很多人都不陌生了.在2.0版本以后的capybara中,新加入了feature spec的写法.在rspec中默认使用spec/feature,而不再使用spec/request.]]>
</summary>
<category term="测试" scheme="http://blog.ligan.me/tags/%E6%B5%8B%E8%AF%95/"/>
<category term="capybara" scheme="http://blog.ligan.me/tags/capybara/"/>
<category term="rspec" scheme="http://blog.ligan.me/tags/rspec/"/>
<category term="ruby" scheme="http://blog.ligan.me/tags/ruby/"/>
<category term="Ruby" scheme="http://blog.ligan.me/categories/Ruby/"/>
</entry>
<entry>
<title><![CDATA[使用rvm更好的管理你的gem]]></title>
<link href="http://blog.ligan.me/2013/09/05/using-rvm-manage-your-gems/"/>
<id>http://blog.ligan.me/2013/09/05/using-rvm-manage-your-gems/</id>
<published>2013-09-04T17:42:00.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<h2 id="rvm一个重要的功能_gemset">rvm一个重要的功能 gemset</h2>
<p>rvm作为一种方便的ruby安装和管理方式已经被大家所习惯和接受.但是rvm中另外一个重要的功能gemset使用率却不高.本文简单介绍一下gemset的正确使用姿势.</p>
<p><strong>gemset本身有很大争议,很多人认为gem的管理交给bundler去做就足够好了,因此使用rbenv.</strong>而我认为gemset相对来说还是比较方便.通过对gem的物理隔离,从而在批量操作gem时候更加放心.比如我曾经手贱使用bundle update升级了所有的gem,之后在给别人演示rails的时候突然惊讶的发现rails版本升到了4.0而吓出一身冷汗 (还没有接触过4.0,还好那次没有出什么丑 ).</p>
<h2 id="gemset基本使用">gemset基本使用</h2>
<p>先从gemset基本使用开始讲起<br><br>我们平时使用rvm不指定gemset时,默认使用的是当前ruby版本下的default gemset,如<code>2.0@default</code>.如果想创建自己的gemset则使用</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">╭─<span class="constant">Gavin</span><span class="variable">@ligan</span> ~ ‹ruby-<span class="number">2.0</span>.<span class="number">0</span>›</div><div class="line">╰─<span class="variable">$ </span>rvm gemset create rails4</div><div class="line"> <span class="constant">Gemset</span> <span class="string">'rails40'</span> created.</div></pre></td></tr></table></figure>
<p>这样我们就拥有了一个gemset “rails40”,想要将该gemset设为默认gemset则使用</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">╭─Gavin<span class="property">@ligan</span> ~ ‹ruby-<span class="number">2.0</span><span class="number">.0</span>›</div><div class="line">╰─$ rvm use <span class="number">2.0</span><span class="property">@rails4</span> --<span class="reserved">default</span></div><div class="line"> Using <span class="regexp">/Users/Gavin/</span>.rvm/gems/ruby-<span class="number">2.0</span><span class="number">.0</span>-p247 <span class="reserved">with</span> gemset rails4</div></pre></td></tr></table></figure>
<p>以上命令可以简化为</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="comment">$</span> <span class="comment">rvm</span> <span class="comment">use</span> <span class="comment">2</span><span class="string">.</span><span class="comment">0@rails4</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">create</span> <span class="literal">-</span><span class="literal">-</span><span class="comment">default</span></div></pre></td></tr></table></figure>
<p>我们还可以对gemset进行删除,切换,导入导出,清空,升级等操作,具体请查看<a href="http://rvm.io/" target="_blank" rel="external">文档</a></p>
<h2 id="项目中的gemset使用">项目中的gemset使用</h2>
<p>我们有了不同的gemset,目的就是为了区分不同项目中不同的环境.在不同的项目中我们可以通过rvmrc, gemfile, ruby-version等来使rvm自动切换到项目当前所需要的环境中.</p>
<p>rvm 1.11之前只支持在项目中添加.rvmrc的方式来切换rvm环境,而在1.22之后则建议使用</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">rvm --ruby-<span class="keyword">version</span> use <span class="number">2.0</span><span class="keyword">@rails4</span></div></pre></td></tr></table></figure>
<p>这样rvm会同时在项目中创建.ruby-version以及.ruby-gemset文件来标识该项目所使用的ruby版本及gemset.同时<strong>这种方式所生成的ruby版本信息同样被rbenv等工具所支持</strong></p>
<p>完成了以上工作之后我们再进入项目文件夹时,ruby版本及gemset就会自动切换到该项目的依赖环境下,相当于执行了一次<code>rvm use 2.0@rails4</code>,而退出该项目文件夹后则会回复默认ruby版本及gemset.</p>
<p>另外,有些时候我们所进入的项目中所包含的gemset信息本地并没有.这个时候我们希望能够自动创建对应的gemset,可以编辑<code>~/.rvmrc</code> 或者 <code>/etc/rvmrc</code>文件,加入</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="attribute">rvm_gemset_create_on_use_flag</span>=<span class="string">1</span></div></pre></td></tr></table></figure>
<p>从此在执行<code>rvm use</code>时候我们不需要加入<code>--create</code>参数便可以直接创建gemset</p>
<h2 id="rvm_gemset体系">rvm gemset体系</h2>
<p>rvm中gemset除了default之外,还有一个global gemset.当前ruby版本下,所有的gemset都会继承global这个gemset下的所有gem.每个ruby版本又分别有一个global,default,以及自己创建的gemset.各ruby版本之间相互独立.上面还有一个所有版本公用的global gemset.</p>
<p>有了global这个gemset之后,我们就可以把一些每个gemset都要用,又不想重复安装的gem统统放入global这个gemset中,比如rake, pry, bundler之类.</p>
<p>可以通过编辑 ~/.rvm/gemsets/global.gems,加入</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">bundler</div><div class="line">awesome_print</div><div class="line">pry</div></pre></td></tr></table></figure>
<p>这样每次安装新的ruby版本时,该版本ruby的global下都会自动安装好以上几个gem</p>
<h2 id="说了这么多我就是不想用gemset">说了这么多我就是不想用gemset</h2>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">"export rvm_ignore_gemsets_flag=1"</span> >> ~/.rvmrc</div></pre></td></tr></table></figure>
<p>这样就会忽略所有的gemset信息.永远的使用默认的gemset,其他项目中的gemset信息不会对你造成困扰.</p>
]]></content>
<summary type="html">
<![CDATA[rvm作为一种方便的ruby安装和管理方式已经被大家所习惯和接受.但是rvm中另外一个重要的功能gemset使用率却不高.本文简单介绍一下gemset的正确使用姿势.]]>
</summary>
<category term="rvm" scheme="http://blog.ligan.me/tags/rvm/"/>
<category term="gem" scheme="http://blog.ligan.me/tags/gem/"/>
<category term="ruby" scheme="http://blog.ligan.me/tags/ruby/"/>
<category term="Ruby" scheme="http://blog.ligan.me/categories/Ruby/"/>
</entry>
<entry>
<title><![CDATA[zsh alias with parameters]]></title>
<link href="http://blog.ligan.me/2013/01/06/zsh-alias-with-parameters/"/>
<id>http://blog.ligan.me/2013/01/06/zsh-alias-with-parameters/</id>
<published>2013-01-06T14:01:00.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<p>以前经常加一些alias来偷懒减少输入。今天在使用pandoc转换markdown文档时也想使用alias简化输入。例如</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pandoc <span class="built_in">list</span><span class="built_in">.</span>markdown <span class="attribute">-t</span> html <span class="attribute">-o</span> <span class="built_in">list</span><span class="built_in">.</span>markdown<span class="built_in">.</span>html</div></pre></td></tr></table></figure>
<p>想要省掉一堆-t html -o 什么的参数,最好是我只需要输入</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pd 文件名</div></pre></td></tr></table></figure>
<p>就可以完成转换,zsh在alias中加参数的方法稍微有些不一样,查看zsh文档的一堆废话后(csh中怎么写的,zsh和csh不一样,balabala),在.zshrc中加入</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="title">pd</span></span>() { pandoc <span class="string">"<span class="variable">$1</span>"</span> -t html -o <span class="string">"<span class="variable">$1</span>"</span>.html; }</div></pre></td></tr></table></figure>
<p>之后试验一下</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">╭─gavin<span class="variable">@Gavin</span>-<span class="constant">PC</span> ~<span class="regexp">/ligan/</span><span class="constant">Training</span>/demo ‹ruby-<span class="number">1.9</span>.<span class="number">3</span>›</div><div class="line">╰─<span class="variable">$ </span>ls</div><div class="line">list.md</div><div class="line">╭─gavin<span class="variable">@Gavin</span>-<span class="constant">PC</span> ~<span class="regexp">/ligan/</span><span class="constant">Training</span>/demo ‹ruby-<span class="number">1.9</span>.<span class="number">3</span>›</div><div class="line">╰─<span class="variable">$ </span>pd list.md</div><div class="line">╭─gavin<span class="variable">@Gavin</span>-<span class="constant">PC</span> ~<span class="regexp">/ligan/</span><span class="constant">Training</span>/demo ‹ruby-<span class="number">1.9</span>.<span class="number">3</span>›</div><div class="line">╰─<span class="variable">$ </span>ls</div><div class="line">list.md list.md.html</div></pre></td></tr></table></figure>
<p>大功告成</p>
]]></content>
<summary type="html">
<![CDATA[以前经常在shell中使用alias来偷懒减少输入,今天想在zsh中加入一个带参数的alias.]]>
</summary>
<category term="zsh" scheme="http://blog.ligan.me/tags/zsh/"/>
<category term="Linux" scheme="http://blog.ligan.me/categories/Linux/"/>
</entry>
<entry>
<title><![CDATA[就这么到了2013年了]]></title>
<link href="http://blog.ligan.me/2013/01/03/jiu-zhe-yao-dao-liao-2013nian-liao/"/>
<id>http://blog.ligan.me/2013/01/03/jiu-zhe-yao-dao-liao-2013nian-liao/</id>
<published>2013-01-03T11:51:00.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<h2 id="2012_过去了">2012 过去了</h2>
<p>世界末日没来,依然没有成为高富帅,被逼着裸婚了,学习没有长进,又浪费了一年.</p>
<h2 id="2013_也立个目标吧">2013 也立个目标吧</h2>
<ul>
<li>找份喜欢的工作</li>
<li>不和傻逼为伍</li>
<li>远离monkey test</li>
</ul>
<p>对了,还有开始写blog</p>
]]></content>
<summary type="html">
<![CDATA[2012 过去了,世界末日没来,依然没有成为高富帅,被逼着裸婚了,学习没有长进,又浪费了一年.]]>
</summary>
<category term="闲聊" scheme="http://blog.ligan.me/tags/%E9%97%B2%E8%81%8A/"/>
<category term="闲聊" scheme="http://blog.ligan.me/categories/%E9%97%B2%E8%81%8A/"/>
</entry>
<entry>
<title><![CDATA[First Blood]]></title>
<link href="http://blog.ligan.me/2012/10/01/first-blood/"/>
<id>http://blog.ligan.me/2012/10/01/first-blood/</id>
<published>2012-10-01T13:50:00.000Z</published>
<updated>2015-02-10T12:02:45.000Z</updated>
<content type="html"><![CDATA[<p>卖个萌,over</p>
]]></content>
<summary type="html">
<![CDATA[<p>卖个萌,over</p>
]]>
</summary>
<category term="闲聊" scheme="http://blog.ligan.me/tags/%E9%97%B2%E8%81%8A/"/>
<category term="闲聊" scheme="http://blog.ligan.me/categories/%E9%97%B2%E8%81%8A/"/>
</entry>
</feed>