Jun 15, 2009

[ANN] Rubytest.vim 0.9.6 Released

Rubytest.vim 0.9.6 is just released. This version contains some small fix:

* support rspec examples looks like
example "this is an example" do
* correctly handle single/double quote escape for rspec examples and vanilla testcases

Check it out here: http://www.vim.org/scripts/script.php?script_id=2612

* Rubytest.vim is a vim plugin which helps you run ruby tests (including vanilla testcases, rspec examples, shoulda examples ..) quickly.

Jun 2, 2009

Infinity in Ruby

I learned this from a post today:


irb(main):001:0> Infinity = 1/0.0
=> Infinity
irb(main):002:0> (0..Infinity).include?(100000000000000)
=> true
irb(main):003:0> (0..Infinity).include?(-1)
=> false
irb(main):004:0> (-Infinity..0).include?(-1)
=> true
irb(main):005:0> (-Infinity..0).include?(-100000000000000000000)
=> true
irb(main):006:0> (-Infinity..0).include?(1)
=> false
irb(main):007:0> everything = -Infinity..Infinity
=> -Infinity..Infinity
irb(main):008:0> everything.include? 0
=> true


But ruby is not like haskell, which eval lazily - so don't try everything.to_a[0..100] :)

May 12, 2009

[ANN] Rubytest.vim 0.9.5 Released

Rubytest.vim is a vim (http://www.vim.org) plugin, which helps you to run ruby test (including vanilla test, rspec, shoulda etc.) in vim.

Changelog
---------

* Support quickfix: you can view test errors in quickfix window now
* Small fixes.

Get it here: http://www.vim.org/scripts/script.php?script_id=2612

May 4, 2009

The problem with young entrepreneurs

"Most of the time, this leads to the well-known case of “solutions looking for problems” - beautiful technology that can’t become a profitable business.

Best ideas are a side-effect from solving significant problems that the entrepreneurs themselves experience, observe and intimately understand. As young Web entrepreneurs, we aren’t sufficiently aware of important real-world problems, since our life mostly consists of hacking, coffee and occasional entertainment."

via

Apr 30, 2009

Something about latest vimperator

Latest vimperator release doesn't work well with Firefox 3.0.x and TabMixPlus: slow autocompletion, broken tab functions.

M.Terada provides two patch to make tabs work decently again, I don't know whehter it's included in vimperator's repository now, but you can download them here and here

For faster auto completion, set complete and preload options like this in your .vimperatorrc may help:


set complete=sbh
set nopreload


Some guys suggest setting wildmode to empty, but I don't like that idea.

Below is mine full .vimperatorrc:


set pageinfo=gfm
set showtabline=2
set defsearch=google
set complete=sbh
set nopreload
set showstatuslinks=2
set smartcase
set newtab=all
"set wildmode=

map l gt
map h gT
map b :bmarks!<Space>
map ;; d
map s :js open("mailto:?SUBJECT='" + escape(document.title) + "'" + "&BODY=" + escape(document.getElementById("urlbar").value))<CR>

map <C-p> :pa<CR>
map <C-k> tg<Space>
map <C-u> <C-v><C-u>
map <C-y> <C-v><C-y>
map <C-R> a<Space>-tags=toread<CR>
map <C-r> :bmarks -tags=toread<CR>
map <C-d> :delbm<CR>
"map <C-l> :sidebar LiveHTTPHeaders<CR>

map <silent> <F9> :js inspectDOMDocument(document)<CR>
map <silent> <F1> :js toggle_element('toolbar-menubar');toggle_element('nav-bar')<CR>
map <silent> <F2> :emenu Edit.Preferences<CR>
map <silent> <F3> :emenu Tools.Live HTTP headers<CR>
map <silent> <F10> :exe ":o dict2 "+content.getSelection()<CR>

autocmd PageLoad .* :js modes.passAllKeys = /(mail\.google\.com)|(google\.com\/reader)/.test(buffer.URL)

set nextpattern+=^\s*下一页\s*$
set previouspattern+=^\s*上一页\s*$

javascript <<EOF
(function(){
var feedPanel = document.createElement("statusbarpanel");
feedPanel.setAttribute("id", "feed-panel-clone");
feedPanel.appendChild(document.getElementById("feed-button"));
feedPanel.firstChild.setAttribute("style", "padding: 0; max-height: 16px;");
document.getElementById("status-bar")
.insertBefore(feedPanel, document.getElementById("security-button"));
})();
EOF

javascript << EOF
toggle_element = function (name) {
document.getElementById(name).collapsed ^= 1;
}
EOF

echo ".vimperatorrc sourced"

" vim: ft=vimperator sw=2 sts=2

Apr 19, 2009

Announcement of rubytest.vim: a vim plugin aims to help you run ruby test conveniently

Rubytest.vim is a vim (http://www.vim.org) plugin, which helps you to run ruby test (including vanilla test, rspec, shoulda etc.) in vim.

http://www.vim.org/scripts/script.php?script_id=2612

Installation
------------

Copy all files to your ~/.vim directory.

Usage
-----

After installation, press t will run the test under your cursor if you are editing a ruby test file.

example:

$ cd
$ vim test/unit/user_test.rb
(move cursor into a test case, press t)

( is mapping to '\' by default in vim)

You can customize the command which will be used to run the test case by settting these options in your vimrc file:

let g:rubytest_cmd_test = "ruby %p"
let g:rubytest_cmd_testcase = "ruby %p -n '/%c/'"
let g:rubytest_cmd_spec = "spec -f specdoc %p"
let g:rubytest_cmd_example = "spec -f specdoc %p -e '%c'"

(%p will be replaced by the path of test file, %c will be replaced by the name of test case under cursor)

Default Key Bindings
--------------------

t: run test case under cursor
T: run all tests in file

You can change default key bindings:

map \ RubyTestRun " change from t to \
map ] RubyFileRun " change from T to ]

http://www.vim.org/scripts/script.php?script_id=2612

Mar 27, 2009

Bidirectional many-to-many relationship in ActiveRecord

Bidirectional many-to-many relationship is a common pattern in design, like friendships between users, memberships between users and groups, etc. In this article I'll illustrate how to implement such a relationship in ActiveRecord/Rails.

Let's start with a little context. Suppose we want to add the populer 'friends' feature for our users, we already have User model, what we need is a join table 'friendships' to connect users.


class User < ActiveRecord::Base
end

class CreateFriendships < ActiveRecord::Migration
def self.up
create_table :friendships, :id => false do |t|
t.integer :left_user_id
t.integer :right_user_id
end
end

def self.down
drop_table :friendships
end
end


The behavior we want, can be written like this:


test "should has many friends" do
users(:quentin).friends << users(:aaron)
assert_equal 1, users(:quentin).friends.reload.count
end


Of course the test is failed when we run it now. After reading our requirements we decide to use has_and_belongs_to_many (habtm) in ActiveRecord because it is enough for now, so we modify our User model like this:


class User < ActiveRecord::Base
has_and_belongs_to_many :friends, :join_table => 'friendships',
:foreign_key => 'left_user_id', :association_foreign_key => 'right_user_id',
:class_name => 'User'
end


Now run our test, passed, perfect! We can leave office and enjoy *fill whatever you like* now.

"Wait", your brilliant colleague says, and add one line to your unit test:


test "should has many friends" do
users(:quentin).friends << users(:aaron)
assert_equal 1, users(:quentin).friends.reload.count
assert_equal 1, users(:aaron).friends.reload.count
end


He runs the test again and it failed. It's unfair if quentin treat aaron as his friend but aaron doesn't do the same to quentin, isn't it? To fix this problem we need to custom insert and delete sql for the habtm relationship:


class User < ActiveRecord::Base
has_and_belongs_to_many :friends, :join_table => 'friendships',
:foreign_key => 'left_user_id', :association_foreign_key => 'right_user_id',
:class_name => 'User',
:insert_sql => 'insert into friendships (`left_user_id`, `right_user_id`) values
(#{id}, #{record.id}), (#{record.id}, #{id})',
:delete_sql => 'delete from friendships where (left_user_id = #{id} and right_user_id
= #{record.id}) or (left_user_id = #{record.id} and right_user_id = #{id})'
end


What we do here, is to add two friendship records (both direction) when we add a user to another's friends set. We do the same when delete a user from one's friends set. We rerun the test and it passes as we expected.

Note the ":id => false" argument when we create the join table 'friendships', without it you'll have troubles when loading friends objects. I think this is a long history bug of ActiveRecord, I don't know why it is not fixed. If you really want to keep the 'id' field and use habtm at the same time, a workaround is customize the finder_sql:


class User < ActiveRecord::Base
has_and_belongs_to_many :friends, ...
:finder_sql => 'select users.* from friendships left outer join users on
friendships.right_user_id = users.id where friendships.left_user_id = #{id}'
end


The bidirectional habtm we build here, is in fact a self-referential bidirectional relationship, it relates models to the same type of models (users to users). What will happen if the relationship is NOT self referential?

In that case we should not use habtm, otherwise our (at least my) brain will be burned by the complicated sqls it brings. Don't waste time on those sqls, ActiveRecord provides another way to build many-to-many relationship: has_many :through

So one day the boss comes to you and asks, "Can we group our friends? I want to put Gates in group Evil and Male, and Linus in group Minix and Antarctic"

"Sure", you answered, how can you say No to your boss?

Now the many-to-many relationship is between groups and users, we need to modify User model, create a Group model and write a migration to modify friendships table. Since we'll use has_many :through for this requirement, we also need a model for the join table 'friendships':


class User < ActiveRecord::Base
has_many :groups
end

class CreateGroups < ActiveRecord::Migration
def self.up
create_table :groups do |t|
t.string :name
t.integer :user_id
end
end

def self.down
drop_table :groups
end
end

class Group < ActiveRecord::Base
belongs_to :user
has_many :friendships
has_many :friends, :through => :friendships
end

class RemodelFriendships < ActiveRecord::Migration
def self.up
remove_column :friendships, :left_user_id
rename_column :friendships, :right_user_id, :friend_id
change_column :friendships, :group_id, :integer, :null => false
end

def self.down
change_column :friendships, :group_id, :integer, :null => true
rename_column :friendships, :friend_id, :right_user_id
add_column :friendships, :left_user_id
# execute "..."
end
end

class Friendship < ActiveRecord::Base
belongs_to :group
belongs_to :friend, :class_name => 'User'
validates_uniqueness_of :friend_id, :scope => :group_id
end


Pretty good, it works. But we have the same problem when we use habtm, the relationship is not bidirectional. How can we fix it?

ActiveRecord allow you to extend the association proxy when use has_many :through, the solution here is to override the proxy's default CRUD methods. Let's create a new file lib/bidirection.rb:


module Bidirection
def <<(friend)
Friendship.create :group_id => friend.groups.default.id, :friend_id => self.proxy_owner.user.id
super(friend)
end

[:delete, :destroy].each {|m|
define_method(m) {|friends|
friends = [friends] unless friends.instance_of?(Array)
friends.each {|friend|
Friendship.first(:conditions => {:group_id => friend.groups.default.id, :friend_id => self.proxy_owner.user.id}).try m
}
super(friends)
}
}

end


Then enhance our has_many :through with it:


class Group < ActiveRecord::Base
...
has_many :friends, :through => :friendships, :extend => Bidirection
end


That's it, you have bidirectional many-to-many relationship between groups and users now. For convinient you can create a default group when create user (use before_create hook), and delegate friends and friends= methods in User model to user's default group, in that way you get the self-referential bidirectional many-to-many relationship betweent users back, as we have when using habtm.