使用RubyParser检查Ruby代码的variable scope

今天参加rubyconfchina,再次聆听ihower的演讲,再次收益匪浅。

中间在听到Variable Scope突然想到可以使用RubyParser来检查,于是写了几行代码测试,果然是可行的。

ihower提到在ruby代码中只有在module,class和def才会创建Varible Scope,比如:

module MyDemo
  var = 1
  class Demo
    var = 2
    def foo
      var = 3
    end
  end
end

其中三个var都在不同的scope。这个在RubyParser的解析结果里面有体现出来。比如,ihower提到def会创建一个scope而define_method不会。我们可以用下面的代码来做个实验

text=-EOF
class Class
  def define_more_methods
    ["aaa", "bbb", "ccc"].each do |name|
      define_method(name) do
        puts name.upcase
      end
    end
  end
end
EOF
RubyParser.new.parse(text)

它的解析结果为

s(:class, :Class, nil,
  s(:scope,
    s(:defn, :define_more_methods, s(:args),
      s(:scope,
        s(:block,
          s(:iter,
            s(:call,
              s(:array, s(:str, "aaa"), s(:str, "bbb"), s(:str, "ccc")),
              :each,
              s(:arglist)
            ),
            s(:lasgn, :name),
            s(:iter,
              s(:call, nil, :define_method, s(:arglist, s(:lvar, :name))),
              nil,
              s(:call, nil, :puts, s(:arglist, s(:lvar, :name)))
            )
          )
        )
      )
    )
  )
)

可以看到class有创建一个scope,defn也有创建一个scope,而define_method却没有。同样地,你也可以通过实验看到class_eval和instance_eval都没有创建scope。完全和ihower讲的一样。

以后要是什么时候碰到Variable Scope的问题,就可以使用RubyParser来检查啦,实践见真知。

Posted in  ruby


barcamp归来

昨天公司参加了barcamp会议,barcamp会议的形式比较特别,分三个房间,任何人都可以做presentation,你可以去听你感兴趣的话题。不过来的人并不都是做技术的,大多是做市场,推销自己想法的比较多。

第一场我就把自己讲的内容搞定了,后面可以安心听听别人的想法。

有三个演讲印象比较深刻

一个是一个老外讲关于上海的演讲,主要是他想为上海的发展做共享,从环境、教育、交通和医疗等方面介绍了上海的问题,希望搭建一个平台来为上海的发展献计献策。惭愧啊,我们平时只会指责政府如何如何。

另一个是一个MM普及网络流行语,比如悲剧啊,打酱油啊,冏啊,她不仅叫那些老外如何说,而且告诉他们这次词的来由,我听下来都收获不少。

最后一个是一个老外介绍node.js,他演讲的形式很特别,没有选择ppt,而是直接拿node.js做一个服务器,通过浏览器来显示演讲的内容,蛮喜欢的

听了一天,感觉比上班还辛苦呢。

Posted in  presentation


nginx上传进度条

项目中经常需要应用的功能之一就是文件上传,一般对于小文件来说不需要特别的处理,但是一旦碰到允许大尺寸文件上传的时候,用户常常会被长时间的没有变化的上传过程而迷惑,这种时候就需要一个上传进度条来提醒用户。比如我最近一个项目允许用户上传100M的视频文件,上传的过程往往需要持续10多分钟,这种情况如果没有进度条的话,用户可能会以为系统出问题了。项目部署的环境为nginx+lighttpd,上传的过程是这样的:

  1. 用户选择上传的视频,点击提交按钮

  2. nginx将视频文件的二进制数据保存为/tmp目录下面的某个文件

  3. lighttpd执行rails的代码对/tmp目录下面的上传文件进行处理

由此可见,上传视频的过程都是由nginx进行处理的,lighttpd并不知情,它只能通过上传表单了解到上传到/tmp目录下的文件名,这样的好处是,费时的上传过程并不会消耗rails进程。所以我们在做上传进度条的时候就要在nginx身上下功夫了。

google了一下,发现网上已经有了解决方案,http://github.com/drogus/jquery-upload-progress

同时修改nginx配置文件 ,增加以下这段:

location ^~ /_upload_progress {
  upload_progress_json_output;
  report_uploads proxied;
}

应用jquery-upload-progress就是采用ajax轮询,请求/_upload_progress,返回一个json,告诉你上传是否开始,上传了多少字节,总共有多少字节,你只需要比较上传多少字节和总共多少字节就可以得到上传数据的进度,至于进度条的显示只需要些点css就可以搞定了。

Posted in  nginx rails


方便地浏览github上所有的issues

我在github上面有好几个repositories,经常会有人来报告报告issue,但问题是有时候手头正好有其它的事情需要处理,就没有办法马上修复issue,随着时间的推移,越来越多的issues被积压下来,而且是分散在各个repositories,这让我找起来很费力,必须首先进入repository页面,再点击issues,才能看到相应的issues。我想要是有个页面能够列出所有repositories的issues该多好啊,不过github上面好像没有找到这样的页面。没办法,只好自己动手丰衣足食。

github提供了api,这大大方便了开发,同时有相应的ruby实现octopi。剩下的工作就很简单了,创建一个sinatra项目,接受用户输入的github用户名,通过用户名获取到该github user,再通过user拿到repositories,通过repository拿到issues,最后通过issue拿到comments,所有的数据都轻而易举地获得了,接下来就是稍微美工一下,然后就发布到heroku上面。半天时间搞定,ruby开发就是个快啊。

heroku上面的地址是:http://github-issues.heroku.com/

Posted in  github sinatra


追踪图片在外部系统的查看次数

随着sns网站和web api的兴起,与第三方网站的交互越来越多。比如我们可以向用户facebook好友上的wall推送数据等等来做网站的推广,这就引来一个问题,我们不能只傻乎乎地做推广,更重要的是统计推广的效果。你总共推送了多少数据,有多少人看到了这些数据,又有多少人点击来到了你的网站?这些数据都是可以用来帮助你改进和提高推广的效果。

对于推送了多少数据和有多少人看到了这些数据,这两者比较容易做到,前者可以在推送数据的时候做记录,后者可以在用户点击进入网站的时候做记录。对于有多少人看到了这些数据就比较麻烦了。

下面拿facebook为例介绍如何统计数据在外部系统的查看次数。如果是纯文字的话几乎没法做到,但是如果是推送图片或视频的话,可以通过图片的显示次数来统计数据。

一般往facebook推送图片或视频等图片的时候,只需要在推送的参数中设置图片或视频的完整url即可。然后facebook在显示图片或视频的时候,会发送请求到服务器上,你只需要捕获这个请求并添加相应的逻辑处理。但是问题是,一般部署的rails应用,除了使用rails server(如thin),还会放置一个web server(如nginx)来做负载均衡,你的图片或视频的请求会直接被web server处理,根本不经过rails server,这样你的逻辑代码就永远不会被调用到了。

解决的方法就是把传递给facebook的图片或视频url由静态url改成动态url,比如:http://yourdomain.com/assets/test.png改成http://yourdomain.com/assets/1,然后由assets_controller来返回图片

class AssetsController < ApplicationController
  def show
    asset = Asset.find(params[:id])
    # add logic to increment asset show count

    send_file asset.attachment.url(:small, false)
  end
end

到此为止,你已经可以统计你推送的数据被多少人看到了,不过事情到这里还没有结束,通过rails server来上传图片效率是很低的,你应该将这项任务交给web server。以nginx服务器为例,它提供了X-Accel-Redirect选项,负责静态文件的上传。

首先,修改nginx的配置文件,将/assets路径加入到X-Accel-Redirect

location /assets {
  root /var/www/staging/current/public;
  internal;
}

这段配置的作用是,如果X-Accel-Redirect指定的路径为/assets/image.png,那么nginx会去寻找/var/www/staging/current/public/assets/image.png文件并上传

然后要将推送到facebook的图片或视频路径由assets/1改为facebook_assets/1,避免和X-Accel-Redirect冲突。

class FacebookAssetsController < ApplicationController
  def show
    asset = Asset.find(params[:id])
    # add logic to increment asset show count

    response.headers['X-Accel-Redirect'] = asset.attachment.url(:small, false)
    render :nothing => true
  end
end

可以看到,只需要在reponse header中增加X-Accel-Redirect即可,render :nothing = true表示rails server不处理图片的上传。nginx看到response header中有X-Accel-Redirect选项,就到/var/www/staging/current/public目录下面去寻找相应的图片并上传。

Lighttpd和Apache2则可以通过X-Sendfile选项来完成同样的事情。

Posted in  rails facebook nginx


Fork me on GitHub