Eugene Codes

Tales from a developing developer

Dynamic Finders and Ghost Methods in Ruby and Rails

Rails does a lot for you, including dynamically generating methods like find_by_[attribute], for every attribute your model has. I had always assumed that rails generated the methods when you start it up. While it’s true that rails generates the methods for you, it’s the where and the how that’s interesting.

method_missing

When you send an object a method in Ruby, it first looks for that method in the object itself, then it looks up the inheritance chain. What happens if it reaches the Object class and still can’t find the method? It calls method_missing, which is defined on Object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal
  attr_accessor :sound

  EMOTIONS = {confused:'?',
              surprised:'!'}

  def initialize(sound)
    self.sound = sound
  end

  def confused_sound
    sound + EMOTIONS[:confused]
  end

end

whiskers = Animal.new("meow")

puts whiskers.sound #=> meow
puts whiskers.confused_sound #=> meow?
puts whiskers.surprised_sound #=> NoMethodError

So how can we take advantage of this? By overriding method_missing, we can actually create dynamic methods on the fly. So before, we were adding the [emotion]_sound manually, now we can add them dynamically.

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
class Animal
  attr_accessor :sound

  EMOTIONS = {confused:'?',
              surprised:'!'}

  def initialize(sound)
    self.sound = sound
  end

  def method_missing(name, *args)
    if match = EMOTIONS.keys.find{|emotion| name.match(/#{emotion}_sound/)}
      sound + EMOTIONS[match]
    else
      super
    end
  end

end

whiskers = Animal.new("meow")

puts whiskers.sound #=> meow
puts whiskers.confused_sound #=> meow?
puts whiskers.surprised_sound #=> meow!
puts whiskers.angry_sound #=> NoMethodError

Now, as long as our Animal class has a particular emotion, we can call [emotion]_sound without defining it ourselves.

respond_to? and respond_to_missing?

Now we need to be able to tell that our Animal class has a method called confused_sound but not angry_sound. The way we do that is by overriding the respond_to? or respond_to_missing? methods. respond_to_missing? has the advantage in that it allows us to use whiskers.method , such as whiskers.method(:confused_sound).

1
2
3
4
5
6
7
8
9
10
class Animal
  ...
  def respond_to_missing?(name, include_private=false)
    EMOTIONS.keys.any?{|emotion| name.match(/#{emotion}_sound/)} || super
  end
end

whiskers.respond_to?("sound") #=> true
whiskers.respond_to?("confused_sound") #=> true
whiskers.respond_to?("angry_sound") #=> false

These are called ghost methods because if we call whiskers.methods, confused_sound won’t show up.

Rails and Ghost methods

Coming back to Rails, we can see how method_missing can be used to dynamically define finders such as find_by_name, find_by_age, etc. However, going through method_missing every time can be a little slow.

As a result, Rails gets even more meta and actually defines the finders as methods on the Model the first time they are called. This way, they are essentially cached, and the next call will no longer go through method_missing.

Stay tuned for a future post on other details of metaprogramming in Rails with define_method and instance_eval.

Palindromes, Sets and XOR

My Flatiron School classmate Arielle Sullivan posted her solution to an interesting programming puzzle:

Determine whether the letters in a given string can be arranged to form a palindrome.

Her solutions is below:

1
2
3
4
5
6
7
8
9
10
11
def possible_palindrome?(string)
  uniq_chars = []
  string.chars.each do |next_char|
    if uniq_chars.include?(next_char)
      uniq_chars.delete(next_char)
    else
      uniq_chars << next_char
    end
  end
  uniq_chars.length <=1 ? true : false
end

The gist of it is this: Iterate through the string to make sure at most one character is unpaired. So for instance with lloo, every character is paired up, so we can make the palindrome lool. The same with llo making the palindrome lol.

If two or more characters are unpaired, this breaks down. So for instance lllo has an unpaired l and therefore can’t form a palindrome.

Looking at line 2 above, my first thought was that this was a good case for the ruby inject method (or reduce).

1
2
3
4
5
6
7
8
9
def possible_palindrome?(string)
  string.chars.inject([]) do |uniq_chars,next_char|
    if uniq_chars.include?(next_char)
      uniq_chars.delete(next_char)
    else
      uniq_chars << next_char
    end
  end.length <=1
end

Looking good! But something was bothering me. We’re essentially doing the following: – If the array of uniques has a character, remove it. – If it doesn’t have a character, add it.

This is essentially the XOR or exclusive or operator! In ruby the xor operator is the ^ character. In boolean expressions, it returns true if one of two expressions is true but not both.

1
2
3
4
true ^ true   #=>false
true ^ false  #=>true
false ^ true  #=>true
false ^ false #=>false

How does this translate to sets and characters? Well, if we have two sets of characters, we can xor them together to get the characters in either but not in both. This is exactly what we want. Unfortunately, you can’t natively xor two arrays in ruby, but you can with the Set class. Let’s give it a try:

1
2
3
4
5
6
7
require 'set'

def possible_palindrome?(string)
  string.chars.inject(Set.new []) do |uniq_chars,next_char|
    uniq_chars ^ [next_char]
  end.length <=1
end

Even better! If we wanted to do this without the Set class, we can xor two arrays ourselves:

1
2
3
4
arr1 = [1,2,3]
arr2 = [3]

(arr1 + arr2) - (arr1 & arr2) #=> [1,2,3,3] - [3] => [1,2]

Let’s break this down. (arr1 + arr2) returns an array that combines the two arrays. (arr1 & arr2) returns an array of items in both. Subtracting the latter from the former results in items that are in either one but not in both.

So our final solution, sans sets would be:

1
2
3
4
5
def possible_palindrome?(string)
  string.chars.inject([]) do |uniq_chars,next_char|
    (uniq_chars + [next_char]) - (uniq_chars & [next_char])
  end.length <=1
end

Custom Javascript Events With 2048

After wasting a few hours playing with 2048, I realized that hey, I’m learning javascript, and 2048 is Javascript! I dove into the 2048 source code to see what I could find.

2048 is entirely client side, which means there is no server to manage. A high level view of the code is below:

  • application.js – Launches the game
  • game_manager.js – Acts as the main controller
  • grid.js – Represents the state of the game board
  • tile.js – Represents a single tile object
  • html_actuator.js – Deals with the front end and updating the html itself.
  • keyboard_input_manager.js – Deals with the input, whether it be keyboard or phone swipes
  • local_storage_manager.js – Deals with saving game state.

Great! Let’s take a look. When the game is started, the GameManager constructor takes in the html_actuator, keyboard_input_manager and storage_manager objects as arguments. We do some basic initializing, and then this:

1
2
3
4
5
6
7
function GameManager(size, InputManager, Actuator, StorageManager) {
  ...
  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.restart.bind(this));
  ...
}

Up until then, I had only really dealt with pretty basic DOM events. Click, hover, submit, were dependable and familiar. Wtf is restart?

Custom Events

Some digging led me to reading about custom events in Javascript. Custom events are pretty much exactly like DOM events, but both the dispatches and the listeners are defined by you.

From the mozilla documentation:

1
2
3
4
5
// Listen for the event.
elem.addEventListener('build',function (e) {...},false);

// Dispatch the event.
elem.dispathEvent(event);

You can also include data in the events you throw.

1
2
3
4
5
var event = new CustomEvent('build', {'detail': elem.dataset.time});

function eventHandler(e) {
  log('The time is: '+e.detail);
}

So where does that leave us? Let’s take another look at the 2048 code.

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
KeyboardInputManager.prototype.on = function (event, callback) {
  if (!this.events[event]) {
    this.events[event] = [];
  }
  this.events[event].push(callback);
};

KeyboardInputManager.prototype.emit = function (event, data) {
  var callbacks = this.events[event];
  if (callbacks) {
    callbacks.forEach(function (callback) {
      callback(data);
    });
  }
};

KeyboardInputManager.prototype.listen = function () {
  var self = this;
  ...
  document.addEventListener("keydown", function (event) {
    ...
        event.preventDefault();
        self.emit("move", mapped);
    ...
  });
}

The “on” method lets us attach callbacks to custom events in the InputManager object. The emit function lets us throw custom events with directional data attached to them.

Let’s go over the steps in order

  1. I hit the up arrow on the keyboard
  2. The listen function captures it and “emits” a “move” event with data “up”
  3. The emit function calls the callback for “move”, which points to the move function in the GameManager.

What about this weird bind thing? Well, the “on” method is defined in the InputManager object, but the callback function, “move”, is defined in the Gamemanager.

We are telling the callback that when it refers to “this”, it should refer to the GameManager, not the InputManager, even though it will be called from the InputManager.

In conclusion, reading through other people’s source code can be both educational and useful. Since I’ve started reading through it, not only have I become a better developer, but it makes playing 2048 itself a lot easier:

What Is Rack, Really?

After spending some time on Ruby foundations, we’ve finally moved onto the web at Flatiron School’s’s 5th Ruby class. The first topic we covered was rack. According to the official site:

“Rack provides a minimal interface between webservers supporting Ruby and Ruby frameworks.”

Well, that helps. Let’s break it down. What exactly is a web server? In its simplest form, you can implement a web server by doing the following:

  1. Listen to a port (80 most of the time) until a request comes in
  2. Deal with the request
  3. Output the response
  4. goto 1

That sounds great! But we don’t want to implement that from scratch. Luckily, there are plenty of web servers already out there! Not only does rack let you easily switch between web servers, it also let’s you use the same middleware across different applications.

Taking a step back

In essence, there was a time where if you wanted to write a web app in Ruby, you had to write the interface to the server, which would change depending on the server you used. No more!

Some big points to take away:

  1. Ruby is not a language for web development. Given the immense popularity of Rails, it’s easy to assume that Ruby was created as a language for web development. In reality, it was created as a general purpose language, and the tools for web development were added over time. Rack and rails are two such tools. The former is a framework, and Rack is the interface that connects the framework to the servers themselves.

  2. The web itself is language agnostic If we take a really big picture view of the web, it is just a bunch of http requests getting flung back and forth between computers. Everything is text and ultimately, whatever language is used on the server will have to create and receive http requests. Rack handles that aspect for Ruby applications.

Sources:

http://stackoverflow.com/questions/15875941/creating-a-web-app-in-ruby-without-a-framework

http://www.youtube.com/watch?v=iJ-ZsWtHTIg

http://rack.github.io/

http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html

Logging With Battleship

After the first week at flatiron School, I decided to punish challenge myself by joining the Ruby Fight Club meetup and signing myself up for the Battleship competition.

The task at hand is to code a simple player class that plays battleship by doing two things:

  1. Placing its ships at the beginning of a game
  2. Given the state of the game board and the remaining ships, making a move by returning a set of x,y coordinates.

My strategy loosely follows the one posted by Nick Berry. While I’ll save the details for another post, the strategy has two basic modes:

  • Search mode – No ship locations are known.
  • Hunt mode – There has been a hit but the ship hasn’t been sunk.

In search mode, the algorithm tries to calculate the most likely places for a ship to have been hidden, using the known information about missed shots and remaining ships. In hunt mode, a ship has been hit and the algorithm looks around the successful hit until the ship has been sunk.

At first I tried my best to be a good little Ruby student and go the test/behavior driven development route. This strategy worked well for setting up many of the early helper methods and even test some of the basic early game strategy. For instance, in the beginning of the game, the four cells in the center have the highest probability of having a ship, so the get_best_moves method should always include [4,4]. Rspec makes testing this ridiculously simple:

1
2
3
4
5
6
7
8
9
before(:each) do
  @eugmill = EugmillPlayer.new()
end

describe "#get_best_moves" do
  it "should include center coordinates for a blank board" do
    expect(@eugmill.get_best_moves(board1,[2,3,3,4,5])).to include([4,4])
  end
end

Bigger problems

Testing with Rspec worked beautifully for these early game scenarios, when our Player object was still innocent and the board untouched by the violence of naval warfare. Difficulty arises when testing for and troubleshooting scenarios 20 moves into the game.

Enter the logger class. Ruby’s built-in logger lets you write to files to keep track of what happens in your application. This way, when your player class makes a bizarre move halfway into the game, you can see exactly what was going on inside your class, as well as the list of moves leading up to that move. Here is an example of one of my log files:

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
I, [2014-02-16T14:27:05.225023 #89817]  INFO -- board states: 
   0  1  2  3  4  5  6  7  8  9
0 [ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
1 [ ][ ][ ][ ][M][ ][ ][ ][ ][ ]
2 [ ][ ][H][ ][ ][ ][M][ ][ ][ ]
3 [ ][ ][ ][M][ ][ ][ ][M][ ][ ]
4 [ ][H][ ][ ][ ][M][ ][ ][ ][ ]
5 [ ][M][ ][ ][M][ ][ ][ ][M][ ]
6 [ ][ ][M][ ][ ][ ][M][ ][ ][ ]
7 [ ][ ][ ][M][ ][ ][ ][M][ ][ ]
8 [ ][ ][ ][ ][ ][M][ ][ ][ ][ ]
9 [ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]

I, [2014-02-16T14:27:05.225136 #89817]  INFO -- search mode: Move 16,Getting best move...
I, [2014-02-16T14:27:05.233883 #89817]  INFO -- game state: Making move 4,0
I, [2014-02-16T14:27:05.631970 #89817]  INFO -- hit: It was a hit!!!
I, [2014-02-16T14:27:05.632115 #89817]  INFO -- hit: [[4, 1], [2, 2], [4, 0]]
I, [2014-02-16T14:27:05.632235 #89817]  INFO -- hit: Ship sunk!size:2
I, [2014-02-16T14:27:05.632408 #89817]  INFO -- hit: Sunk ship coords: [[4, 0], [4, 1]]
I, [2014-02-16T14:27:05.633557 #89817]  INFO -- board states: 
   0  1  2  3  4  5  6  7  8  9
0 [ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
1 [ ][ ][ ][ ][M][ ][ ][ ][ ][ ]
2 [ ][ ][H][ ][ ][ ][M][ ][ ][ ]
3 [ ][ ][ ][M][ ][ ][ ][M][ ][ ]
4 [H][H][ ][ ][ ][M][ ][ ][ ][ ]
5 [ ][M][ ][ ][M][ ][ ][ ][M][ ]
6 [ ][ ][M][ ][ ][ ][M][ ][ ][ ]
7 [ ][ ][ ][M][ ][ ][ ][M][ ][ ]
8 [ ][ ][ ][ ][ ][M][ ][ ][ ][ ]
9 [ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]

From this we know that on move 16, it takes a shot at [4,0]. We can do this for any move in the game. Now if we’re curious about why it made that move, we can also pull up our second log file which logs the probability distributions for the board at each move:

1
2
3
4
5
6
7
8
9
10
11
12
I, [2014-02-16T14:27:05.233150 #89817]  INFO -- pboard: Board for move 16
I, [2014-02-16T14:27:05.233301 #89817]  INFO -- pboard: 
[10, 15, 19, 19, 17, 21, 17, 17, 15, 10]
[14, 16, 17, 8 , 0 , 12, 10, 15, 18, 15]
[19, 21, 26, 16, 13, 12, 0 , 6 , 15, 17]
[19, 13, 16, 0 , 7 , 8 , 6 , 0 , 10, 17]
[22, 14, 21, 12, 8 , 0 , 8 , 10, 12, 21]
[17, 0 , 6 , 5 , 0 , 6 , 7 , 7 , 0 , 17]
[17, 5 , 0 , 6 , 8 , 7 , 0 , 6 , 8 , 19]
[17, 11, 6 , 0 , 10, 7 , 6 , 0 , 8 , 15]
[15, 16, 15, 10, 12, 0 , 8 , 8 , 14, 14]
[10, 14, 17, 17, 21, 17, 19, 15, 14, 10]

Skipping over how the probability weights are calculated, we can see that cell [4,0] has a weight of 22, which is the maximum for this board, and also the reason it was targeted.

So how do we set up our logging? In this case, we initialize our loggers whenever a new Player object is created:

1
2
3
4
5
6
7
8
class EugmillPlayer
  def initialize
    @movelogger = Logger.new("./players/logs/mylog.txt")
    @boardlogger= Logger.new("./players/logs/board_log.txt")
    ...
  end
  ...
end

Then, whenever we want to log a piece of information, we can call our logger

1
2
3
4
5
6
7
@logger.info('board states'){pprint_board(state)}
  if @target_mode == :search
    @logger.info("search mode") {"Move #{@move_counter},Getting best move..."}
    moves = get_best_moves(state,ships_remaining,pboard)
    row,col = moves[rand(moves.size)]
    @logger.info('game state') {"Making move #{row},#{col}"}
...

The logger has different severity levels, depending on whether we’re logging an error or just debugging information. There are multiple ways to pass messages to our logger. In this case I am passing a label as a string parameter, and a the bulk the message in a block. The result looks like the screenshot posted above.

From a technical perspective, there isn’t anything difficult about the logger class. If the logging is too terse, debugging is difficult, and if it is too verbose, the logs grow out of control pretty quickly. Finding the right mix makes logger indespensible to debugging complex scenarios quickly.

Ultimately, I found logger to be complementary to Rspec, rather than a replacement. With Logger I can catch strange behavior and try to reproduce it, while with Rspec I can make sure to test for and prevent the same errors from happening again.