压缩js和css文件的rake

对于Web应用来说,一个页面的HTTP请求数越多,往往导致页面加载的时间越长,服务器的负担也越重。对于每个HTTP请求,都要进行握手,客户端说hello,服务器端也说hello,然后再传输内容,所以要尽量减少请求的数量。

页面的HTTP请求,除了html本身之外,还有javascript、css和images。其中images可以通过css sprite来解决,而javascript和css则可以通过压缩合并,来减少HTTP的请求。

今天写了一个rake,来压缩js和css,是基于yui compressor的。

首先下载yui compressor,将jar文件copy到lib目录下,当然因为yui compressor是基于java的,所以你要先配好java的环境。

然后就是写rake文件

namespace :minifier do
  def minify(input_files, output_file)
    if input_files.class == String
      `java -jar lib/yuicompressor-2.4.2.jar #{input_files} -o #{output_file}`
    else
      input_files.each do |input_file|
        `java -jar lib/yuicompressor-2.4.2.jar #{input_file} -o #{input_file}_min`
      end
      `rm #{output_file}`
      `cat #{input_files.collect{|file| file + '_min'}.join(' ')}  #{output_file}`
      input_files.each do |input_file|
        `rm #{input_file}_min`
      end
    end
  end

  desc minify
  task :minify = [:minify_js, :minify_css]

  desc minify javascript
  task :minify_js do
    minify(FileList[
                      'public/javascripts/jquery.js',
                      'public/javascripts/jquery-ui.js',
                      'public/javascripts/jrails.js',
                      'public/javascripts/application.js'
                    ],
           'public/javascripts/all.js')
  end

  desc minify css
  task :minify_css do
    FileList['public/stylesheets/**/*.css'].each do |file|
      minify(file, file)
    end
  end

end

我是将所有的js文件都压缩然后合并成一个all.js文件,对于css,由于我是使用haml的sass,已经通过@import将css合并了,所以只需对每个css进行压缩即可。

运行rake:

rake minifier:minify

你会看到一个很小的all.js文件,所有的css文件大小也都减少了很多。

最后你需要修改js的include方式,如果是production环境的,就只需include一个all.js文件即可

- if RAILS_ENV == 'production'
  = javascript_include_tag 'all'
- else
  = javascript_include_tag 'jquery'
  = javascript_include_tag 'jquery-ui'
  = javascript_include_tag 'jrails'
  = javascript_include_tag 'application'

这下每个页面都基本只有一个js和一个css的HTTP请求了

Posted in  rails


选择合适的虚拟主机

之前系统一直无法访问,主要是原来的虚拟主机用起来实在是不方便,之前之所以选择它的原因一是支持Rails,二是价格超便宜,才3.95$一个月。不过我的rails server总是crash,技术支持也无法给我一些满意的答复,搞得我很是郁闷,只能选择放弃。还好老美都支持一个月免费全额退款。

现在转到了host monster,一个月5.95$,支持Rails,control panel很不错,最关键的是支持SSH,这下就方便很多了。慢慢挖掘吧

Posted in  life


通过数组转置来组织ActiveRecord的conditions

使用ActiveRecord的conditions最基本的方法就是数组:

:conditions => ['first_name = ? and middle_name = ? and last_name = ?', 'George', 'W', 'Bush']

更灵活的方法是使用Hash来组织:

:conditions => {:first_name => 'George', :middle_name => 'W', :last_name => 'Bush'}

这样在动态构建查询条件的情况非常有帮助,比如根据查询参数来构建conditions:

conditions = {}
conditions.merge!({:first_name => params[:first_name]}) if params[:first_name]
conditions.merge!({:first_name => params[:middle_name]}) if params[:middle_name]
conditions.merge!({:first_name => params[:last_name]}) if params[:last_name]

:conditions = conditions

但是Hash conditions也有其限制,不支持LIKE,不支持Not Null等等,能不能结合Array Conditions的强大和Hash Conditions的灵活呢?答案当然是肯定的:

conditions = []
conditions << ['first_name LIKE ?', "%#{params[:first_name]}%"] if params[:first_name]
conditions << ['middle_name LIKE ?', "%#{params[:middle_name]}%"] if params[:middle_name]
conditions << ['last_name LIKE ?', "%#{params[:last_name]}%"] if params[:last_name]

:conditions = [conditions.transpose.first.join(' AND '), *conditions.transpose.last]

其实就是通过数组转置来构建查询语句的。

Posted in  rails activerecord


平民窟的百万富翁

上周日陪小笨蛋去永华电影院看了平民窟的百万富翁,非常赞的一部电影。通过一场答题游戏来贯穿整部影片,每道题目都能引出一个故事,所有的故事似乎有都是有联系的。

最搞笑的是主人公小时候为了能够要到明星的签名,居然不惜跳到粪池,当时影院里一篇惊呼,我是笑死了。

没看过的同学们,推荐大家去欣赏下哦!

Posted in  life


关于网络相册权限的解决方案

之前做i9i8网站时有这样的需求,用户设置自己相册的权限,可以是所有人都能看,可以是相册主人的好友可见,可以是只有相册主人自己能看到,也可以是通过输入密码看到。因为网站是用php写的,自然想到的是用php做权限控制,不过leader给了一个更好的方案,用J2EE的Filter,所有的相册请求都经由Filter来过滤,相对就很独立,重用性也更好。

处理的方式是把相册的id作为url的parameter传递,而当前用户的key以及查看相册的密码通过cookie来传递。流程是这样的:

1. 根据url中的相册id去数据库中查找相应的相册记录

2. 依据相册的权限进行相应的处理:

2.1. 如果是所有人都能看的,则通过
2.2. 如果是相册主人才能看到的,则比较cookie中的当前用户和相册的主人
2.3. 如果是相册主人的好友可见,则查询相册的主人与cookie中的当前用户是否是好友
2.4. 如果是通过输入密码才可见的,则比较cookie中的密码与相册设置的密码是否一致

其中,如果不通过则重定向到表示权限不够的错误图片的url。

代码如下:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    try {
        // 获取要访问的相册
        Photoalbum album = getPhotoalbum(httpRequest.getRequestURI());
        String privacy = album.getPrivaty();

        // 检查访问相册的权限
        if (ALL.equalsIgnoreCase(privacy)) {
            // 所有人可见
            chain.doFilter(request, response);
        } else if (SELF.equalsIgnoreCase(privacy)) {
            // 相册主人才可见
            String userKey = getCookie(httpRequest, USER_COOKIE_NAME);
            check(userKey);
            String myUserId = getUserId(userKey);
            if (album.getUserId().equals(myUserId)) {
                chain.doFilter(request, response);
            } else {
                RequestDispatcher rd = httpRequest.getRequestDispatcher(ERROR_IMAGE);
                rd.forward(request, response);
            }
        } else if (FRIEND.equalsIgnoreCase(privacy)) {
            // 相册好友可见
            String userKey = getCookie(httpRequest, USER_COOKIE_NAME);
            check(userKey);
            String myUserId = getUserId(userKey);
            if (isFriend(album.getUserId(), myUserId)) {
                chain.doFilter(request, response);
            } else {
                RequestDispatcher rd = httpRequest.getRequestDispatcher(ERROR_IMAGE);
                rd.forward(request, response);
            }
        } else if (BYPASSWORD.equalsIgnoreCase(privacy)) {
            // 输入密码可见
            String albumPwd = getCookie(httpRequest, ALBUM_PASSWORD_COOKIE_PREFIX + album.getId());
            check(albumPwd);
            if (album.getPassword().equals(albumPwd)) {
                chain.doFilter(request, response);
            } else {
                RequestDispatcher rd = httpRequest.getRequestDispatcher(ERROR_IMAGE);
                rd.forward(request, response);
            }
        } else {
            RequestDispatcher rd = httpRequest.getRequestDispatcher(ERROR_IMAGE);
            rd.forward(request, response);
        }
    } catch (Throwable t) {
        RequestDispatcher rd = httpRequest.getRequestDispatcher(ERROR_IMAGE);
        rd.forward(request, response);
    }
}

Posted in  java web


Fork me on GitHub