Jack comes to code

Ruby / Rails / Sinatra / APIs

‘NoSqlize’ PostgreSql With HSTORE in Rails (3.2+)

Many people asked why Mac OSX, Rails and Heroku prefer PostgreSql to MySql. This post is talking about one of its outstanding features: hstore

One year ago, Ryan Bates’s post had introduced hstore, but there are a few updates for Rails 3.2. If you did not know what hstore is, this is its usage in Rails:

1
user = User.create(:interests => {male: false, femal: false, it_is_my_little_secret: true})

It’s easy to find there are json-like key/value pairs in the example above. It’s more awesome that the key it_is_my_littler_secret is dynamic, which can be customized by front end users. Let’s see how it could happen with Rails now:

Get Started

Gemfile
1
gem 'activerecord-postgres-hstore'
config/application.rb
1
config.active_record.schema_format = :sql #As the schema for hstore can't be represented by ruby
Terminal
1
rails g hstore:setup # Run 'CREATE EXTENSION IF NOT EXISTS hstore'
Migration File
1
2
add_column :users, :interests, :hstore
execute 'CREATE INDEX users_interests ON users USING GIN(interests)'  #Index on the hstore field
Termial
1
rake db:migrate

The basic setup is done, now we are looking into how to use hstore in Rails’ MVC framework:

Usage in Rails

app/models/user.rb
1
2
3
  attr_accessible :interests #To make sure interests is not mass assignment protected
  store_accessor :interests, %w{male, female} #Put any default keys showing in html forms
  serialize :interests, ActiveRecord::Coders::Hstore
app/views/users/edit.html.haml
1
2
3
4
5
6
7
8
9
10
  =form_for @user do |f|
   .row
    =f.label :male
    =f.check_box :male
   .row
    =f.label :female
    =f.check_box :female
   .row
    =label :new_key, "Other"
    =text_field :interests, :new_key
app/controllers/users_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
def update
  @user = User.find(params[:id])
  if @user.update_attributes(params[:user])
    if params[:interests].present? and params[:interests][:new_key].present?
      @user.interests = @user.interests.merge(params[:interests][:new_key] => true) #Add the new key to the interests
      @user.save
    end
    render "#{some_path_with_success_message}"
  else
    render "#{some_path_with_fail_message}"
  end
end

Yeah, that’s it. You could be more flexible by not fixing any keys for the hstore field.

Querying hstore fields is not pain at all, here’s the full list of hstore operations

Wana comment or punch on the post?

Upgrading to Mountain Lion(2)

With basic fix from (1) of Mountain Lion for Rails projects, most of application should run as well as in Lion. While bad things always happen unexpectedly. When creatig a new rails app, or adding a new ruby version to RVM, some exceptions would be like this:

1
2
3
4
5
6
7
8
9
10
11
Installing json (1.7.4) with native extensions
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension
.
        C:/Programozas/Ruby192/bin/ruby.exe extconf.rb
creating Makefile

make
'make' is not recognized as an internal or external command, operable program or batch file.


Gem files will remain installed in C:/Programozas/Ruby192/lib/ruby/gems/1.9.1/gems/json-1.7.4 for inspection.

Believe above is one of the gem version using make, which is somehow not defined as the command it should have been. The reasons are you miss GCC 4.2 (if you are using a ruby version older than 1.9.3) and Apple sets the ownership of /usr/local to root. Commands to go:

1
2
3
4
> sudo chown -R `whoami` /usr/local
> brew tap homebrew/dupes
> brew install apple-gcc42
> sudo ln -s /usr/bin/gcc /usr/bin/gcc-4.2

Unfortunately, now when you install or reinstall a ruby version with RVM, there are still weird errors. Even if many people are happy with work-around solutions like:

http://stackoverflow.com/questions/11660673/install-ree-1-8-7-with-rvm-on-mountain-lion

http://stackoverflow.com/questions/11664835/mountain-lion-rvm-install-1-8-7-x11-error

You can try them, but neither work for me. My last approach is to reinstall RVM from (1.10 to 1.14), then rejoice!

1
2
> rvm implode
> curl -L https://get.rvm.io | bash -s stable

(You may be unhappy with the last solution, but I guess Mountain Lion users need to do so soon or later)

Upgrading to Mountain Lion(1)

Version upgrading becomes a continuous plain for developers. Plugins, packages, frameworks, languages and now Mac OS come to offer an upgrade. If you wait until your current one goes deprecated or unsupported, you may be likely to spend a few times more hours on it. Besides, various dependencies will make any upgrading risky. Fortunately, Mountain Lion seems not to get things too messy, but majorly you have to install Xcode 4.4 and reinstall imagmagick and Rmagick if they are used. Only a few steps to go on Mountain Lion:

  1. Install Xcode 4.4 from App Store

  2. Install Commands Line Tools from Xcode, Preference -> Downloads

  3. Install XQuartz

  4. In terminal:

1
2
3
  brew update
  brew unlink imagmagick
  brew install --fresh imagemagick

It takes a few mintues, but can’t be simpler to go.

Rspec in Turbo Mode: Setup Spork + Factory Girl + Rspec

Rspec is normally preferrable against Rails’ Test::Unit. However, developers can get anxious easily when running slow Rspec hundreds of time every day. It is slow because every time you start a test, even if there’s only one file, Rspec loans all files. Instead, Sport forks a copy of your Rails server on DRb server in the background, when you kick off your testing.

The basic installation and configuration can’t be simpler with the instruction from its README. However, people still find problems afterwards:

First, make sure you have turned off cache class in environment conf:

config/environments/test.rb
1
config.cache_classes = false

Second, clean dependencies at the end of Spork.prefork block every time you start Spork:

spec/spec_helper.rb
1
2
3
Spork.prefork do
  ActiveSupport::Dependencies.clear
end

Simply ask FactoryGirl to reload new fixture every time you run test

spec/spec_helper.rb
1
2
3
4
Spork.each_run do
  # This code will be run each time you run your specs.
  FactoryGirl.reload
end

Here we go! Spork also supports different Ruby frameworks and IDEs.

Why We Use Ruby or Rails?

Having been using Ruby and Rails for a few years, recently I find it becomes trendy, even if in Hong Kong, a commercial and finanacial city more people want to build their web application in such a *new language.

When being asked why they like Ruby or Rails, their answers among:

  • It’s preferred by lots of foreign developers
  • Some professors said it is the trend of web development in the future
  • PHP is pretty boring
  • Twitter’s built in RoR
  • Some of my developers in the team like to try it
  • It is cooler

Yes, it is cooler to me, but I don’t think these reasons persuade many people move from another programming lanuage to Ruby. Here I give why I still stick to this language:

  • Ruby Community

I haven’t seen any programming community as social as Ruby’s. Github is the most well known project collabration and open source hosting site with a slogan as Social Coding. *Social means developers feel happy to share what they have, to listen what is interesting and even to contribute what they can to make the earth run faster. A stats of lanuages in Github, in which Ruby is the socialest server-side language (guessing every web app needs JavaScript).

Every year there are different scale Ruby Conferences all around of world and uncountable meetups. Many developers pay themselves for debating with others, giving speaches and helping promote Ruby and its community. Ruby or Rails itself is not faultless or the best of all and even can be hacked at some point, but its followers believe things get better everyday as long as the community is well there.

  • Open Source

Because of the community is strong, there’re many open source gems or plugins are there. Java programmers probably find Rails projects don’t need much coding, as all of modules are free to download.

A few year ago, I was in a dinner but it finally turned to be a debate between a Microsoft fan and a Ruby developer. The former working in a governmental institution holded Microsoft or IBM gives full support, which is reliable to large scale projects. While the Ruby guy said giving a google or shouting about an error will fix your problems faster.

I would stand with Microsoft if it were 7 years ago, but now in the age of Internet, which features/functions you want to build or which errors you come across some people might post them somewhere as public and searchable resource. We definitely wants technical support from all developers on the earth rather than from a single company making you spend a thousand bucks per month.

  • Launage itself

Here I’d like to save thousands of words, as there are many posts give much praise to it.

http://www.linuxjournal.com/article/5915 See More

People are saying Ruby is good, but everyone involved needs to know why.

Still remmeber in the last Red Dot Ruby Conf, @nzkoz (one of Rails core team) complained about how many pull requests and emails he sees every morning, which makes him feel like this:

Coming Back From Singapore Red Dot Ruby Conf

去年因为工作的原因没能参加新加坡第一届的rubyconf, 今年巧合的因为工作的原因(公司资助所有人都报名参加)有幸参加. 今年还是叫Red Dot(不知为何), 还是有Andy Croll组织.

由于今年组织方有了一年的大会经验, 可圈可点的地方真是不少, 于是趁还有印象跟去年的Taiwan Rubyconf做了个比较:

  • 讲者水平 (Red Dot 胜)

Red Dot嘉宾的份量明显比上年增强不少, 有来自Google, Github, Heroku的, 还有Rails Core Team的@nzkoz. 而且几乎所有的讲者都很乐意跟任何人交谈, 也不吝啬给纪念品Github的@holman. 虽然有些talk在之前的某些ronf上也有提过, 但是讲者们还都是很负责任的加入新的内容或加以修改. 不过缺点是或许因为如此, 很多小排公司的有份量的讲者没有能够入围.

  • 与会者水平 (Red Dot 胜)

说实话, 不能确定所有的听众的水平如何, 但是交谈下来发现很多人不比台上的讲者差, 而且更加多元化, 本地的听众几乎不到3成.

  • 会外活动安排 (Red Dot胜)

这次Speakers Meeting安排了在大会之前, 而在最后一天的晚上, 在市中心有个Post Conference Party, 而且赞助商给每人免费提供两杯酒水, 让大家都多了一个理由再聚一聚.

  • 会议场所设施安排 (平手)

Red Dot的优点是会议在新加坡国立大学的文化中心举行, 会议室的大小刚好合适. 最令我高兴的一点是, 有点儿像听音乐剧, 所有人都可以在2楼, 甚至3楼的地方俯瞰讲台, 有种想VIP Seat的感觉. 但是缺点是绝大多数座位比较狭窄, 而且没有Taiwan Rubyconf上那样大家都一个桌台. 另外也没有准备给台下发言的话筒, 这点Taiwan的中央研究院要好很多. 两者都可以改善的是最好把会议地点不要安排的那么远, 所有人都要为来去伤脑经.

  • 会议饮食安排 (平手)

Red Dot 虽然没有提供早餐(可能估计很多讲者都会住酒店), 但是提供的午餐和饮品在数量上和质量上都占优.

看起来, 这次Red Dot Rubyconf要优胜不少, 毕竟头几个要素才最重要. 但是代价是几乎4倍的票价, 即使Early-Bird Price也是3倍左右. 不能说这样一个高水平的Conf就使我的Ruby水平突飞猛进, 因为:

1.不是所有的讲题和内容都是”稀有的”. 很多讲者讲的东西, 台下精通的人不在少数, 甚至研究更深的都有

2.不是所有的讲题都涉及Ruby或者Rails. 还是想很多有关运作性能, 前端开发和架构分析的内容

3.最重要的一点, 我始终认为一个听众通过Conference可能会有所启发, 但是更大成长可能会在于在听完talk之后如何去做, 可以是尝试新的工具, 可以是检讨现有的系统, 也可以是专研任何新的插件.

所以我认为一个成功的conference不应该是一组Presentation, 所有成员, 组织者, 讲者和听众的互动以及之后的延伸才是最重要的.

快要下飞机了, 下一篇可能会讲讲Ruby Community.

Play Around Time Range in Ruby on Rails

Recently in Ruby 1.9.2, came across lots of confusing definitions about Time, DateTime, TimeWithZone… Finally the point taking me to remove hands from keyboards and to think about and clarify them was the ‘INFINITE’ warning:

Time#succ warning
1
lib/active_support/time_with_zone.rb:327: warning: Time#succ is obsolete; use time + 1

This warning keeps being throwing and your implement is stuck on somewhere.

In fact it has nothing to do with the warning itself or the succ method deprecation, but the usage of how to get a range of a time period. Some examples:

Time Range Examples
1
2
3
4
5
6
7
8
(1.days.ago .. DateTime.now) # => Tue, 21 Feb 2012 09:29:12 UTC +00:00..2012-02-22 17:29:12 +0800
1.days.ago.class # => ActiveSupport::TimeWithZone
DateTime.now.class # => Time
(DateTime.now - 1.day .. DateTime.now) # => 2012-02-21 17:31:25 +0800..2012-02-22 17:31:25 +0800

(1.days.ago .. DateTime.now).to_a # => *Infinite warnings as above
(DateTime.now - 1.day .. DateTime.now).to_a # => [Tue, 21 Feb 2012 17:33:14 +0800, Wed, 22 Feb 2012 17:33:14 +0800]
(10.seconds.ago .. DateTime.now).to_a # => Not infinite but 11 warnings as above

Here figures out the problem, to count on a range in time-like formats, for ActiveSupport::TimeWithZone, it counts seconds by seconds, while DateTime counts by day.

Furthermore, if the task is to check if a time is within a time range, you could use include? or cover? in Range class, but they are not always the same:

Task: Check within a time range
1
2
(10.seconds.ago..Time.now).include?(Time.now-1.second) # => 11 warnings as above
(10.seconds.ago..Time.now).cove?(Time.now-1.second) # => true

The reason is that include? will convert numerical (including ActiveSupport::TimeWithZone) parameters into integers, while cover? does not. Here are their source code:

Source code of Range#inlcude?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static VALUE
range_include(VALUE range, VALUE val)
{
    VALUE beg = RANGE_BEG(range);
    VALUE end = RANGE_END(range);
    int nv = FIXNUM_P(beg) || FIXNUM_P(end) ||
             rb_obj_is_kind_of(beg, rb_cNumeric) ||
             rb_obj_is_kind_of(end, rb_cNumeric);

    if (nv ||
        !NIL_P(rb_check_to_integer(beg, "to_int")) ||
        !NIL_P(rb_check_to_integer(end, "to_int"))) {    /* Where to integer conversion happens; */
        if (r_le(beg, val)) {
            if (EXCL(range)) {
                if (r_lt(val, end))
                    return Qtrue;
            }
            else {
                if (r_le(val, end))
                    return Qtrue;
            }
        }
        return Qfalse;
    }
    ...
}
Source code of Range#cover?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static VALUE
range_cover(VALUE range, VALUE val)
{
    VALUE beg, end;

    beg = RANGE_BEG(range);
    end = RANGE_END(range);
    if (r_le(beg, val)) {
        if (EXCL(range)) {
            if (r_lt(val, end))
                return Qtrue;
        }
        else {
            if (r_le(val, end))
                return Qtrue;
        }
    }
    return Qfalse;
}

Hello Octopress

Octopress is another creature after github’s Octocat that speeds developers’ productivity, but in a different field, blogging. The same thing is both are awesome and…having 4 times more legs as we do.

Discover both creatures
1
2
3
4
5
6
7
8
9
10
11
class Octopress
  def is_extinct?
    self.first ? true : false
  end
end

class Octocat
  def count
    1 #only can be found from github but nowhere else
  end
end