ice-breaking with ruby project structure
Everything is an object in ruby.
Thus visualising everything as an
object and modeling your problem based on the principles of Object
Oriented Programming (OOP) becomes very important.
During my second week at
Codeignition, we started with a very simple problem and then extended it
everyday to understand some OOP principles such as inheritance,
namespacing and method overriding. Thanks to our mentor, Shobhit Srivastava, some software
engineering skills always step in to show us the beauty of this world in
bright sunlight and this time they were-
- Test Driven Development, lovingly known as TDD,
- Don't Repeat Yourself, the DRY principle and
- Pair programming.
The basic problem given to us was-
Problem - Coordinates of two points are given, return length of line segment formed by the points.
Simple! Isn't it? Solution to above problem will be a Line class which have attributes start_point and end_point and a method length which gives distance between these two points. We can implement above class either based on the assumption that coordinates of these two points are given to us in the form of two arrays, i.e. [x1,y1],[x2,y2] or that we are given coordinates as x1,y1,x2,y2.
Now, one could argue that Points are separate identities themselves and the method Line#length does nothing but returns distance between two instances of Point class. So our solution can be refactored to add Point class with attributes x_coordinate, y_coordinate and a class method distance_between point1, point2.
Writing Ruby code- The conventions!One can make any number of classes in a single ruby file, write logic in this file and get it executed. But such code is hard to understand and tough to debug. Thus, we have conventions. When everybody follows some conventions, all our folder structures are alike, which in turn makes collaborated works easy.
An easy to maintain, clear folder structure for Ruby projects is-
All our classes and modules that means functionality goes in lib folder. That means if we want to make a class named Line for our given problem, it goes in Line.rb file in lib folder. Class names in ruby are conventionally written in CamelCase while method names are written in snake_case. Conventionally one file has one class. So lib folder will contain two files Line.rb and Point.rb as per the solution discussed above.Now, while working on any project in any programming language, we should always follow Test Driven Developement(TDD) approach. When our projects grow bigger, our code base increases, the specs we had written for each small unit of our code are our only friends in debugging. If you are new to TDD and don't see any benefit for why you should follow TDD, visit my friend Debashish's blog here.
So all our specs go in spec folder. We write one spec file for each ruby class file as classname_spec.rb in spec folder.
There are many libraries available for testing in ruby. One of the ones that has gained popularity and use is RSpec. To use RSpec, we write a Gemfile for our project. Gemfile is the place where we write all our dependencies for the project. A sample Gemfile, one for our current project will be - When we write Bundler looks up to the source "http://rubygems.org" for the gems we declared. We can mention multiple sources and bundler will look up in those sources in order. Bundler installs specified versions of gems if mentioned and latest stable realeases of gems if versions are not specified. </p>
After running bundle install, a Gemfile.lock file is created in current directory. This file is the place where Bundler records the exact versions of gems that were installed. In this way, it is ensured that when we install the dependencies on some other machine for the same project, bundler will look into Gemfile.lock file for the versions of gems to be installed rather than installing latest stable releases of all the gems.
This way gemfile and bundler manage our dependencies. And folder structure keeps our code clean and more readable. We will look into rake tasks and an extended exercise on Object Oriented Design based on our problem in next blog. Till then, Carpe Diem!