Object Based Routing in Express.js

July 11, 2023

On the other day, I was looking over the source code of the Sails.js framework. Sails.js framework has a nice routing configuration based on the routes.js file. In this file, you can have the exported object in the following way.

module.exports = {
  'get /': 'HomeController.index',
  'get /pages/contact': 'PagesController.contact',
  'post /users': 'UsersController.create',
};

When the user visit GET /, the framework maps the HomeController.js file from the controllers folder and executes the index method or an action (as in terms of MVC). The key in the above object is HTTP (GET) verb, followed by a space, and then route(/) and the value (of the object key) is the file name without extension(HomeController) followed by dot(.) and then action name(index). Nice and clean mapping!

Sails.js framework is based on Express. In other words, the above code at the end should be converted into standard app.get(), app.post(), etc. methods. So, I thought about the possibility of adding this object-based routing in a simple Express application. Rest of the article is walk-through on how one can add. I do not yet recommend trying this in a production application.


Create a folder with the name routing-object. Go inside, and then create a package.json file to mark this folder as a package/module.

$ mkdir routing-object
$ cd routing-object
$ npm init -y

Install the Express(express) as the production dependency and Nodemon(nodemon) as the development dependency(-D).

$ npm i express
$ npm i -D nodemon

Nodemon is used in development mode to restart the server when we do the changes. Let's add the dev and start scripts in package.json file to run the application in development and production mode respectively.

{
  "name": "routing-object",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "4.18.2"
  },
  "devDependencies": {
    "nodemon": "3.0.0"
  }
}

Finally, create an app.js file and write the following skeleton Express code.

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

We can easily run this application in development mode using dev script.

$ npm run dev
[nodemon] starting `node app.js`
Application is up and running on port 3000

With these changes, the base Express application is up and running.


Let's add three routes GET /, GET /pages/contact, and POST /users as mentioned in the object at starting of this article. I'll just return simple messages for all these routes.

const express = require("express");
const app = express();

app.get("/", (req, res) => {
  res.json({ message: "home page" });
});

app.get("/pages/contact", (req, res) => {
  res.json({ message: "contact us" });
});

app.post("/users", (req, res) => {
  res.json({ message: "user created" });
});

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

Test these routes in the browser and/or Insomnia.

With these changes, our application is now supporting the above-mentioned routes.

Now, we're going to do the changes in the application in such a way that it should not affect the output but the structure of the application.


Go ahead and create a routes.js file in the route folder.

$ touch routes.js

Open this file and copy the object mentioned in this article at the beginning.

module.exports = {
  "get /": "HomeController.index",
  "get /pages/contact": "PagesController.contact",
  "post /users": "UsersController.create",
};

The plan is when the user called POST /users, we should look for the file name UserController.js. Even further we'll look for this file in the controllers folder. So, let's create the controllers folder first in the root folder.

$ mkdir controllers

In this folder, based on the above routes object, we need to create three files - HomeController.js, PagesController.js, and UserController.js.

$ touch controllers/HomeController.js
$ touch controllers/PagesController.js
$ touch controllers/UsersController.js

HomeController.js should have the index method.

module.exports = {
  index: () => {

  },
};

When the user visit GET /, this index method ultimately runs. In other words, if we map the following code with the above code,

app.get("/", (req, res) => {
  res.json({ message: "home page" });
});

Then

(req, res) => {
  res.json({ message: "home page" });
}

should be within the index method in HomeController.js file as follow.

module.exports = {
  index: (req, res) => {
    res.json({ message: "home page" });
  }
}

Similarly, PagesController.js should have the contact method.

module.exports = {
  contact: (req, res) => {
    res.json({ message: "contact us" });
  }
}

And finally, the UsersController.js file should have the create method.

module.exports = {
  create: (req, res) => {
    res.json({ message: "user created" });
  },
};

With these changes, the routes are ready and the associated code is implemented. Next, we need to map these two.


In the app.js file, go ahead and remove three routes code as we have that code in other places.

const express = require("express");
const app = express();

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

To call the method from the files or controller's files, we need to first import these files. For this, we can use require-all npm package. This package requires all the files and makes them available for use to directly call. Go ahead stop the server and install this package.

$ npm i require-all

As mentioned in the documentation of this package, we can write the following code.

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

This controllers object now has reference to all the modules or files within controllers folder. To call index method from HomeController we can simply write

controllers.HomeController.index()

Let's use the require-all package in the app.js file and require all the controllers.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

With these changes, we just figure out what to do with the values of the routes object. Next, we need to fetch the routes from routes.js file and attach the respected method to each of the routes by loop through.


Let's simply require the routes.js file to fetch all the routes.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

We're going to use for...in loop to loop through the object.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

The route is the key and it contains two things - HTTP verb and route itself. We need to fetch both the details from route. To fetch the HTTP verb, we can simply use the RegEx as follow.

const verbExpr = /^(get|post|put|delete)\s+/;
const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

The first line is the RegEx for four HTTP verbs and the second line check for the verbs and return and saved them into the verb variable.

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

Next, we need to fetch the route itself. Fetching the route is as simple as replacing the verb with an empty string as whatever remains must be the route.

let path = route;
path = path.replace(verbExpr, "");

Instead of directly changing the route iterator, I used the intermediate path variable and saved the route in it.

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

We now have all the needed code but in de-composed mode.


Even though all the methods within controllers are available in the controllers object, we can not dynamically call them. We need to save the controllers and methods separately to call.

To save the controllers and method from object value, we can simply split by dot(.).

let location = routes[key];
location = location.split('.');
let controllerLocation = location[0];
let methodLocation = location[1];

The above code saves the value of the route object in location and we split it by dot(.). With this, we can have the controller in controllerLocation and the method in methodLocation variables in callable form.

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");

  let location = routes[key];
  location = location.split('.');
  let controllerLocation = location[0];
  let methodLocation = location[1];
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

The exact callable form is within controllers object. So, let's fetch specific controllers and methods from the controller per route.

let controller = controllers[controllerLocation];
let method = controller[methodLocation];

Let's add these lines to our app.js file.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");

  let location = routes[key];
  location = location.split('.');
  let controllerLocation = location[0];
  let methodLocation = location[1];

  let controller = controllers[controllerLocation];
  let method = controller[methodLocation];
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

With these changes, we now have the verb, route, and method to call.


The only code we now need to write is,

app[verb](path, method);

The above code attaches .get() or .post() method to app and the path and method as the argument to it.

Let's add these lines in our app.js file to complete the working Express application.

const express = require("express");
const app = express();

const controllers = require("require-all")({
  dirname: __dirname + "/controllers",
  filter: /(.+Controller)\.js$/
});

const routes = require("./config/routes");

for (let route in routes) {
  const verbExpr = /^(get|post|put|delete)\s+/;
  const verb = key.match(verbExpr || [])[key.match(verbExpr || []).length - 1] || null;

  let path = route;
  path = path.replace(verbExpr, "");

  let location = routes[key];
  location = location.split('.');
  let controllerLocation = location[0];
  let methodLocation = location[1];

  let controller = controllers[controllerLocation];
  let method = controller[methodLocation];

  app[verb](path, method);
}

app.listen(3000, () => {
  console.log("Application is up and running on port 3000");
});

Go ahead and re-run all three routes in the browser and/or Insomnia. You should see the identical output.

The above code is not complete. I intentionally left many scenarios and improvements. For example,

  1. You can add all the HTTP verbs in verbExpr and even do case-insensitive matching.
  2. You can place this code into a separate file.
  3. You can have better names for the variables.
  4. You can even implement your require-all package as this package is quite small as one file only.
  5. You can add error checking as what if someone forgot to add. between controller and method name of added tab between HTTP verb and route instead of single space, etc.

Learning Rails Pt. 3

March 22, 2023

Continue from previous article, let's add one more route /about/company. You know the steps now to make this route working in Rails application.

First, add a route entry like this.

Rails.application.routes.draw do
  get "/about" => "about#index"
  get "/about/company" => "about#company"
end

Second, we already have the AboutController. So, we just now need to add company method.

class AboutController < ApplicationController
  def index
  end

  def company
  end
end

Finally, we already have about folder. So, we just need to create company.html.erb with following code inside this file.

<h1>About company</h1>

Visiting the /about/company should display this About company header in the browser.

Learning Rails Pt. 2

March 20, 2023

Open the http://localhost:3000/about in the web browser. Obviously, you get the No route matches [GET] "/about" error. Because, this route doesn't exist.

The routes for the Rails application are defined in config/routes.rb file. Open this routes.rb file in the text editor. You should see the following code.

Rails.application.routes.draw do
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
end

Remove all the comments from this code.

Rails.application.routes.draw do
  
end

Add the /about route line like this.

Rails.application.routes.draw do
  get "/about" => "about#index"
end

This line means, when GET /about is called, run the about's index method. This about is a controller (or class) and index is an action (or method within class).

Reload the /about in the browser. You should now get uninitialized constant AboutController error. Because, we do not have AboutController in about_controller.rb file.

All the controllers for the Rails application are defined within app/controllers folder. In app/controllers, create a file with name about_controller.rb. Open this file in text editor and write the following code.

class AboutController < ApplicationController
end

We defined the AboutController controller that Rails is looking for. This controller is extended with helpful functionalities from ApplicationController controller provided by Rails.

Reload the /about in the browser again. You should now get The action 'index' could not be found for AboutController error. Because, AboutController does not have index action.

Let's define an empty index method in this controller like this.

class AboutController < ApplicationController
  def index
  end
end

Reload the /about in browser for one more time. You should now get No template for interactive request error. Because, we do not have index.html.erb template or view for the method to fulfil the request.

All the views for the Rails application are defined within app/views folder. Create a file with name index.html.erb in folder with name about inside the app/views folder. Here, we need to create about folder first and then index.html.erb file in this created about folder. Open this file in the text editor and write the following code.

<h1>About</h1>

Reload the /about in browser for the last time. You should see the About header in browser now.

In summary, Rails check for the routes in routes.rb file and run the associated action from given controller. Finally, with the help from associated view, request will be fulfilled.

Learning Rails Pt. 1

March 15, 2023

Go ahead and create a new Rails application using following command.

$ rails new hello-world

This will create a Rails application in hello-world folder.

Go inside the hello-world folder.

$ cd hello-world

Run the created Rails application using following command.

$ rails server

This command boots up the server at http://localhost:3000. Open this in web browser and you should see the Rails default welcome page. If that is the case, you just have created your very first web application using the Rails framework.

GTK4 Tutorial Pt. 4

March 09, 2023

Vala is the programming language but the interesting one. After compilation, it produces the C code and then this C code further fed to C compiler to get the final output.

Create a dump.vala file and write the following code. Nothing is new in this code and we already write this same code in previous articles.

public class Hello {
  public string name;

  public Hello(string name) {
    this.name = name;
  }

  public void greet() {
    stdout.printf("hello, %s\n", this.name);
  }
}

int main(string[] args) {
  string name = "world";
  Hello hello = new Hello(name);
  hello.greet();
  
  return 0;
}

Let's run this file using Vala compiler but with -C option.

valac -C dump.vala

This -C flag outputs the C code into the file with same name as the Vala file i.e. dump.c file. Open this dump.c file and you should see the following code.

/* dump.c generated by valac 0.56.3, the Vala compiler
 * generated from dump.vala, do not modify */

#include <glib-object.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>


/* ... more core ...*/
/* ... more core ...*/
/* ... more core ...*/


int
main (int argc,
      char ** argv)
{
    return _vala_main (argv, argc);
}

You don't have to worry about this C code and even you don't need to know this C code produced by Vala compiler. I show this step to you to give some background information such as Vala use GLib Object and GLib libraries extensively as you can see from the header files of this C code. Some of the code and syntax you'll going to use in Vala depends on these libraries and C in general. So, don't be surprise!

Apart from this, even though Vala is general purpose programming language, it is mainly used to develop the GUI application for the GNU/Linux system using GTK library as it has tight integration with GTK library and the ecosystem. elementary OS extensively use the Vala language to build most of their applications including the Pantheon, their desktop environment.

Vala gives nice abstraction like higher level languages such as Java and C# to make desktop application using GTK which is written in C language. That being said, let's continue with Vala language and build the hello, world desktop application using GTK library (GTK4).

GTK4 Tutorial Pt. 3

March 05, 2023

In this article, we're going to do some structural changes in existing Hello.vala file that we created in previous article. Following is the code we currently have in the Hello.vala file.

public class Hello {
  public void greet(string name) {
    stdout.printf("hello, world\n");
  }

  public static int main(string[] args) {
    string name = "world";
    Hello hello = new Hello();
    hello.greet(name);
    
    return 0;
  }
}

I don't think that main() should be within the Hello class as main() has nothing to do with Hello class and we should follow separation of concern.

public class Hello {
  public void greet(string name) {
    stdout.printf("hello, world\n");
  }
}

public static int main(string[] args) {
  string name = "world";
  Hello hello = new Hello();
  hello.greet(name);
  
  return 0;
}

As main() is not part of the class, we don't have to worried about public and static.

public class Hello {
  public void greet(string name) {
    stdout.printf("hello, world\n");
  }
}

public static int main(string[] args) {
  string name = "world";
  Hello hello = new Hello();
  hello.greet(name);
  
  return 0;
}

Let's run this program and confirm the output. It should not effect the output as we did structural changes for refactor.

valac Hello.vala && ./Hello
hello, world

Yes, it is working!

Even further, I don't think we should have the main() method within Hello.vala file at all. Because, Hello.vala file should suppose to the Hello class and nothing else. main() method is a special method and we should have it in the main.vala file.

Let's create a new file with name main.vala and cut-paste the main() method from Hello.vala file to newly created main.vala file. After doing this modification, we should have the following code in the Hello.vala file.

public class Hello {
  public void greet(string name) {
    stdout.printf("hello, world\n");
  }
}

Following is the code we now should have in the main.vala file.

int main(string[] args) {
  string name = "world";
  Hello hello = new Hello();
  hello.greet(name);
  
  return 0;
}

With these modifications, you or our colleague can easily guess from the file name that Hello.vala should contains the Hello class code and main.vala should contains main() method.

We now have two files instead of one. We need to compile both files at the same time otherwise it'll generate errors. In such a case, the command to run two or more file is follow.

valac Hello.vala main.vala -o Hello && ./Hello
hello, world

Here, we've compiled both Hello.vala and main.vala files using valac compiler. After successful compilation it'll generate an object file (-o) or an executable with name Hello. You can give any name you like, but Hello make more sense here. Finally, we're ran the executable and it print the hello, world as expected output.


Before I complete this article, I would like to cover one more concept in Vala and that is constructor. We'll cover the constructor in more details in future articles. But, in this article, I want to give a glimpse of it.

Continue with the name variable in our hello, world example, instead of passing name variable while calling the greet() method, it make more sense to pass it while creating an object in main.vala in following way.

int main(string[] args) {
  string name = "world";
  Hello hello = new Hello(name);
  hello.greet();
}

Notice, I removed the name from greet() method call and now passed it while creating an object from the Hello class. Based on this, we need to do some changes in Hello class to accepts this modification.

First, let's define the class attribute or class variable with name name in the Hello.vala file.

public class Hello {
  public string name;

  public void greet() {
    stdout.printf("hello, %s\n", name);
  }
}

Next, let's define a method with name Hello() i.g. same name as the class name that accepts the name parameter with type of string. This is our constructor method and it'll accept the arguments passed while creating an object or instance of this class. We can assigned this passed argument to created class variable using this keyword.

public class Hello {
  public string name;

  public Hello(string name) {
    this.name = name;
  }

  public void greet() {
    stdout.printf("hello, %s\n", name);
  }
}

Finally, within greet() method, instead of name, we need to write this.name to access the set value of the name variable.

public class Hello {
  public string name;

  public Hello(string name) {
    this.name = name;
  }

  public void greet() {
    stdout.printf("hello, %s\n", this.name);
  }
}

Our program now is back in shape to print the hello, world in the terminal. But, this type we've used class constructor in Vala. Let's run the programs and confirm.

valac Hello.vala main.vala -o Hello && ./Hello
hello, world

Yeah. It is working!

I don't want to hold you more with this article. Enjoy rest of the day. See you in the next chapter after sometime. The plan is to do some re-structure in next article. I'll not introduce any new concept in the next article except will talk about bit of compilation pipeline in case you're interested.

GTK4 Tutorial Pt. 2

March 03, 2023

In this article, we're again going to write the hello, world program. But, this time with object-oriented paradigm. Create a new file with name Hello.vala (H is capital in file name) and open in your favorite text-editor.

Let's define a Hello class with class keyword. CamelCase is used to name the classes in Vala.

class Hello {

}

It is always good practice to use access modifiers with class while defining it such as public, protected, private, etc. I don't have any reason to hide this Hello class.

public class Hello {

}

Class can have many things inside it. But, it is important to have the main() method. Otherwise, our program wouldn't run! Let's add the minimal version of the hello, world program code we know from the previous section.

public class Hello {
  int main(string[] args) {
    stdout.printf("hello, world\n);
    
    return 0;
  }
}

But, this wouldn't work. Why? Because, method within class not run automatically. We need to either run by creating an instance of the class or mark it as static as class method. We'll going to mark this method as static.

public class Hello {
  static int main(string[] args) {
    stdout.printf("hello, world\n);
    
    return 0;
  }
}

As I said for the class, it is also good practice to define methods with access modifiers. In case of the main() method, it must be public otherwise you'll get the error.

public class Hello {
  public static int main(string[] args) {
    stdout.printf("hello, world\n);
    
    return 0;
  }
}

That's it! This is how you can write the hello, world program in Vala using object-oriented way. Let's run and confirm the output!

valac Hello.vala && ./Hello
hello, world

Yeah! The program work as expected.


Let's do the same modifications as we previously did after our hello, world program was written correctly. You're correct! We're going to define the greet() method within Hello class.

public class Hello {
  public void greet() {
    stdout.printf("hello, world\n");
  }

  public static int main(string[] args) {
    stdout.printf("hello, world\n);
    
    return 0;
  }
}

Like I said previously, to call this greet() method, we need to create an instance of class or an object using new operator.

public class Hello {
  public void greet() {
    stdout.printf("hello, world\n");
  }

  public static int main(string[] args) {
    Hello hello = new Hello();
    hello.greet();
    
    return 0;
  }
}

Running this program should print the same hello, world as the output.

valac Hello.vala && ./Hello
hello, world

Next, let's define a name variable with world as the initial value to print the same hello, world text like we did previously.

public class Hello {
  public void greet(string name) {
    stdout.printf("hello, world\n");
  }

  public static int main(string[] args) {
    string name = "world";
    Hello hello = new Hello();
    hello.greet(name);
    
    return 0;
  }
}

Again, running this program should print the same hello, world as the output.

valac Hello.vala && ./Hello
hello, world

With these changes for the hello, world program, let's take a break. We'll meet again in next article and do further modifications in this same hello, world program.

GTK4 Tutorial Pt. 1

February 28, 2023

Viewpoint may be largely unrelated to its usefulness. Many if not most of the software we use is probably obsolete according to the latest design criteria. Most users could probably care less if the internals of the operating system they use is obsolete. They are rightly more interested in its performance and capabilities at the user level. Ken Thompson, The Tanenbaum-Torvalds Debate

Before you follow this series, you need Vala on your computer. Refer the Installation Guide on HOWTO install Vala on different platforms and distros.

This note or tutorial is written as part of the learning process. So, obviously it will have errors in terms of grammar, typo, code, explanation and so on. You are welcome to suggest a correction. The note is concise and contains numerous reference (archive) links for detail/better explanation. Enjoy!

---

Create a new file with name hello.vala. Here, hello is the name of the file and .vala is the extension of the Vala programming language.

touch hello.vala

(Of course, you can create this new file in many different ways. I wrote command to make instruction clear!)

Open the created hello.vala in text-editor of your choice and write the text hello, world as that's what we're interested to print.

hello, world

This hello, world text is string in Vala. String in Vala must be enclosed within double quotes only (single quotes are for characters).

"hello, world"

The string hello, world is not going to be printed in terminal by default. We need to invoke printf() method from stdout object to print the string in terminal. Don't worry about this stdout object for now. It is default available in any project to use.

stdout.printf("hello, world")

Vala is one of those programming language that requires semicolon or ; to terminate the statement.

stdout.printf("hello, world");

Vala, being compiled programming language requires main() method to start the execution.

int main() {
  stdout.printf("hello, world");
}

We need to return 0 from the main() method at the end as we have already mentioned int as return type of this method.

int main() {
  stdout.printf("hello, world");

  return 0;
}

Finally, let's add \n after hello, world text to print it nicely in terminal.

int main() {
  stdout.printf("hello, world\n");

  return 0;
}

That's it! This is our hello, world program in Vala!

To run this program, we need to invoke Vala compiler or valac on this file using following command.

valac hello.vala

After successful compilation, you'll get hello (after the name of the file) as an executable. You can simply run this executable using following command.

./hello
hello, world

In other words, you need to run two command to see the final output in Vala.

valac hello.vala
./hello
hello, world

Or you can use && operator to run both commands in single command line.

valac hello.vala && ./hello
hello, world

In future, we'll going to accept the command-line arguments while running the program/application. To make that works, I'm updating the prototype of the main() method to access the command-line arguments.

int main(string[] args) {
  stdout.printf("hello, world\n");

  return 0;
}

You wouldn't see any difference while running this program again in terms of output. But, this modification is good to have.

valac hello.vala && ./hello
hello, world

---

Let's do some modifications in this hello.vala program while on the same time printing the hello, world text in the terminal. How about creating a new method with name greet() that prints the same hello, world text?

void greet() {
  stdout.printf("hello, world\n");
}

int main(string[] args) {
  stdout.printf("hello, world\n");

  return 0;
}

This greet() method returns nothing hence I mentioned void as return type of this method. We can call this method from main() method at the place of printf() statement.

void greet() {
  stdout.printf("hello, world\n");
}

int main(string[] args) {
  greet();

  return 0;
}

Even after doing these changes, we'll see the same output in the terminal as it was previously - hello, world text.

valac hello.vala && ./hello
hello, world

Let's even go further and print the text world from the name variable define inside the main() method as follows.

void greet(string name) {
  stdout.printf("hello %s\n", name);
}

int main(string[] args) {
  string name = "world";
  greet(name);

  return 0;
}

In main() method, we defined the name variable with world as the initial value. Then in next line, we passed this defined variable to greet() method as an argument. We also modifies the greet() method to accept one string parameter. Finally, we used %s placeholder to print the value of name at the place of text world.

Again, running this program prints the same output.

valac hello.vala && ./hello
hello, world

This is still hello, world program in Vala but with variable and supporting method.

Let's take a break and meet again to write the hello, world program but this time in object-oriented manner.

ગિરની વાતો

February 26, 2023

ગિરનારનાં પગથિયાં પાર ઢબ ઢબ લાકડીઓ ટેકા લેતી આવતી હતી. નાનાં છોકરાંને તેડીને બે મજૂરણો ચડી આવતી હતી. એમના બોલ મોતી જેવા વીણી શકાતાં હતાં. એક ડોસીનો બોલ પકડાયો: "શું કરું બાઈ? ગાંડી થઇ જાઉં તો મલક ઠેકડી કરશે કે હારીને ગાંડી થઇ ગઈ. એટલે જ રોજ ડુંગરા ચડવા-ઊતરવા રિયા."

જુવાનજોધ દીકરાઓના અકાળ મોત પછી સંસારમાં એકલી થઇ પડેલી શ્રમજીવી મા ઉપહાસ ન થાય તે સારું ઉદ્યમમાં મસ્ત છે; છતાં આપણા વાર્તાકારો એ પાત્રોને ગાંડપણ અને આત્મહત્યા સુધી લઇ જવામાં જ શોભા સમજે છે.

*

ત્રણ ડોળીવાળા ગિરનારની પહેલી ટૂક પર ચોથાની રાહ જોતા હતા.

'આવ્યા, આવ્યા. બાપુ આ આવ્યા.' બોકાનીદાર એક ખડતલ મરદ આવી પહોંચ્યો. 'આહીં ધૂણે આવો. જરા તાપી લ્યો. ચા પી લ્યો.'

મૂંગા મરદે તાપ્યું. ચા પતાવી ડોળી લઈને ચાર મરદો પગથિયાં ઊતરવા લાગ્યાં. અરધો રસ્તો કપાયો તેટલામાં જ 'બાપુ'નો જીવન-ઇતિહાસ જડી ગયો. ચાર દીકરા, દીકરાના પણ જુવાન પરણાવેલા દીકરા, ચાલી નીકળ્યા. દીકરાની ને દીકરાના દીકરાની વહુઓ જુદાં ઘર માંડીને મજૂરીએ ચડી ગઈ હતી. બાપુ એંશી વર્ષની અવસ્થાએ ડોળીઓ ચડાવતા-ઉતારતા રહ્યા છે. એમનો જીવનસંતોષ એક જ હતો: સાઠ વર્ષથી જાત્રાળુ બાયું-બેન્યુંને ગરનાર ચડાવ્યો-ઉતરાવ્યો છે, કોઈની સામે કે'દી ય નજર માંડી નથી, કોઈને કે'દી કષ્ટ દીધું નથી, આઘુંપાછું વેણ કહ્યું નથી.

ડોળી ઊંચકનાર પચાસ-સો મજૂરોનો નૈતિક આદર્શ આ ઇસ્માઇલ-બાપુ છે. એમની આપવીતી એ શ્રમજીઓની મહાગાથા છે. મૂંગું રહેવામાં જ માનવતાનું ગૌરવ સમજનાર એ ડુંગર જેવડું દુખ પચીસ-પચાસ સાથીઓની મૂંગી દિલસોજીથી મધુર બની ટાંટિયામાં જીવન-વાટ ખેંચવાનું જોર મૂકે છે.

આપણે [લેખક] ચીતરીએ છીએ જુદ્ધિ પાત્રસૃષ્ટ્રિ, પલેપલ દયાની યાચનાઓ પુકારતી, દુખ અને અન્યાનના બબડાટા કરતી. આપણને સ્વાનુભવ નથી.

- ઝવેરચંદ મેઘાણી

Learning Rust Pt. 6

February 20, 2023

Writing a condition is quite easy in Rust and follow simple and intuitive syntax and rule.

  1. No need to write the condition within bracket.
fn main() {
  let x = 4;
  
  if x % 2 == 0 {
    println!("{} is even.", x);
  }
}
  1. The expression must be evaluated to boolean value, otherwise, Rust will complain.
fn main() {
  let x = 4;
  
  if x {
    println!("{} is even.", x);
  }
}

As usual, we've the else with if.

fn main() {
  let x = 4;
  
  if x % 2 == 0 {
    println!("{} is even.", x);
  } else {
    println!("{} is odd.", x);
  }
}

Or you can even use else if in some specific scenario.

fn main() {
  let x = 4;
  
  if x > 0 {
    println!("{} is positive.", x);
  } else if x < 0 {
    println!("{} is negative.", x);
  } else {
    println!("{} must be zero.", x);
  }
}

Learning Rust Pt. 5

February 18, 2023

Rust is static typed language and we need to define data with the types. The above program works due to a concept called the type inference which is supported by Rust nicely.

When you assign an initial value to a variable, Rust will infer the initial value and based on it, it'll auto assign the default type good type. In following example, the type of x will be i32 as an integer.

fn main() {
  let x: i32 = 12;
  println!("{}", x);
}

We've few integer data types in Rust such as i8, i16, i32, i64, and i128 as signed integer and u8, u16, u32, u64 and u128 as unsigned integer.

fn main() {
  let x: u8 = 12;
  println!("{}", x);
}

If the assigned value contains decimal point in it, it'll be treated as floating-point number and the default value will be f64. For the float, we've f32 and f64 and f64 being the default one.

fn main() {
  let x = 13.3;
  println!("{}", x);
}

As usual, we've more primitive data types as we have found in other programming language such as char for character with single character. The main difference in Rust that you'll notice is, you can have the UTF-8 character. The default and the only type for the character is char.

fn main() {
  let x = '🥳';
  println!("{}", x);
}

Finally, the bool or boolean is yet another primitive data type that has true and false as one of the possible values.

fn main() {
  let x: bool = true;
  println!("{}", x);
}

We've seen the string in double quotes. But, string is not the primitive data type in Rust. We'll talk about string in future.

Learning Rust Pt. 4

February 15, 2023

let keyword is used to define a variable in Rust.

fn main() {
  let name = "Bruce";
  println!("Hello, {}", name);
}

We can define as many variables as we want using let. But, we need to use them once defined. Otherwise, compiler will throw a warning (not error) as unused variables.

fn main() {
  let name = "Bruce";
  let age = 29;
  println!("Hello, {}", name);
}

Even further, compiler also suggests the variable to prefix with _ if variable is defined in this way (defined but no plan to use for now) to remove the warning.


let actually create an immutable variable. In other words, you can't change the value of it once defined. You'll get an error about assigning the value to immutable variable.

fn main() {
  let x = 12;
  x = 13;
  println!("{}", x);
}

If we're intentionally plan to change the value as suggested above, we need to mut the variable.

fn main() {
  let mut x = 12;
  x = 13;
  println!("{}", x);
}

But, this again throw a warning about unused assignment as value 12 that is assigned in above program is re-assigned again before it actually used. So, assigning 12 is useless.