TILs - Fueling Curiosity, One Insight at a Time

At Codemancers, we believe every day is an opportunity to grow. This section is where our team shares bite-sized discoveries, technical breakthroughs and fascinating nuggets of wisdom we've stumbled upon in our work.

Published
Author
user-image
Nitturu
How to write tests for external APIs?

This can be achieved using the gem "webmock".

Using webmock we will similate the external API call and use mockdata as the response to the APIs.

step1: install the gem "webmock"
step2: add the following lines to rails helper.

Ruby

require 'webmock/webmock_api.rb'

config.before(:each) do
    stub_request(:any, /domain_name/).to_rack(WebmockApi
end


step3: create mock data responses for the APIs inside fixtures folder in spec.

member_api_success.json

Ruby

# spec/fixtures/member_api_success.json
[
  {
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]",
    "phone": "123-456-7890",
    "membership_type": "Gold",
    "status": "active"
  },
  {
    "id": 2,
    "name": "Jane Smith",
    "email": "[email protected]",
    "phone": "987-654-3210",
    "membership_type": "Silver",
    "status": "inactive"
  }
]


step4: inside spec create webmock file. Inside webmock create webmock_api.rb file. In this file we will simulate the responses for the API using mock data we have created.

Ruby

class WebmockApi
  SPEC_FIXTURES_PATH = 'spec/fixtures'.freeze
  MEMBERS_SUCCESS = File.read("#{SPEC_FIXTURES_PATH}/members_api_success.json").freeze
  POSTS_SUCCESS = File.read("#{SPEC_FIXTURES_PATH}/posts_api_success.json").freeze
  ERROR = File.read("#{SPEC_FIXTURES_PATH}/error.json").freeze

  def self.call(env)
    new.call(env)
  end

  def call(env)
    action = env['REQUEST_METHOD']
    path = env['PATH_INFO']
    params = env['QUERY_STRING']

    case path
    when '/external_members_api_path'
      params.include?('test_user') ? [ 200, {}, [ MEMBERS_SUCCESS ] ] : [ 500, {}, [ ERROR ] ]
    when '/external_post_api_path'
      params.include?('new_post') ? [ 200, {}, [ POSTS_SUCCESS ] ] : [ 500, {}, [ ERROR ] ]
    end
  end
end


step5: write the test cases for the APIs in requests folder.

Ruby

require 'rails_helper'
require 'webmock/rspec'

RSpec.describe "Members", type: :request do
  describe "GET /members_search_path" do
    context "when the members API call is successful" do
      it "returns member details from the API" do
        get "/external_members_api_path", params: { member: "test_user" }

        expect(response).to have_http_status(:ok)
        user = response.parsed_body["member"]
        expect(["id"]).to eq("123456")
        expect(user["name"]).to eq("abc")
      end
    end
    
    context "when the members API call is successful" do
      it "returns error message from the API" do
        get "/external_members_api_path", params: { member: "unknown_user" }

        expect(response).to have_http_status(:ok)
        expect(response).to have_http_status(:internal_server_error)
        error = response.parsed_body["error"]
        expect(error).to eq("Something went wrong")
      end
    end
  end
end


#ruby on rails
Published
Author
user-image
Amber Srivastava
SEND SLACK MESSAGE AS A THREAD

To send a message as a thread in Slack using the Slack API, we can use the chat.postMessage method with the thread_ts parameter. This parameter specifies the timestamp (ts) of the parent message you want to reply to, creating a thread.

Here’s how to send a threaded message:
1. Get the ts (timestamp) of the Parent Message
• If you’re replying to a specific message, you’ll need its ts value. You can retrieve it by fetching messages in the channel, or from the response of a previously sent message.
2. Send a Threaded Message Using thread_ts
• Use thread_ts in the chat.postMessage payload to post your message as a reply in the thread.
Example:-

TypeScript

import { WebClient } from "@slack/web-api";

const client = new WebClient("YOUR_SLACK_BOT_TOKEN");

async function sendThreadedMessage(channel: string, parent_ts: string, message: string) {
  try {
    // Post a new message as a reply in the thread
    const response = await client.chat.postMessage({
      channel,
      text: message,
      thread_ts: parent_ts, // This makes it a threaded message
    });
  } catch (error) {
    console.error("Error sending threaded message:", error);
  }
}

// Usage example
sendThreadedMessage("C123456789", "1688852910.123456", "This is a reply in the thread.");


If we don't have any parent message then,we can first send a message and then use its ts as the thread_ts for replies:

TypeScript

async function sendMessageWithThread(channel: string, message: string, replyMessage: string) {
  try {
    // Send the parent message
    const parentMessage = await client.chat.postMessage({
      channel,
      text: message,
    });

    // Reply to the message in a thread
    await client.chat.postMessage({
      channel,
      text: replyMessage,
      thread_ts: parentMessage.ts,
    });
  } catch (error) {
    console.error("Error sending messages:", error);
  }
}

// Usage example
sendMessageWithThread("C123456789", "This is the main message", "This is a reply in the thread.");


#C04A9DMK81E #slack #slackapi #thread
Published
Author
user-image
Satya
When working with Stimulus, it's common to dynamically update DOM elements. While string interpolation works, using HTML <template> elements is a cleaner and more maintainable approach.
#CU6U0R822 #stimulus #templates

String interpolation

JavaScript

// In your stimulus controller 
updateList() {
  this.listTarget.innerHTML = `
    <div class="flex gap-2">
      <span>${this.name}</span>
      <button>Delete</button>
    </div>
  `
}


HTML Templates

JavaScript

// In your view 
<template data-list-target="template">
  <div class="flex gap-2">
    <span data-placeholder="name"></span>
    <button>Delete</button>
  </div>
</template>

// In your Stimulus controller
updateList() {
  const template = this.templateTarget.content.cloneNode(true)
  template.querySelector('[data-placeholder="name"]').textContent = this.name
  this.listTarget.appendChild(template)
}

Published
Author
user-image
Syed
In a Rails application, we can provide different views and behaviours based on the type of device accessing our application. One of the ways to achieve this is by using the set_variant method along with mobile-specific templates like index.html+mobile

1. First, determine if the request is coming from a mobile device and set variant in the controller.

Ruby

def set_variant
    browser = Browser.new(request.user_agent)

    if browser.device.mobile?
      request.variant = :mobile
    else
      request.variant = :desktop
    end
  end


2. Now, create mobile-specific views. For example, if we have an index.html.erb view, we can create a mobile-specific version by adding +mobile to the filename.

Ruby

app/views/orders/index.html.erb
app/views/orders/index.html+mobile.erb


With the variant set, Rails will automatically choose the correct view.

#rubyonrails
Published
Author
user-image
Ayasha
useFetch is a Nuxt composable used to fetch data in a server-side or client-side context, ensuring data is fetched before rendering the component. It is primarily used for making HTTP requests and providing a reactive way of managing the fetched data.

useAsyncData is very similar to useFetch, but it allows for fetching data asynchronously, without blocking the server-side rendering process. It's useful when you want to fetch data in a non-blocking way, enabling the page to render without waiting for the data.

Key Difference:
useFetch fetches data synchronously during SSR, blocking the rendering process until the data is available.
useAsyncData fetches data asynchronously, allowing the page to render without waiting for the data.
#fetch #nuxt #useFetch #useAsyncData
Published
Author
user-image
Nitturu
In Rails, forms can be created in two ways: with a URL (using form_with url: ...) or with a model (using form_with model: ...).
But, when to use which?

Form with URL (form_with url: ...)
This form is used when you specify a URL directly and typically use it for non-resourceful actions or when you don’t have a specific model associated with the form.

Ruby

<%= form_with url: some_path, method: :post do |form| %>
  <%= form.text_field :some_field %>
  <%= form.submit "Submit" %>
<% end %>


The form_with_url is suitable for forms that don't map directly to a model, like search forms, login forms, or other custom actions etc.

Form with Model (form_with model: ...)
This form is bound to an instance of a model, allowing Rails to automatically set the form action (URL) and method (POST or PATCH) based on whether the model is a new record or an existing one.

Ruby

<%= form_with model: @record do |form| %>
  <%= form.text_field :name %>
  <%= form.submit %>
<% end %>


Rails determines the correct URL and HTTP method based on the record's state:
New Record: If @record.new_record? is true, Rails generates a POST request to the model’s create route.
Existing Record: If @record.persisted? is true, Rails generates a PATCH request to update the model’s update route.
The form_with_model is suitable for forms that directly interact with a model, such as forms for creating or editing a resource (like User, Post, etc.).

#ruby on rails
Published
Author
user-image
Adithya Hebbar
System Analyst
To update a user’s password in AWS Cognito and set it as permanent, we can use the AWS CLI with the following admin command:

Code

aws cognito-idp admin-set-user-password \\
    --user-pool-id <pool-id> \\
    --username <cognito-username> \\
    --password <password> \\
    --permanent


#auth #aws #cognito
Published
Author
user-image
Anujeet Swain
System Analyst
Query Defaults in React-Query
Any option we pass to React-Query besides the query key can have its default values and can be set by following ways:
• Passing defaultOptions object to query client as global defaults.

Code

const queryClient = new QueryClient(
 defaultOptions: {
  queries: {
   staleTime: 10 * 1000
  }
 }
}


• Setting default options for subset of queries using Fuzzy Matching by setQueryDefaults method

Code

queryClient.setQueryDefaults(
 ['todos','list'],
 {staleTime: 10 * 1000}
) 
//This sets default stale time of 10secs for all the matched queries having keys 'todos' and 'list'


• Setting default options within useQuery for fine grain control over specific query.

Code

useQuery({
 queryKey: ['todo'],
 staleTime: 10 * 1000,
});


Each method takes precedence over the others in this order.
#react-query #customizing-defaults
Published
Author
user-image
Amber Srivastava
Promise.allSettled() :

Purpose: Executes multiple promises and waits for all of them to settle (either resolve or reject).
Returns: A promise that resolves with an array of objects. Each object has:
status: Either "fulfilled" or "rejected".
value: The resolved value (if fulfilled) or reason: The rejection reason (if rejected).
Example

JavaScript

const promises = [
  Promise.resolve(1),
  Promise.reject('Error'),
  Promise.resolve(2),
];

Promise.allSettled(promises).then((results) => {
  results.forEach((result) => {
    if (result.status === 'fulfilled') {
      console.log('Result:', result.value);
    } else {
      console.log('Error:', result.reason);
    }
  });
});

Output :
Result: 1
Error: Error
Result: 2


#CCT1JMA0Z
Published
Author
user-image
Nitturu
Git Rebase:

It is similar to merge, but the difference is merging brings the changes from the main branch into your current branch by creating a merge commit that combines the histories of both branches.

Rebasing applies your branch’s commits on top of the main branch, making it look as if your work was started from the latest main commit.

• First pull the latest changes of main branch.
• Then navigate to the working branch
• run the command git rebase main
• if there any conflicts resolve and continue rebase.
• After rebasing completely, force push the changes.
#git
Published
Author
user-image
Nitturu
To avoid N+1 queries in Rails, you can use the .includes method to eager-load associated records, which reduces the number of database calls.

Suppose you have two models: Order and Item, where an Order has many Items. Without eager-loading, querying each order’s items individually would lead to N+1 queries.


Ruby

orders = Order.all 
orders.each do |order| 
  puts order.items # Each order triggers a separate query for items
end


By using .includes, Rails will fetch all associated items in a single additional query:


Ruby

orders = Order.includes(:items)
orders.each do |order|
  puts order.items # No extra query is triggered here
end


This approach loads Order records in one query and then fetches all associated items in a second query, avoiding the N+1 issue.

#CU6U0R822
Published
Author
user-image
Nitturu
before_action runs a specified method before the controller action. It’s useful for tasks that need to happen before executing the main action, such as authentication, setting up a resource, or ensuring permissions.


after_action runs a specified method after the controller action has executed. It’s useful for tasks that need to happen after the response is rendered, such as logging activity, tracking metrics, or cleaning up resources.



Ruby

class PointsController < ApplicationController
  before_action :set_user_points, only: [:show, :redeem]
  after_action :update_points_history, only: [:redeem]

  def show
    # Show points balance
  end

  def redeem
    # Redeem points logic
  end

  private

  def set_user_points
    @points = current_user.points
  end

  def update_points_history
    # Log the points redemption action
  end
end



In the above example set_user_points will execute before the controller actions show and redeem. update_points_history will execute after the redeem action.

#CU6U0R822
Published
Author
user-image
Ayasha
onDelete: Cascade in Prisma automatically deletes the child records when a parent record is deleted.

Code

model User {
  id    Int     @id @default(autoincrement())
  posts Post[]
}

model Post {
  id     Int   @id @default(autoincrement())
  userId Int
  user   User  @relation(fields: [userId], references: [id], onDelete: Cascade)
}


Now when you delete a user:

Code

await prisma.user.delete({ where: { id: 123 } })


All their posts are automatically deleted too!

#prisma #schema
Published
Author
user-image
Nived
Combining Commits with Git Squash
Squashing commits allows us to combine multiple related commits into a single one, helping to keep the commit history clean.

Let's say if we want to combine 3 separate commits which are related to same thing into one clean commit,

1. Checkout the branch

Code

git checkout branch-name


2. Run the following command

Code

git rebase -i HEAD~3


3. Modify the rebase file. Git will open a text window with last 3 commits

Code

pick 7f9d4bf first commit
pick 3f8e810 second commit
pick ec48d74 third commit


pick means to keep the commit as is.
• To squash the second and third commits into the first one, change pick to squash / s for those commits.
4. After we save and exit, another text editor will pop-up with commit messages

Code

# This is a combination of 3 commits.
# The first commit message:

fix for bug

# Commit message for #2:
Updated this

# Commit message for #3:
Added comments & updated README


Simply saving this will result in a single commit with a commit message that is a concatination of all 3 messages.
We can choose which one we want, or we can create a new message entirely.
5. Complete the Rebase
After saving, we now have a single commit representing the previous three.

#git #rebase
Published
Author
user-image
Nived
Concerns
A Rails concern is just a plain Ruby module that extends the ActiveSupport::Concern module provided by Rails.
They help in organizing and reusing code across controllers and models by extracting common functionality into modules.


There are 2 main blocks in a concern

1. included

1. Any code inside this block is evaluated in the context of the including class.
2. if sample class includes a concern, anything inside the included block will be \tevaluated as if it was written inside the sample class.
3. This block can be used to define Rails macros like validations, associations, and scopes.
4. Any method you create here becomes instance methods of the including class.
2. class_methods

1. Any methods that you add here become class methods on the including class.
Example:

typically concerns are located in app/controllers/concerns or app/models/concerns

Ruby

module ExampleConcern
  extend ActiveSupport::Concern

  included do
    # any code that you want inside the class
    # that includes this concern
  end

  class_methods do
    # methods that you want to create as
    # class methods on the including class
  end
end


Including this concern in a controller:


Ruby

class SomeController < ApplicationController
 include ExampleConcern
end


#RubyOnRails #concerns #CU6U0R822
Published
Author
user-image
Adithya Hebbar
System Analyst
Here is how to generate robots.txt in Next.Js - App Router. Add a robots.js or robots.ts file in your app directory

Code

import type { MetadataRoute } from 'next'
 
export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: '/private/',
    },
  }
}


This will add or generate a robots.txt file that matches the https://en.wikipedia.org/wiki/Robots.txt#Standard|Robots Exclusion Standard in the root of app directory to tell search engine crawlers which URLs they can access on your site.

#js #nextjs #seo
Published
Author
user-image
Ayush
We can generate a UUID in Rails using SecureRandom.uuid without needing any gem
#CU6U0R822
Published
Author
user-image
Giritharan
System Analyst
Managing Global Attributes with ActiveSupport::CurrentAttributes in Rails

In Rails, ActiveSupport::CurrentAttributes simplifies the process of storing global, thread-safe data like Current.user or Current.account during requests or jobs. It should be limited to top-level globals, such as user and request details, which are needed across all actions.

In controllers, Rails automatically resets Current between requests, so we don’t need to manually clear it. However, In Active Jobs, we need to manually reset Current after each job to prevent data from leaking between job executions. We achieve this using the after_perform callback.

Code Example:
*app/models/current.rb*:


Ruby

class Current < ActiveSupport::CurrentAttributes
  attribute :user, :account, :request_id
end


app/jobs/my_job.rb:


Ruby

class MyJob < ApplicationJob
  after_perform :clear_current_attributes

  def perform(params)
    set_current_attributes(params[:user_id])
  end

  private

  def set_current_attributes(user_id)
    Current.user = User.find_by(id: user_id)
    Current.request_id = SecureRandom.uuid
  end

  def clear_current_attributes
    Current.reset
  end
end


In summary: Rails handles resetting Current for controllers, but for jobs, we must manually reset it after each job to avoid data leakage.

#current #currentAttributes #CU6U0R822
Published
Author
user-image
Aman Suhag
System Analyst
In TypeScript, a tuple is a typed array with a fixed number of elements, where each element may have a different type. Unlike regular arrays, which can hold any number of elements of the same type, tuples define a specific sequence of element types and their corresponding positions.
Key Characteristics of Tuples:
1. Fixed Length: The number of elements in a tuple is fixed. You must specify how many elements the tuple can contain.
2. Different Types: Each element in a tuple can be of a different type. The type for each position is defined.
3. Access by Index: Elements are accessed by their index, just like regular arrays, but the types of the elements at each index are known and enforced by the type system.
let user: [string, number?] = ["Alice"]; // number is optional
#typescript #tuple
Published
Author
user-image
Ayasha
the fallbackData parameter in useSWR to provide default data for your fetch request. This is super useful when you want to display initial data while waiting for the network request to resolve. The fallback data will be used as the initial value for the data until the fetcher returns the actual data.


JavaScript

const {
  data,
  mutate,
  error,
} = useSWR(endpoint, fetcherFunction, {
  fallbackData: initialData,
});


#swr #hook

Showing page 7 of 42

Your competitors are already using AI.
The question is how fast you want to unlock the value.

Don't know where to start?

AI is everywhere but it's unclear which investments will actually move your metrics and which are expensive experiments.

Your data isn't ready

Most AI projects fail at the data layer. Pipelines, quality, access all need work before LLMs can deliver value.

Internal teams are stretched

Your engineers are shipping product. They don't have capacity to also become AI specialists with production-grade experience.

Legacy systems block everything

Aging, undocumented codebases make AI integration slow, risky, and expensive. They need to move first.

Don't worry. We've got you covered.

Start with the audit.