Byte Friendly

Random thoughts on programming and related topics.

Ruby: How to Override Class Method With a Module

| Comments

This seems to be a popular interview question. It indeed requires advanced knowledge of ruby.

You have a class with a class method. Write a module that, when included, will override that class method.

Explanation of the problem

Now classic way of mixing in class methods is this (and it doesn’t solve the problem, of course).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module FooModule
  def self.included base
    base.extend ClassMethods
  end

  module ClassMethods
    def bar
      puts "module"
    end
  end
end

class Klass
  include FooModule

  def self.bar
    puts 'class'
  end
end


Klass.bar #=> class

When modules are included or extended into a class, its methods are placed right above this class’ methods in inheritance chain. This means that if we were to call super in that class method, it would print “module”. But we don’t want to touch original class definition, we want to alter it from outside.

So, can we do something?

Good for us, ruby has a concept of “open classes”. This means that we can change virtually everything in the app, even some 3rd-party libraries. Every class can be “opened” and new methods can be added to it, or old methods can be redefined. Let’s look how it works.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Klass
  def self.bar
    puts 'class'
  end
end

class Klass
  def self.bar
    puts 'class 2'
  end
end

Klass.bar #=> class 2

The second class definition does not overwrite previous one, it opens and alters it. In this case, it happened to define a method with the same name. This resulted in old method being overwritten by the new one. This works with any classes, even base library classes.

1
2
3
4
5
6
7
8
9
puts [1, 2, 3].to_s #=> [1, 2, 3]

class Array
  def to_s
    "an array: #{join ', '}"
  end
end

puts [1, 2, 3].to_s #=> an array: 1, 2, 3

Or the same code can be rewritten as

1
2
3
4
5
6
7
8
9
puts [1, 2, 3].to_s #=> [1, 2, 3]

Array.class_eval do
  def to_s
    "an array: #{join ', '}"
  end
end

puts [1, 2, 3].to_s #=> an array: 1, 2, 3

Applying the knowledge

Let’s start with simpler things, like overriding an instance method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Klass
  def say
    puts 'class'
  end
end

module FooModule
  def self.included base
    base.class_eval do
      def say
        puts "module"
      end
    end
  end
end


Klass.send(:include, FooModule)

Klass.new.say #=> module

Modules have a special callback that gets called every time a module is included in a class. We can use that to call class_eval on that class and redefine a method.

Replacing a class method is done in a similar way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Klass
  def self.say
    puts 'class'
  end
end

module FooModule
  def self.included base
    base.instance_eval do
      def say
        puts "module"
      end
    end
  end
end


Klass.send(:include, FooModule)

Klass.say #=> module

The only difference here is that we call instance_eval instead of class_eval. This can be a very confusing part. In short, class_eval creates instance methods and instance_eval creates class methods.

Mongoid, db.system.namespaces Queries

| Comments

Recently I faced some issues with Mongoid when upgrading my Rails app from REE+Passenger to MRI 1.9.3+Unicorn.

There are some Resque workers in the background. After some deploy they started to consume a ton of traffic from MongoDB. After some investigation, I found that they heavily read system.namespaces collection. I tried upgrading to latest versions of mongoid(2.4.3) and mongo(1.5.2) to no avail. This does not happen with normal unicorn workers. This also does not happen if I downgrade mongoid to 2.0.1.

I am still not sure what’s happening here. I’ll update this post when I discover something.

How to Quickly Lock Screen in Mac OS X?

| Comments

I am tired of googling the same information over and over, so I am posting it here.

To quickly lock your screen, press Ctrl+Shift+Eject.

Also, you can press Fn+Ctrl+Eject to quickly restart your Mac, shut it down or put to sleep.

Email Validation Done Right

| Comments

Let’s imagine that you have to check if a string is a valid email. You could come up with something like:

1
/[a-zA-Z0-9\.]+@[a-z]+\.[a-z]+/

It works, right? WRONG. Sure it’ll handle a couple of your test examples. But it’s not ready for real world usage. Here’s a standards compliant Perl regex.

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
/(?(DEFINE)
   (?<address>         (?&mailbox;) | (?&group;))
   (?<mailbox>         (?&name;_addr) | (?&addr;_spec))
   (?<name_addr>       (?&display;_name)? (?&angle;_addr))
   (?<angle_addr>      (?&CFWS;)? < (?&addr;_spec) > (?&CFWS;)?)
   (?<group>           (?&display;_name) : (?:(?&mailbox;_list) | (?&CFWS;))? ;
                                          (?&CFWS;)?)
   (?<display_name>    (?&phrase;))
   (?<mailbox_list>    (?&mailbox;) (?: , (?&mailbox;))*)

   (?<addr_spec>       (?&local;_part) \@ (?&domain;))
   (?<local_part>      (?&dot;_atom) | (?&quoted;_string))
   (?<domain>          (?&dot;_atom) | (?&domain;_literal))
   (?<domain_literal>  (?&CFWS;)? \[ (?: (?&FWS;)? (?&dcontent;))* (?&FWS;)?
                                 \] (?&CFWS;)?)
   (?<dcontent>        (?&dtext;) | (?&quoted;_pair))
   (?<dtext>           (?&NO;_WS_CTL) | [\x21-\x5a\x5e-\x7e])

   (?<atext>           (?&ALPHA;) | (?&DIGIT;) | [!#\$%&'*+-/=?^_`{|}~])
   (?<atom>            (?&CFWS;)? (?&atext;)+ (?&CFWS;)?)
   (?<dot_atom>        (?&CFWS;)? (?&dot;_atom_text) (?&CFWS;)?)
   (?<dot_atom_text>   (?&atext;)+ (?: \. (?&atext;)+)*)

   (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
   (?<quoted_pair>     \\ (?&text;))

   (?<qtext>           (?&NO;_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
   (?<qcontent>        (?&qtext;) | (?&quoted;_pair))
   (?<quoted_string>   (?&CFWS;)? (?&DQUOTE;) (?:(?&FWS;)? (?&qcontent;))*
                        (?&FWS;)? (?&DQUOTE;) (?&CFWS;)?)

   (?<word>            (?&atom;) | (?&quoted;_string))
   (?<phrase>          (?&word;)+)

   # Folding white space
   (?<fws>             (?: (?&WSP;)* (?&CRLF;))? (?&WSP;)+)
   (?<ctext>           (?&NO;_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
   (?<ccontent>        (?&ctext;) | (?&quoted;_pair) | (?&comment;))
   (?<comment>         \( (?: (?&FWS;)? (?&ccontent;))* (?&FWS;)? \) )
   (?<cfws>            (?: (?&FWS;)? (?&comment;))*
                       (?: (?:(?&FWS;)? (?&comment;)) | (?&FWS;)))

   # No whitespace control
   (?<no_ws_ctl>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

   (?<alpha>           [A-Za-z])
   (?<digit>           [0-9])
   (?<crlf>            \x0d \x0a)
   (?<dquote>          ")
   (?<wsp>             [\x20\x09])
 )

 (?&address;)/x

I couldn’t even imagine that the matter is this complex.

jQuery Chaining

| Comments

I am beginning to fall in love with jQuery. Screw you, bicycle inventors! :-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function next_step() {
    // note how elegant and self-explanatory this code is.
    var cl = 'step_selected';
    $('.' + cl).removeClass(cl).next().addClass(cl);

    // Also look at previous version of the code,
    // before I remembered about chaining methods.

    // var li = $('.step_selected');
    // li.removeClass('step_selected');
    // li.next().addClass('step_selected');

    // Not as sexy, right? :-)
}

Funny Side Benefit :-)

| Comments

There I was, minding my own business, trying to solve problems in graph theory and I accidentally made a Sudoku puzzle solver! Isn’t it funny how life turns out sometimes? But that’s just how awesome LINQ is.

Eric Lippert on LINQ

Atomic Updates, Part 2

| Comments

Let’s review another common scenario: posts and votes. Mongo’s approach would be to store vote count in the post itself (caching) and keep voters also in the post, in an array field. Let’s assume we want to register a new vote on post. This operation consists of three steps:

  1. ensure that the voter hasn’t voted yet, and, if not,
  2. increment the number of votes and
  3. add the new voter to the array.

MongoDB’s query and update features allows us to perform all three actions in a single operation. Here’s what that would look like from the shell:

1
2
3
// Assume that story_id and user_id represent real story and user ids.
db.stories.update({_id: story_id, voters: {'$ne': user_id}},
                  {'$inc': {votes: 1}, '$push': {voters: user_id}});

What this says is “get me a story with the given id whose voters array does not contain the given user id and, if you find such a story, perform two atomic updates: first, increment votes by 1 and then push the user id onto the voters array.”

This operation highly efficient; it’s also reliable. The one caveat is that, because update operations are “fire and forget,” you won’t get a response from the server. But in most cases, this should be a non-issue.

quote from MongoDB site.

How would you implement this in your regular SQL database?

MongoId and Atomic Increment

| Comments

I am starting to learn this new piece of technology, the MongoDB. It has many sweet features. However, using Ruby driver directly is not too comfortable. Plus we’re all hooked to ORM sweeteners. So people started developing ORM libraries for MongoDB and Ruby (MongoId, MongoMapper to name a few).

I have decided to try MongoId first. It seems to be a quite good library. However, it still has gaps in documentation. Today I was finding out how to perform an atomic increment of a field. In SQL world this would look like:

1
update users set spam_count = spam_count + 1;

Mongo Ruby driver supports this kind of operation, but MongoId doesn’t. So I was trying to get down to the driver. And this is what I couldn’t easily find in the docs. If you’re like me, I’ll save you a couple of hours. Here it is:

1
2
# assume we know object id already
User.collection.update({'_id' => '4c05848e45760182b5000001'}, {'$inc' => {'spam_count' => 1}})

Intuitive Behaviour

| Comments

Today I was looking at exception stack trace. One of suspicious places was ‘…\reports_controller.rb:129”. Okay, navigating to this location using RubyMine is a piece of cake. Ctrl+Shift+N to get to reports_controller.rb, then Ctrl+G to position caret at specified line. But hey, that’s two actions. I hit Ctrl+Shift+N, put ‘reports_controller.rb:129’ in and voila! It worked just as I expected! RubyMine: +1 to intuitivity, +1 to overall impression.

Advanced Default Parameters

| Comments

Today I was quite amazed by one of Ruby features. It is about default values of method parameters. For example you can do something like this:

1
2
3
def get_current_actions(project_id, status_id = params[:status_id] || DEFAULT_STATUS_ID)
    # implementation goes here
end

The code is saying basically this: “if status_id is not passed explicitly, try to take its value from params array. If it doesn’t contain specified key, then fall back to a constant”. This feature (as almost all the rest of Ruby magic) made avaiable by Ruby’s nature: it is interpreted language. This type of code is totally unusual to guys like me, who come from the world of static typing and compiled languages. But I think I’m gonna get used to it :-)