babinho's weblog

Ruby and Coffee

Rails Integration Testing With MiniTest::Spec and Capybara

| Comments

So you want to do integration testing for your application, good for you. If you have chosen MiniTest and it’s specing DSL MiniTest::Spec as your test framework here is an example how to setup the integration tests to run fairly fast. Integration tests are similar to Cucubmer features, if you have used that before. I just like this style better because Cucumber, IMO adds too much noise, but it serves you as application documentation. I will also user Capybara for the webpage testing. Because we are using minitest-spec-rails gem, we don’t need to tinker with the config/application.rb and test settings, because ActiveSupport::TestCase is a subclass of MiniTest::Spec

First we will set up the gemfile, with the needed gems( I also use guard for my automated tests, but it’s worth writing another blog post, soon hopefully)

1
2
3
4
5
6
group :development, :test do
  gem 'minitest-spec-rails'
  gem 'capybara_minitest_spec' # for capybara integration and spec matchers
  gem 'capybara-webkit' # for headless javascript tests
  gem 'turn' # for prettier test output
end

After that we need to set up out test/test_helper.rb to use Turn and capybara

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require 'rubygems'
require 'rails/test_help'
require 'minitest/autorun'
require 'capybara/rails'
require 'turn'

class ActionDispatch::IntegrationTest
  include Rails.application.routes.url_helpers # for x_path and x_url helpers
  include Capybara::DSL
  include PossibleJSDriver # switching Capybara driver for javascript tests, look below
end

# switching to the javascript driver in integration tests
module PossibleJSDriver
  def require_js
    Capybara.current_driver = :webkit
  end

  def teardown
    super
    Capybara.current_driver = nil
  end
end

After you have everything set up you can use all of this in your integration tests, here is an example of one integration test, without and with javascript browser turned on(require_js turns on the webkit driver)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require 'test_helper'
class LoginTest < ActionDispatch::IntegrationTest
  before { visit login_path }
  describe "signup text" do
    it "must have a signup text" do
      page.must_have_content("Don't have an account?")
    end
    it "must have a signup link" do
      page.find_link('Sign up').wont_be_nil
    end
    it "signup link must go to the signup path" do
      require_js # this one turns on the webkit javascript driver
      click_link('Sign up')
      current_path.must_equal(signup_path)
    end
  end
end

I have tested the integration test speed with :webkit as a default capybara driver and it runs at the same speed as the normal :rack_test driver, so you can go that way too.

Resources used:
https://github.com/metaskills/minitest-spec-rails
https://github.com/blowmage/minitest-rails
https://github.com/blowmage/minitest-rails-capybara
http://stackoverflow.com/questions/5655154/how-do-you-perform-javascript-tests-with-minitest-capybara-selenium/13296544#13296544
https://github.com/thoughtbot/capybara-webkit

I Just Joined Toptal.com

| Comments

Toptal is a relatively new company, similar in many ways to other freelancing sites available, but it has a twist.

I received an email from a recruiter last month and after a few hours inspecting the site, and watching the CEO’s talk at the Harvard Business School http://youtu.be/Ffjx_hl3IE8 i decided to contact them. I first had a nice chat with the recruiter explaining in depth what the company does and what can be our mutual benefits if i continue to pursue the arrangement. Later that day i was contacted by the CTO and scheduled a skype call with him. The skype call was a first test, Toptal cares that all developers are very fluent in English, not only written, but also in spoken form. The interview was no stress at all, a standard chat between two engineers that just met. After that i received the next test, solving some automated tests.  The tests weren’t really complicated but the time is limited to 45 minutes, i could write about the perils of automated testing, but that isn’t the case there, it’s just one of the screens. Interview number 3 was a pair programming session where another developer joined my shared screen session and gave me assignments, also solving some algorithms, but with him watching me writing code. After i passed that test, there was another, to create an AJAX web application with REST API and authentication. I did my best on a limited schedule and passed that test too and became a member of a growing number of their network of senior freelance developers.

To save you the trouble, i’ll write something about Toptal and why they are not the same as Elance, Odesk or other freelance sites. Toptal hires freelance engineers, then tests them, and has people working with them and the clients to find the best match for the engineer and the client. That way you already get tested and able engineers to work with them. Also the engineer is instructed in detail about the job he is interviewing for and can prepare for it. All of the recruiters are engineers so it makes the stuff easier because everyone understands the problems. You as a client, have the possibility to try out a few developers until you find one that suits you and can hire them either part or full time whatever suits you.

The bottom message is: If you are in need of top talent to develop you application or you are a skilled developer who wants to work for some of the best companies in the world join Toptal and give us a try.

POS Printing on Android

| Comments

So, you want to develop your big Point Of Sale(POS) solution on android but don’t know how to print the receipts. You have a few options:

  1. Make a centralized mobile web application, reachable on wifi and use your tablets to take orders and print the receipts at the bar.
  2. Use some kind of a Windows Mobile/CE handheld device with a printer
  3. Make an Android app and print via Bluetooth

Due to the requirements of the project i have been working on, i have made a solution somewhere between 1 and 3, using Ruby on Rails for back end administration and api, and using Android tablets for billing. As for the printing of the receipts, the only POS printer we could find on our market with Bluetooth capabilities was Bixolon SPP-R200 so i decided to use it. And the Bixolon support guy Jörg from Germany was really helpful, and sent me the android api so all i had to do was include one jar file and write a class for handling the printer. You also get the whole example application, with every api call available.

For my needs, and future compatibility, i first integrated everything into the activity, yes silly of me, but then i extracted it into a class and also created a printing interface which my app  now uses, and it can be used across multiple apps very easily. First a basic printing interface:

1
2
3
4
5
6
public interface Printer {
  public boolean getStatus();
  public boolean connectPrinter();
  public boolean disconnectPrinter();
  public boolean print(String textToPrint);
}

And i implemented the class using the Bixolon Api here:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import com.bixolon.android.library.BxlService;
public class BixolonPrinter implements Printer {
  BxlService mBxlService;
  boolean conn;

  @Override
    public boolean getStatus() {
      return conn;
    }

  @Override
    public boolean connectPrinter() {
      if (conn != true) {
        CheckGC("Connect_Start");
        mBxlService = new BxlService();
        if (mBxlService.Connect() == 0) {
          conn = true;
        } else {
          conn = false;
        }
        CheckGC("Connect_End");
      }
      return conn;
    }

  @Override
    public boolean disconnectPrinter() {
      if (mBxlService.Disconnect() == 0) {
        conn = false;
      }
      else
      {
        conn = true;
      }
      return !conn;
    }

  @Override
    public boolean print(String textToPrint) {
      boolean result = false;
      CheckGC("PrintText_Start");
      int returnValue;
      returnValue = mBxlService.PrintText(textToPrint,
          BxlService.BXL_ALIGNMENT_CENTER, BxlService.BXL_FT_DEFAULT,
          BxlService.BXL_TS_0WIDTH | BxlService.BXL_TS_0HEIGHT);
      if (returnValue == BxlService.BXL_SUCCESS) {
        returnValue = mBxlService.LineFeed(2);
        result = true;
      }
      else {
        result = false;
      }
      CheckGC("PrintText_End");
      return result;
    }

  void CheckGC(String FunctionName) {
    Runtime.getRuntime().maxMemory();
    System.runFinalization();
    System.gc();
    Runtime.getRuntime().maxMemory();
  }
}

All that is remaining in your activity is to declare a variable of the Printer type and instantiate a new BixolonPrinter class.

1
2
3
4
5
6
7
8
9
10
public class TestActivity extends Activity {
  Printer printer;

  @Override
    public void onCreate(Bundle savedInstanceState) {
      // TODO: implement this into settings and choose based on that
      printer = new BixolonPrinter();
      printer.connectPrinter();
    }
}

I chose this path because one company that is in mobile solutions in Croatia heard a presentation about the application, and decided to lend us a Zebra mobile POS printer(don’t really know which model), to implement it into the app. I will publish the implementation here as soon as i can. You can get the Bixolon api and documents by registering to the bixolon developer’s site Bixolon developer site

Limit and Offset in Oracle

| Comments

If you are considering about using some kind of pagination like will_paginate or some pagination of your own flavor, and you are stuck using Oracle i have a solution for you. Oracle is known not to have limit and offset helpers for the select statements. Those two pieces of code that are missing are helping us to paginate properly. Yeas, you can use rownum, and i am using it too, but we have to encapsulate our select a few times, so it won’t make trouble for us(bad data, not ordering properly or something).

I have already made a patch for arel, so it’s embedded in Rails, if you really have use Oracle as your database.

Let’s say that we want to list all the employees from scott.emp table, and order them by empno descending and fetch the records from rows of that data 41 to 50.

1
2
3
4
SELECT * FROM (
  SELECT raw_sql_.*, rownum raw_rnum_
    FROM (select * from scott.emp order by empno DESC) raw_sql_ )
    WHERE raw_rnum_ between 41 and 50

Yes, i know it looks stupid, but it works, and until Oracle implements limit and offset into it, sadly, this is the only way.

Integrating Turbolinks in Your Rails Application

| Comments

We are all excited in a new addition to rails plugins family that is turbolinks.

If you have tried to include it in your application, you may have noticed that turbolinks doesn’t trigger your $.ready, because there is no full page reload. As a consequence your javascripts that are bound to $.ready won’t trigger. I will only hack the finished event that is “page:change” which occurs when the page is refreshed. You have other events and their descriptions on the turbolinks github page. Here is a fast hack that i came with. Some of the code is taken from turbolinks library.

First have your $.ready trigger the “page:change” event. This is done, so we can bind everything that is now bound to document.ready to “page:change”

1
2
3
4
5
6
7
$.ready ->
  triggerEvent "page:change"

triggerEvent = (name) ->
  event = document.createEvent 'Events'
  event.initEvent name, true, true
  document.dispatchEvent event

Now when we have $.ready calling “page:change” we can use it in our application like this

1
2
document.addEventListener "page:change", ->
  alert "I am called from $.ready and page:change"

Will Paginate in Rails Using Remote Request

| Comments

Have you ever wondered why is there no remote option for will_paginate gem? It’s too complex and data dependent to handle all possible situations. I have made a workaround that can help you implement simple ajax pagination for your rails application. I will be replacing the whole yield part here, but you can customize it whatever way you like.

  1. Extract your required view into a partial so you have
app/views/posts/index.html.erb
1
< %= render "index" %>
app/views/posts/_index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<h1>Listing posts</h1>
<table id="posts">
  <tr>
    <th>Title</th>
    <th>Body</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
 < %= render partial: 'post', collection: @posts %>
</table>
< %= will_paginate @posts, remote: true %>
<br />
< %= link_to 'New Post', new_post_path, remote: true %>

Note the remote: true part of the will_paginate call, we will bind the javascript to it in an instance.

  1. Add a div surrounding your yield tag in your layout
app/views/layouts/application.html.erb
1
2
3
<div id="content>
  < %= yield %>
</div>
  1. Create index.js.erb which will replace the contents of the div with paged table data.
app/views/posts/index.js.erb
1
$("div#content").html('< %= escape_javascript(render "index") %>');
  1. Bind the will_paginate link click to the rails remote call using coffeescript in
app/assets/javascripts/posts.js.coffee
1
2
3
4
$('.pagination[remote=true] a').live 'click', ->
  window.history.pushState(null, 'hi', $(this).attr("href"))
  $.rails.handleRemote($(this))
  return false

We also change the address in the navigation bar with PushState, because it can happen someone will press F5 or something and reload with a different params[:page].

Update: As my coworker Oliver mentioned, i forgot to include responding to js in our controller for the index action

1
2
3
4
5
6
7
def index
  @posts = Post.paginate(per_page: 8, page: params[:page]
  respond_to do |format|
    format.html
    format.js
  end
end

Installing Rails Server on Ubuntu 12.04 With RVM, Nginx, Passenger and Oracle Support

| Comments

We have a new product ready and it is a Ruby on Rails webshop bundled with an ERP software written on Oracle Forms 6i. I will not go into the details of this work, maybe later i will make some posts of the process but as it is very linked to current software(only authentication is done with Devise, every other data and processing is done internally on Oracle database) there is no sense for me to explain it deeper. Off to the installation.

Server installation and configuration

First we need a clean install of Ubuntu server 12.04, x86 or x64 version will work all the same, but be careful later on when you download the Oracle instant client. On the installation, fill all the relevant data needed, and for installed services, choose only the ssh server, as you won’t be needing anything more. Another reminder: this will only be the webserver, assuming that you have the Oracle database installed somewhere in network reach.

After you login to your server for the first time, take your time to set up the ip address because it defaults to dhcp. Edit the /etc/network/interfaces file and fill your address, gateway and dns servers(this is a new thing, i think starting in 12.04 /etc/resolv.conf gets updated whenever you restart networking, so everything you write there will get overwritten)

/etc/network/interfaces
1
2
3
4
5
6
7
8
9
10
11
# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
    address 192.168.0.100
    netmask 255.255.255.0
    gateway 192.168.0.1
    dns-nameservers 8.8.8.8 8.8.4.4.

After the config is saved, restart the networking and do a full update/upgrade of the server to get the latest packages installed:

1
2
3
sudo /etc/init.d/networking restart
sudo apt-get update
sudo apt-get -y dist-upgrade

After all is done, reboot your server.

Installing RVM and latest Ruby version

After your server is updated you can proceed with installing RVM and the latest stable Ruby(as i’m writing this, the latest version is 1.9.3p194 so i will be using this version)

First we install RVM prerequisites:

1
2
3
4
5
6
7
8
# rvm requires curl and git to install, and you will probably need them on your server so go ahead and install them
apt-get -y install git-core curl

# Installing RVM as sudo, to get RVM ruby system wide
curl -L get.rvm.io | sudo bash -s stable

# sourcing rvm environment so you can use it untill you login again
source /etc/profile.d/rvm.sh

And that will install RVM and make it available for all users that belong to the rvm group(hint: edit /etc/groups and add rvm after the desired username). After installing RVM and adding our user to the  rvm group we are off to installing latest Ruby version

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# install all requirements needed for YARV/MRI Ruby (you can easily get these by running 'rvm requirements' in the terminal)
sudo apt-get install build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion

# install requirements needed for Oracle Instant client
sudo apt-get install libaio1

# install latest stable 1.9.3 and set it as default ruby version
rvmsudo rvm install 1.9.3 --default

# disable installation of RDoc and RI when installing gems
echo 'gem: --no-ri --no-rdoc'  >> ~/.gemrc

# install bundler gem, as you will later need for deployment
rvmsudo gem install bundler

Installing Passenger and Nginx web server

After you have ruby and everything needed installer go and install passenger gem and nginx web server with it

1
2
3
4
5
6
# install passenger gem
rvmsudo gem install passenger

# install passenger nginx module (and nginx if you haven't installed it yet), choose first option(Yes: download, compile and install Nginx for me) when it asks you what to do
# if there are more requirements the script will tell you and after you install them rerun the command
rvmsudo passenger-install-nginx-module

The installation will set up the nginx.conf file for you and show you how you can enable a rails site on your server. But i will include a sample config later on. Just for convenience, symlink nginx conf folder that is in /opt/nginx/conf to /etc/nginx and download linode nginx startup/shutdown script

1
2
3
4
5
6
7
8
# symlink nginx conf folder to /etc/nginx
sudo ln -s /opt/nginx/conf /etc/nginx

# download linode nginx init script and create the service
wget -O init-deb.sh http://library.linode.com/assets/660-init-deb.sh
sudo mv init-deb.sh /etc/init.d/nginx
sudo chmod +x /etc/init.d/nginx
sudo /usr/sbin/update-rc.d -f nginx defaults

Installing Oracle instant client

To connect your Rails application to Oracle database, you will have to install Oracle client, either full or instant one. As installing full client requires alien and converting .rpm packages to .deb it’s a bit of a hassle. Also, it will consume more space on your drive, and all that is unnecessary, we are trying to keep it small here.

First you should download zip files from Oracle site depending what architecture you are running: Oracle Instant Client 11.2 for linux:  X86X64

Download only instantclient-basic, instantclient-sqlplus and instantclient-sdk zip archives and unzip them all into the same instantclient_12_2 _ folder. Now as sudo move that folder so the path is /opt/oracle/instantclient_11_2 _

After that you only have to add two environment variables to be able to install ruby-oci8 gem, which is prerequisite for running Ruby applications on Oracle database. Add the following lines to /etc/enviroment file

1
2
3
4
LD_LIBRARY_PATH=/opt/oracle/instantclient_11_2

# as i am from Croatia, and we use our national date and money preferences, my NLS_LANG is this one, yours will maybe be different
NLS_LANG=CROATIAN_CROATIA.AL32UTF8

After you reboot you have to symlink the oracle library so it will function properly(i don’t know why they don’t do this themselves).

1
ln -s /opt/oracle/instantclient_11_2/libclntsh.so.11.1 /opt/oracle/instantclient_11_2/libclntsh.so

After that try and install ruby-oci8 gem.

1
rvmsudo gem install ruby-oci8

If there are no errors, and there should not be if you followed the guide, you are set to deploy your application on the server you just installed. In the next post, i plan to write something about tips, tricks and caveats of developing Rails applications on Oracle database.

Useful links to get and learn about all the stuff i have mentioned in the post:

Ruby Version Manager(RVM): https://rvm.io
Ruby: http://ruby-lang.org
Oracle Instant Client 11.2 for linux:  X86, X64
Passenger server: http://www.modrails.com/

Importance of Installing Your Own Development Machine

| Comments

If you are a software developer like me, every once in awhile comes the opportunity to install your new development machine. Some of the causes can be a new job, new computer or just reinstalling your current computer.

I am trying to emphasize the importance of installing your own machine, just like Jedi knights have to build their own lightsabers, you should be competent enough to install the required software on your development machine.

I will try not to go into platform specific rants or anything, just a guideline on how to do it.

  1. Know your OS - be it windows, linux or osx, please be familiar with the os you are planning to develop on, if you get stuck, search for some tutorials on the web. You should be very friendly with the command line, if you are planning to use linux, and even in some cases osx.

  2. Know your platform - be it ruby, java, python, .net or anything else, learn how to install the basic tools to get you running, you have great video tutorials for just about anything, so go ahead and watch them.

  3. Don’t be afraid to ask someone - take this one with caution, it is ok to ask for an advice, or if you get stuck, but never ask another developer to install some of the tools for you, as you then seem incompetent in their eyes.

And do try to follow some of the people creating the technology you are using on social networks, you can see some tips and tricks, update and version release announcements and everything else. Looking at a few cat pictures is sure worth it considering the knowledge you are getting from these people.

Deploying Rails Applications on Windows

| Comments

Due to some legacy issues, and HP workstations being shit that you can’t turn the hardware virtualization on, i had an issue with one client regarding speed and loading time of an application. My common deployment server is running Ubuntu on VMWare or Oracle VBox, passenger + nginx, or apache even. But, this “server” is just a regular machine(people here don’t want to invest in real hardware, or just don’t have the money).

So i had to deploy the app on windows 7, and i will show you how.

First, there are few steps you have to take care of:

  1. Install Ruby with the ruby installer http://rubyinstaller.org/downloads/

  2. Install Ruby DevKit from the same source(this will be needed for you to compile some native binaries)

  3. Install Thin server(gem install thin)

  4. Set up dropbox deployment for your application(i know this is monkeypatching as hell, but if it works…) as described on Rob Conery’s blog

  5. Test the app and make sure everything is working ok, (run rails server), then thin start -e production, just to make sure everything is ok, and all of your gems work on windows(fix or replace the ones that don’t)

  6. Set up the task scheduler(this is a fine piece of software on Win 7 and up) as described  here, to run rails application almost as a windows service( you start with ‘thin start -e production -p 80 -c “path_to_your_app” ‘)

  7. Write a nifty batch script that will restart the server, run migrations, compile the assets and whatnot.

1
2
3
4
5
6
7
8
9
cd "path_to_your_app"

bundle exec rake db:migrate RAILS_ENV=production

bundle exec rake assets:precompile

schtasks /End /TN "name_of_your_task_in_scheduler"

schtasks /Run /TN "name_of_your_task_in_scheduler"
  1. After you pull to your dropbox synced repository, login to the server and execute the batch file from there to almost automate the deployment process.

I know this is not capistrano, and cap deploy, and it is hacked as it can be, and also, it works, it’s a small client so i don’t expect much trouble with this.

Lesson here: Try as much as you can to deploy your rails server on a linux server, best in a virtual machine. But if HP messes with you and you have some shitty hardware running windows, this is the best way i have found. If anyone has anything better, put it into comments, i’m happy to make my life easier.

References:

1. http://wekeroad.com/danger-danger/git-and-dropbox-sitting-in-a-tree

2. http://unicornless.com/systems-administration/run-thin-as-windows-service

Preventing Users From Exiting a Form on Your Website With JQuery

| Comments

I have encountered a problem with users on a webapp that i co-created. The users would click back after entering data in a form, or close the current tab or something. They were so used to client-server workflow, with open connections to the database that prevented that kind of actions. So i got handed the assignment to prevent the users from doing almost anything but submitting on a data entry form without a notice.

It seemed as a hell of a solution, but i managed to make a simple plugin that works on all major browsers, except Opera. Using window.onbeforeunload, we can prevent the user from doing anything that would hurt their unsaved data. you just load the javascript file, put a tag into your forms that you want to protect and it works out of the box with JQuery.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var isSubmitted = false;
window.onbeforeunload = function() {
  var message = "You could have unsaved changes!";
  if ($('form[data-validate-exit="true"]').length > 0)
  {
    if (!isSubmitted)
    {
      return message;
    }
  }
}
document.ready = function() {
  $('form[data-validate-exit="true"]').find('button[type="submit"]').click(function() {
      window.isSubmitted = true;
    });
}

The message doesn’t show on firefox, but a convenient one will show, enough to warn the user on his actions.

The html code in your form should be like this:

1
2
3
<form data-validate-exit="true">
  Your html here
</form>

One friendly suggestion if you follow this path, after you implement this on any of your web apps, use Opera for development, or comment the stuff out for your own sake, you will loose your mind clicking on the leave validations.