Sergey Potapov

A word from rustacean, rubist and linuxoid.

Почему я изучаю Эсперанто

Как вы знаете, я начал изучать Эсперанто, некоторым даже успел “съесть” мозг, но всё же большинство по-прежнему задают вопросы почему и для чего я это делаю. Из этого можно прийти к выводу, что либо я безумен, либо я понимаю что-то, что другие не могут понять. Поэтому я решил написать небольшую статью и постараться объясниться.

Install More Screensavers on Mate Desktop

I switched from Gnome3 to Mate desktop and noticed that I’m able to use only few sreensavers. If you’re using Mate (it’s default for Linux Mint) you might experience the same problem.

There is a workaround how to use much more screensavers with Mate as usually able to do with Gnome.

Install packages with additional screensavers:

1
apt-get install xscreensaver-data-extra xscreensaver-gl-extra

Go to /usr/share/applications/screensavers directory:

1
cd /usr/share/applications/screensavers

There are located number of .desktop files. You should edit them by replacing the line

1
OnlyShowIn=GNOME;

with

1
OnlyShowIn=GNOME;MATE;

.

Obviously it’s a routine to change them all manually. So use sed tool to edit all files in once:

1
find . -name '*.desktop' | xargs sed -i 's/OnlyShowIn=GNOME;/OnlyShowIn=GNOME;MATE;/'

Now you are able to use more than hundred screensavers on Mate!

Failed to Add New Printer in Debian Wheezy

After migrating to Debian Wheezy (current test Debian repository) I faced a problem with installing a local USB printer: on attempt to add new USB printer (HP LaserJet M1005) I got an error message: “Failed to add new printer in Debian Wheezy” (“Не удалось добавить новый принтер” if you have Russian localization).

After googling for I a while I found a good workaround for it. All you need is to use pk-helper package from Sid (unstable Debian version), since it seems to be buggy in Wheezy.

So add Sid repository to your /etc/apt/source.list:

1
deb http://ftp.ua.debian.org/debian/ sid main non-free contrib

And reinstall cups-pk-helper package using Sid repository:

1
aptitude reinstall cups-pk-helper --target sid

That’s all. Now try to connect your USB printer. Btw, there is the bug report.

Rational Can’t Be Coerced Into BigDecimal in Ruby 1.9.3

Trying to move a rails application from ruby 1.8.7 to 1.9.3 I ran into coercion issue of Rational class.

Ruby 1.9.3:

1
2
3
4
5
6
7
8
require 'bigdecimal'
require 'rational'

# You can multiply Rational against BigDecimal
Rational(1) * BigDecimal('1')  # => <BigDecimal:a566d0,'0.1E1',9(36)>

# But you can't do the same when you change order
BigDecimal('1') * Rational(1)  # => TypeError: Rational can't be coerced into BigDecimal

On other hand in Ruby 1.8.7:

1
2
3
4
5
6
7
8
require 'bigdecimal'
require 'rational'

# BigDecimal * Rational works OK
BigDecimal('1') * Rational(1)  # => 1.0 (Float)

# But Rational * BigDecimal doesn't
Rational(1) * BigDecimal('1')  # => TypeError: Rational can't be coerced into BigDecimal

It’s looks weird. So I can only say for sure that Rational -> BigDecimal coercion is not implemented in Ruby.

I’ve tried to fix it with simple monkey patch:

1
2
3
4
5
6
7
8
9
10
11
# Works only for Ruby1.9.3
class Rational
  def coerce(value)
    case value
    when BigDecimal
      return self, value
    else
      super
    end
  end
end

And it works OK against simple examples:

1
2
Rational(1) * BigDecimal('1')  # => <BigDecimal:a566d0,'0.1E1',9(36)>
BigDecimal('1') * Rational(1)  # => <BigDecimal:a566d0,'0.1E1',9(36)>

But it causes intermittent segmentation faults when I run Rails application.

Any ideas about the coercion? Is it expected behaviour of ruby1.9.3? I found no bugs reported this issue on https://bugs.ruby-lang.org.

I’ll appreciate any feedback. Thanks.

Custom Expectations With RSpec

I know you love RSpec’s expect DSL like this:

1
expect { raise("Boom!") }.to raise_error(RuntimeError, "Boom!")

We often write our own custom matchers and I wanna show how is easy to write custom expectation.

Desired DSL

Usually when I do things like this I start with DSL. I think it’s important since it must be convenient to use and easy to read. So turn on your imagination and spend some time on it.

In my example I’m gonna create an expectation to test text written to standard output and standard error. There are samples how I wanna use it(desired DSL):

1
2
3
4
5
# Test text written to standard output
expect { puts "Hello!" }.to write("Hello!")

# Test text written to standard error
expect { warn "Stop it!" }.to write("Stop it!").to(:error)

Pg_power - ActiveRecord Extension for PostgreSQL

I am happy to announce that TMXCredit released pg_power gem - an ActiveRecord extension which allows to use number of PostgreSQL features with Rails.

What you can do with pg_power?

  • Use PostgresSQL schemas in your Rails project.
  • Add comments to PostgreSQL database with Rails migrations.
  • Use foreign keys (we imported foreigner functionality and made it schema aware).
  • Use partial indexes.
  • Add indexes concurrently.

You’ll find enough documentation in README file.

Quick usage example

Assume you want to create tables countries and languages in demography schema.

At first we need to create demography schema:

db/migrate/create_demography_schema.rb
1
2
3
4
5
class CreateDemographySchema < < ActiveRecord::Migration
  def change
    create_schema 'demography'
  end
end

Now let’s create tables:

db/migrate/create_demography_languages.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CreateDemographyLanguages < ActiveRecord::Migration
  def change
    # Create table `languages` in schema `demography`
    create_table "languages", :schema => "demography" do |t|
      t.string :name
      t.string :code, :limit => 2
    end

    # Add PostgreSQL comments
    set_table_comment "demography.languages", "List of languages"
    set_column_comments "demography.languages",
        :name => "Full name of language in English",
        :code => "ISO 639-1 code"
  end
end
db/migrate/create_demography_countries.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CreateDemographyContries < ActiveRecord::Migration
  def change
    # Create table `countries` in schema `demography`
    create_table "countries", :schema => "demography" do |t|
      t.string :name

      # In real life you likely would have many-to-many associaton
      t.integer :language_id
    end

    # Add PostgreSQL comments
    set_table_comment "demography.countries", "List of world countries"
    set_column_comments "demography.languages",
        :name        => "Full name of country in English",
        :language_id => "Most popular language in the country"

    # Add foreign key and create index on demography.countries.language_id
    add_foreign_key("demography.countries", "demography.languages")
  end
end

Great! Now we need to set table names in models to make ActiveRecord know that these tables are located in demography schema.

app/models/language.rb
1
2
3
class Language < ActiveRecord::Base
  set_table_name "demography.languages"
end

It will work. But I would recommend you to create module Demography which would represent demography schema and move those models to it. One more benefit is that you can define schema prefix in module and models will use it build table name automatically.

app/models/demography.rb
1
2
3
4
5
module Demography
  def self.table_name_prefix
    'demography.'
  end
end
app/models/demography/language.rb
1
2
3
module Demography::Language
  # No need to use set_table_name anymore
end

I hope you will enjoy pg_power. Let us know what you think!

Thanks. Sergey Potapov.

Ruby Performance Tricks

I did some benchmarks to find out which alternatives to write code work faster. I wanna share it with you. All benchmarks are made against ruby 1.9.3p194 MRI.

Do not use exceptions for a control flow

The next example is pretty stupid but it shows how exceptions slow against conditional statements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
require 'benchmark'

class Obj
  def with_condition
    respond_to?(:mythical_method) ? self.mythical_method : nil
  end

  def with_rescue
    self.mythical_method
  rescue NoMethodError
    nil
  end
end

obj = Obj.new
N = 10_000_000

puts RUBY_DESCRIPTION

Benchmark.bm(15, "rescue/condition") do |x|
  rescue_report     = x.report("rescue:")    { N.times { obj.with_rescue    } }
  condition_report  = x.report("condition:") { N.times { obj.with_condition } }
  [rescue_report / condition_report]
end

MRI 1.9.3:

ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
                        user     system      total        real
rescue:           111.530000   2.650000 114.180000 (115.837103)
condition:          2.620000   0.010000   2.630000 (  2.633154)
rescue/condition:  42.568702 265.000000        NaN ( 43.991767)

MRI 1.8.7 (REE has similar result):

ruby 1.8.7 (2011-12-28 patchlevel 357) [x86_64-linux]
                        user     system      total        real
rescue:            80.510000   0.940000  81.450000 ( 81.529022)
if:                 3.320000   0.000000   3.320000 (  3.330166)
rescue/condition:  24.250000        inf       -nan ( 24.481970)

Unexpected Ruby Behaviour

Ruby is a cool language with intuitive grammar. However there are a number of things which don’t seem to be expected. It might take long hours to debug some weird issues for unenlightened newbies.

Тестируем вложенные ActiveRecord-модели с RSpec

Иногда бывает так, что вам нужно построить большой граф вложенных объектов, и конечно же протестировать, что ваш “builder” работает так, как нужно. На самом деле задача элементарная, но я всё же попробую поискать наиболее элегантный путь её решения.