Beauty of CoffeeScript

Written by Rashmi Yadav on

As we are web developer we mostly write JavaScript day by day in our job. But as a Ruby developer when I look raw JavaScript code I don't like that. I want to write JavaScript in a beautiful way.

CoffeeScript is a beautiful programming language which allows us to write less code like Ruby, Python.

There are lot of great things which we can use in CoffeeScript to write beautiful code which turns into proper JavaScript.

I found some great tricks and useful syntax in CoffeeScript so thought to share them.

Use @ instead of this

Usually we write this to refer object/element

$('.account-link').click ->
  alert($(this).html())

We can use @

$('.account-link').click ->
  alert($(@).html())

Also to call a class instance method we can use @

class Calculation
  sum: (a, b)->
    @print_sum(a+b)

  print_sum: (total)
    console.log("Total is #{total}")

p = new Person
p.sum()

Some useful Operators

CoffeeScript provides operators alias very pretty it feels we are writing English sentence.

is and isnt

For example to check a boolean value

In JavaScript we do like

var a;

a = true;

if (a === true) {
  alert('rays');
}

But in CoffeeScript we can do

a = true
alert('Rays') if a is on

is is alias for === and on is for true. We can also use yes for true

a = true
alert('Rays') if a is yes

Like is there isnt for !== in JavaScript

a = false
alert('Rays') if a isnt yes

Chained Comparisons

If we need to compare a variable with two values what we do in JavaScript

a = 5
a > 1 && a > 4

In CoffeeScript

a = 5
1 < a > 4

As I got to know this is same as in Python

Existential operator ?

If we want to check for null and undefined

if (a!=null && typeof a!='undefine')
  alert('rays')

In CoffeeScript just do

alert('rays') if a?

And we also have to check that a should not be 0 then in JavaScript

if (a!=null && typeof a!== 'undefine' && a!=0)
  alert('rays')

In CoffeeScript

alert('rays') if(a?=0)

Or call a function on object if that exists

object?.sum()

It similar to try in Ruby

Splats ...

Splats ... which is useful to send variable number of arguments both at function calling an invocation.

For example

printAddress = (address1, address2...) ->
  alert("#{address1} #{address2.join(',')}")

printAddress('12', 'Mansarover', 'Jaipur')

Same we can do while invoking function

printAddress = (address1, address2..) ->
  alert("#{address1} #{address2.join(',')}")

printAddress(['12', 'Mansarover', 'Jaipur']...)

List comprehensions

Sometime we need to put a condition in loop fo example

for book in books
  alert(book.name) if book.price > 18

We can make it short using comprehension

alert((book.name) for book in books when book.price > 18

Isn't it look pretty now?

Fat arrow

In CoffeeScript we have arrow -> for function. It also has => Fat arrow which is helpful when used callback-based libraries like jQuery or Prototype. So if we need to call JavaScript event and want to use class instance variable then we need to use fat arrow =>

class Person
  constructor: () ->
    @name = 'rashmi'
  
  print_name: ->
    $('.show_name').click =>
      alert(@name)
  
p = new Person
p.print_name()

In the above example on click event we want to access instance variable @name which can only be access by Fat arrow =>

I am sure you also find these tricks useful while writing CoffeeScript

There are lot of resources where you can learn about CoffeeScript. You can find most of on CoffeeScript website

CarrierWave Better Storage

Written by Rashmi Yadav on

I am using CarrierWave gem for uploading files and faced a problem of file storage after my application reached a certain number of uploaded files.

I have a User model and avatar field for uploading photo of a user.

As my application growing I was getting to many users in system.

So when users count exceeded 32768 in database I started getting an error of "Too many links" in logs.

Something like this

Errno::EMLINK - Too many links - /home/sites/apps/binaryfunction/releases/20140110162610/public/uploads/user/avatar/42435

When I checked file numbers by running following command in system inside

$ cd /home/sites/apps/binaryfunction/releases/20140110162610/public/uploads/user/avatar

$ ls | wc -l

I found that the files count inside my avatar directory has reached 32768.

What is this error and this number and how can we fix it

I looked over internet and found that there is a maximum limit for files under a directory.

It depends on what filesystem your distribution uses. If you use a newer desktop distribution (recent version of Fedora), you probably use ext4. If you use something older, it is most likely ext3.

So each filesystem has it own number to store file in directory, like

ext2: -> 32768

ext3: -> 31998

ext4: -> 64000

It won't store more files after reaching this number. And start complaining about "Too many links".

CarrierWave gives us default storage path when we generate a uploader

Usually AvatarUploader looks like

class AvatarUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

According to this all files will be stored in uploads/user/avatar/ directory for a user avatar.

So for user#id 1 it will be like uploads/user/avatar/1.png.

This will work fine until you reach to maximum number of files(i.e. for ext2 32768).

It means after 32768 your user's avatar will not be saved as your filesystem don't allow more than that inside a dir.

No worry we have a solution here ..yeahhhhh :-)

Paperclip has solved this problem by giving a better storage path for your files by default.

If you have noticed that paperclip generates different directory structure for your files.

An example of file storage path in paperclip

users/avatar/000/000/013/small/1

You can see here that they have this nested directories “000/000/013”.

They called this id_partition.

But CarrierWave do not have this nested directory structure by default.

Lets implement this with CarrierWave uploader.

class AvatarUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{id_partition}/#{model.id}"
  end

  private

  def id_partition
    case id = model.id
    when Integer
      ("%09d" % id).scan(/\d{3}/).join("/")
    # can add more checks if you have other id type (i.e. string for monogdb)
    else
      nil
    end
  end
end

So using above id_partition we will be nesting our directories like

"000/000/001"

If our file count reaches 1000 it will create another nested directory like

"000/001/000"

So we have solved this problem for our AvatarUploader. But what if we have many uploaders in system and we want this in all of our uploaders.

I would suggest to create a BaseUploader and put this method inside that and all other uploaders can simply inherit BaseUploader.

class BaseUploader < CarrierWave::Uploader::Base
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{id_partition}/#{model.id}"
  end

  private

  def id_partition
    case id = model.id
    when Integer
      ("%09d" % id).scan(/\d{3}/).join("/")
    else
      nil
    end
  end
end

And AvatarUploader will become

class AvatarUploader < BaseUploader
end

To verify this id_partition work you can simply write a test something like

require 'spec_helper'

describe BaseUploader do
 describe 'store_dir' do
   it 'should have id_partition included' do
     model_id = 123
     uploader = BaseUploader.new
     model = double(id: model_id)
     uploader.stub(:model).and_return(model)

     expect(uploader.store_dir).to match(/000\/000\/#{model_id}\/#{model_id}/)
   end

   it 'should have another id_partition when id is higher' do
     model_id = 1001
     uploader = BaseUploader.new
     model = double(id: model_id)
     uploader.stub(:model).and_return(model)

     expect(uploader.store_dir).to match(/000\/001\/001\//)
   end
 end
end

By doing this you can easily solve problem of "Too many links" with CarrierWave and avoid having too many files inside one folder.

I know it is a bit hard to migrate all your uploads when you reach to this number of filess so it is better to have this in first place to avoid problems in future.

I hope this article will be helpful to you.

There are some other articles where you can find more about this.

paperclip id_partition

mkdir(): Too Many Links – What It Is and How to Fix

Git bare repository

Written by Rashmi Yadav on

Bare and Non bare repository

Git is a very powerful version control system to maintain our code. We can share and keep history of our code so effectively.

We have a remote repository which is actually called bare repository and the local one called non bare repository. So Git bare repository is a shared repository where we push our code. It does not have files and directories like non bare repository and not even .git so its not for development or to work on.

And also you cannot run git status/pull/push/fetch commands in bare repository.

A remote repository should always be a bare repository.

You can clone your remote repository with bare option

git clone --bare [your remote repositry url]

Here you will see that there are no files and directories as we have in our development or working repository.

Lets try it out on your local system.

Create a Git bare repository

mkdir test_bare
cd test_bare_repo
git init --bare

When we do ls -lart you will see something like

bare repo

Now create a non bare repository and add bare repository as origin

mkdir test_non_bare
cd test_non_bare
git init
git remote add origin /Users/raysrashmi/crap/test_bare_repo [path of bare repo created above]

Or you can clone bare repository

git clone /Users/raysrashmi/crap/test_bare_repo test_non_bare

Now add README file here

touch README
git push origin master

As you see we can write code here and push to remote repository which is bare.

Now clone your development repository 'test_non_bare'

mkdir git clone /Users/raysrashmi/crap/test_non_bare test_clone
cd test_clone

If we update README file, commit and then try to push we can't because we are pushing to non bare repository.

Migrating a Git repository

Sometime we require to migrate our Git repository to another location. we have to use bare repository

Run following commands for migrating repository

git clone --bare git@myhost.com:username/old-repo.git
cd old-repo.git
git push --mirror git@myhost.com:username/new-repo.git

Mirroring from bare repository will keep all you history every brach and tag of you remote repository. and will not push any local branch or changes to new repository