Gulp Task Runner

Gulp.js is a task runner or build tool for web development. When working on a javascript projects, quite often we have to do some repetitive tasks such as minifying the css or javascript, concatenating them into one file to reduce the HTTP round trip between server to the client browser, optimizing images, running the tests etc. Depending on the size of the projects, managing those tasks can be very time consuming and tedious. But with the help of a task runner like Gulp.js, you can really automate this whole process and just focus on your application. Let me show you how you can integrate gulp.js on your application from scratch.

Installing Gulp In Your Machine

Gulp is based on Node.js. You can install it via the Node package manager (npm).

npm install -g gulp

[Note: Of course you have to have node installed on your machine. If you don't, simply go to http://nodejs.org/ and install it.]

Integrate Gulp In The Project

Now we need to install gulp as our project dependency. Inside your project root directory create a package.json file and for now just leave a empty object there.

package.json:

{
}

Now run this command:

npm install gulp --save-dev

After the download is finished, you will see that the package.json will contain something like this:

{
   "devDependencies": {
       "gulp": "^3.8.7"
   }
}

Now the way gulp work is each task is separated through modules. so each module does exactly one task. for example to minify css we can install the “gulp-minify-css” plug-in:

npm install gulp-minify-css --save-dev

 

Working With Gulp

Create a css file for example site.css:

#container {
   color: green;
}

We will create a task to minify this file. Gulp tasks are reside in gulpfile.js. Let’s create that file:

var gulp      = require("gulp"),
    minifycss = require("gulp-minify-css");

gulp.task("css", function() {
    return gulp.src("css/site.css")
        .pipe(minifycss())
        .pipe(gulp.dest("build/css"));
});

First we brought in the required packages. In this case gulp and gulp-minify-css. Next, we created a task called “css” ( you can name it whatever you want ).
Inside that task we told gulp what is the file path for our css that we want to minify (you can certainly specify multiple files). Than we minify the css file using the minifycss() method that “gulp-minify-css” provides. Finally we specify the destination directory for our minified file. You can see that we can chain methods together to describe our tasks (much like jQuery).

Running Gulp

Open your terminal and type:

// gulp <task_name>
// in our case it's:
gulp css

Gulp will go through the gulpfile.js file and find the “css” task and run that task.
Now if you open build/css directory you will see a file site.css which contains the minified css. Like so:

#container{color:green}

You can also make a default task that will contain multiple tasks. for example:

gulp.task("default", ["task_1", "task_2", "task_3"]);

The benefit of this is now instead of calling gulp task_1 or gulp task_2, you can simple call gulp. That will trigger the default task and run all the tasks that are specified in the default task.

You can also “watch” specific files so that you don’t have to manually run gulp each time your source files change. lets create a task for that:

gulp.task("watch", function() {
    gulp.watch("css/site.css", ["css"]);
});

Now simply run gulp watch. Now gulp will automatically trigger “css” task that we defined earlier whenever you change our source css file.

Real World Example

Lets create a real world example. We will minify, concat our css and js files and also compress our image files. Our directory structure will look like this:

before-gulp

First lets download all the gulp plug-ins that we need via npm.

npm install gulp --save-dev
npm install gulp-minify-css --save-dev
npm install gulp-concat --save-dev
npm install gulp-imagemin --save-dev
npm install gulp-rename --save-dev
npm install gulp-uglify --save-dev
npm install del --save-dev

/* You can download all plugins in one line of code:
 * npm install gulp-plugin-1 gulp-plugin-2 --save-dev
/*

Lets edit the gulpfile.js:

var gulp      = require("gulp"),
    del       = require("del"),
    uglify    = require("gulp-uglify"),
    concat    = require("gulp-concat"),
    rename    = require("gulp-rename"),
    imagemin  = require("gulp-imagemin"),
    minifycss = require("gulp-minify-css");

var paths = {
    css: {
        site: [
            "css/vendor/foundation.css",
            "css/style.css"
        ]
    },
    scripts: {
        vendors: [
            "js/vendor/jquery.js",
            "js/vendor/toastr.js"
        ],
        app: [
            "js/customerModule.js",
            "js/productModule.js"
        ]
    },
    images: "img/**/*"
};

var css = function(path, outputFileName, destination) {
    destination = destination || "build/css";

    return gulp.src(path)
        .pipe(concat(outputFileName))
        .pipe(gulp.dest(destination))
        .pipe(rename({ suffix: ".min" }))
        .pipe(minifycss())
        .pipe(gulp.dest(destination));
};

var scripts = function(path, outputFileName, destination) {
    destination = destination || "build/js";

    return gulp.src(path)
        .pipe(concat(outputFileName))
        .pipe(gulp.dest(destination))
        .pipe(rename({ suffix: ".min" }))
        .pipe(uglify())
        .pipe(gulp.dest(destination));
};

gulp.task("Clean", function(cb) {
    del(["build"], cb);
});

gulp.task("Images", ["Clean"], function() {
    return gulp.src(paths.images)
        .pipe(imagemin({optimizationLevel: 5}))
        .pipe(gulp.dest("build/img"));
});

gulp.task("VendorScripts", ["Clean"], function() {
    return scripts(paths.scripts.vendors, "vendor.js");
});

gulp.task("AppScripts", ["Clean"], function() {
    return scripts(paths.scripts.app, "app.js");
});

gulp.task("SiteCSS", ["Clean"], function() {
    return css(paths.css.site, "site.css");
});

gulp.task("Watch", function() {
    gulp.watch(paths.scripts.app, ["AppScripts"]);
    gulp.watch(paths.css.site, ["SiteCSS"]);
    gulp.watch(paths.images, ["Images"]);
});

gulp.task("default", ["Watch", "SiteCSS", "Images", "VendorScripts", "AppScripts"]);

Here, we’ve two function css and scripts for some code re-usability. Both function accepts three parameter

path           = what is file path for the source files
outputFileName = name of the final output file
destination    = destination path for final resulted file

All of the plug-ins used here are self-explanatory. We have used the “del” plug-in so that on each gulp run it will delete the whole build directory. We have also create a default task that will trigger the others tasks. Now just run:

gulp

Now, lets check the solution directory again:

after-gulp

You can see that gulp has created a build directory and inside that it create css and js directory with both minified and unminified files. Also creates a img folder that contains the compressed images.

That’s it for today. For further details check the documentation:

http://gulpjs.com/
http://gulpjs.com/plugins/

Project: BookArena

BookArena is a simple responsive SPA based library manager built with asp.net web api and angularJS. It also uses Microsoft’s new membership implementation called Identity to facilitate ACL in the application.

See the live application: http://bookarena.shibbir.net/

GitHub Link: https://github.com/shibbir/bookarena

Technology used in this project:

Introduction to CodeIgniter Template library

Today we are going to implement master page concept in our php application with a template library called CI template library by Colin Williams[Please keep in mind that this is not a codeigniter tutorial or how to set up codeIgniter application type post]. I am assuming that we have already familiar with codeigniter framework and wanted to use a template library.

Whats Master Page

A master page defines the common structures and HTML markups that you need for all the pages of your web application. Its more like a centralized way to manage the shared components. For example, the header, footer, navigation menu etc are quite static and common in all pages. It also lets you specify some area or region that you can fill with the appropriate data based on the current route.

Required Tools

Setup Our Demo Application with CodeIgniter

First step: Download the CI framework. Now extract the files and save them to a folder in your web server root directory. I am using wamp so my webserver root directory is C:\wamp\www.
Second Step: Download bootstrap. Now create a folder named “content” to the root of your application. Inside the content folder paste all the js, css and font folder that bootstrap gives you.

Third Step: Download CI template library. The template library package contains three files. The installation process is pretty straight forward. You will use only two files:

  • /libraries/Template.php: copy this file and paste into YourApplicationDirectory/system/libraries/
  • /config/template.php: copy this file and paste into YourApplicationDirectory/application/config/
  • /views/template.php: we don’t need this file

We can now use the template library in our CI application.

Configure CI Template Library

First open the YourApplicationDirectory/application/config/template.php file in your favorite text editor. Set the value of $template['default']['template'] to this:


$template['default']['template'] = 'master-layout';

This is where you will specify the name of your default master layout or template (with or without the “.php” extension). In this case the library will look for a file master-layout.php in the views folder.

Under this you will find $template['default']['regions'] with some predefined regions. Regions are like placeholder within the main template where you can specify what content will be loaded. You can customize the name of the regions. For this demo app, we only need three regions:


$template['default']['regions'] = array(
	'title',
	'header',
	'content'
);

Now, load the template library via YourApplicationDirectory/application/config/autoload.php


$autoload['libraries'] = array('template');

Also load the URL helper class. This is not required to work with template library. But we will use the CI’s built in base_url() function. So, we need to import this helper.


$autoload['helper'] = array('url');

Now, lets create a master layout. Create a file called master-layout.php under the views folder and paste the following html snippets:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); ?>

<!DOCTYPE html>
<html lang=en>
	<head>
		<meta charset=utf-8>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

        <title><?=$title?></title>
        <link rel="stylesheet" href="<?=base_url('content/css/bootstrap.min.css')?>" />
        <style>
        	.container { padding-top: 20px; }
        	.content { min-height: 200px; }
        </style>
	</head>

	<body class="container">
		<header class="well">
			<h2><?=$header?></h2>
		</header>
		<div class="content well">
			<h3><?=$content?></h3>
		</div>
		<footer class="well well-sm">
			<h5>My Website footer.</h5>
		</footer>
	</body>
</html>

The above template contains three regions- title, header and content. Those are the regions that we specified in our template configuration file. At the moment they are blank. Lets fill those regions with the data.

Now, modify the default welcome controller that’s shift with codeigniter:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Welcome extends CI_Controller
{
	public function index()
	{
		$this->template->write('title', 'My Awesome Website');
		$this->template->write('header', 'My Awesome Article');
		$this->template->write('content', 'My Awesome Content');
		$this->template->render();
	}
}

Template’s write() method is the most basic way of writing content to a region. You have to provide at least two parameter in it.

  • First parameter is the name of the region we want to write to
  • Second parameter is the string that will be shown on that region

finally, the render() method will render our template to the browser. Note that we no longer need $this->load->view(). The render method will automatically load the default template specified in the config/template.php file.

Caution: This render() method should be the last thing called from any public Controller methods that make use of the Template Library.

Now if we did everything right we should have a output like this:
ci_template_with_regionNow, lets say instead of some simple string that we passed through our controller to the view, we want to pass an array. This data may come from a web service or from a database. For the sake of this demo we will create some dummy data and pass them to the view for rendering.

We will use write_view() for this. The templates write_view() allows us to write an entire view to a region. Also we can pass an array of data using this method. Lets take an example.

Create a customer method inside the Welcome controller:

public function customer()
{
	$data['customers'] = array(
		"John" => "john@freak.com",
		"Ben" => "ben@freak.com",
		"Joe"=>"joe@freak.com",
		"Nina" => "nina@freak.com",
		"Nasri" => "nasri@freak.com"
	);

	$this->template->write('title', 'Manage Customer');
	$this->template->write('header', 'All Registerted Customers');
	$this->template->write_view('content', 'welcome/customer', $data);
	$this->template->render();
}

Note that, we are still using the same write method for title and header. Now, take a look to the write_view() method. The template will now try to load views/welcome/customer.php file into the content region.

Lets create that view. Make a folder called welcome under the views folder. And inside there create a file called customer.php

<table class="table">
	<thead>
    	<tr>
        	<th>Name</th>
            <th>Email</th>
        </tr>
    </thead>
    <tbody>
        <?php foreach ($customers as $key => $value): ?>
        <tr>
            <td><?=$key?></td>
            <td><?=$value?></td>
        </tr>
        <?php endforeach; ?>
    </tbody>
</table>

Now, if we browse the url YourLocalhostAddress/YourApplicationDirectory/index.php/welcome/customer we should see something like this:

ci_template_with_data_passed_to_regionOK. That’s all for this post. There is lot more you can do with this library. For example, you can have multiple template files in your application and you can load them dynamically where you needed using $this->template->set_master_template(). Just read the documentation and you should be good to go. What I have showed here is the most basic way to integrate the CI template library in a CI application.

You can download the whole demo application from here.

Project: IIMS (CI Version)

I am so happy to announce that the first beta version of IIMS (Inventory & Invoice management system ) is launched. Actually it was deployed two months ago. IIMS is an easy-to-use, online inventory and invoice management system that also help you manage your customers, employees.

Technology used in this project:

To check the app please go to http://iims.shibbir.net/

Some screen shots of the app:

Beginning IndexedDB With IDBWrapper

Whats an IndexedDB

According to MDN (Mozilla Developer Network)
IndexedDB is an API for client-side storage of significant amounts of structured data and for high performance searches on this data using indexes.

Basically, its a set of “object stores”. An object store is just a mechanism for storing data in a database. The stores are like a table in SQL database. You can have as many databases as you like, and as many stores(or tables) within each database. Unlike the LocalStorage, which is based on named key/value pairs, IndexedDB supports the storage of structured data.

The biggest advantages of IndexedDB is that its provides good search performance, since data can be indexed according to search keys.

Whats IDBWrapper

The API that IndexedDB provides are quite complex. To overcome this complexity, IDBWrapper, which is an excellent library, provides a wrapper for IndexedDB which is easier to use. The primary goal of this library is:

Ease the use of indexedDB and abstract away the differences between the existing impls in Chrome, Firefox and IE10

How to use IDBWrapper

Lets build a small todo application using IDBWrapper. First create a html file. we’ll call it todo.html
<!DOCTYPE html>
<head>
	<meta charset=utf-8 />
	<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

	<title>Simple Todo List using IDBWrappper</title>

	<style>
		body {
			margin-top: 50px;
		}
		#container {
			margin: auto;
			width: 900px;
		}
		th {
			text-align: left;
			padding: 8px 8px 8px 0;
		}
		td {
			border-top: 1px solid #DDDDDD;
			padding: 8px 8px 8px 0;
		}
	</style>
</head>
<body>
	<div id="container">
		<form>
			<input type="text" id="title" placeholder="Enter Title" />
			<input type="text" id="description" placeholder="Enter Description" />
			<input type="button" id="saveTodo" value="Add Todo" />
		</form>
		<hr />
		<h3>Your todo list</h3>
		<div id="todos"></div>
	</div>
	<script src="idbstore.min.js"></script>
	<script src="main.js"></script>
</body>
</html>

we have two input fields for the title and description of a todo. We have a button that’s going to save that todo into our IndexedDB database. Then we added two scripts file before the closing body tag. “idbstore.min.js” file is the IDBWrapper library. And we also include a main.js file which we’ll be create next.

Now create a script file named “main.js”. Inside there:

first create a variable called todos.
var todos = new IDBStore({
	storeName: "todos",
	onStoreReady: refreshTodo
});
that will create a store object (table) named todos.
storeName = name of the table. in our case its "todos"
onStoreReady = when the store object is ready, it will trigger the refreshTodo() callback
Next, create the refreshTodo() function.
function refreshTodo () {
	todos.getAll(todoList);
}

getAll method is provided by IDBWrapper, which will try to fetch all the data inside the todos table. Now if getAll operation was successful, it will receive an array of all objects and pass them to the todoList() callback:

function todoList(data) {
	if(data.length) {
		var table = ["<table><tr><th>Title</th><th>Description</th><th>Action</th></tr>"],
			k = 1;
		data.forEach(function (key) {
			table[k++] = '<tr><td>';
			table[k++] = key.title;
			table[k++] = '</td><td>';
			table[k++] = key.description;
			table[k++] = '</td><td>';
			table[k++] = "<input type='button' value='Done' onclick='deleteTodo(" + key.id + ")' />";
			table[k++] = '</td></tr>';
		});
		table[k++] = '</table>';

		document.getElementById("todos").innerHTML = table.join('');
	}
	else {
		document.getElementById("todos").innerHTML = "Congratulation! You have done all the tasks.";
	}
}

so, finally todoList() receives all the todos data and just displaying them on the page. Obviously, when the application first run there will be no data to display.

So now, we need to write some code for adding a todo. First attach a click event handler to the saveTodo button:

document.getElementById("saveTodo").addEventListener("click", addTodo);
now create the addTodo() function:
function addTodo () {
	var titleField = document.getElementById("title"),
		descriptionField = document.getElementById("description");

	var data = {
		title: titleField.value,
		description: descriptionField.value
	};

	if(data.title === "" || data.description === "") {
		alert("Please enter valid data!");
	} else {
		todos.put(data, function() {
			titleField.value = "";
			descriptionField.value = "";
			refreshTodo();
		});
	}
}

here we just collect the title and description from the input fields and uses the IDBWrapper’s put() method to save that todo into our todos table. And then after saving the data we called the refreshTodo() function that will redraw all the todos into our page. Pretty simple.

Now, you’ll noticed that we also put a button inside each row in our html table. that will call deleteTodo() function and we also passed an id to that function. Now, you’ll be wondering where that id is came from. After all We didn’t specify anything like id. We only have title and description. So, here is the thing, when we create a todo object and store them in our todos table, the database will automatically add a unique key named “id”. So, that’s where the id came from. However, we can tell the IDBWrapper to use our own custom field to use as an unique key by defining a keyPath property. See the docs for more details.

Finally, we need to create a deleteTodo() function:
function deleteTodo (id) {
	todos.remove(id, refreshTodo);
}

here, we receive the id and uses the remove method to remove that particular todo from the database. Then again called the refreshTodo() callback.

And that’s all. We just created our very first application using IndexedDB :)

By the way, I’ve also made an simple contact manager application using IndexedDB, which you can download from here.

Project: IHWBD

This is a small project I’ve made for IHWBD (International Homeware Bangladesh), which is one of the Bangladesh’s unique trading company which introduced all kinds of homeware products to the market. It took me around two months (Dec’ 2011 – Jan’ 2012) to complete. Because I also had to continue my graduation, I can hardly manage time for this project. But, Alhamdulillah the project completed within the deadline. Beside the main website I had to make a separate admin panel, so that they can upload new products and manage them, manage user settings, control image slideshow etc.

Project: Code Technologies

আমার লাইফের বানানো প্রথম ওয়েবসাইট। ভার্সিটি থাকতে একবার একটা পার্টটাইম চাকরির অফার পাই জুনিয়র ডেভেলপার হিসাবে। প্রথম দিন চাকরির ইন্টারভিউ দিতে গিয়ে দেখি আমাদের স্কুলের শামীম ভাই ইন্টারভিউ নেয়ার জন্য বসে আছেন। আমিতো পুরাই অবাক!! ওইদিন তারিখ ছিল ০৫/০৮/২০১১. যাই হোক ইন্টারভিউ মোটামুটি দিলাম। সেই দিন রাতে ফোন করে তারা জানালেন যে আমাকে সিলেক্ট করা হয়েছে। ওখানে জয়েন  করার পর আমার প্রথম কাজই ছিল অফিসের নিজস্ব ওয়েবসাইট বানানো। শামীম ভাই অনেক হেল্প করছেন। প্রায় দুই মাস পর ওয়েবসাইটের কাজ শেষ হল। তার কিছু স্ক্রিনশটঃ

Beginning RequireJS

জাভাস্ক্রিপ্ট এমন এক জিনিস যে প্রজেক্টের বয়স বাড়ার সাথে সাথে এটা ম্যানেজ করা কঠিন থেকে কঠিন হতেই থাকে। আবার অনেক সময় অনেক প্লাগইন ইউজ করতে হয়। তো এই যে আমরা এত এত প্লাগইন আমাদের প্রজেক্টে ইমপোর্ট করি বেশির ভাগ সময়ই দেখা যায় একটা জাভাস্ক্রিপ্ট লাইব্রেরি (প্লাগইন) আরেকটা লাইব্রেরির উপর নির্ভরশীল।

যেমন যদি আমাদের myScript.js নামে একটা কাস্টম ফাইল থাকে যেটাতে এই কোড আছেঃ

$(function () {
     alert("document is loaded");
});

তো দেখা যাচ্ছে আমাদের এই কোডে jQuery এর একটা ফাংশন আছে। এখন এই কোড ইউজ করতে হলে আমাদের jQuery লাইব্রেরি ইমপোর্ট করতে হবে। অনেকটা এরকমঃ

<script src="http://code.jquery.com/jquery.min.js"></script>
<script src="myScript.js"></script>

এখানে গুরুত্তপূর্ন ব্যাপার হচ্ছে jQuery অবশ্যই myScript.js এর আগে ইমপোর্ট করতে হবে। কারন আমাদের কাস্টম ফাইল jQuery এর একটা ফাংশন ইউজ করছে। তাই আমরা যদি এদের অর্ডার চেঞ্জ করে দেই এভাবেঃ

<script src="myScript.js"></script>
<script src="http://code.jquery.com/jquery.min.js"></script>

এখন আর আমাদের কোড কাজ করবে না। কারন jQuery পরে ইমপোর্ট করা হয়েছে। এখন ব্রাউজার কনসোল খুললে দেখা যাবে এই এরর দিচ্ছেঃ


ReferenceError: $ is not defined

তো দেখা যাচ্ছে আমাদের myScript.js সবসময় jQuery এর উপর নির্ভরশীল।

আবার যদি আমরা BACKBONE ইউজ করতে চাই সেটা আবার UNDERSCORE উপর নির্ভরশীল। আবার UNDERSCORE স্বয়ং jQuery এর উপর নির্ভরশীল।

এই সমস্যা সমাধানের উপায় হচ্ছে সবসময় যেই লাইব্রেরি আগে লোড হওয়া দরকার সেটা আগে লোড করা। কিন্তু যেহেতু আমাদের অনেক থার্ড পার্টি লাইব্রেরি প্রজেক্টে ইমপোর্ট করা লাগে, তাই কে কোন লাইব্রেরির উপর নির্ভরশীল তা মনে রাখা সবসময় সম্ভব হয় না। তাই আমাদের এমন কোন সল্যুশন দরকার যাতে করে আমরা ইচ্ছেমত লাইব্রেরি ইমপোর্ট করব কিন্তু আমাদের কখনই কে কার আগে লোড হবে এগুলা নিয়ে চিন্তা করা লাগবে না।

সমাধানঃ

logo
RequireJS হচ্ছে এ সমস্যার অন্যতম একটি সমাধান। RequireJS এ আমরা শুধু একটা কনফিগার অপশন লিখব আর RequireJS নিশ্চিত করবে যে সকল লাইব্রেরি ঠিকভাবে লোড করা হয়েছে।

প্রথমে আমরা RequireJS ইমপোর্ট করবঃ

<script src="require.js" data-main="main"></script>

এখানে data-main এ একটা ফাইলের নাম দেয়া আছে। অর্থাৎ main হচ্ছে main.js।
আর main.js এ নিচের কোড লিখবঃ

// প্রথমে বলে দিতে হবে কোন কোন লাইব্রেরি লোড করতে হবে। ফাইল অর্ডারিং কোন ফ্যাক্ট না
requirejs.config({
     paths: {
          "jquery": "http://code.jquery.com/jquery.min"
           // এখানেও ফাইলের শেষে .js এক্সটেনশন দরকার নাই
     }
});

require(["jquery"], function ($) {
     // এখানে [] এর মধ্যে বলে দিতে হবে কোন কোন ফাইল লোড করা থাকতে হবে, যদি
     // একের অধিক ফাইল দরকার হয় তবে কমা সেপারেট করে বলে দিতে হবে।
     // যেমন- ["jquery", "otherFile"]. এখন নিচের কোড তখনই রান করবে
     // যখন [] এর ভিতরে যা উল্লেখ করা আছে তা লোড শেষ হবে
     $(function () {
          alert("document is loaded!");
     });
});

কমন কিছু কনফিগারেশনঃ


baseUrl: // পথ সেট করে দেয়া যাবে যে requireJS কোন লোকেশনে ফাইল খুজবে।
         // যদি baseUrl সেট করা না হয় তবে ডিফল্ট ভ্যালু হবে যে পেজ এ requireJS
         // রেফার করা হয়েছে তার লোকেশন

paths: // ফাইল পথ বলে দিতে যেগুলো baseUrl এ থাকবেনা। যেমন- যদি কোন ফাইল
       // CDN থেকে রেফার করা হয় তবে ওই ফাইল অবশ্যই baseUrl এ খুজলে
       // পাওয়া যাবে না

waitSeconds: // স্ক্রিপ্ট লোড করার আগে requireJS কত সেকেন্ড অপেক্ষা করবে

scriptType: // স্ক্রিপ্ট টাইপ বলে দেয়া যাবে। ডিফল্ট হচ্ছে "text/javascript"

AMD with RequireJS

এখন অনেক সময় কাজের সুবিদার্থে (কোড রি-ইউজ করা, কোড ক্লিন রাখা ইত্যাদি) আমাদের বিভিন্ন মডিউল তৈরি করা লাগে যেমন – কাস্টমার মডিউল, ইউজার মডিউল। এবার দেখব আমরা কিভাবে “AMD” প্যাটার্ন ফলো করে এই মডিউল গুলো তৈরি করে কাজ করব। “AMD (Asynchronous Module Definition)” হচ্ছে জাভাস্ক্রিপ্টে মডিউল তৈরি করার একটা পপুলার গাইডলাইন।

আমদের প্রজেক্টের ডিরেক্টরি স্ট্রাকচার হবে এরকম।

  • index.html
  • js
    • main.js
    • libs
      • jquery.js
      • require.js
      • underscore.js
      • backbone.js
    • module // আমাদের মডিউল গুলো এখানে থাকবে
      • module1.js
      • module2.js

index.html:

<!DOCTYPE html>
<html>
<head>
     <meta charset=utf-8 />
     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />

     <title>AMD & RequireJS</title>
</head>
<body>
     <script src="js/libs/require.js" data-main="js/main"></script>
</body>
</html>

এবার আমরা একটা মডিউল তৈরি করব যার লোকেশন হবে “js/module/repository.js”:

define("repository", function() {
     var customerData = [
          { id: 1, name: "Nina" },
          { id: 2, name: "Tina" },
          { id: 3, name: "Mina" }
     ];

     return {
          customerData: customerData
     };
});

এখানে, “define” মেথড দিয়ে মডিউল তৈরি করতে হয়। প্রথম প্যারামিটার হচ্ছে মডিউল এর নাম (যেমন আমাদের ক্ষেত্রে “repository”)। এটা অপশনাল। যদি না নেই তাহলে বাই ডিফল্ট ফাইলের নামটাই মডিয়ুলের নাম হবে। এটা তখনি দেয়া জরুরী যখন আমরা আরো অনেক ফাইল একসাথে কনক্যাট করে একটা ফাইল বানাব।

দ্বিতীয় প্যারামিটার হচ্ছে একটা অ্যারে যেটা আমরা দেই নাই। এই অ্যারেতে থাকবে এই মডিউল রান করার জন্য অন্য যেসব মডিউল বা লাইব্রেরি আমাদের দরকার। এটার উদাহরণ আমরা পরে দেখব।

শেষের প্যারামিটার হচ্ছে একটা কলব্যাক ফাংশন যেটার ভিতরে ওই মডিউলের কোড থাকবে।

এবার আমরা main.js এর ভিতর সবকিছু কনফিগার করব:

requirejs.config({
     paths: {
          "jquery": "libs/jquery.min",
          "repository": "module/repository"
     },
     baseUrl: "js"
});

require(["jquery", "repository"], function ($, repo) {
     $(function () {
          var data = repo.customerData,
          output = [],
          k = 0;

          data.forEach(function (key) {
               output[k++] = "<p>";
               output[k++] = "Id: " + key.id + " | ";
               output[k++] = "Name: " + key.name;
               output[k++] = "</p>";
          });
          $("body").append(output.join(""));
     });
});

এখন ধরে নেই আমাদের কাস্টম মডিউল “repository” অন্য আরেকটা মডিউলের উপর নির্ভরশীল। যেটার নাম হচ্ছে “js/module/service.js”

define("service", function() {
     var orders = [
          { customerId: 1, orderId: "ORD101" },
          { customerId: 2, orderId: "ORD201" },
          { customerId: 3, orderId: "ORD301" }
     ];

     return {
          orders: orders
     };
});

এখন এই মডিউলটা আমরা আমাদের “repository” মডিউলে ব্যাবহার করব। প্রথমে আমাদের “main.js” ফাইলে “service” মডিউলের রেফারেন্স যোগ করবঃ

requirejs.config({
     paths: {
          "jquery": "libs/jquery.min",
          "repository": "module/repository",
          "service": "module/service"
     },
     baseUrl: "js"
});

এরপর আমাদের “repository” মডিউলের “define” মেথডের দ্বিতীয় প্যারামিটারে আমাদের এই মডিউলের জন্য ডিপেন্ডেন্সি বলে দিব। এক্ষেত্রে সেটা হচ্ছে “service” মডিউলঃ

define("repository", ["service"], function(service) {
     var customerData = [
          { id: 1, name: "Nina", orderId: service.orders[0].orderId },
          { id: 2, name: "Tina", orderId: service.orders[1].orderId },
          { id: 3, name: "Mina", orderId: service.orders[2].orderId }
     ];

     return {
          customerData: customerData
     };
});

Loading Non AMD scripts

এবার আমরা দেখব কিভাবে স্ক্রিপ্ট লোড করব যেগুলো এমডি প্যাটার্ন ফলো করে তৈরি করা হয় নাই। আমরা এবার “Backbone” আমাদের প্রজেক্টে অ্যাড করব। “Backbone” হচ্ছে একটা পপুলার জাভাস্ক্রিপ্ট ফ্রেমওয়ার্ক। কিন্তু “Backbone” যেহেতু “AMD” না তাই আমাদের একটু কনফিগার করে নিতে হবেঃ

requirejs.config({
     shim: {
          underscore: {
               exports: "_"
          },
          backbone: {
               exports: "Backbone",
               deps: ["underscore"]
          }
     },
     paths: {
          "jquery": "libs/jquery.min",
          "underscore": "libs/underscore-min",
          "backbone": "libs/backbone-min",
          "repository": "module/repository",
          "service": "module/service"
     },
     baseUrl: "js"
});

প্রথমে “paths” অবজেক্ট এর ভিতরে “Backbone” এবং “Underscore” ফাইলের লোকেশন বলে দিলাম। “Underscore” লোড করা দরকার কারন “Backbone” নিজেই এর উপর নির্ভরশিল। এরপর “shim” অবজেক্ট এর ভিতরে আমরা এগুলো লোড করলাম। লক্ষ্য করে দেখুন যে “Backbone” ডিফাইন করার সময় “Underscore” কে এর ডিপেন্ডেন্সি হিসাবে দেখান হয়েছে।

এখন আমরা “backbone” ইউজ করতে পারবঃ

require(["jquery", "underscore", "backbone"], function ($, _, B) {
     console.log("Underscore Version: " + _.VERSION);
     console.log("Backbone Version: " + B.VERSION);
});

সব মিলিয়ে আমাদের “main.js” হবে এরকমঃ

requirejs.config({
     shim: {
          underscore: {
               exports: "_"
          },
          backbone: {
               exports: "Backbone",
               deps: ["underscore"]
          }
     },
     paths: {
          "jquery": "libs/jquery.min",
          "underscore": "libs/underscore-min",
          "backbone": "libs/backbone-min",
          "repository": "module/repository",
          "service": "module/service"
     },
     baseUrl: "js"
});

require(["jquery", "repository"], function ($, repo) {
     $(function () {
          var data = repo.customerData,
              output = [],
              k = 0;

          data.forEach(function (key) {
               output[k++] = "<p>";
               output[k++] = "Id: " + key.id + " | ";
               output[k++] = "Name: " + key.name + " | ";
               output[k++] = "Order Id: " + key.orderId;
               output[k++] = "</p>";
          });
          $("body").append(output.join(""));
     });
});

require(["jquery", "underscore", "backbone"], function ($, _, B) {
     console.log("Underscore Version: " + _.VERSION);
     console.log("Backbone Version: " + B.VERSION);
});

Last Programming Contest

২০১২ সালের বুয়েট IUPC ছিল লাইফের শেষ প্রোগ্রামিং কন্টেস্ট। কন্টেস্ট মানেই ছিল নতুন টি-শার্ট, নতুন ব্যাগ, ক্যালকুলেটর, বুফে ডিনার। চ্যাম্পিয়ন হবার চিন্তা ভুলেও মাথায় আসতনা। প্রতি কন্টেস্টে কন্টেস্ট আওয়ার ছিল পাঁচ ঘন্টা করে। সবগুলো কন্টেস্টই অনেক এনজয় করছি প্রথম তিন ঘন্টা পর্যন্ত। এর পরের দেড় ঘন্টা ছিল খুবই বিরক্তিকর। দুই-একটা যা করার মত ছিল তা করা হয়ে গেছে। এখন আর কোন কিছুই মিলে না। সময়ও কাটে না। খাবার-দাবার অলরেডি সব শেষ। শেষে কাজ-কাম না পাইয়া সলিটেয়ার খেলতাম। বড় ভাইরা পাশ দিয়ে যাইত আর আমাদের দেখে মুচকি হাসত! শেষের আধা ঘন্টায় আবার পুরাই প্রেশার। একটা প্রবেলম দেখা যেত হয় হয় করতেছে :idea: । ওইটা নিয়ে তিনজনের লাস্ট মিনিট পর্যন্ত পর্যায়ক্রমে লাফালাফি চলত — চেয়ার বদলা-বদলি আর কি। কন্টেস্ট শেষে মনে হইত ইশ আর বিশ-পচিশ মিনিট টাইম পাইলে কিছু একটা মনে হয় হয়েই যাইত। আহা কি সব দিন ছিল। কন্টেস্ট করার বদৌলতে ভার্সিটতে ভাল সুবিধা পেতাম। দেখা যেত ক্লাস টেস্ট আর কন্টেস্ট একই দিনে পরছে। সেক্ষেত্রে ক্লাস টেস্ট না দিলেও চলত, বা দেখা যেত কন্টেস্টের জন্য ক্লাস টেস্ট পিছানো হইছে। এখন কন্টেস্ট না থাকায় সবচেয়ে ক্ষতি হইছে যে টি-শার্ট এখন কিনে পরা লাগে। নেটওয়ার্কিং কন্টেস্ট, গেমিং কন্টেস্ট, প্রোগ্রামিং কন্টেস্ট সবই করা হইছে (এবং সবগুলাতেই সাকসেস রেট জিরোর কাছাকাছি)।

ভার্সিটির সিনিয়ররা অনেক হেল্প করছে বিশেষ করে রাইসুল ভাই, ইমদাদ, ইমরান ভাই। আমাদের সময় আমরা যারা কন্টেস্ট করতাম তাদের ভিতর সবচেয়ে ভাল প্রোগ্রামার ছিল ইমদাদুল। সাদেক ভাই এখনো ভার্সিটির জুনিয়র প্রোগ্রামারদের জন্য কাজ করে যাচ্ছেন। কন্টেস্টের সাথে সম্পৃক্ত সবার কথাই মনে থাকবে – মামুন স্যার, কামরুদ্দিন নূর স্যার, আমরান স্যার, প্রণব ভাই, সাদেক ভাই, মাহবুব ভাই, সারোয়ার ভাই, লিজা আপু, রাইসুল ভাই, ইমদাদ, ইমরান ভাই, ফিদা হাসান, আমাদের ইমরান, মিথুন, শুভ, তৌহিদ, মিতু। এখনো ওই সময়টা অনেক মিস করি :(

Project: Bill Management System

Finally, I’ve completed my first commercial software. It takes around four months to complete (off course I didn’t worked every day). I’ve used PHP, jQuery, jQueryUI, and MySQL. The software includes all the basic functionality of a CRUD system. It will be used in a dish company. They can register their customers into the system, maintain their monthly bill, dues etc. They can generate custom reports like zone based report, due report etc. They can maintain information about their staffs too. Here is some of the screen shots ;-)