How to Create a Ruby Function on AWS Lambda

Amazon has been the biggest tech giant for two decades. The main reason for the success and revenue of Amazon is considered AWS. AWS is empowering almost 40 percent of the cloud market. 

Suppose you are running a website with millions of users, and one day you are just surfing through your website, and suddenly it stops responding or starts producing crashes. You can lose millions of dollars just because your site has collapsed for 1 hour. 

AWS lambda stops you and your site from suffering such kinds of collapses. AWS Lambda lets you concentrate on your application’s codebase without the stress of server maintenance, leading to mishaps with your website. 

AWS Lambda executes your code whenever you trigger an event or whenever it’s required. It is a compute service that lets you run your code without the headache of managing servers. AWS Lambda will only operate if your code is running and triggering requests. You can scale up from a few users per week to one million users per second. You can code almost any kind of application or backend service on AWS Lambda as it will take care of all your server maintenance, security, scalability, caching, OS maintenance, capacity monitoring, and logging. Using AWS CodePipeline and AWS CodeBuild, you can also deploy serverless applications composed of functions and triggered by events.

In this article, we will be deep-diving into the integration of ruby functions using AWS Lambda. In addition to the main topic, we will also be seeing why we should use AWS lambda, the language supported in AWS Lambda, and much more. After the end of this article, you will deploy a ruby function on AWS Lambda successfully.

The 411 on AWS Lambda

Amazon launched AWS Lambda in 2014 AWS re:Invent conference, by Dr. Tim Wagner. Using AWS Lambda, you can run code on multiple events as either an HTTP request via Amazon API gateway or some modification to database values and table updates. The code we run on AWS Lambda is like a formula in a spreadsheet called Lambda functions. Once your function is ready, you can trigger it anytime. Lambda functions are stateless functions that have to be dependent on infrastructure. 

Some of the key features of AWS Lambda that you should know are: 

Why Use AWS Lambda? 

There are many reasons which correctly justify why you should use AWS Lambda. It is appropriate for almost all kinds of applications until you run the code in one of the languages mentioned in AWS Lambda’s documentation. The languages supported by AWS include Ruby, Go, Java, NodeJs, Python, etc. You can visit the documentation for a detailed understanding of exact runtimes supported by AWS Lambda; you just have to take care of the coding part of your application. AWS Lambda provides the appropriate runtime environment for your app. AWS does not provide you with the facility of changing the operating system environment at runtime and can not log in to compute instances. All this helps AWS Lambda seamlessly manage all resources, infrastructure, and scale up memory usages accordingly. 

If you want customized compute services, AWS also offers you different services. For example, AWS EC2 allows you to choose everything like operating systems, memory, network, and the entire software stack. In this case, you will be responsible for memory scaling and fault tolerance, etc. Managing everything manually is not preferred because it may lead to serious issues in the application, but if you believe in your knowledge, then you can go ahead with manual operations.

AWS Events

In response to various events like lifecycle events, responding to HTTP requests, changing the values in the database, or some scheduled events, triggers can be configured to invoke a function using AWS Lambda. In short, we can say that an event is a sign of change in an environment. This environment can be an application, Software as a Service( SaaS ) service, or any custom application or app. 

Example of AWS events:

AWS Lambda invokes functions when any of the above events are triggered to communicate with other AWS services.

Event is a JSON of origin and information of associated events, depending on the event. The structure of events differs from one event to another. At runtime, Lambda converts the event into an object and passes it to your function.

An example event for Application Load Balancer:

{
    "requestContext": {
        "elb": {
            "targetGroupArn": "arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/lambda-279XGJDqGZ5rsrHC2Fjr/49e9d65c45c6791a"
        }
    },
    "httpMethod": "GET",
    "path": "/lambda",
    "queryStringParameters": {
        "query": "1234ABCD"
    },
    "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "accept-encoding": "gzip",
        "accept-language": "en-US,en;q=0.9",
        "connection": "keep-alive",
        "host": "lambda-alb-123578498.us-east-2.elb.amazonaws.com",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
        "x-amzn-trace-id": "Root=1-5c536348-3d683b8b04734faae651f476",
        "x-forwarded-for": "72.12.164.125",
        "x-forwarded-port": "80",
        "x-forwarded-proto": "http",
        "x-imforwards": "20"
    },
    "body": "",
    "isBase64Encoded": false
}

You might be thinking, what are the services AWS Lambda communicates with by invoking events? Well, there are various kinds of services that AWS Lambda uses. Here are some examples:

Services that AWS uses for reading data are:

To invoke Lambda functions synchronously:

To invoke Lambda functions asynchronously:

Other services that are used by AWS Lambda are:

You can follow the given links for detailed ideas about each service. There is also a concept of Amazon EventBridge, a serverless event bus service that helps your application connect with data from various sources. EventBridge delivers a stream of real-time data from our applications, SaaS applications, and AWS services, and that data is routed to targets such as AWS Lambda. We have explained AWS EventBridge here because it is important for understanding how AWS events exactly work. 

Which Languages are Compatible with AWS Lambda?

AWS Lambda provides support for various programming languages through the use of runtimes. Runtimes are a language-specific environment to compile and run a specific language's code. You have to choose a runtime and Linux distribution to create a container image. You will have to create a different container image if you want to change the runtime.

You can write a Lambda function in some specified languages only. But due to a wide range of languages and runtime, it is not so difficult to deploy your application on AWS Lambda.

AWS’s languages currently support Ruby, Java, Go, NodeJs, Powershell, C#, .NET, and Python. For supporting other languages you can use custom runtimes.

Common Use Cases

To know whether AWS is perfect for your application or not, you can check the following features and use cases:

How to Create Ruby Functions in AWS Lambda

In the initial days of the launch of AWS Lambda in 2014, only the support of Java, NodeJs, and python due to which these languages started revolutionizing and dominating the kingdom of serverless hosting and cloud services. But back in 2018, Ruby got some fame because AWS Lambda finally launched support for Ruby. 

Nowadays, AWS Lambda supports Ruby 2.5 and Ruby 2.7 runtimes. The very first step from where you should start is a concept of the handler. The console creates a file named `lambda_function.rb`, which exports a function known as lambda_handler that takes an event object and context object. Lambda calls the lambda handler whenever the function is invoked. In Ruby, function names are generally in lower cases, and an underscore replaces space. 

The basic lambda_function.rb with the function lambda_handler will look something like that:

#lambda_function.rb
def lambda_handler(event:, context:)
end

The above given is the simplest form of lambda function and easiest form.

Now you have some idea of AWS lambda and Lambda functions, follow this step-by-step process for creating a Ruby function in AWS Lambda.

Step 1: Create an AWS account and sign in to the Lambda console.

Before working with baby services in AWS, you need to have an AWS account. You can create it using your Amazon account or another email ID. You will get a free trial for learning purposes, but you have to show your credit card to them at the time of sign up. You can follow their guide to get started.

Step 2: Create two S3 buckets

After you have created your AWS account, create two S3 buckets. S3 stands for simple storage service and is a public cloud storage resource provided by Amazon web services. One of the S3 buckets is used to store the object initially, and another one is used for storing them when they are renamed or being moved to another location. If you are finding it troublesome to create S3 buckets, refer to the Amazon documentation. 

Step 3: Creating Ruby function using AWS Lambda Console

In this, we will be writing the actual code for our function. We have two options for writing the function: either directly write in the Lambda console or in our favorite editor compatible with AWS, and then upload it to AWS. In this post, we are creating a function in the lambda console. While creating the S3 bucket, make sure to choose the same region as your Lambda. Service can not easily communicate in different regions. So make sure the region of your lambda and S3 is the same.

A good region can save a lot of work and hard work you may have to do in the future as some regions do not have all features required by your application. As a beginner, you can visit the region which is nearest to you.

Now coming back to the topic, now we will be creating the ruby function using the Lambda console. Go to the link https://console.aws.amazon.com/lambda, and it will open a console in your default region. 

After you have logged in, you will see a form in which you have to fill in your function’s details, like function name, runtimes, permission, etc. After filling the form, click on the orange Create function button. After clicking on the create function, you will see a beautiful diagram depicting how lambda works with other services. 

Step 4: Triggering the Lambda function from the event

Here we are triggering our Lambda function through the S3 trigger. In the view that opened after creating the function, click on S3 there. After that, some options will open for the S3 trigger configuration. From the drop-down, select that S3 bucks that have uploaded images. After selecting, hit Add. Now you have defined an event, it will trigger the lambda function whenever the image uploads to that bucket.

Step 5: Making changes with a lambda function

In this step, we are going to configure our lambda function. Click on the Lambda function box from the view where we have the S3 bucket box. After clicking, it will open an editor-like view to find the lambda_handler.rb file and lambda_handler function inside it. You can write your custom code here. We will be sharing one example function at the end of the article.

After configuring go to the file and then save the file. After that, you have to give some permissions for some extra tasks. For example, allowing the copy of data from one bucket to another by a function requires some extra privileges. From the scroll down, you have to select the Lambda function; moving down to the form will give you a link that will direct you to the IAM service and then click on attach the policy.

Lambda Function Example

We will be now showing you an example Ruby function on AWS Lambda. In this function, we will be uploading a file and then write a function to rename it and move it to another bucket.

Information reading from an event

The `lambda_handler` is the main function that procs all the events. Lambda_hadler function takes event and context as input. The Ruby hash is converted into a JSON string from an event and passed to lambda_handler

For example, let us see the modified lambda_handler function below.

def lambda_handler(event:, context:)
    first_record = event["Records"].first
    bucket_name = first_record["s3"]["bucket"]["name"]
    file_name= first_record["s3"]["object"]["key"] 
end

In these lines of code, you can clearly see an S3 bucket we want to store some images. The key value in the S3 bucket could be used to identify the object we are storing, So in the lambda_handller function, we have to pass bucket name i.e., S3, and key-value from the event.  The second line of the code represents how we are retrieving the first object from the event.

Ruby works very smoothly when it comes to string manipulation. We have to shorten the file name of the Ruby function, for the function we will be using Ruby’s inbuilt library. This library takes the first 21 characters of any file anime as the file name.

short_name = file_name[0..20]

If you have a little bit of programming knowledge, you understand that line is the same as selecting the first 21, i.e., 0 to 20, elements in an array.

Configuring AWS SDK

We do not need to add any extra third-party libraries, to use the AWS SDK in Lambda function in Ruby. AWS SDK is already configured in the Lambda function in Ruby. We just need to write a required statement on top of `lambda_function.rb` for using AWS SDK in our project. The given code should be added on top of `lambda_function.rb`:

require "aws-sdk-s3"

It imports all the functions and features available in aws-sdk-s3, and brings them to our file. It works in the same way as the `require` method works in NodeJs. It will help you connect with the s3 service.

Changing the location of files

To change the file’s location from one bucket to another, we have to use the move to function on the object and reference the original name of the object. In the code given below, we create a new client in the eu-west-1 region, and then an s3 resource. The new s3 resource is used to reference the object. Then we call the move_to method to call the s3 service which then copies the original file to a new location, and deletes it from the original location. The following code also shows how we can use the AWS SDK’s built-in function to move the file.

# Get reference to the file in S3
client = Aws::S3::Client.new(region: 'eu-west-1')
s3 = Aws::S3::Resource.new(client: client)
object = s3.bucket(bucket_name).object(file_name)

# Move the file to a new bucket
object.move_to(bucket: 'upload-image-short-name', key: short_name)

Combining the code

When all the above codes conjoin together we have a function that will move one file from one to another location and change the filename to the first 21 letters.

require "aws-sdk-s3"

def lambda_handler(event:, context:)
    # Get the record
    first_record = event["Records"].first
    bucket_name = first_record["s3"]["bucket"]["name"]
    file_name = first_record["s3"]["object"]["key"]

    # shorten the name
    short_name = file_name[0..20]

    # Get reference to the file in S3
    client = aws::S3::Client.new(region: 'eu-west-1')
    s3 = aws::S3::Resource.new(client: client)
    object = s3.bucket(bucket_name).object(file_name)

    # Move the file to a new bucket
    object.move_to(bucket: 'upload-image-short-name', key: short_name)
end

Testing the function

Let us test the function. Upload a file to the S3 bucket, which has a name greater than 11 letters. After some time, it will move, rename the file and move to another S3 bucket. If everything works fine, you have done a great job.

Some extra concepts while working with Ruby in AWS

While working in AWS Lambda in Ruby, you will need operations like logging, error handling, and context object. In this section, we will be explaining special concepts while with AWS Lambda in Ruby. 

Lambda Context Object in Ruby

The context object provides information about methods and properties, which contains information about the execution environment, invocation, and function. Context objects are passed to the handler when the Lambda function fires.

Example context methods: 

`get_remaining_time_in_millis` - Returns the number of milliseconds left before the execution times out

Properties:

function_name: Name of the corresponding Lambda function.

function_version: Version of the Lambda function.

invoked_function_arn: The Amazon Resource Name (ARN) is used to invoke the function.

memory_limit_in_mb: Amount of memory allocated for that function

aws_request_id: Identifier for the invocation function.

log_group_name: Log group of the function.

log_stream_name: Log stream of function instance.

deadline_ms: Timeout for the execution of the function, Unix time milliseconds.

client_context: The application used by the client provides this client’s context to the Lambda.

Function Logging in Ruby

Logging plays a major role during the development and deployment of APIs. AWS has made logging very easy by sending all the function metrics automatically to Amazon Cloudwatch. A Cloudwatch Logs log group and a log stream come with each instance of your Lambda function. You can watch your function logs using the AWS Command-line interface, the Lambda console, or the Cloudwatch console.

An example Ruby function that will return logs is:

# lambda_function.rb

def handler(event:, context:)
    puts "## ENVIRONMENT VARIABLES"
    puts ENV.to_a
    puts "## EVENT"
    puts event.to_a
end

For more detailed logs we can use the Logger library:

# lambda_function.rb

require 'logger'

def handler(event:, context:) 
  logger = Logger.new($stdout)
  logger.info('## ENVIRONMENT VARIABLES')
  logger.info(ENV.to_a)
  logger.info('## EVENT')
  logger.info(event)
  event.to_a
end

For using the Lambda console, you can get detailed information from this Accessing Amazon Cloudwatch Logs for AWS Lambda of AWS.

For more information about logging in AWS Lambda using Ruby refer to the official docs of AWS.

Function errors in Ruby

Whenever the Lambda function generates an error, a JSON representation of the error is generated. 

Example function which generates error is:

def handler(event:, context:)
    puts "Processing event..."
    [1, 2, 3].first("two")
    "Success"
end

The above code generates a type error. JSON that is generated by the AWS lambda for this error is given below:

{
  "errorMessage": "no implicit conversion of String into Integer",
  "errorType": "Function<TypeError>",
  "stackTrace": [
    "/var/task/function.rb:3:in `first'",
    "/var/task/function.rb:3:in `handler'"
  ]
}

For more detailed information for function errors and how it works, you refer to the function error in Ruby.

Try it For Yourself

Now hope you have learned a lot about AWS Lambda and the deployment of Ruby functions using AWS Lambda. Ruby is a marvelous application and certainly has a wide variety of applications. You can build a robust backend application using Ruby.

After the announcement of Ruby support on AWS, AWS Lambda opened the door for various kinds of innovative development. Now it's your turn to create a ruby function using AWS lambda and better your experience with Ruby and AWS.