Monday, May 30, 2011

Using QtDesigner for Ruby Programming

QtDesigner is a GUI designer for Qt.
I'd like to try using it for ruby apps.

1. Preparation
a. Qt Creator - All-in-one Qt IDE, which includes Designer. Installed through Ubuntu Software Center
b. qtbindings - Ruby bindings for Qt lib. See this for installation
c. rbui4 - A tool to convert Designer outputs (ui) to ruby files, installed with qtbindings gem

2. Create a Project Folder
~/work/ruby/gt/ui_test01     # project folder
~/work/ruby/qt/ui_test01/ui  # subfolder to place QtDesigner outputs (ui files)

3. Create Forms using Designer
I followed an example shown in the second chapter of "Foundations of Qt Development" (by Johan Thelin from Apress). And I created two forms that look like:













This is a simple Phone Book application. The idea is that when you click the "Add New" or "Edit" button in the List Dialog (shown on the left), it opens the Edit Dialog (shown on the right) to add or edit item. The "Delete" button will delete a selected item from the list, and the "Clear" button will delete selected items in the list.

Note that there is a red line that links the "Clear" button and the List box. The line signifies Qt's event handling mechanism: Signal and Slot. In the Designer, you draw the line by dragging from one object (Clear button) to another (Listbox) in Edit Connections mode, selecting clicked() as it's signal and the clearSelection() as its signal-receiving slot. When you make an event connection like this within the Designer, you will not have to write codes to make the connection between signal and slot as you will see below. (I did not create any link for the other 3 buttons. I will have to write codes manually for these connections.)

The Edit Dialog form is created using an existing template which already contains a pair of the CANCEL/OK buttons. And these buttons already have connections so that I will not have to write codes for their connections.

One important thing to remember when using the Designer is to add a grid lauout to the dialog itself (not any widget group within); othewise, widgets in the dialog will not line up nicely.




Save these files (listdialog.ui and editdialog.ui) in /ui folder.
$ cd ~/work/ruby/qt/ui_test01/ui
$ ls
editdialog.ui  listdialog.ui

4. Convert to Ruby files using rbuic4
$ rbuic4 listdialog.ui -x -o listdialog_ui.rb 
$ rbuic4 editdialog.ui -x -o editdialog_ui.rb 
-x = generate extra code to test the class
-o = output file

5. A Quick Test for Generated ruby files
$ ruby listdialog_ui.rb
The result is:


















$ ruby editdialog_ui.rb
The result is:














5. Contents of ui files
QtDesigner's forms are just xml files. Here is the content of listdialog.ui:

 ListDialog
 
  
   
    0
    0
    539
    481
   
  
  
   Phone Book
  
  
   
    
     
      
       
        Add new
       
      
     
     
      
       
        Edit
       
      
     
     
      
       
        Delete
       
      
     
     
      
       
        Qt::Vertical
       
       
        
         20
         40
        
       
      
     
     
      
       
        Clear
       
      
     
    
   
   
    
   
  
 
 
 
  
   clearButton
   clicked()
   list
   clearSelection()
   
    
     495
     455
    
    
     337
     391
    
   
  
 



And here is the contents of editdialog.ui file:

 EditDialog
 
  
   
    0
    0
    421
    143
   
  
  
   Editor
  
  
   
    
     
      
       
        Name
       
      
     
     
      
     
     
      
       
        Number
       
      
     
     
      
     
    
   
   
    
     
      Qt::Vertical
     
     
      
       20
       15
      
     
    
   
   
    
     
      
       
        Qt::Horizontal
       
       
        
         108
         20
        
       
      
     
     
      
       
        Qt::Horizontal
       
       
        QDialogButtonBox::Cancel|QDialogButtonBox::Ok
       
      
     
    
   
  
 
 
 
  
   buttonBox
   accepted()
   EditDialog
   accept()
   
    
     410
     132
    
    
     157
     274
    
   
  
  
   buttonBox
   rejected()
   EditDialog
   reject()
   
    
     410
     132
    
    
     286
     274
    
   
  
 



6. Contents of generated rb files
The tool rbuic4 (Ruby UI Compiler) generates ruby files (rb) from QtDesigner's output files (ui). So listdialog_ui.rb looks like this:
=begin
** Form generated from reading ui file 'listdialog.ui'
**
** Created: 日 6月 19 11:49:30 2011
**      by: Qt User Interface Compiler version 4.7.0
**
** WARNING! All changes made in this file will be lost when recompiling ui file!
=end

require 'Qt4'

class Ui_ListDialog
    attr_reader :gridLayout
    attr_reader :verticalLayout
    attr_reader :addButton
    attr_reader :editButton
    attr_reader :deleteButton
    attr_reader :verticalSpacer
    attr_reader :clearButton
    attr_reader :list

    def setupUi(listDialog)
    if listDialog.objectName.nil?
        listDialog.objectName = "listDialog"
    end
    listDialog.resize(539, 481)
    @gridLayout = Qt::GridLayout.new(listDialog)
    @gridLayout.objectName = "gridLayout"
    @verticalLayout = Qt::VBoxLayout.new()
    @verticalLayout.objectName = "verticalLayout"
    @addButton = Qt::PushButton.new(listDialog)
    @addButton.objectName = "addButton"

    @verticalLayout.addWidget(@addButton)

    @editButton = Qt::PushButton.new(listDialog)
    @editButton.objectName = "editButton"

    @verticalLayout.addWidget(@editButton)

    @deleteButton = Qt::PushButton.new(listDialog)
    @deleteButton.objectName = "deleteButton"

    @verticalLayout.addWidget(@deleteButton)

    @verticalSpacer = Qt::SpacerItem.new(20, 40, Qt::SizePolicy::Minimum, Qt::SizePolicy::Expanding)

    @verticalLayout.addItem(@verticalSpacer)

    @clearButton = Qt::PushButton.new(listDialog)
    @clearButton.objectName = "clearButton"

    @verticalLayout.addWidget(@clearButton)


    @gridLayout.addLayout(@verticalLayout, 0, 1, 1, 1)

    @list = Qt::ListWidget.new(listDialog)
    @list.objectName = "list"

    @gridLayout.addWidget(@list, 0, 0, 1, 1)


    retranslateUi(listDialog)
    Qt::Object.connect(@clearButton, SIGNAL('clicked()'), @list, SLOT('clearSelection()'))

    Qt::MetaObject.connectSlotsByName(listDialog)
    end # setupUi

    def setup_ui(listDialog)
        setupUi(listDialog)
    end

    def retranslateUi(listDialog)
    listDialog.windowTitle = Qt::Application.translate("ListDialog", "Phone Book", nil, Qt::Application::UnicodeUTF8)
    @addButton.text = Qt::Application.translate("ListDialog", "Add new", nil, Qt::Application::UnicodeUTF8)
    @editButton.text = Qt::Application.translate("ListDialog", "Edit", nil, Qt::Application::UnicodeUTF8)
    @deleteButton.text = Qt::Application.translate("ListDialog", "Delete", nil, Qt::Application::UnicodeUTF8)
    @clearButton.text = Qt::Application.translate("ListDialog", "Clear", nil, Qt::Application::UnicodeUTF8)
    end # retranslateUi

    def retranslate_ui(listDialog)
        retranslateUi(listDialog)
    end

end

module Ui
    class ListDialog < Ui_ListDialog
    end
end  # module Ui

if $0 == __FILE__
    a = Qt::Application.new(ARGV)
    u = Ui_ListDialog.new
    w = Qt::Dialog.new
    u.setupUi(w)
    w.show
    a.exec
end

And editdialog_ui.rb looks like this:
=begin
** Form generated from reading ui file 'editdialog.ui'
**
** Created: 金 6月 3 20:45:49 2011
**      by: Qt User Interface Compiler version 4.7.0
**
** WARNING! All changes made in this file will be lost when recompiling ui file!
=end

require 'Qt4'

class Ui_EditDialog
    attr_reader :gridLayout_2
    attr_reader :gridLayout
    attr_reader :nameLabel
    attr_reader :nameEdit
    attr_reader :numberLabel
    attr_reader :numberEdit
    attr_reader :verticalSpacer
    attr_reader :horizontalLayout_3
    attr_reader :horizontalSpacer
    attr_reader :buttonBox

    def setupUi(editDialog)"
    if editDialog.objectName.nil?
        editDialog.objectName = "editDialog"
    end
    editDialog.resize(421, 143)
    @gridLayout_2 = Qt::GridLayout.new(editDialog)
    @gridLayout_2.objectName = "gridLayout_2"
    @gridLayout = Qt::GridLayout.new()
    @gridLayout.objectName = "gridLayout"
    @nameLabel = Qt::Label.new(editDialog)
    @nameLabel.objectName = "nameLabel"

    @gridLayout.addWidget(@nameLabel, 0, 0, 1, 1)

    @nameEdit = Qt::LineEdit.new(editDialog)
    @nameEdit.objectName = "nameEdit"

    @gridLayout.addWidget(@nameEdit, 0, 1, 1, 1)

    @numberLabel = Qt::Label.new(editDialog)
    @numberLabel.objectName = "numberLabel"

    @gridLayout.addWidget(@numberLabel, 1, 0, 1, 1)

    @numberEdit = Qt::LineEdit.new(editDialog)
    @numberEdit.objectName = "numberEdit"

    @gridLayout.addWidget(@numberEdit, 1, 1, 1, 1)


    @gridLayout_2.addLayout(@gridLayout, 0, 0, 1, 1)

    @verticalSpacer = Qt::SpacerItem.new(20, 15, Qt::SizePolicy::Minimum, Qt::SizePolicy::Expanding)

    @gridLayout_2.addItem(@verticalSpacer, 1, 0, 1, 1)

    @horizontalLayout_3 = Qt::HBoxLayout.new()
    @horizontalLayout_3.objectName = "horizontalLayout_3"
    @horizontalSpacer = Qt::SpacerItem.new(108, 20, Qt::SizePolicy::Expanding, Qt::SizePolicy::Minimum)

    @horizontalLayout_3.addItem(@horizontalSpacer)

    @buttonBox = Qt::DialogButtonBox.new(editDialog)
    @buttonBox.objectName = "buttonBox"
    @buttonBox.orientation = Qt::Horizontal
    @buttonBox.standardButtons = Qt::DialogButtonBox::Cancel|Qt::DialogButtonBox::Ok

    @horizontalLayout_3.addWidget(@buttonBox)


    @gridLayout_2.addLayout(@horizontalLayout_3, 2, 0, 1, 1)


    retranslateUi(editDialog)
    Qt::Object.connect(@buttonBox, SIGNAL('accepted()'), editDialog, SLOT('accept()'))
    Qt::Object.connect(@buttonBox, SIGNAL('rejected()'), editDialog, SLOT('reject()'))

    Qt::MetaObject.connectSlotsByName(editDialog)
    end # setupUi

    def setup_ui(editDialog)
        setupUi(editDialog)
    end

    def retranslateUi(editDialog)
    editDialog.windowTitle = Qt::Application.translate("EditDialog", "Editor", nil, Qt::Application::UnicodeUTF8)
    @nameLabel.text = Qt::Application.translate("EditDialog", "Name", nil, Qt::Application::UnicodeUTF8)
    @numberLabel.text = Qt::Application.translate("EditDialog", "Number", nil, Qt::Application::UnicodeUTF8)
    end # retranslateUi

    def retranslate_ui(editDialog)
        retranslateUi(editDialog)
    end

end

module Ui
    class EditDialog < Ui_EditDialog
    end
end  # module Ui

if $0 == __FILE__
    a = Qt::Application.new(ARGV)
    u = Ui_EditDialog.new
    w = Qt::Dialog.new
    u.setupUi(w)
    w.show
    a.exec
end
7. Some notes on generated ruby codes (1) You should not modify these files manually because they will be lost when re-generated. (2) The block "if $0 == __FILE__ ... end" was added by -x option.
$ rbuic4 listdialog.ui -x -o listdialog_ui.rb    # when -x option is used....
if $0 == __FILE__                                # this block is added due to -x option above
    a = Qt::Application.new(ARGV)
    u = Ui_ListDialog.new
    w = Qt::Dialog.new
    u.setupUi(w)
    w.show
    a.exec
end
This is convenient in that you can run it immediately (before writing any codes) to see how its form looks like. (3) Another convenience for this block is that it can be used as a template for your main ruby program as you will see below (main.rb). (4) The connection between signal and slot is made using string literals of method names (including parenthesis):
Qt::Object.connect(@clearButton, SIGNAL('clicked()'), @listView, SLOT('clearSelection()'))
Qt::Object.connect(@buttonBox, SIGNAL('accepted()'), editDialog, SLOT('accept()'))
Qt::Object.connect(@buttonBox, SIGNAL('rejected()'), editDialog, SLOT('reject()'))
(5) A dialog within Qt application is an instance of Qt::Dialog or Qt::Widget class. However, you see from the examples above that dialog classes in generated ruby ui files (Ui_ListDialog and Ui_EditDialog) do not inherit from Qt::Dialog or from Qt::Widget.
class Ui_ListDialog
...
end
class Ui_EditDialog
...
end
This means that somewhere in my application, I have to creates a dialog object that inherits from Qt::Dialog and that somehow I have to link it to the dialog class that defines its ui elements created by the Designer. That is done by the 3 lines in above example:
u = Ui_ListDialog.new  # insatance of ui class
w = Qt::Dialog.new     # instance of Qt::Dialog
u.setupUi(w)           # link the two to make the Qt dialog object to use UI elements
(6) A new module "Ui" is created and empty subclasses (ListDialog, EditDialog) inherited from the generated classes (Ui_ListDialog, ListDialog) is created in the new module Ui. The idea is that your own codes and changes are to be made in subclasses so that re-generating will not erase your changes. (7) The method setupUi() makes its form alive in your application by creating a bunch of instance variables for widgets and layouts in the form. A more Ruby-like method setup_ui() is created to call setupUi(). (8) The method retranslateUi() is defined for localization/internationalization of UI. 8. Creating main.rb My first version of main program started with a copy from a generated ui file (listdialog_ui.rb) and modified little. First I load those two ui files. I use subclass Ui::ListDialog instead of the parent Ui_ListDialog class. I can changes the subclass without worrying about being over-written by re-generation of ui codes. Here the goal is just to open the Edit Dialog. There is no functionality: clicking on a button does nothing.
# main.rb
# version 1
#       
require 'Qt4'
require './ui/listdialog_ui'   # load ui file generated by the Designer. I get an error without "./"  in Ruby 1.9.2
require './ui/editdialog_ui'   # load ui file generated by the Designer

if $0 == __FILE__
    a = Qt::Application.new(ARGV)
    w = Qt::Dialog.new
    u = Ui::ListDialog.new     # use subclass
    u.setup_ui(w)              # more rubyish, replacing setupUi()
    w.show
    a.exec
end

Run it.
$ ruby main.rb
The result.


















9. Updating main.rb
Now I would like to add a functionality: clicking "Add New" or "Edit" button opens an Edit Dialog. To do this, I create two Qt::Dialog classes (MainForm and EditForm) and link them to ui objects created with the Designer. Then I declare 3 slot methods that respond to cliked() signals. (I do not have to declare 'clear_selection()' slot method - already done within the Designer by drawing a line from Clear button to the List Widget.) Those slot methods create an instance of EditForm and calls exec method so that the dialog opens in modal mode. Note that I compare the return value of exec with fixed number 1. I am supposed to use predefined constant Qt::Accepted, but it crashes on me when I use it.

# main.rb
# version 2
#       

require 'Qt4'
require './ui/listdialog_ui'
require './ui/editdialog_ui'

class MainForm < Qt::Dialog

  slots 'add_item()', 'edit_item()', 'delete_item()'  # declaration
  # slots 'clear_selection'  -- in parent class: Ui_ListDialog  

  def initialize
    super
    @ui = Ui::ListDialog.new
    @ui.setup_ui(self)
    Qt::Object.connect(@ui.addButton, SIGNAL('clicked()'), self, SLOT('add_item()'))
    Qt::Object.connect(@ui.editButton, SIGNAL('clicked()'), self, SLOT('edit_item()'))
    Qt::Object.connect(@ui.deleteButton, SIGNAL('clicked()'), self, SLOT('delete_item()'))
    #-- in parent class: Ui_ListDialog
    #Qt::Object.connect(@ui.clearButton, SIGNAL('clicked()'), self, SLOT('clear_selection()')) 
 
    self.show
  end
 
  def add_item()
    d = EditForm.new(self)
    if(d.exec == 1) # I use "1" instead of Qt::Accepted constant becuase it crashes on me
    end
  end
 
  def edit_item()
    d = EditForm.new(self)
    if(d.exec == 1) # I use "1" instead of Qt::Accepted constant becuase it crashes on me
    end
  end
 
  def delete_item()
  end
 
  # -- in parent class: Ui_ListDialog
  # def clear_selection()
  # end

end

class EditForm < Qt::Dialog

  def initialize(parent=nil)
    super(parent)
    @ui = Ui::EditDialog.new
    @ui.setup_ui(self)
    self.show
  end

end

if $0 == __FILE__
  a = Qt::Application.new(ARGV)
  MainForm.new
  a.exec
end




10. Final main.rb
Finally I define all slot methods, giving all required functions to the program.

# main.rb
# version 3
#       
require 'Qt4'
require './ui/listdialog_ui'
require './ui/editdialog_ui'

class MainForm < Qt::Dialog

  slots 'add_item()', 'edit_item()', 'delete_item()'
 
  def initialize
    super
  
    @ui = Ui::ListDialog.new
    @ui.setup_ui(self)
 
    Qt::Object.connect(@ui.addButton, SIGNAL('clicked()'), self, SLOT('add_item()'))
    Qt::Object.connect(@ui.editButton, SIGNAL('clicked()'), self, SLOT('edit_item()'))
    Qt::Object.connect(@ui.deleteButton, SIGNAL('clicked()'), self, SLOT('delete_item()'))
 
    self.show
 
  end
 
  def add_item()
    d = EditForm.new(self)
    if(d.exec == 1) # OK clicked
      @ui.list.add_item(d.name + ": " + d.number)
    end
  end
 
  def edit_item()
    if(@ui.list.current_item) # if any item is selected
      temp = @ui.list.current_item.text
      a = temp.split(/: /)
      d = EditForm.new(self)
      d.name = a[0]
      d.number = a[1]
      if(d.exec == 1) # OK clicked
        @ui.list.current_item.text = d.name + ": " + d.number
      end
    end
  end
 
  def delete_item()
    @ui.list.current_item.dispose # delete selected object
  end
  
end

class EditForm < Qt::Dialog
 
  def initialize(parent=nil)
    super(parent)
    @ui = Ui::EditDialog.new
    @ui.setup_ui(self)
    self.show
  end

  def name
    @ui.nameEdit.text
  end
 
  def name=(s)
    @ui.nameEdit.set_text(s)
  end
 
  def number
    @ui.numberEdit.text
  end
 
  def number=(s)
    @ui.numberEdit.set_text(s)
  end
 
end

if $0 == __FILE__
  a = Qt::Application.new(ARGV)
  MainForm.new
  a.exec
end

Sunday, May 29, 2011

Ruby Sites

1. Ruby Home Pages
Ruby Language: http://www.ruby-lang.org/en/
JRuby (Java): http://www.jruby.org/
Iron Ruby (.Net): http://ironruby.net/
MacRuby (OSX): http://www.macruby.org/
Ruby Enterprise Edition: http://www.rubyenterpriseedition.com/
Rubinius: http://rubini.us/

2. Version Managers
rvm (Ruby Version Manager for Linus/OSX): https://rvm.beginrescueend.com/
pik (Ruby Version Manager for Windows):https://github.com/vertiginous/pik

3. RubyGems
RubyGems.org: http://rubygems.org/
GitHub: https://github.com/rubygems
RubyForge: https://rubyforge.org/projects/rubygems/

4. Web Frameworks
Ruby on Rails: http://rubyonrails.org/
Ramaze: http://ramaze.net/
Sinatra: http://www.sinatrarb.com/
Merb: http://www.merbivore.com/
Padrino: http://www.padrinorb.com/

5. Ruby on Cloud
Heroku: http://www.heroku.com/
EngineYard: http://www.engineyard.com/
DotCloud: https://www.dotcloud.com/

6. Desktop GUI Framwork
Shoes: http://shoesrb.com/
Bowlin: http://bowlineapp.com/
WxRuby: http://wxruby.rubyforge.org/
FxRuby: http://www.fxruby.org/
QtRuby (QtBindings): https://github.com/ryanmelt/qtbindings
QtRuby (QtRuby4): http://rubyforge.org/projects/korundum/
RubyGtk2 (Ruby GNOME2): http://ruby-gnome2.sourceforge.jp/

7. Mobile Ruby
Rhodes: http://rhomobile.com/products/rhodes/


8. Ruby Documents
Ruby-Doc: http://www.ruby-doc.org/

9. Ruby News
Ruby Inside: http://www.rubyinside.com/

Thursday, May 26, 2011

Installing qtbindings (alt Qt-Ruby) on Ubuntu

Installed qtbindings (Ruby gem for Qt took kit).

1. Requirements
Qt 4.6.x
cmake 2.6.3+
gcc 4.x
Ruby


2. Install qtbindings.
$ gem install qtbindings
Building native extensions.  This could take a while...
Successfully installed qtbindings-4.6.3.2
1 gem installed
Installing ri documentation for qtbindings-4.6.3.2...
Installing RDoc documentation for qtbindings-4.6.3.2...

3. Test.
$ irb
ruby-1.9.2-p180 :001 > require 'Qt'
 => true 
ruby-1.9.2-p180 :002 > a = Qt::Application.new(ARGV)
 => # 
ruby-1.9.2-p180 :003 > button = Qt::PushButton.new('Hello World!',nil)
 => # 
ruby-1.9.2-p180 :004 > button.resize(300,30)
 => nil 
ruby-1.9.2-p180 :005 > button.show()
 => nil 
ruby-1.9.2-p180 :006 > a.exec()

4. Result.





5. Resouces.
RubyGems.org: http://rubygems.org/gems/qtbindings
QtBindings Homepage: https://github.com/ryanmelt/qtbindings
Qt Homepage: http://qt.nokia.com/

Wednesday, May 25, 2011

Installing json

Installed json.
$ gem install json
Building native extensions.  This could take a while...
Successfully installed json-1.5.1
1 gem installed
Installing ri documentation for json-1.5.1...
Installing RDoc documentation for json-1.5.1...

Displaying List of Tables in PostgreSQL

1. Simple List of Tables (in 'public' schema)
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public';
The result:
table_name 
------------
test
table1
(2 rows)

2. Display List of Tables with Owners (in 'public' schema)
SELECT c.relname AS table_name, u.usename AS owner
FROM pg_class c, pg_user u, pg_namespace n
WHERE c.relowner = u.usesysid 
AND c.relkind = 'r' 
AND c.relnamespace=n.oid
AND n.nspname = 'public';
The result:
table_name |   owner 
-----------+-----------
test       | socrateos
table1     | socrateos 
(2 rows)

3. Display List of Tables from All Schemata (except system's ones)
SELECT c.relname AS table_name, u.usename AS owner, n.nspname AS schema
FROM pg_class c, pg_user u, pg_namespace n
WHERE c.relowner = u.usesysid 
AND c.relkind = 'r' 
AND c.relnamespace=n.oid
AND n.nspname != 'information_schema' AND n.nspname !='pg_catalog';
The result:
table_name |   owner   | schema 
-----------+-----------+--------
test       | socrateos | public
table1     | socrateos | public
(2 rows)

Friday, May 20, 2011

Switching to PostgreSQL in Netzke Demo

I have successfully installed Netzke on my Ubuntu, but it uses MySQL. I want to use PostgreSQL.

1. Resources.
Netzke homepage: http://netzke.org/
Netzke demo code: https://github.com/skozlov/netzke-demo
My Installation experience: http://socrateos.blogspot.com/2011/05/installing-netzke-demo-app-on-ubuntu.html
A Solution to 'rake aborted! no such file to load — pg' error: http://www.petermac.com/rubyrails-3-and-postgresql/

2. Edit the config\database.yml file.
adapter: postgresql    # was: mysql2
username: postgres     # was: root
host: localhost        # was: socket: /var/run/mysqld/mysqld.sock

3. Create Database and Migrate for PostgreSQL.
But I got an error saying "no such file to load -- pg" even though I have pg installed and can be loaded, for example, in irb.
$ rake db:create && rake db:migrate
rake aborted!
no such file to load -- pg
....
$ gem list --local

*** LOCAL GEMS ***

...
pg (0.11.0)
...
$ irb
ruby-1.9.2-p180 :001 > require 'pg'
 => true 
ruby-1.9.2-p180 :002 > 

4. A solution to the problem: "no such file to load -- pg"
Thanks to Peter Mac Giollafhearga, there is a simple solution. You have to edit Gemfile, replacing mysql2 with pg.
source 'http://rubygems.org'

gem 'rails', '3.0.4'

gem 'pg'    # was: 'mysql2'

gem 'haml'

gem 'coderay'
gem 'faker'

gem 'netzke-core',        :path => File.expand_path('../vendor/gems/netzke-core', __FILE__)
gem 'netzke-basepack',    :path => File.expand_path('../vendor/gems/netzke-basepack', __FILE__)
gem 'netzke-persistence', :path => File.expand_path('../vendor/gems/netzke-persistence', __FILE__)

gem 'carrierwave'

gem 'inploy'

gem 'will_paginate', '~>3.0.pre2'

group :profiling do
  gem 'rack-perftools_profiler', '~> 0.1', :require => 'rack/perftools_profiler'
end

5. Try again to Crate Database and Migrate for PostgreSQL.
$ rake db:create && rake db:migrate
This time, it worked.

6. Launch the Server and See the Result.
$ rails s
=> Booting WEBrick
=> Rails 3.0.4 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-05-19 20:59:37] INFO  WEBrick 1.3.1
[2011-05-19 20:59:37] INFO  ruby 1.9.2 (2011-02-18) [i686-linux]
[2011-05-19 20:59:37] INFO  WEBrick::HTTPServer#start: pid=8030 port=3000

Thursday, May 19, 2011

Installing a Netzke demo app on Ubuntu

Installed a Netzke demo successfully but with some hiccups.

1. Resouces.
Demo souce codes: https://github.com/skozlov/netzke-demo
Netzke Home page: http://blog.writelesscode.com/

2. Install demo code and netzke gems: netzke-core, netzke-basepak, and netzke-persistence.
$ git clone git://github.com/skozlov/netzke-demo.git && cd netzke-deom
$ git clone git://github.com/skozlov/netzke-core.git vendor/gems/netzke-core 
$ git clone git://github.com/skozlov/netzke-basepack.git vendor/gems/netzke-basepack 
$ git clone git://github.com/skozlov/netzke-persistence.git vendor/gems/netzke-persistence 

3. Insall required gems. It failed to install mysql2
$ bundle
....
Installing mysql2 (0.2.6) with native extensions /home/socrateos/.rvm/rubies/ruby-1.9.2-p180/lib/ruby/1.9.1/rubygems/installer.rb:483:in `rescue in block in build_extensions': ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError)
....

3.1. Install missing mlysql.
$ sudo apt-get install libmysql-ruby libmysqlclient-dev

3.2 Rerun bundle. I aborted when it was installing perftools.rb; it seemed hung.
$ bundle
...
Installing perftools.rb (0.5.6) with native extensions  # it hangs here
^C

3.3 Then I tried to install perftools.rb separately. It turned out that it just takes a very long time to install. You just have to wait for several minutes!
$ gem install perftools.rb
Building native extensions.  This could take a while...    # go and get a coffee!
Successfully installed perftools.rb-0.5.6
1 gem installed
Installing ri documentation for perftools.rb-0.5.6...
Installing RDoc documentation for perftools.rb-0.5.6...

4. Created links to extjs and sencha-touch folders in public directory.
$ ln -s ~/work/extjs/ext-3.3.1/ public/extjs
$ ln -s ~/work/extjs/sencha-touch-1.1.0/ public/sencha-touch

5. Create database and migrate. But I got an error.
$ rake db:create && rake db:migrate
...
Could not find rack-perftools_profiler-0.4.0 in any of the sources
Try running `bundle install`.

5.1. Run bundle again to install remaining required gems.
$ bundle install
...
Using perftools.rb (0.5.6) 
Installing rack-perftools_profiler (0.4.0) 
Using thor (0.14.6) 
Installing railties (3.0.4) 
Installing rails (3.0.4) 
Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
Thank you!

5.2. Try again to create database and migrate. Still I got an error.
$ rake db:create && rake db:migrate
...
Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)
...
rake aborted!

5.3 Modified my config\database.yml file, giving the following info for all 3 databases.
password: secrete                                     # was empty
  socket: /var/run/mysqld/mysqld.sock # replaced /tmp/mysql.sock

5.4 Try one more time to create the database and migrate.
$ rake db:create && rake db:migrate
(in /home/socrateos/work/ruby/rails/netzke-demo)
(in /home/socrateos/work/ruby/rails/netzke-demo)
==  CreateBosses: migrating ===================================================
-- create_table(:bosses)
   -> 0.1495s
==  CreateBosses: migrated (0.1496s) ==========================================

==  CreateClerks: migrating ===================================================
-- create_table(:clerks)
   -> 0.1622s
==  CreateClerks: migrated (0.1623s) ==========================================

==  AddSessionsTable: migrating ===============================================
-- create_table(:sessions)
   -> 0.1624s
-- add_index(:sessions, :session_id)
   -> 0.3012s
-- add_index(:sessions, :updated_at)
   -> 0.2921s
==  AddSessionsTable: migrated (0.7561s) ======================================

==  CreateNetzkeComponentStates: migrating ====================================
-- create_table(:netzke_component_states)
   -> 0.1633s
-- add_index(:netzke_component_states, :component)
   -> 0.2925s
-- add_index(:netzke_component_states, :user_id)
   -> 0.3761s
-- add_index(:netzke_component_states, :role_id)
   -> 0.3185s
==  CreateNetzkeComponentStates: migrated (1.1508s) ===========================

==  AddImageToClerks: migrating ===============================================
-- add_column(:clerks, :image, :string)
   -> 0.3050s
==  AddImageToClerks: migrated (0.3051s) ======================================
It worked.

6. Now launch the server.
$ rails s
=> Booting WEBrick
=> Rails 3.0.4 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2011-05-18 06:05:59] INFO  WEBrick 1.3.1
[2011-05-18 06:05:59] INFO  ruby 1.9.2 (2011-02-18) [i686-linux]
[2011-05-18 06:05:59] INFO  WEBrick::HTTPServer#start: pid=3105 port=3000

7. Now go to http://localhost:3000 and see the result.

Monday, May 16, 2011

Installing pg on Ubuntu with rvm

Reinstalled pg (ruby-pg) on Ubuntu since I am now using rvm (Ruby Version Manager).
$ rvm use 1.9.2
Using /home/socrateos/.rvm/gems/ruby-1.9.2-p180
$ gem install pg
Building native extensions.  This could take a while...
Successfully installed pg-0.11.0
1 gem installed
Installing ri documentation for pg-0.11.0...
Installing RDoc documentation for pg-0.11.0...
$ irb
ruby-1.9.2-p180 :001 > require 'pg'
 => true 
ruby-1.9.2-p180 :002 > exit

Saturday, May 7, 2011

Ruby Cheat Sheets

1.1 Ruby Shebang
#!/usr/bin/env ruby

2.1 String Literals (Double quotes and Single Quotes)
puts "Year #{Time.now.year}"  # => "Year 2011"
puts 'Year #{Time.now.year}'  # => "Year #{Time.now.year}"
puts "First line.\nSecond line." 
  # => "First line.
  #     Second line."
puts 'First line.\nSecond line.' 
  # => "First line.\nSecond line."
puts "Mary's Lamb"  # => "Mary's Lamb"
puts 'Mary\'s Lamb' # => "Mary's Lamb"   -- single quote escape sequence
puts "C:\\Program Files\\" # => "C:\Program Files\"
puts 'C:\\Program Files\\' # => "C:\Program Files\" -- backslash escape sequence

2.2. String Literals (using Custom Delimiter, "Here document")
t = <<MY_DELIMITER   # EOS, EOF, EOC are often used.
First line.
Second line.
MY_DELIMITER
puts t
  # => "First line.
  #     Second line."

2.3 String Concatenation
puts "ABC"+"DEF"  # => "ABCDEF" 
puts "ABC" "DEF"  # => "ABCDEF"  (same as above)
puts "*" * 20 # => "********************"


2.4 String Length and Byte Size
"the world".encoding.to_s # => "UTF-8" (in Ruby 1.9.x or later)
"the world".encoding # => NoMethodError (in Ruby 1.8.x)
"the world".length # => 9 (number of characters, not of bytes, in Ruby 1.9.x or later)
"あ".length # => 1 (one character long in Ruby 1.9.x)
"あ".length # => 3 (three bytes long in Ruby 1.8.x)
"あ".bytesize # => 3 (bytes in Ruby 1.9.x)
"First line\nSecond line".length # => 22 ("\n" is one character)
'First line\nSecond line'.length # => 23 ('\n' consists of two characters)


2.5 Uppercase and Lowercase
"the world".capitalize # => "The world"
"THE WORLD".capitalize # => "The world"
"the world".upcase # => "THE WORLD"
"THE WORLD".downcase # => "the world"


2.6 Substring
"abcdefg"[0] # => "a" (in Ruby 1.9.x; You get '97' Ascii code in Ruby 1.8.x)
"abcdefg"[1] # => "b" (in Ruby 1.9.x; You get '98' Ascii code in Ruby 1.8.x)
"abcdefg"[1,3] # => "bcd"  (string of 3 characters starting from position 1) 
"abcabc".include? "abc" # => true (the string contains the substring "abc")
"abcabc".index "abc" # => 0 (position of the first occurrence of the substring)
"abcabc".index "abc", 2 # => 3 (position of the first occurrence from the position 2)
"abcabc".rindex "abc" # => 3 (position of the first occurrence from the tail)

2.7 String Alignment
s = "Ruby String"
s.center(20)     # => "    Ruby String     " 
s.ljust(20)      # => "Ruby String         " 
s.rjust(20)      # => "         Ruby String" 
s.center(20,'*') # => "****Ruby String*****" 

2.8 Removing Last Character or End-Of-Line with chop or chomp
"Sentence\n".chop       # => "Sentence" (remove any last char)
"Sentence\n".chop.chop  # => "Sentenc"  (remove any last char)
"Sentence\n".chomp      # => "Sentence" (remove only last end-of-line code if any)
"Sentence\n".chmp.chmp  # => "Sentence" (remove only last end-of-line code if any)
"Sentence\r".chop       # => "Sentence" (remove any last char)
"Sentence\r".chop.chop   # => "Sentenc"  (remove any last char)
"Sentence\r".chomp       # => "Sentence" (remove only last end-of-line code if any)
"Sentence\r".chomp.chomp # => "Sentence" (remove only last end-of-line code if any)
# '\r\n' is considered as a single character.
"Sentence\r\n".chop        # => "Sentence" (remove any last char)
"Sentence\r\n".chop.chop   # => "Sentenc"  (remove any last char)
"Sentence\r\n".chomp       # => "Sentence" (remove only last end-of-line code if any)
"Sentence\r\n".chomp.chomp # => "Sentence" (remove only last end-of-line code if any)
# Multi-byte codes in Ruby 1.9.x (UTF-8)
"第二章".chop.chop            # => "第" (removed two characters)
# Multi-byte codes in Ruby 1.8.x (ASCII)
"第二章".chop.chop.chop.chop  # => "第" (removed four bytes: two two-byte characters)
# Destructive method!
s = "Sentence\n"
s.chomp  # => "Sentence"   (remove end-of-line code if any, but...)
s        # => "Sentence\n" (original string not affected)
s.chomp! # => "Sentence"   (remove end-of-line code if any, and ...)
s        # => "Sentence"   (original string changed)

Wednesday, May 4, 2011

First Bowline App Failed to Run on Ubuntu

Here is my first Bowline app based on its home page example.

$ rvm use 1.9.2
 Using /home/socrateos/.rvm/gems/ruby-1.9.2-p180
$ bowline-gen app helloworld
Generating with app generator:
     [ADDED]  vendor
     [ADDED]  lib
     [ADDED]  db
     [ADDED]  build
     [ADDED]  log
     [ADDED]  public
     [ADDED]  app
     [ADDED]  app/models
     [ADDED]  app/binders
     [ADDED]  app/helpers
     [ADDED]  app/windows
     [ADDED]  config
     [ADDED]  config/initializers
     [ADDED]  config/first_run
     [ADDED]  app_first_run
     [ADDED]  Rakefile
     [ADDED]  Gemfile
     [ADDED]  public/index.html
     [ADDED]  public/javascripts/application.js
     [ADDED]  public/stylesheets/application.css
     [ADDED]  script/console
     [ADDED]  script/run
     [ADDED]  script/init
     [ADDED]  script/build
     [ADDED]  script/generate
     [ADDED]  config/environment.rb
     [ADDED]  config/environments/production.rb
     [ADDED]  config/environments/development.rb
     [ADDED]  .gitignore
     [ADDED]  public/icon.png
     [ADDED]  script
     [ADDED]  public/javascripts/jquery.js
     [ADDED]  public/javascripts/jquery.chain.js
     [ADDED]  public/javascripts/superclass.js
     [ADDED]  public/javascripts/bowline.js
     [ADDED]  public/javascripts/bowline.chain.js
     [ADDED]  app/windows/main_window.rb
     [ADDED]  config/application.yml
     [ADDED]  config/database.yml
     [ADDED]  config/boot.rb
     [ADDED]  README
$ cd helloworld
$ bowline-bundle
Calculating dependencies...
Updating source: http://rubygems.org
Updating source: http://gems.rubyforge.org
~/.rvm/gems/ruby-1.9.2-p180/gems/bowline-bundler-0.0.4/lib/bowline/bundler/resolver.rb:115:Warning: Gem::Dependency#version_requirements is deprecated and will be removed on or after August 2010.  Use #requirement
Caching: activemodel-3.0.7.gem
Caching: activesupport-3.0.7.gem
Caching: bowline-0.9.4.gem
Caching: bowline-bundler-0.0.4.gem
Caching: builder-2.1.2.gem
Caching: diff-lcs-1.1.2.gem
Caching: extlib-0.9.15.gem
Caching: highline-1.6.1.gem
Caching: i18n-0.5.0.gem
Caching: rubyzip2-2.0.1.gem
Caching: supermodel-0.1.4.gem
Caching: templater-1.0.0.gem
Downloading tzinfo-0.3.27.gem
Installing tzinfo (0.3.27)
Installing extlib (0.9.15)
Installing highline (1.6.1)
Installing diff-lcs (1.1.2)
Installing templater (1.0.0)
Installing bowline-bundler (0.0.4)
Installing builder (2.1.2)
Installing supermodel (0.1.4)
Installing rubyzip2 (2.0.1)
Installing activesupport (3.0.7)
Installing i18n (0.5.0)
Installing activemodel (3.0.7)
Installing bowline (0.9.4)
    **************************************************

      Thank you for installing Bowline.
      
      If you're on Linux, please install the WebKit library:
        sudo apt-get install libwebkit-dev

    **************************************************
Done.
$ ./script/run
Setting up Bowline. This could take a while...
Retrieving http://bowline.s3.amazonaws.com/linux/bowline-desktop.zip
:              100% |oooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:01
Extracting bowline-desktop.zip
Retrieving http://bowline.s3.amazonaws.com/linux/libs.zip
:              100% |oooooooooooooooooooooooooooooooooooooooooo| Time: 00:00:01
Extracting libs.zip
 uninitialized constant Gem::VERSION
~/work/ruby/bowline/helloworld/vendor/gems/ruby/1.9.1/environment.rb:41:in `eval'
 ~/.bowline/libs/rubylib/1.9.1/rubygems/specification.rb:443:in `initialize'
 (eval):3:in `new'
 (eval):3:in `'
 ~/work/ruby/bowline/helloworld/vendor/gems/ruby/1.9.1/environment.rb:41:in `eval'
 ~/work/ruby/bowline/helloworld/vendor/gems/ruby/1.9.1/environment.rb:41:in `'
 ~/work/ruby/bowline/helloworld/vendor/gems/ruby/1.9.1/environment.rb:2:in `'
 ~/work/ruby/bowline/helloworld/vendor/gems/environment.rb:4:in `require'
 ~/work/ruby/bowline/helloworld/vendor/gems/environment.rb:4:in `'
 ~/work/ruby/bowline/helloworld/config/boot.rb:8:in `require'
 ~/work/ruby/bowline/helloworld/config/boot.rb:8:in `'
 ~/work/ruby/bowline/helloworld/script/init:2:in `require'
 ~/work/ruby/bowline/helloworld/script/init:2:in `'

Sunday, May 1, 2011

Installing Bowline

Bowline is a Ruby/HTML desktop app framework.

1. Resources.
Home page: http://bowlineapp.com/
Github: https://github.com/maccman/bowline
Forum: http://groups.google.com/group/bowline-dev

2. Installation on Ubuntu
$ rvm use 1.9.2
Using ....../.rvm/gems/ruby-1.9.2-p180
$ gem install bowline
    **************************************************

      Thank you for installing Bowline.
      
      If you're on Linux, please install the WebKit library:
        sudo apt-get install libwebkit-dev

    **************************************************
Successfully installed highline-1.6.1
Successfully installed diff-lcs-1.1.2
Successfully installed extlib-0.9.15
Successfully installed templater-1.0.0
Successfully installed rubyzip2-2.0.1
Successfully installed supermodel-0.1.4
Successfully installed bowline-bundler-0.0.4
Successfully installed bowline-0.9.4
8 gems installed
Installing ri documentation for highline-1.6.1...
Installing ri documentation for diff-lcs-1.1.2...
Installing ri documentation for extlib-0.9.15...
Installing ri documentation for templater-1.0.0...
Installing ri documentation for rubyzip2-2.0.1...
Installing ri documentation for supermodel-0.1.4...
Installing ri documentation for bowline-bundler-0.0.4...
Installing ri documentation for bowline-0.9.4...
Installing RDoc documentation for highline-1.6.1...
Installing RDoc documentation for diff-lcs-1.1.2...
Installing RDoc documentation for extlib-0.9.15...
Installing RDoc documentation for templater-1.0.0...
Installing RDoc documentation for rubyzip2-2.0.1...
Installing RDoc documentation for supermodel-0.1.4...
Installing RDoc documentation for bowline-bundler-0.0.4...
Installing RDoc documentation for bowline-0.9.4...

$ bowline-gen --help
Usage: bowline generator_name [options] [args]

Generate components for your application or entirely new applications.

Available generators
    app                              Generates a new application.
    binder                           Generates a new binder.
    helper                           Generates a new helper.
    migration                        Generates a new database migration.
    model                            Generates a new model.
    window                           Generates a new window.

General options:
    -p, --pretend                    Run, but do not make any changes.
    -f, --force                      Overwrite files that already exist.
    -s, --skip                       Skip files that already exist.
    -d, --delete                     Delete files that have previously been generated with this generator.
        --no-color                   Don't colorize the output
    -h, --help                       Show this message
        --debug                      Do not catch errors

3. Install Webkit Dev library

Open synaptic.
Search and install libwebkit-dev.