Rails tips

class Moo
  private

  def self.foo
    puts 'foo'
  end
end
Foo.foo
=> 'foo'
moo = Moo.new
moo.foo = 'foo'
moo.bar
moo = Moo.new.tap { |a| a.foo = 'foo'; a.bar }
class Moo
  attr_accessor :foo

  def initialize(&block)
    yield self if block_given?
  end

  def bar
    puts "bar!"
  end
end

moo = Moo.new do |a|
  a.foo = 'foo'
  a.bar
end

puts moo.foo
class Moo
  def initialize(&block)
    instance_eval &block if block_given?
  end

  def moo(val = nil)
    @moo = val unless val.nil?
    @moo
  end

  def bar
    puts "bar!"
  end
end

a = Moo.new do
  moo 'moo~!'
  bar
end

puts a.moo
class CreateDataVolumes < ActiveRecord::Migration
  def up
    Data::VOLUMES.times do |volume|
      create_table "data_#{volume}" do |t|
        # ...
      end
    end
  end
end
class CreateDataVolumes < ActiveRecord::Migration
  class Data < AR::Base
    VOLUMES
  end

  def up
    Data::VOLUMES.times do |volume|
      # ...
    end
  end
end

Abstract classes vs interfaces

public interface INumber {
    public INumber add(INumber other);
}

public class RomanNumber implements INumber {
    public RomanNumber add(INumber other) {
        // when `other` is not a RomanNumber
        // convert other to RomanNumber
        // add `other` to `this`
    }
}

public class HexadecimalNumber implements INumber {
    public HexadecimalNumber add(INumber other) {
        // convert other to HexadecimalNumber and add...
    }
}
public abstract class Number {
    public abstract Integer toDecimal();
    public abstract Number fromDecimal(Integer);

    public Number add(Number other) {
        returh fromDecimal(this.toDecimal() + other.toDecimal());
    }
}

public class RomanNumber extends Number {
    public Integer toDecimal() {
        // convert `this` to Integer
    }

    public RomanNumber fromDecimal(Integer n) {
        // convert `n` to RomanNumber
    }
}

public class HexadecimalNumber extends Number {
    public Integer toDecimal() {
        // convert `this` to Integer
    }

    public HexadecimalNumber fromDecimal(Integer n) {
        // convert `n` to HexadecimalNumber
    }
}
Vector2d implements IVector { // THIS IS NOT A CONSTRUCTOR! @Override public void Vector() { } } ```

<p>and compare it to this:</p>

```java public abstract class BaseVector { public abstract BaseVector() { } } public class Vector2d extends
BaseVector { public Vector2d() { // now it DOES HAVE a default constructor super(); } } ```

Erlang practice

Foreword

First time I faced functional programming, I was impressed. Just a bit. That was back in 2012. The second time, I was studying real functional programming at the university. Today was the final test.

We were taught many interesting things about functional programming and lot of things about Haskell. But there were only two lectures and two practices on Erlang. And nothing was told about its distributed programming abilities.

I was a bit disappointed by this fact. So, I turned on my girlfriend's laptop, installed Erlang and created this short intro to distributed programming in Erlang.

Read more

Robo created in a Cinema4d

This is a 3D model of a robot (called him Robo) which I made in Cinema4D some two and a half years ago.

Connecting Lenovo P780 to ADB on Ubuntu

Deploying Rails project to Heroku

group :production do
    gem 'pg'
    gem 'rails_12factor'
    gem 'puma'
end
production:
    adapter: postgresql
    encoding: unicode
    pool: 5
    url: URL_GOES_HERE
heroku config | grep HEROKU_POSTGRESQL
heroku config:set DATABASE_URL=URL_GOES_HERE

Microsoft' error messages cipher

I have a T-SQL trigger creation script, which runs OK:

CREATE TRIGGER data_modified ON Northwind.dbo.Customers FOR INSERT, UPDATE, DELETE
AS

declare @rows as int;
set @rows = @@ROWCOUNT;

IF @rows = 0
BEGIN
    print 'no rows were affected';
    return;
end

if exists(select * from inserted)
begin
    if exists(select * from deleted)
    begin
        print 'updated ' + @rows + ' rows';
    end
    else
    begin
        print 'inserted ' + @rows + ' rows';
    end
end
else
begin
    print 'deleted ' + @rows + ' rows';
end

Yet, when I run some INSERT query, I got an error saying:

Msg 245, Level 16, State 1, Procedure data_modified, Line 21
Conversion failed when converting the varchar value 'inserted ' to data type int.

Mysterious, isn't it? Let's dig in, shall we?

Read more

Setting up Rails webserver

Foreword

This tutorial I wrote when was quitting my previous job, almost one year ago. But it's still handy!

Abstract Rails application setup

$ git clone .../project_name.git
$ cd project_name
$ [sudo] bundle install
$ cat config/database.yml
$ # create database and/or change config/database.yml settings
$ rake db:migrate RAILS_ENV=production
$ rake db:seed RAILS_ENV=production # don't worry if one fails
$ # start the server of your choice

Puma webserver

Application-wide settings

First you need to set up Puma for your specific project. For this purpose, add this line to the Gemfile:

gem 'puma'

Then, run [sudo] bundle install.

When you are done, you should be able to create a Puma config file at $PROJECT_DIR/config/puma.rb:

def home_dir
    '/home/user/$PROJECT_DIR/'
end

def path(p)
    File.join(home_dir, p)
end

directory home_dir
environment 'development'
daemonize
pidfile path('tmp/pids/puma.pid')
state_path path('tmp/pids/puma.state')
stdout_redirect path('log/puma.log'), path('log/error.puma.log'), true
threads 0, 1
bind 'tcp://0.0.0.0:5100'
activate_control_app

More details here: https://github.com/puma/puma/blob/master/examples/config.rb

Now, add project root path to the /etc/puma.conf file, e. g.:

/home/user/project_name

Start Puma at boot

There is a specific utility, called Jungle. It manages your applications' instances at startup.

Ububtu-based systems

First of all, create /etc/init/puma.conf file and fill it with this:

# /etc/init/puma.conf - Puma config

# This example config should work with Ubuntu 12.04+.  It
# allows you to manage multiple Puma instances with
# Upstart, Ubuntu's native service management tool.
#
# See workers.conf for how to manage all Puma instances at once.
#
# Save this config as /etc/init/puma.conf then manage puma with:
#   sudo start puma app=PATH_TO_APP
#   sudo stop puma app=PATH_TO_APP
#   sudo status puma app=PATH_TO_APP
#
# or use the service command:
#   sudo service puma {start,stop,restart,status}
#

description "Puma Background Worker"

# no "start on", we don't want to automatically start
stop on (stopping puma-manager or runlevel [06])

# change apps to match your deployment user if you want to use this as a less privileged user (recommended!)
setuid apps
setgid apps

respawn
respawn limit 3 30

instance ${app}

script
# this script runs in /bin/sh by default
# respawn as bash so we can source in rbenv/rvm
# quoted heredoc to tell /bin/sh not to interpret
# variables
exec /bin/bash &lt;&lt;'EOT'
  # set HOME to the setuid user's home, there doesn't seem to be a better, portable way
  export HOME="$(eval echo ~$(id -un))"

  cd $app

  if [ -d "$HOME/.rbenv/bin" ]; then
    export PATH="$HOME/.rbenv/bin:$PATH"
  elif [ -f  /etc/profile.d/rvm.sh ]; then
    source /etc/profile.d/rvm.sh
  elif [ -f /usr/local/rvm/scripts/rvm ]; then
    source /etc/profile.d/rvm.sh
  elif [ -f "$HOME/.rvm/scripts/rvm" ]; then
    source "$HOME/.rvm/scripts/rvm"
  elif [ -f /usr/local/share/chruby/chruby.sh ]; then
    source /usr/local/share/chruby/chruby.sh
    if [ -f /usr/local/share/chruby/auto.sh ]; then
      source /usr/local/share/chruby/auto.sh
    fi
    # if you aren't using auto, set your version here
    # chruby 2.0.0
  fi

  logger -t puma "Starting server: $app"

  exec bundle exec puma -C config/puma.rb
EOT
end script

Now, create /etc/init/puma-manager.conf and fill it with this:

# /etc/init/puma-manager.conf - manage a set of Pumas

# This example config should work with Ubuntu 12.04+.  It
# allows you to manage multiple Puma instances with
# Upstart, Ubuntu's native service management tool.
#
# See puma.conf for how to manage a single Puma instance.
#
# Use "stop puma-manager" to stop all Puma instances.
# Use "start puma-manager" to start all instances.
# Use "restart puma-manager" to restart all instances.
# Crazy, right?
#

description "Manages the set of puma processes"

# This starts upon bootup and stops on shutdown
start on runlevel [2345]
stop on runlevel [06]

# Set this to the number of Puma processes you want
# to run on this machine
env PUMA_CONF="/etc/puma.conf"

pre-start script
  for i in `cat $PUMA_CONF`; do
    app=`echo $i | cut -d , -f 1`
    logger -t "puma-manager" "Starting $app"
    start puma app=$app
  done
end script

And create a blank /etc/puma.conf file. This will be filled for each application separately.

Caveat:

You need to customise /etc/init/puma.conf to:

  • Set the right user your app should be running on unless you want root to execute it!
    • Look for setuid apps and setgid apps, uncomment those lines and replace apps to whatever your deployment user is.
    • Replace apps on the paths (or set the right paths to your user's home) everywhere else.
  • Uncomment the source lines for rbenv or rvm support unless you use a system wide installation of Ruby.

Now, start Jungle like this: sudo start puma-manager. And all your applications should be available when you reboot the machine.

More details at https://github.com/puma/puma/tree/master/tools/jungle/

Debian-based systems

PENDING

Starting up and shutting down

To start up the application is easy enough. Just navigate yourself to project directory and run the following: puma -C config/puma.rb.

If you want to shut down one, run this command in the project directory: [sudo] pumactl -S tmp/pids/puma.state halt.

Speeding up with Ruby native extensions

Foreword

At my job, our current project has many bottle-necks, where Ruby really sucks on its performance. We were thinking on how to optimize them, and finally come to usage of Ruby Native API.

Our project uses Redis and MySQL hardly, so much of statistic data is stored in Redis. For speeding up. But one fine day made us use a reduce on a set of statistic data from Redis. And that’s where we got stuck on Ruby’ performance. Our server timed out in a minute of waiting for that reduce to complete.

Read more

Чоткій блог. Часть третя