Reload Original PagePrint PageEmail Page

notepad.cc C2C - 42qu.com

C2C不是电了商务里的C2C (Consumer to Consumer ),而是Copy to China的缩写

。 notepad.cc

是个实用的随手贴网站 , 我喜欢用它来记录自己的灵感点滴 ; 今天, 我们就从notepad.cc C2C开始我们的python网站开发之旅 ; 作品的地址是 42qu.cc

网站后面可以自己敲接任意地址 , 比如

http://42qu.cc/zuroc

会自动保存你写的文字 , 你可以用它来跟朋友分享文章 源代码见

hg clone https://zuroc@bitbucket.org/zuroc/42qu-notepad

Tornado 是 FriendFeed 开源的 python web 框架 ; FriendFeed的创始人Bret Taylor是Google Maps的作者 , FriendFeed 被 Facebook 收购以后 , 他又成为了Facebook的CTO ; 另外, Facebook 之前的 CTO , 被马克扎克伯格称做“曾经最好的朋友”的Adam DAngelo , 离开Facebook之后自己创业写了一个新网站 Quora

, 用的也是tornado框架

; 不八卦了 , 我们先安装tornado

另外, 我们需要一个可靠的数据库链接 , 我们安装mysql-python和dbutils

sudo easy_install -U distribute
sudo easy_install mysql-python
sudo easy_install dbutils

创建文件夹 42qu-notepad , 开始我们的项目

首先 , 我们通过之前安装是phpmyadmin登录数据库, 然后创建一个用户 work , 给予用户创建 work_ 为前缀的数据的权限 ; 然后在ssh命令行创建数据库 work_notepad , 其中 -p 后面接的是密码

mysql -uwork -p42qu -e 'create database work_notepad'

然后用工具创建表

CREATE TABLE  `work_notepad`.`notepad` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `txt` mediumtext NOT NULL,
  `url` varchar(128) NOT NULL,
  `time` bigint(20) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `url` (`url`) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

notepad.cc会给访问者分配一个9位的网址

出于性能考虑 , 我们用mysql的原则是坚决不用表连接 ; 豆瓣 , Friendfeed , Quora 都遵从这个原则。 参考文章 how friendfeed uses mysql

另外我们时间类型用bigint来存储, 这一点是学习的Quroa, 我这样做的主要目的是方便与msgpack+memcache配合。

然后, 来查看nginx配置文件

~/42qu-notepad $ cat nginx.conf

内容如下

server {
    listen 80;
    server_name 42qu.cc;

    location / {
        expires -1;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

server {
    listen 80;
    server_name nf.42qu.us;
    charset utf-8;
    location /{
        expires -1;
        autoindex on;
        root    /home/work/42qu-notepad/static;
    }
}

其中 42qu.cc 是这个项目的访问域名 , nf.42qu.us 是这个项目的静态文件域名 ; 这里 我们给 主站 和 静态 文件 配置了2个完全不同的域名, 主站的cookie不会包含在静态文件的访问请求中 , 从而可以节省流量 -- 当然, 你不care带宽的话用同一个域名也无所谓; 因为是开发服务器, 静态文件服务器的缓存设置的是 expire -1 (立即失效)

coffee script 提供了一种新方式来书写网页代码。 通常来说,这种 ruby/python 风格的语法要比传统的 javascript 来的更简短。 因为目前没有受到主流浏览器的广泛支持, coffee script 会将自己编译为对等的 javascript 代码。 它的可读性更强、更优雅,而且可以兼容 所有 浏览器环境。 gentoo 包管理器的 node.js 版本比较老, 还没更新 , 所以我们编译安装 node.js

mkdir ~/down
cd ~/down
version=0.8.11
wget http://nodejs.org/dist/v$version/node-v$version.tar.gz
tar zxvf ./node-v$version.tar.gz
cd node-v$version

./configure
make
sudo make install

其中最后三步 , 也是 linux 下面最常见的手工安装软件三部曲 ; nodejs 附带了一个自己的包管理器 npm。 我们可以借助 npm 使用如下命令安装 coffee script :

sudo npm install -g coffee-script

假设我们的项目下有 coffee 和 js 两个目录。 我们来写一个 Hello World 脚本:

保存为 hello.coffee , 后输入命令:

随后就可以看到生成了 hello.js 文件,内容如下:

// Generated by CoffeeScript 1.3.3
(function() {

  alert('hello world');

}).call(this);

手工编译很麻烦; 于是,有人写了一个名叫 jitter 的项目, 可实现自动编译:

sudo npm install -g jitter

进行安装 之后你可以简单地切换到项目目录下,使用:

nohup jitter coffee static/js > jitter.log 2>&1 &

就可以做到这一点了。 nohup是个很有用的命令 , 可以让程序在当前窗口关了之后也继续运行 ; 如果我在 coffee 下创建一个新目录,并在里面创建了一个 .coffee 的 文件; jitter 也会帮我在 js 下面创建一个同样的目录后以相同的目录结构生成文件。

可以监控coffee文件改动的日志 当你的coffee script有语法错误的时候 , 你可以在这里看到提示;

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

import sys

reload(sys)

sys.setdefaultencoding('utf-8')

from os.path import dirname, abspath, exists, join

from mako.lookup import TemplateLookup

DEBUG = True

PREFIX = dirname(abspath(__file__))

_lookup = TemplateLookup(

    directories=join(PREFIX, 'html'),

    module_directory='/tmp/mako',

    disable_unicode=True,

    encoding_errors='ignore',

    default_filters=['str', 'h'],

    filesystem_checks=DEBUG,

    input_encoding='utf-8',

    output_encoding=''

)

def render(html, **kwds):

    return _lookup.get_template(html).render(**kwds)

import MySQLdb

from DBUtils.PersistentDB  import PersistentDB as DB

def _connection(*args, **kwds):

    kwds['maxusage'] = False

    persist = DB (MySQLdb, *args, **kwds)

    conn = persist.connection()

    return conn

connection = _connection(

    host='127.0.0.1', user='root', passwd='42qu', db='work_notepad'

)

因为之前在豆瓣工作的时候一直用的mako模版 , 所以我喜欢用 mako 模版 而不是 tornado 自带的模版 , 这需要重载 tornado 的 RequestHandler 安装 mako

handler.py 如下

1

2

3

4

5

6

7

8

9

from tornado import web

from config import render

class Handler(web.RequestHandler):

    def render(self, template_name=None, **kwds):

        kwds['request'] = self.request

        kwds['this'] = self

        if not self._finished:

            self.finish(render(template_name, **kwds))

我们除了启用mako模版作为默认的模版 , 同时给模版传入了两个默认参数 request 和 this , 方便我们写代码

当用户没有自定义网址的时候, 我们给用户随机生成一个9位的网址 逻辑很清晰 , 看代码 , 不多说

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

from handler import Handler

from random import choice

from config import connection

from time import time

URL_ENCODE = 'abcdefghijklmnopqrstuvwxyz0123456789'

def txt_by_url(url):

    url = url.lower()

    cursor = connection.cursor()

    cursor.execute('select txt from notepad where url=%s', url)

    txt = cursor.fetchone()

    if txt:

        txt = txt[0]

    else:

        txt = ''

    return txt

class HandlerIndex(Handler):

    def get(self, url):

        if not url:

            while True:

                url = ''.join(choice(URL_ENCODE) for i in xrange(9))

                if not txt_by_url(url):

                    break

            self.redirect(url)

        else:

            self.render('/index.html', txt=txt_by_url(url), url=url)

    def post(self, url):

        if url:

            cursor = connection.cursor()

            url = url.lower()

            txt = self.get_argument('txt', False).rstrip()

            now = time()

            cursor.execute(

                'insert into notepad (url,txt,`time`) values '

                '(%s,%s,%s) ON DUPLICATE KEY UPDATE txt=%s,`time`=%s',

                (url, txt, now, txt, now)

            )

        self.finish({'time':now})

import tornado.web

from config import DEBUG

application = tornado.web.Application(

    [

        (r"/(.*)", HandlerIndex),

    ],

    debug=DEBUG

)

if __name__ == '__main__':

    import tornado.ioloop

    application.listen(8888)

    tornado.ioloop.IOLoop.instance().start()

这是一个基模版 , 我们没有写html标签 , 其中 ${next.body()} 是指代子模版的主体 这是参考 Google HTML/CSS Style Guide 中的省略写法

每三秒保存一次文章

$ ->
    txt = $ '#textarea'
    date = new Date()
    posted = true
    timer = 0
    txt_val = ''
    post = ->
        if not posted && (txt_val!=txt.val())
            posted = false
            $.post(
                location.href,
                {txt: txt.val()},
                ->
                    #$('.save_hint').html("#{date.toLocaleTimeString()} 保存成功!")
                    posted = true
                    txt_val = txt.val()
            )
        timer && clearTimeout timer
        timer = setTimeout(post,3000)

    key = ->
        posted = false
        return 1

    txt.keydown(key)
    txt.keyup(key)

    post()
    focus txt[0]

::...


免责声明:
当前网页内容, 由 大妈 ZoomQuiet 使用工具: ScrapBook :: Firefox Extension 人工从互联网中收集并分享;
内容版权归原作者所有;
本人对内容的有效性/合法性不承担任何强制性责任.
若有不妥, 欢迎评注提醒:

或是邮件反馈可也:
askdama[AT]googlegroups.com


点击注册~> 获得 100$ 体验券: DigitalOcean Referral Badge

订阅 substack 体验古早写作:


关注公众号, 持续获得相关各种嗯哼:
zoomquiet


自怼圈/年度番新

DU22.4
关于 ~ DebugUself with DAMA ;-)
粤ICP备18025058号-1
公安备案号: 44049002000656 ...::