Thursday, December 26, 2013

Why Strings Are Mutable in Ruby

Objects can be either mutable or immutable. Mutable objects are objects whose states can be changed, while immutable objects are objects whose states never change after creation. One important implication of immutable objects are any modification of existing objects results in new objects.

Immutable objects has many desirable properties.

  • Immutable objects are thread-safe. Since any modification of existing objects results in creating new one, there is no need to set up lock to avoid conflicts while multiple threads attempt to access the object at the same time. 
  • Immutable objects make good hash keys, because the value of the hash key should never change. 
  • Immutable objects have higher memory efficiency, because the same object can be pointed to by multiple variables, thus enhancing reusability. 
Why Ruby does not adopt immutable string if it has so many advantages? The reason is it is intuitive to think of string as mutable, because you can do so many operations on a string, concatenation, appending etc. Ruby thus makes string mutable by default at the expense of performance. 

For example, appending on a string didn't creating new string.

> a = 'Hello'
=> "Hello" 
> a.object_id
=> 70161220331780 
> a << ' world!'
=> "Hello world!" 
> a.object_id
=> 70161220331780 # object_id does not change!

Don't confuse the code above with the following code

> a = 'Hello'
=> "Hello" 
> a.object_id
=> 70161220396780 
> a += ' world!'
=> "Hello world!" 
> a.object_id
=> 70161220412700 

As  "+=" creates a new string and then assign it to "a". The old string is not changed. You can make a string as immutable by using the method "freeze". After you freeze a string, an attempt to change it results in RuntimeError.

> a = 'Hello'
 => "Hello" 
> a.freeze
 => "Hello" 
> a << ' world!'
RuntimeError: can't modify frozen String

Why we need "freeze". What if we don't have it. Then we can't use string as hash key in Ruby. Because if you change the value of the string, you can't use the old value to refer to the right position in the hash. Here is how Ruby deal with it. It copies the string, freezes the copy and use the copy as the hash key.

Friday, December 13, 2013

Ruby: load vs require, include vs extend

When it comes to including files within another file, there are two methods we can use, load and require.

Here is a table that gives the differences

loadrequire
file pathabsolute/relativeabsolute/relative
file extensionRequiredOptional
Can load a file multiple times YesNo

The file path and file extension rows are pretty straightforward. The most important difference is the third row, Can load a file multiple times. You can load a file multiple times by using method load, but for require, you can't. That's because The absolute path of the loaded file is added to $LOADED_FEATURES. Every time a new file is going to be required, ruby checks if there is already a same absolute path in $LOADED_FEATURES, and if there is, the file won't be loaded again.

It's easy for beginner to confuse load/require with include/extend. To some extent, these two groups of methods are similar. However, they are used under different context. include and extend are used to adding methods from a module to classes. If you include a module within a  class, all the methods within that module will be added to the class as its instance methods. If you extend a module within a  class, all the methods within that module will be added to the class as its class methods.  See the example below,

module Mod_1
  def hello1
    puts "Hello from Mod_1.\n"
  end
end

module Mod_2
  def hello2
    puts "Hello from Mod_2.\n"
  end
end

class Klass
  include Mod_1
  extend Mod_2
  def hello3
    puts "Hello from Klass.\n"
  end
end

a = Klass.new
a.hello1      
Hello from Mod_1
=> nil 
Klass.hello2
Hello from Mod_2
=> nil
a.hello3
Hello from Klass
=> nil

Tuesday, December 10, 2013

Rails router, route, path, URL, resource routing

When it come to routing, there is a lot of confusing terms. In this blog, I'll try to shed some light on their meanings.

Router
Rails router is just a piece of code that handles HTTP verb and URL combos and dispatch them to a controller's action.

Route
Route is just the mapping from HTTP verb and URL combo to controller's action.
For example
get '/patients/:id', to: 'patients#show'

Path vs URL
Paths and URLs are easy to be confused.
Say we have a users controller, we can have a path and a URL for it, shown below.
Path => /users
URL => http://www.example.com/users
As we can see here, URLs are just paths prefixed with the current host, port and path prefix.

Resource Routing
In Rails, resource routing allows you to quickly declare all of the common routes for a given resourceful controller by using a single line. For example, you have a controller called photos.

resource :photos

By adding this line into our routes.rb file, we creates the following seven routes.

HTTP VerbPathActionUsed for
GET/photosindexdisplay a list of all photos
GET/photos/newnewreturn an HTML form for creating a new photo
POST/photoscreatecreate a new photo
GET/photos/:idshowdisplay a specific photo
GET/photos/:id/editeditreturn an HTML form for editing a photo
PATCH/PUT/photos/:idupdateupdate a specific photo
DELETE/photos/:iddestroydelete a specific photo
For example, if Rails application receives a incoming request for 
GET /photos/1
The router would dispatch that request to the show action in the photos controller with {id: '1'} in params.