Setting up rubygems without root access

Recently we had to install our rails app on a box, but we didn't have root access. The machine had ruby 1.8.6 and rubygems 0.9.6 (ouch). We can live with Ruby 1.8.6; but to run modern rails, we needed modern rubygems.

The rubygems install page has a helpful section on how to install locally without root access. Note: be sure to add export GEM_HOME=~/rubygems/gems to your .bashrc or your .bash_profile or else the next time you log in, your system gets confused again.

$ mkdir rubygems
$ cd rubygems-a.b.c
$ export GEM_HOME=~/rubygems/gems
$ ruby setup.rb --prefix=~/rubygems

Unfortunately, this didn't go as smoothly as I hoped. The result:

" `gem_original_require': no such file to load -- rubygems/exceptions"

and many other similar error messages.

The issue was that since the server installation already had an old version of rubygems installed, my local gem script was calling up the old rubygems files, but using my new GEM_HOME. A big mess.

What needs to happen is that your new gem executable needs to load your new rubygems files, and work with your new GEM_HOME. There may be a better way to do this, but I have found a workable solution for my case. It involves messing with the executable files that rubygems installs.

If you take a look at your gem executable, you'll see it is just a text file, a ruby program that executes itself.

$ cat  ~/rubygems/bin/gem

#!/usr/local/bin/ruby
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++

require 'rubygems'
require 'rubygems/gem_runner'
require 'rubygems/exceptions'

...

If you are familiar with shell scripts, you'll know that the first line (the shebang) specifies the binary to use to process the file. In this case, its #!/usr/local/bin/ruby. In general this is great. Ruby loads up the file and runs the script and magic happens. But what if the require 'rubygems' line is loading up the wrong rubygems?

We need to tell ruby where to find our custom rubygems libraries. Luckily, ruby has an option for this, -I path/to/lib. So, all we need to do is open up our gem executable and modify the shebang line to include this option.

$ vim  ~/rubygems/bin/gem

#!/usr/local/bin/ruby -I ~/rubygems/lib
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++

require 'rubygems'
require 'rubygems/gem_runner'
require 'rubygems/exceptions'

This tells the ruby interpreter to start the process with our custom path in the include path. If we have a newer version of the rubygems library there, they get required instead of the old versions. I can now install gems locally into my GEM_HOME without needing root access like so:

$ gem install mongrel

But that's not all. mongrel also installs its own executable, and when you try to run it, it will require rubygems. And yes, you guessed it, it will find the old version of rubygems and get completely confused. So again, we need to go into the executable file ~/rubygems/gems/bin/mongrel_rails and modify the shebang line.

As far as I can tell, you would need to do this for each and every gem that installs its own executable.

Update: after further consideration, using the $LOAD_PATH environment variable would probably be more robust, but my app is already installed. Perhaps you will let me know how it goes with $LOAD_PATH

Posted by Adeh Tue, 20 Jul 2010 03:52:00 GMT


Start a new project and push to remote git repo

Like many folks out there, we have been making the switch to git from svn. Although we struggled with git early on, after using both for some time I have come to appreciate the benefits of git. So here is our way of starting new projects with git.

Using gitosis on our server, we create an empty remote repo first. Then, one guy gets the project going and pushes the code into the empty repo. Here are the commands to get that going.

$ cd path/to/new/project
$ git init
$ git add .
$ git commit -m 'initial commit'
$ git remote add origin git@devserver.com:newproject.git
$ git push origin master

Posted by Adeh Tue, 25 May 2010 08:09:00 GMT


functional tests with machinist and authlogic

Authlogic and Machinist are two libraries that are a great help in getting a Rails app up and running quickly. But once we had a prototype running and some solid unit tests going, I ran into a snag. How do I create Users and UserSessions in a way that agrees with AuthLogic so that my requires_user before filter is working properly within a functional test?

The Machinist test_case.rb has some good explanation, but they focus on fixtures and leave out some easy steps.

Here's how I setup my functional tests: In test_helper.rb I added a login() method :
  def login(u = nil) 
    user = User.make(u)
    assert UserSession.create(user)
    user
  end
In blueprints.rb, i added the fields that AuthLogic needs to authenticate a user:
User.blueprint do
  email   { "testuser@company.biz" }
  password    { "test" }
  password_confirmation    { password }
  
  #this is all needed so we can log in for functional tests
  password_salt {Authlogic::Random.hex_token }
  crypted_password { Authlogic::CryptoProviders::Sha512.encrypt(password + password_salt) }
  persistence_token { Authlogic::Random.hex_token }
  single_access_token { Authlogic::Random.friendly_token }
  perishable_token { Authlogic::Random.friendly_token }
  active { true }
end
Finally, in the functional tests, you can simply use login() to simulate a session within the functional test:
  test "should force login" do
    get :index
    assert_redirects_to login_path
  end
  
  test "should get index" do
    login
    get :index
    assert_response :success
    assert_not_nil assigns(:items)
  end
I hope this helps. If you have any suggestions or improvements, please leave a comment.

Posted by Adeh Tue, 18 Aug 2009 01:05:00 GMT


AJAX loading notice based on Prototype

Loading Notice is a javascript plugin, built on Prototype, which automatically shows an indictor when a slow AJAX is under process. Basically, it’s like the “loading” div appears in gmail when busy. And when the users scroll or resize the window, it will still keep showing on the top of the page.

Check it out here: http://github.com/shaokun/loading_notice/tree/master

HOWTO

1. Install the plugin:

$ ruby script/plugin install git://github.com/shaokun/loading-notice.git
$ rake loading_notice:install

2. Include the library into your project:

<%= javascript_include_tag 'prototype', 'loading_notice' %>
<%= stylesheet_link_tag 'loading_notice' %>

3. Add a “loading” div and initialize a javascript object:

<div id="loading" style="display: none">Loading...</div>

<script type="text/javascript">
  window.onload = function() {
    window.loading_notice = new LoadingNotice("loading");
  }
</script>

4. That’s all!

Posted by Shaokun Mon, 20 Jul 2009 05:55:00 GMT


mongrel --prefix does not properly notify rails 2.1.1

As of Rails 2.1.1, ActionController::AbstractRequest.relative_url_root no longer exists. It has been replaced by ActionController::Base.relative_url_root. This allows you to run your rails app in a virtual sub-directory of your webserver. The mongrel rails handler uses this method to set the relative url for your rails app. When working properly, mongrel_rails start --prefix /app would result in rails automatically generating URLs like http://somedomain/app/action.

Currently in mongrel 1.1.5 and Rails 2.1.1, if you wish to have this functionality you must set the relative_url_root by hand somewhere in your environment like so:
ActionController::Base.relative_url_root = "/app" if 'production' == ENV['RAILS_ENV'] 
I have submitted a patch to mongrel, but if you can’t wait for the new release of mongrel, here is the patch:
Index: lib/mongrel/rails.rb
===================================================================
--- lib/mongrel/rails.rb        (revision 1036)
+++ lib/mongrel/rails.rb        (working copy)
@@ -147,9 +147,15 @@
         require env_location
         require 'dispatcher'
         require 'mongrel/rails'
-
-        ActionController::AbstractRequest.relative_url_root = ops[:prefix] if ops[:prefix]
-
+        
+        if ops[:prefix] 
+          if ActionController::Base.respond_to?('relative_url_root=')
+            ActionController::Base.relative_url_root = ops[:prefix] # new way to set the relative URL in Rails 2.1.1
+          else
+            ActionController::AbstractRequest.relative_url_root = ops[:prefix] 
+          end
+        end
+        
         @rails_handler = RailsHandler.new(ops[:docroot], ops[:mime])
       end

Posted by Adeh Thu, 09 Oct 2008 19:52:00 GMT


mysmGit 之Git on Windows快速上手

大家好,本文旨在让Window环境下的Github新用户短时间内快速上手Git,不需要者请就此打住。


目标:看完本文,读者们可以应用在Windows下与Github结合进行项目的创建、添加控制文件、本地提交、远程更新和远程发布等常用的功能。


背景:一个月前,技术总监阿德带领我创建了名字叫“FancyEditor”的迷你型JS开源项目,并用新兴的Git作为版本控制工具,以后我们kudelabs http://github.com/kudelabs/ 上面将会开放更多的源码与网友们分享、学习,共同进步。

刚开始用Git感觉很陌生,走了些弯路,特地写下本文提醒自己别再犯同样的错误,也鼓励下新加入Git的朋友,欢迎你。


一、 本人认为mysmGit目前是相当好用的Windows Git,当前使用的版本是Git-1.5.6.1,大家可以从: http://code.google.com/p/msysgit/ 得到最新的版本。

安装的过程不要管其他东西,一直点击<下一步>到安装结束。

安装完成后,新建一个项目文件夹,单击鼠标右键你会看到选项‘Git GUI Here’ 还有‘Git Bash Here’,请选择‘Git Bash Here,该版本GUI视图给本人感觉实用性不好,先不讲它。


二、 设置SSH公钥,注意:这一步的正确配置非常重要,我们至少得有一个SSH公钥才能连接并操作Github或其他repo点上的项目。


跟着弹出Git窗口,DOS模式也许对于不习惯命令行的广大Windows用户是一种受罪。请咬咬牙忍一忍,因为真正需要用到的命令行并不是很多。

这里我们要配置本地与github.com之间的SSH 公钥

首先,请输入:ssh-keygen -C "你的email地址" -t rsa ,确定回车键连续4次,不管中间要求输入其他东西。该命令将生成一对非对称的\私密钥,默认它们被存储在:

XP/2003用户:c:\Documents and Settings\登陆名\.ssh

Vista用户: c:\Users\登陆名\.ssh

.ssh文件夹下面,密钥放在id_rsa文件里面,不用理会它;

SSH公钥放在id_rsa.pub里面,请用文本编辑器打开它,复制里面所有的字符。



假定此时的你已经注册了Github用户,在账号管理account页面,有个大大的SSH Public Keys”栏目,请点击“add another public keyTitle可空(默认值为email)或者任意,直接黏贴刚才复制的字符在Key文本域里面,最后点击Add Key

SSH成功创建后:


三、 创建新的项目/ Create a New Repository,全局设置

Github网站上创建一个新的项目

 

记住你看到的Your Clone URL: git@github.com:git-on-windows/rookies.git

这路径比较统一容易记,git@github.com冒号后面的第一个单词是你的用户名,’/’斜杠号后面是你的项目名称.git

Okay,回到刚才的DOS窗口,我们要设置全局用户名称以及Email以方便Git知道你是谁。

分别输入:


git config --global user.email “你的email地址”

git config --global user.name “你的名称”


其实不严格来说,这一步可忽略,它并不影响以后你对项目的各种操作行为,只是让Git记录是由谁在执行操作。本人愚见认为在多人的团队开发过程中同一个项目用同一个SSH公钥的时候可见效果。

四、简单Git命令实战

依次输入下列命令:

1). git init #系统初始化git目录:

Initialized empty Git repository in D:/git_on_windows/.git/


2). touch READ #touch表示创建文件,文件名称是“READ”


3). git add READ #把文件READ加入git的版本控制


4). git commit -m 'any commit message' #提交新版本至git仓库,后面是提交信息:

Created initial commit b09873d: any commit message

0 files changed, 0 insertions(+), 0 deletions(-)

create mode 100644 READ


5). git remote add mybranch git@github.com:git-on-windows/rookies.git #创建本地与远程服务器的分支mybranch


6). git pull mybranch master #把远程服务器master分支的内容更新至本地mybranch分支


7). git push mybranch master #把本地mybranch分支所有的修改提交至远程git服务器

恭喜,朋友们,到这里我们已经完成了Git入门,请尽情享受Git带来的乐趣吧!


五、最后,顺便提一下刚开始用git命令容易出现的错误以及解决方法:


1).“Permission denied (publickey).”错误,SSH公钥没有设置好,请看本文章第二点:设置SSH公钥。


2).“fatal: Not a git repository”错误,尚未初始化git目录,请用: git init命令。


3).“fatal: 'origin': unable to chdir or not a git archive”错误,不存在分支 ’origin’,这个’origin’可能是本地分支,也可能是远程分支;若是本地分支应该用类似git remote add origin git@github.com:username/projectname.git 的命令来解决,若是远程分支,请注意它的个时是否正确


4). “! [rejected] master -> master (non-fast forward) error: failed to push some refs to 'git@github.com:git-on-windows/rookies.git’ ”错误,执行某git命令前需要更新项目,可用类似命令:git pull yourbranch master 解决


5).” Repository not found. If you've just created it, please try again in a few seconds.”错误,很大原因是没有设置好远程路径,或者路径错误,请记住标准的git路径类似:git@github.com:username/ projectname.git


错误集合待更新中。。。


Posted by Mysen Wed, 24 Sep 2008 04:53:00 GMT


kudelabs public code repository

We have started a new code repository at http://github.com/kudelabs. We will be using this to host our own plugins, utility files, and example code that we wish to make public. Hopefully, something here is of use to others in the community. If you find ways to improve upon our code, we will be happy incorporate your ideas as well. We'll be posting new code when it is ready, you can use github's "follow" feature to learn about new items.

So far I am impressed with github's features, there really isn't anything quite like it. I look forward to interacting with more developers through the site.

Posted by Adeh Tue, 02 Sep 2008 22:41:00 GMT


Rake task to help manage svn rename

I recently had to rename several views+controllers in a project. This is a very wearisome task since its not just a directory or a file; but directories, files, tests, fixtures, and other files that get generated by the Rails generator helpers. I searched for a little piece of code to help manage this, but didn't find anything. That may be due to the fact that earlier versions of rake did not work with arguments very well. Here is my little rake task, you can put it into a file in your lib/tasks directory and run it using:
$rake svn:rename[old_name,new_name] # notice no spaces
And here is the code:
namespace :svn do
  FILES_TO_RENAME = [ "app/views/%s",
                      "app/controllers/%s_controller.rb", 
                      "app/models/%s.rb", 
                      "test/unit/%s_test.rb", 
                      "test/functional/%s_controller_test.rb", 
                      "test/fixtures/%s.yml"]
  task :rename, :from, :to do |t, args|
    FILES_TO_RENAME.each do |s|
      from_f = s % args.from
      to_f = s % args.to
      if File.exists?(File.expand_path(from_f))
        puts "Renaming #{from_f} -> #{to_f}"
        puts %x[svn mv #{from_f} #{to_f}]
      else
        puts "Skipping #{from_f}. File not found."
      end  
    end
  end
  
end
Please leave comments if you see a way to improve this. Feel free to modify it to your needs and tell me about it.

Posted by Adeh Fri, 13 Jun 2008 21:48:00 GMT


Prototype fix for the IE bug where 204 => 1223

I ran into a strange problem when working on a new polling Ajax feature for one of our projects. Before I get into the issue, I'll set up the context.

I don't like the brute force approach of the PeriodicalUpdater, so instead I wanted to roll my own solution that utilized proper HTTP response codes to manage browser behavior. In particular, if there is nothing to report from the server, I can use the 204 - No Content (See W3C Documents) response code. In this way, I can simply use the onSuccess callback to schedule another check after an interval if there is no response body.

This worked wonderfully in Firefox and Safari, as expected. Of course, on IE, instead of calling onSuccess, it seemed to be calling onError, even though no error occurred. After displaying the status code in the error message, I found that IE was reporting the status code not as 204, but as 1223. As far as I can tell, there is no reason for this. I have found that other JS libraries have addressed the issue, so I went into prototype.js and solved the problem in the code.

I've submitted a patch to the Prototype team through their Lighthouse page, and the fix should get into the 1.6.0.3 release. If you are impatient and need the fix now, or are interested in getting into prototype hacking, here is the simple change:

prototype.js:199

 success: function() {
    var status = this.getStatus();
	// accept 1223 as a successful status, IE interprets 204 as 1223
    return (!status && this._allowStatusZero) || (status >= 200 && status < 300) || (status == 1223);
  },

Posted by Adeh Tue, 03 Jun 2008 22:05:00 GMT