Ruby 迷

关注Ruby, Rails, JavaScript开发技术博客

GitHub Mate: 单文件下载更方便

GitHub 不是 Dropbox,GitHub 侧重的是代码分享,而不是文件存储,比如曾经有的文档上传功能也被砍掉了 Goodbye, Uploads。 而且 GitHub 还有意让文件下载复杂化,每次想下载一个文件都要右键Raw,然后目标另存为,请问还能做的再麻烦一点吗。 Git 本身不像 SVN 支持目录或文件克隆,所以对于体积大的项目,又是网速差的环境只能==了。

所以才写了 GitHub Mate 这个 Chrome 插件,完全开源:源码

目前源码逻辑很简单,有两个功能,一个是点击文件图标下载文件,实现逻辑在这里。 主要有3步:

  1. 在 document 上代理所有文件图标上的 click 事件,这样不用分别绑定事件,而且支持 GitHub 的 Pjax。
  2. 根据点击的图标计算出文件的实际下载路径。
  3. 添加一个匿名a标签,并使用HTML5的 download 属性来设置下载路径,模拟点击a标签开始下载。

另一个功能是显示未读通知(notification)数,通过添加一个定时器去抓取notification页面,方法很原始。其实对于我这种不太活跃的所谓“开源爱好者“没啥用处,基本不会有什么通知,以后会做成可选的,节省资源。

未来考虑支持文件夹下载,初步想法是找类似 Heroku, Nitrous.io 这种免费的服务来做后台自动clone仓库,并打包文件夹,然后回传地址,这样做实现起来不难,关键是对于大项目延迟会比较大,小项目下载文件夹速度提升又不大,还需要在考虑考虑。

不得不说,Chrome 插件体系设计实在太简洁了,而且对插件(extension)和应用(app)都有很好的支持和细分。 官方文档 示例都非常赞。有人说Chrome web store未来会替代Google Play和App Store,目前我只能“呵呵”,以后逆袭也不一定。

Ruby Simple HTTP Server With Rack

最近使用JavaScript比较多,如果是静态页面,Chrome AJAX请求本地文件经常会出现。

1
2
3
XMLHttpRequest cannot load
file:///*******. 
Origin null is not allowed by Access-Control-Allow-Origin.

这里因为Chrome安全机制不允许AJAX加载本地文件,你可以启动Chrome时加参数chrome.exe --allow-file-access-from-files。 但我不喜欢这种方法,偏好启动一个web server。

Python 2.* 的做法是python -m SimpleHTTPServer,Python 3.* 的做法是python -m http.server,Ruby的做法也非常简单。

2步搞定:

1. 安装rack,并新建rack配置文件config.ru:

1
2
gem install rack
echo "run Rack::Directory.new('')" > ~/config.ru

2. 切换到任意目录运行

1
rackup ~/config.ru

如果觉得命令长就建立alias alias rp='rackup ~/config.ru'

默认启动的server端口是9292,-p [port_num]可以修改端口号,打开浏览器即可当前目录的文件列表: http://locahost:9292/

Appendix: rack and rackup

Rack:

Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between (the so-called middleware) into a single method call.

Rackup:

Rackup is a useful tool for running Rack applications, which uses the Rack::Builder DSL to configure middleware and build up applications easily.

Thanks to

http://blog.samsonis.me/2010/02/rubys-python-simplehttpserver/

https://github.com/rack/rack

Advanced Rails Debug

这是我在Ruby Tuesday上分享的记录,介绍几种调试Rails程序的方法

1. ruby-debug, ruby-debug19

没人维护,bug多。Ruby 1.9.3后不推荐使用

2. debugger

ruby 1.9.3后的选择,功能强大,缺点是没有语法高亮。

项目中引用debugger方法

修改Gemfile

1
2
3
group :development, :test do
  gem 'debugger'
end

在需要设置断点的地方:

1
require 'debugger'; debugger

查看源文件方法

1
2
3
list       #往后翻看代码
list- [n]  #往前翻看代码
list=      #查看当前运行处代码

list可以缩写为l

查看方法栈(stack frames)

1
2
3
backtrace/where               # 查看
frame [n]                     # 跳到指定stack frames
up [n] (缩写u)  down [n]     # 在stack frames层里来回切换

执行控制

1
2
3
4
next           # 执行下一行,遇到方法调用不进入。
step           # 执行一步,遇到方法调用进入。
continue/c     # 执行到下一个断点。
finish [n]     # 执行到指定的frame才返回。注意编号是从下往上数。

其它常用命令

1
2
help
info

3. pry

替代irb,我最喜欢的是语法高亮。

1
2
3
4
5
6
7
8
help
cd ..
whereami
self
ls
show-doc
show-method
edit-method

pry-nav

pry只是替代erb,并不能调试rails,加上rails后即可pry-nav。 增加3种基本调试指令。

1
2
3
step
next
continue

pry-stack_explorer

Pry的插件,用于查看方法栈

1
2
3
4
5
6
7
(pry) main: 0> show-stack

Showing all accessible frames in stack (5 in total):
--
=> #0 [method] gamma
   #1 [method] beta
   #2 [method] alpha

使用pry调试rails项目

修改Gemfile

1
2
3
4
5
group :development, :test do
  gem 'pry'
  gem 'pry-nav'
  gem 'pry-stack_explorer' # 如果不查看方法栈,可以省略
end

在需要设置断点的地方:

1
binding.pry

4. 参考

https://github.com/cldwalker/debugger

https://github.com/pry/pry

https://github.com/pry/pry-stack_explorer

http://guides.rubyonrails.org/debugging_rails_applications.html

Speed Up With Rails Cache

这是我在Ruby Tuesday上分享的记录,介绍Rails缓存的使用方法

Two Big Problems

There are only two hard problems in Computer Science: cache invalidation and naming things. – Phil Karlton

Fortunately, Rails has made it perfect!

启用缓存

默认development模式禁用缓存,production环境启用缓存

1
config.action_controller.perform_caching = true

缓存核心:Rails.cache

3种基本操作

  • Rails.cache.write 'foo', 'bar'

  • Rails.cache.read 'foo'

  • 不存在则写,存在则读

Rails.cache.fetch 'a_big_data' do { (1..1000000).inject(:+) }

缓存默认是以文件形式保存,文件位置 ./tmp/cache

缓存原理

1
2
3
4
5
def body_html
  Rails.cache.fetch "#{cache_key}/body_html" do
    render(body)
  end
end

缓存策略

  • Rails.cache
  • Fragment caching
  • Action caching
  • Page caching
  • HTTP caching

Fragment Caching

1
2
3
4
5
6
7
8
9
10
11
<%= cache @post do %>
  <p>
    <b>Title:</b>
    <%= @post.title %>
  </p>

  <p>
    <b>Content:</b>
    <%= @post.content %>
  </p>
<% end %>

手动设置过期

1
2
expire_fragment(:controller => 'products', :action => 'recent',
:action_suffix => 'all_products')

Fragment Caching 效果

Fragment caching result

Fragment Caching key生成策略

1
2
3
4
5
6
cache 'explicit-key'      # views/explicit-key
cache @post               # views/posts/2-1283479827349
cache [@post, 'sidebar']  # views/posts/2-2348719328478/sidebar
cache [@post, @comment]   #
views/posts/2-2384193284878/comments/1-2384971487
cache :hash => :of_things # views/localhost:3000/posts/2?hash_of_things

Action Caching

1
2
caches_action :index, :cache_path => proc {|c| { :tag =>
Post.maximum('updated_at') } }

Page Caching

1
caches_page :index

特点: * 很快但无用 * 第一次访问时会在public目录生成静态html结尾文件,此后访问就会跳过所有validation和filter。

HTTP Caching

报文头:
1
Cache-Control: max-age=0, private, must-revalidate
示例:
1
2
3
4
5
6
7
def show
  @post = Post.find params[:id]

  if stale? @post, :etag => @post.posted_at do
    respond_with @post
  end
end
1
2
3
4
5
6
7
8
9
10
def index
  @posts = Post.all

  if stale?(:last_modified => @posts.last.updated_at.utc)
    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @posts }
    end
  end
end
特点:
  • 如果没有修改,直接返回304,不需要返回网页内容
  • 最有效的缓存方式
  • 工作在协议层,更快
  • 使用HTTP头(Last-Modified, ETag, If-Modified-Since, If-None-Match, Cache-Control)

Tips

  • 别碰swapper,除非非它不可。
  • 为所有缓存使用自动过期的key。
  • 经常把 belongs to:touch => true 结合使用
  • 使用 Rails.cache 来缓存查询到的数据
  • 在每次部署应用后不要忘记设置ENV[‘RAILS_APP_VERSION’]
  • 一定要缓存你的assets静态文件。
  • 缓存粒度一定要小,以此提高命中率

Thanks to

http://www.broadcastingadam.com/2012/07/advanced_caching_revised/

http://guides.rubyonrails.org/caching_with_rails.html

http://railslab.newrelic.com/scaling-rails