Skip to content

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.

  1. Anujeet SwainSystem Analyst
    While using React-Query, Cache invalidation is key for keeping your data up-to-date with server state.
    Data becomes "stale" after a set time (staleTime), and stale data gets re-fetched when the query is re-triggered (e.g., on component remount, on focus, or on manual refetch).

    Following cache invalidation techniques are used to set the data as "stale" in react query:
    • Implicitly setting up the staleTime .
    • Adding triggers to react-query: refetchOnWindowFocus , refetchOnReconnect, refetchOnMount , refetchInterval .
    • Manual invalidation for specific queries using queryClient.invalidateQueries() .
    To have more control over specific query invalidation, we can utilise the queryKey property:

    Code

    queryClient.invalidateQueries({
      queryKey: ['todos'],
    })



    Code

    queryClient.invalidateQueries({
      queryKey: ['todos', { type: 'done' }],
    })


    #react-query #cache-invalidation #data-synchronization
  2. Aman SuhagSystem Analyst
    Understanding context.params and req.nextUrl.searchParams() in Next.js
    1. context.params for Dynamic Routes
    In Next.js, dynamic routes are created by using brackets in the file names inside the pages directory (or in the /app directory in the case of the App Router). For example, if you create a file called [id].js, you are creating a dynamic route where id is a parameter.
    Example:

    JavaScript

    const { id } = context.params;


    2. req.nextUrl.searchParams() for Query Strings
    Query strings are parameters passed through the URL, typically after a ? symbol. These are useful for handling additional data or filters that don’t affect the route structure.
    In Next.js, with the App Router or when using API routes, you can use req.nextUrl.searchParams() to access query parameters.
    Example:

    JavaScript

    const searchParams = req.nextUrl.searchParams;
    const searchTerm = searchParams.get('query'); // ?query=nextjs


    Key Differences:

    • Dynamic Routes (context.params) are part of the URL path, like /product/123, where 123 is dynamically extracted.
    • Query Strings (req.nextUrl.searchParams) are optional parameters passed in the URL, like /api/search?query=nextjs.
    Both are useful depending on whether you want to make the parameters part of the URL path or pass them as additional optional information.
    #nextJS #query #dynamic-params
  3. Pagy Gem for Efficient Pagination in Rails

    A fast, lightweight, and efficient solution for pagination in Ruby on Rails applications.


    Why pagy?

    1. Faster and less resource heavy when compared to other pagination gems like Kaminari or Will Paginate
    2. Highly customizable : We can easily configure pagy through pagy.rb initializer file like default items per page.etc
    3. "Helpful" Helpers : Pagy provides various helper methods that make it easy to implement pagination in views with minimal code.
    4. Efficiency: It significantly reduces the number of queries, making it ideal for large datasets.
    5. Performance-Oriented: Pagy is claiming to perform up to 40x faster than other pagination gems such as Kaminari and Will Paginate
    Example Usage:

    Code for basic pagination:

    In the controllers (e.g. application_controller.rb)


    Ruby

    include Pagy::Backend



    In the Helpers (e.g. application_helper.rb)


    Ruby

    include Pagy::Frontend



    Wrap your collections with pagy in your actions :

    Ruby

    @pagy, @records = pagy(Product.all)


    Optionally set your defaults in the pagy initializer pagy.rb :


    Ruby

    # Set default items per page and navigation size
    Pagy::DEFAULT[:items] = 10  # items per page
    Pagy::DEFAULT[:size]  = [1, 4, 4, 1]  # control how many navigation links are shown




    In the view:


    Ruby

    <%= pagy_nav(@pagy) %>


    1. pagy_nav: Renders the full pagination navigation links (next, previous, and page numbers).
    2. pagy_info: Displays pagination information such as the range of items being shown and the total count.
    Some additional helpers:

    1. pagy.from: This returns the starting index of the current page’s items.
    2. https://pagy.to|pagy.to: This returns the ending index of the current page’s items.
    3. pagy.count: This returns the total number of items being paginated.

    Example Usage:


    Ruby

    Showing <%= pagy.from(@pagy) %> to <%= pagy.to(@pagy) %> of <%= @pagy.count %> items.


    If we are on page 1 and displaying 8 items per page and total count is 20, This would display Showing 1 to 8 of 20 items Giving more clarity about the pagination, making it user-friendly

    #pagy #pagination #RubyOnRails
  4. Aman SuhagSystem Analyst
    CSRF stands for Cross-Site Request Forgery, which is a type of attack where a malicious actor tricks a user into performing unwanted actions on a web application in which the user is authenticated. The attacker essentially "forges" a request from the user's browser without their consent, taking advantage of the user's active session with the target website.

    How CSRF Works:
    1. User Authentication: The user logs into a website (e.g., a banking website) and receives a session cookie that keeps them authenticated.
    2. User Visits a Malicious Site: While logged in, the user visits a malicious site or clicks on a malicious link.
    3. Forged Request: The malicious site generates a hidden request (such as a form submission) to the target site (e.g., bank) on behalf of the user, utilizing the user's active session and browser cookies.
    4. Unintended Action: Since the user is authenticated, the target site processes the request as valid, allowing the attacker to perform actions like transferring money, changing account details, etc.

    CSRF Prevention Mechanisms:
    To protect against CSRF, developers can implement several mechanisms:
    1. CSRF Tokens: The most common and effective defense.
    ◦ Every form submission or sensitive request includes a hidden, random token (CSRF token) that is unique to the user's session.
    ◦ The server validates the token before processing the request, ensuring the request originated from a legitimate source.
    2. SameSite Cookies: A modern defense where cookies are only sent with requests originating from the same site.
    ◦ Setting SameSite attribute in cookies can prevent browsers from sending cookies in cross-origin requests.
    ◦ Set-Cookie: sessionID=abc123; SameSite=Strict;
    NextAuth.js and CSRF Protection:
    In the context of NextAuth.js, CSRF protection is enabled by default when handling authentication requests. It ensures that malicious websites can’t perform unwanted actions on behalf of a logged-in user.
    #csrf #security
  5. Swr:
    Stale-While-Revalidate is a popular strategy that first returns the data from stale(cache), then send the fetch request (revalidate), and finally come with the up-to-date data.

    Why swr?
    Instant Loading (Faster User Experience)
    Background Revalidation of the data
    Improves Offline/Slow Network Experience

    We have two most common hooks from SWR data-fetching library to implement swr in our React/Next application -
    1. useSWR - with useSWR we can easily fetch data from an API or any source. The hook automatically handles everything like caching, revalidation, error handling, and cache data updates. For example, it revalidates the data when component mounts, when the user refocuses the browser tab, reconnects to the internet, or on custom revalidation interval.

    JavaScript

    import useSWR from 'swr';
    
    // Fetcher function
    const fetcher = (url) => fetch(url).then((res) => res.json());
    
    function MyComponent() {
      const { data, error, isLoading } = useSWR('https://api.example.com/data', fetcher);
    
      if (error) return <div>Error loading data</div>;
      if (isLoading) return <div>Loading...</div>;
    
      return <div>{JSON.stringify(data)}</div>;
    }


    2. useSWRMutation - It complements useSWR by providing a way to modify data, such as performing create, update, or delete operations. While useSWR is designed for reading data and displaying it in your app, useSWRMutation focuses on mutating or updating that data on the server side.

    JavaScript

    import useSWRMutation from 'swr/mutation';
    
    // Function to update data
    const updateData = (url, { arg }) =>
      fetch(url, {
        method: 'PUT',
        body: JSON.stringify(arg),
        headers: { 'Content-Type': 'application/json' },
      });
    
    function MyMutationComponent() {
      const { trigger, isMutating } = useSWRMutation('https://api.example.com/data', updateData);
    
      const handleUpdate = async () => {
        await trigger({ id: 1, name: 'New Name' });
      };
    
      return (
        <div>
          <button onClick={handleUpdate} disabled={isMutating}>
            Update Item
          </button>
          {isMutating && <span>Updating...</span>}
        </div>
      );
    }


    #swr #hook #react #next
  6. Aman SuhagSystem Analyst
    Schema Validation with Zod: Why and How?
    When building APIs, validating the structure and content of incoming requests is crucial for security, consistency, and reliability. Using a library like zod allows you to define schemas that describe the expected data format and then easily validate incoming data. Here’s how and why we use it in a Next.js API.
    Why Validate?
    1. Security: Ensure only well-formed data reaches your application, preventing injection attacks or bad data.
    2. Consistency: Guarantees that the data has the correct shape, making it easier to work with.
    3. Error Handling: Helps provide meaningful error messages when the validation fails.
    Example: Standup Schema Validation
    Using zod, we can define a schema for our "standup" object and use it to validate incoming requests in our API route.

    Code

    import { z } from "zod";
    
    // Define a schema for the standup data
    const standupSchema = z.object({
      name: z.string().trim().nonempty(),
      days: z.number().array().min(1).max(7),
      time: z.string().datetime(),
      postStandupTime: z.string().datetime(),
      standupLead: z
        .object({
          name: z.string().trim().nonempty(),
          id: z.string(),
        })
        .required(),
      questions: z
        .array(z.object({ text: z.string().min(1), isActive: z.boolean() }))
        .min(1),
      channel: z.object({
        name: z.string().trim().nonempty(),
        id: z.string(),
      }),
      members: z
        .array(
          z.object({
            name: z.string().trim().nonempty(),
            id: z.string(),
          })
        )
        .min(1),
      isActive: z.union([z.boolean(), z.undefined()]),
      schedulerId: z.string().optional(),
      timezone: z.string().min(1),
      postType: z.string().min(1),
    });
    
    export type StandupDetail = z.infer<typeof standupSchema>;


    Validating Incoming Data
    In your API route, you can use safeParse to validate the request body against the schema. This ensures the data is valid before proceeding with further logic.

    Code

    export async function PATCH(req: NextRequest) {
      const body = await req.json();
      
      // Validate the request body using the schema
      const response = standupSchema.safeParse(body);
      
      if (!response.success) {
        const { errors } = response.error;
        return NextResponse.json(
          { error: { message: "Invalid Payload", errors } },
          { status: 400 }
        );
      }
      
      // If successful, extract the validated data
      const {
        name,
        days,
        time,
        postStandupTime,
        questions,
        channel,
        members,
        isActive,
        timezone,
        standupLead,
        postType,
      }: StandupDetail = response.data;
    
      // Proceed with your logic using the validated data
    }


    Key Benefits -
    • Type Safety: With zod and TypeScript, you get strong typing, ensuring that the validated data has the expected shape.
    • Automatic Error Handling: If the validation fails, you can easily catch errors and send meaningful feedback.
    • Data Consistency: Guarantees that the incoming data adheres to a predefined structure, making your API more reliable and robust.
    #zod #type-security #json
  7. Amber Srivastava
    How to test an API endpoint.

    Lets say we have to test an API endpoint api/projects

    Code

    describe("POST /api/projects", () => {
        test("redirect with 401 status if the user is not logged in", async () => {
          jest
            .spyOn(nextAuth, "getServerSession")
            .mockImplementation(() => Promise.resolve());
    
          const req = createRequest<APIRequest>({
            method: "POST",
            url: "/api/projects",
            body: projectData,
          });
    
          const res = await postProjectHandler(req as any);
          expect(res.status).toEqual(401);
        });
    
        test("return 400 if the request body is invalid", async () => {
          jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
            Promise.resolve({
              user: {
                id: user.id,
              },
            })
          );
    
          const req = createRequest<APIRequest>({
            method: "POST",
            url: "/api/projects",
            body: {},
          });
    
          req.json = jest.fn().mockResolvedValue(req.body);
    
          const res = await postProjectHandler(req as any);
    
          expect(res.status).toEqual(400);
        });
    
        test("store project in database", async () => {
          jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
            Promise.resolve({
              user: {
                id: user.id,
              },
            })
          );
    
          const mockResponse = {
            ok: true,
            team: {
              id: "T08DABCD",
              name: "Prisma",
            },
          };
    
          const mockList = jest.fn().mockResolvedValue(mockResponse);
          https://slackClient.team.info|slackClient.team.info = mockList;
    
          const req = createRequest<APIRequest>({
            method: "POST",
            url: "/api/projects",
            body: {
              ...projectData,
            },
          });
    
          req.json = jest.fn().mockResolvedValue(req.body);
    
          const res = await postProjectHandler(req as any);
          const json = await res.json();
    
          expect(res.status).toEqual(201);
          expect(json.project.name).toEqual("Test name");
          expect(json.project.description).toEqual(
            "Test description"
          );
        });
      });


    1. describe: This is a block in Jest used to group related tests together. In our case, it groups tests for the POST /api/projects endpoint. It helps organize tests logically.
    2. test: Each test block defines an individual test case. It contains a name (description) of what it’s testing and a function with the test logic. The three tests are:
    • User is redirected with a 401 if they are not logged in.
    • A 400 status is returned if the request body is invalid.
    • The project is stored in the database when valid data is provided.
    3. Mocking:
    • jest.spyOn(): This creates a mock (or "spy") for a specific function. In this case, we are mocking the getServerSession function from nextAuth to control its output (whether the user is logged in or not). This avoids actually calling the real function and allows you to simulate different conditions.
    • Mock Implementation: With .mockImplementation(), we’re replacing the real function with a custom one. For example, in the first test, Promise.resolve() is returned to simulate a missing session (not logged in).
    4. createRequest<APIRequest>(): This is likely a helper function that creates a mock request object for testing. We use this to simulate an HTTP request (like sending a POST request to your /api/projects endpoint). It includes:
    • method: Specifies that it’s a POST request.
    • url: The endpoint being tested.
    • body: The request data sent with the POST request.
    5. postProjectHandler(req as any): This function is our handler for the POST /api/projects endpoint. It processes the incoming request (in req), validates the data, and performs actions like saving to the database or returning errors. We’re testing the results of this function.
    6. Assertions:
    • expect(res.status).toEqual(401): This is checking if the response status matches the expected value. For example, in the first test, we expect a 401 status if the user isn’t logged in.
    • expect(json.project.name).toEqual("Test name"): Here, you're checking if the response JSON contains the correct project name.
    #CCT1JMA0Z #promises #C041BBLJ57G #jest #appRouter
  8. Amber Srivastava
    How to test an API endpoint.

    Let's say we have to test an endpoint /api/projects.

    Code

    describe("POST /api/projects", () => {
        test("redirect with 401 status if the user is not logged in", async () => {
          jest
            .spyOn(nextAuth, "getServerSession")
            .mockImplementation(() => Promise.resolve());
    
          const req = createRequest<APIRequest>({
            method: "POST",
            url: "/api/projects",
            body: projectData,
          });
    
          const res = await postProjectHandler(req as any);
          expect(res.status).toEqual(401);
        });
    
        test("return 400 if the request body is invalid", async () => {
          jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
            Promise.resolve({
              user: {
                id: user.id,
              },
            })
          );
    
          const req = createRequest<APIRequest>({
            method: "POST",
            url: "/api/projects",
            body: {},
          });
    
          req.json = jest.fn().mockResolvedValue(req.body);
    
          const res = await postProjectHandler(req as any);
    
          expect(res.status).toEqual(400);
        });
    
        test("store project in database", async () => {
          jest.spyOn(nextAuth, "getServerSession").mockImplementation(() =>
            Promise.resolve({
              user: {
                id: user.id,
              },
            })
          );
    
          const mockResponse = {
            ok: true,
            team: {
              id: "T08DABCD",
              name: "Prisma",
            },
          };
    
          const mockList = jest.fn().mockResolvedValue(mockResponse);
          https://slackClient.team.info|slackClient.team.info = mockList;
    
          const req = createRequest<APIRequest>({
            method: "POST",
            url: "/api/projects",
            body: {
              ...projectData,
            },
          });
    
          req.json = jest.fn().mockResolvedValue(req.body);
    
          const res = await postProjectHandler(req as any);
          const json = await res.json();
    
          expect(res.status).toEqual(201);
          expect(json.project.name).toEqual("Test name");
          expect(json.project.description).toEqual(
            "Test description"
          );
        });
      });

  9. Ashwani Kumar JhaSenior System Analyst
    Using the server.table() method in Hapi.js we can retrieve all the registered routes in a server instance. It returns an array of route groups, containing details like the HTTP method, path, settings and other relevant information.
    #hapi.js #server
  10. Aman SuhagSystem Analyst
    Sending Body Data as json:
    The json function in the request is simulating the body of the request. In a real-world scenario, when you send a request with data (like when you're submitting a form or sending some payload), it is typically part of the body of the HTTP request.
    In your test case:

    Code

    json: async () => ({
      standupData,
    }),


    This simulates the request body. Since you're testing a PATCH method (which is often used to update resources), the server expects some data to update the resource. This json function acts like a mock for what would be req.body in a real request. In your API handler, you'd probably be extracting the body data using something like:

    Code

    const data = await req.json();


    Thus, the test needs to mock this body data to simulate a real request. It's done asynchronously here because in actual implementations, reading the body data is often asynchronous (e.g., when streaming).
    Passing Context (with params):
    The context object typically holds parameters that are passed via the route in a Next.js API request, especially when using dynamic routes (e.g., /api/standups/[id]/toggle-status). In Next.js, when you define a dynamic API route like /api/standups/[id]/toggle-status, the id is part of the route path, and Next.js passes it in the context.params object.
    In your test case:

    Code

    const context = {
      params: {
        id: standup.id,
      },
    };


    You're simulating the dynamic parameter id as part of the request. This is necessary because your API handler likely expects id to come from the route context (not just from the query string or body). In the handler, you're accessing context.params.id:

    Code

    const { id } = context.params as { id: string };


    This allows your API handler to know which standup you're targeting for the update.
    #app-router #dynamic-routes #json
  11. The dig method in Ruby is used to safely extract nested values from arrays or hashes. It allows us to traverse a data structure without worrying about whether each level of the structure exists, thus avoiding NoMethodError exceptions that occur when trying to call methods on nil.

    Syntax:

    Ruby

    hash_or_array.dig(*keys)


    Example:

    Ruby

    data = {
      user: {
        profile: {
          name: "John",
          age: 30
        }
      }
    }
    
    name = data.dig(:user, :profile, :name) # => "John"


    #ruby #CU6U0R822
  12. Vaibhav YadavSenior System Analyst
    ### PostgreSQL's Foreign Data Wrapper (FDW)

    PostgreSQL's Foreign Data Wrapper (FDW) extension and its capabilities for accessing remote databases. FDW provides a standard method for connecting to and querying tables in different databases as if they were part of the local database. This feature is particularly useful for integrating data across multiple PostgreSQL instances or accessing data from different servers.

    #### What is FDW?

    Foreign Data Wrapper (FDW) is an extension in PostgreSQL designed to connect to external data sources. With FDW, users can perform operations on remote tables seamlessly, making it easier to integrate and analyze data across various systems.

    #### How to Use FDW

    Here's a concise guide on setting up and using FDW:

    1. Install the FDW Extension:
    To enable FDW, the extension must be installed in the PostgreSQL database:


    Code

    sql
       CREATE EXTENSION IF NOT EXISTS postgres_fdw;



    2. Create a Foreign Server:
    Define the remote database connection details, including the host, port, and database name:


    Code

    sql
       CREATE SERVER my_foreign_server
       FOREIGN DATA WRAPPER postgres_fdw
       OPTIONS (host 'your_host', port '5432', dbname 'remote_db');



    3. Set Up User Mapping:
    Configure authentication details for accessing the remote server:


    Code

    sql
       CREATE USER MAPPING FOR local_user
       SERVER my_foreign_server
       OPTIONS (user 'remote_user', password 'remote_password');



    4. Create Foreign Tables:
    Define tables in the local database that map to the remote database tables:


    Code

    sql
       CREATE FOREIGN TABLE foreign_table_name (
           column1 datatype,
           column2 datatype
           -- other columns
       )
       SERVER my_foreign_server
       OPTIONS (schema_name 'public', table_name 'remote_table');



    5. Query the Foreign Tables:
    Query the foreign tables as if they were local tables:


    Code

    sql
       SELECT * FROM foreign_table_name;



    #### Example Use Case

    For instance, if there are two databases on the same PostgreSQL server—sales_db and hr_db—FDW can be used to access employee data from hr_db while working within sales_db. This setup simplifies data integration and reporting across different databases.

    FDW streamlines data access and management, providing a powerful tool for integrating and analyzing data from multiple sources within PostgreSQL.

    #postgres #database #fdw
  13. Adithya HebbarSystem Analyst
    To make your Django application ASGI-compatible and run it with Daphne, follow these steps:

    1. Install Daphne: Install Daphne if you haven't already:


    Python

    pip install daphne


    2. Ensure ASGI Configuration: Your asgi.py file should look like this:


    Python

    import os
       from django.core.asgi import get_asgi_application
    
       os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project_name.settings')
    
       application = get_asgi_application()


    3. Update settings.py: Add the ASGI_APPLICATION setting to point to your ASGI application:


    Python

    ASGI_APPLICATION = 'your_project_name.asgi.application'


    Replace 'your_project_name.settings' with the path to your Django settings module.

    4. Run Daphne: Start Daphne with your ASGI application using:


    Python

    daphne -u your_project_name.asgi:application


    Alternatively, run it on a specific port:


    Python

    daphne -p 8000 your_project_name.asgi:application


    This setup will get your Django application running with Daphne as the ASGI server.

    #python #django #daphne
  14. Ashwani Kumar JhaSenior System Analyst
    A Lambda layer in AWS Lambda is a feature that lets us manage and share common code and libraries across multiple Lambda functions. By creating a Lambda layer, we can package common code in its own module, which can then be used by our Lambda functions. This reduces duplication and simplifies the management of shared code.
  15. Adithya HebbarSystem Analyst
    We can generate a base64-encoded random key easily using either openssl or a combination of dd and base64. Here's how:


    Code

    openssl rand -base64 32


    Alternatively, using dd and base64:


    Code

    dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64


    Both commands will give you a 32-byte random key encoded in base64.

    #encryption #openssl
  16. Adithya HebbarSystem Analyst
    To rename a table in TypeORM, follow these steps:

    • Create a new migration.

    Code

    npx typeorm migration:create -n RenameTable


    This will create a new migration file in the migrations directory

    • Edit the Migration File:

    Code

    import { MigrationInterface, QueryRunner } from "typeorm";
    
    export class RenameTable1724826351040 implements MigrationInterface {
    
        public async up(queryRunner: QueryRunner): Promise<void> {
            await queryRunner.renameTable('old_table_name', 'new_table_name');
    
        }
    
        public async down(queryRunner: QueryRunner): Promise<void> {
            await queryRunner.renameTable('new_table_name', 'old_table_name');
        }
    
    }


    • Run the migration:

    Code

    npm run typeorm -- migration:run


    #typeorm #js
  17. Handling Image Uploads with Active Storage in Rails

    Active Storage simplifies file uploads in Rails by attaching files to models.

    Setup: Install Active Storage and run rails active_storage:install and rails db:migrate to create necessary tables.

    Model Configuration: Use has_many_attached :images to allow multiple image uploads in our model. Example:

    Ruby

    class SomeModel < ApplicationRecord
      has_many_attached :images
    end


    Form: Ensure the form includes multipart: true and allows multiple file uploads with form.file_field :images, multiple: true.

    Controller: Permit images in the strong parameters with images: []. Example:

    Ruby

    def some_params
      params.require(:some_model).permit(:note, images: [])
    end


    Migration: Remove old image columns if switching from direct storage to Active Storage.
    #CU6U0R822 #activestorage #fileupload
  18. Adithya HebbarSystem Analyst
    To logout from Keycloak using the signOut function in NextAuth, you need to override the default behavior to ensure that the user is properly logged out from Keycloak as well. Here's how you can update your signOut function:

    JavaScript

    async signOut({ token }) {
      if (token.provider === "keycloak") {
        const issuerUrl = authOptions.providers.find((p) => p.id === "keycloak")
          .options!.issuer!;
        const logOutUrl = new URL(
          `${issuerUrl}/protocol/openid-connect/logout`
        );
        logOutUrl.searchParams.set("id_token_hint", token.id_token!);
        await fetch(logOutUrl);
      }
    }


    #keycloak #nextauth #nextjs #js
  19. GiritharanSystem Analyst
    Delegating Permissions in Pundit:

    I encountered a scenario where I needed to retrieve the scope of one policy and use it within another policy. Specifically, I wanted to delegate permissions from one policy to another.

    To address this issue, I learned to use Pundit's methods for manually retrieving policies and scopes:
    • Retrieving a Policy

    Ruby

    Pundit.policy(user, record)  # Returns nil if the policy does not exist
    Pundit.policy!(user, record) # Raises an exception if the policy does not exist


    • Retrieving a Policy Scope:

    Ruby

    Pundit.policy_scope(user, ModelClass)  # Returns nil if the policy scope does not exist
    Pundit.policy_scope!(user, ModelClass) # Raises an exception if the policy scope does not exist


    These methods allowed me to delegate permissions effectively by retrieving and applying the appropriate scopes and policies

    #rails #pundit #pundit-policy #authorization
  20. Quick Tip: How can we open a PR from one repository to another repository

    1. Create the branch in the current repo

    Code

    git checkout -b new-branch


    2. Add the destination repository as a remote.

    Code

    git remote add destination https://github.com/username/destination-repo.git


    3. Push the new branch to the destination repository

    Code

    git push destination new-branch


    4. To check all the remote repositories added, we can do:

    Code

    git remote -v


    Then, we create a PR in the destination repository from the new branch. This process effectively copies the changes from the original PR into a new PR in a different repository.

    #github #git

Showing page 9 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.

Book a free discovery call