diceline-chartmagnifiermouse-upquestion-marktwitter-whiteTwitter_Logo_Blue

Today I Learned

Meteor.js powered React hook for retrieving articles with error and loading state handling

The custom hook created uses Meteor's useTracker hook and the subscribe method to subscribe to the article publication, as well as the ArticlesCollection to find the article with the matching _id (which is the same as the slug in this case).

const useArticle = (slug: string): any => {
    const [error, setError] = useState<Error | null>(null)

    return useTracker(() => {
        const sub = Meteor.subscribe('article', { _id: slug })
        if (sub.ready()) {
            const article = ArticlesCollection.findOne({ _id: slug })
            if (article) {
                return {
                    loading: false,
                    error: null,
                    article,
                }
            } else {
                setError(new Error(`Article with slug "${slug}" not found`))
                return {
                    loading: false,
                    error,
                    article: undefined,
                }
            }
        } else {
            return {
                loading: true,
                error: null,
                article: undefined,
            }
        }
    }, [slug, setError])
}

This hook can be used in a React component to display the article, its loading state and error:

const Article = ({ slug }: { slug: string }) => {
    const { loading, error, article } = useArticle(slug)

    if (loading) {
        return <div>Loading...</div>
    }

    if (error) {
        return <div>Error: {error.message}</div>
    }

    return (
        <div>
            <h1>{article.title}</h1>
            <p>{article.body}</p>
        </div>
    )
}

Eager Loading with Subqueries in Laravel Eloquent

Eager loading with subqueries allows you to load related models using a subquery instead of joining the tables, which can improve performance in certain situations. For example, if you have a User model and a Post model, you can count the number of posts created by each user in the last month using a subquery like this:

$users = User::withCount(['posts as post_count' => function ($query) {
    $query->select(DB::raw('count(*)'))
          ->where('created_at', '>', now()->subMonth());
}])->get();

Laravel Request Authorization: Understanding the Differences between Request Classes and Policies

In Laravel, both request classes and policies can handle authorization.

Request classes have an authorize method that is called automatically by the framework before the request is handled. It is a convenient way to perform authorization checks on a specific request.

Policies provide a more flexible and reusable way to handle authorization across multiple request classes. They are classes that organize authorization logic around a particular model or resource. They allow you to define granular abilities for your application's users and groups.

When both the request's authorize method and a policy's __call method (or method with the same name as the action) return false for the same action, the framework will consider the user as unauthorized and will return a 403 Forbidden response.

In such scenarios, the priority is given to the authorize method of the request class. If it returns true, the framework will continue to process the request and will not check against any policy. However, if it returns false or throws an exception, Laravel will check the policy's method with the same name as the action, and if it returns false, the user will be considered unauthorized and a 403 Forbidden response will be returned.

In summary, both request classes and policies can handle authorization in Laravel, with priority given to the authorize method of request classes.

Use TouchID on MacBook Pro for Terminal sudo prompts

In order to use TouchID on MacBook Pro for Terminal sudo prompts, we need to enable Apple's Touch ID PAM module pam_tid.so (https://opensource.apple.com/source/pam_modules/pam_modules-173.1.1/modules/pam_tid/pam_tid.c.auto.html).

Just edit /etc/pam.d/sudo and add

auth sufficient pam_tid.so

Heads up, when you do a system update this change will be most probably overwritten. In order to make it persistent, you need to create a launchd daemon.

Create a new file called pam-tid.sh in a shared path

vim /Users/Shared/pam-tid.sh
#!/bin/bash

if ! grep 'pam_tid.so' /etc/pam.d/sudo --silent; then
  sed -i -e '1s;^;auth       sufficient     pam_tid.so\n;' /etc/pam.d/sudo
fi

Create a new com.graffino.pam.plist file:

vim /Users/Shared/com.graffino.pam.plist`

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.graffino.pam</string>

    <key>KeepAlive</key>
    <false/>

    <key>LaunchOnlyOnce</key>
    <true/>

    <key>RunAtLoad</key>
    <true/>

    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/pam-tid.sh</string>
    </array>
</dict>
</plist>

Start the daemon

Improving mapping performance with web workers

We can use web workers to execute the computationally intensive mapping operation in a separate thread. This can be done by following these steps:

  1. Create a new TS file for the worker
// worker.ts

// define the input and output types
type WorkerInput<T, U> = { data: T, mapFn: (x: T) => U };
type WorkerOutput<U> = { data: U[] };

// function for the actual work on the data
function doWork<T, U>(input: WorkerInput<T, U>): Promise<WorkerOutput<U>> {
  // Apply the mapping function to the input data.
  const mappedData = input.data.map(input.mapFn);

  // Return the mapped data as a promise.
  return Promise.resolve({ data: mappedData });
}
  1. Use the worker in the main.ts file
// main.ts
const worker = new Worker('worker.ts');

// Define the mapping function.
const mapFn = (x: number) => x * x;

// Send data and the mapping function to the worker for processing.
worker.postMessage({ data: [1, 2, 3], mapFn });

// Listen for the message with the result from the worker
worker.addEventListener('message', (event) => {
    console.log(`Received message from worker: ${event.data}`)
});

Matching dates within a monthly Interval that crosses over multiple years with PHP and Carbon

function getDatesInRange($dates, $start, $end) {
    $datesInRange = $dates->filter(function ($date) use ($start, $end) {
        if ((int) $start->format('Y') === (int) $end->format('Y')) {
            $start->year((int)$date->format('Y'));
            $end-> year((int)$date->format('Y'));
        } elseif ((int) $start->format('Y') < (int) $end->format('Y')) {
            if ((int) $date->format('m') >= (int) $start->format('m')) {
                $start->year((int) $date->format('Y'));
                $end->year((int) $date->format('Y'))->addYears(1);
            } else {
                $end->year((int) $date->format('Y'));
                $start->year((int) $date->format('Y'))->subYears(1);
            }
        }

        return Carbon::parse($date)->isBetween($start, $end);
    });

    return $datesInRange;
}

How to optimize the performance of the map function in JavaScript

To optimize the performance of a map function in JavaScript, you can do a few things:

  1. Use the forEach method instead of map if you are not creating a new array from the iteration. forEach is faster because it does not create a new array.

  2. Use the map method on a smaller array if possible. For example, if you have a large array and you only need to map over a subset of it, create a new array with just the subset and use map on that.

  3. If you are using map to transform each element of the array, use a specialized method for the transformation instead of using map. For example, if you are transforming each element to a string, use the toString method instead of using map.

  4. If you are using map to filter an array, use the filter method instead. filter is optimized for filtering and will be faster than using map for that purpose.

  5. Use a for loop instead of map if you need to perform complex operations on each element of the array. map is optimized for simple transformations, so a for loop will be faster for more complex operations.

Redirect unauthenticated users with meteor.js and react-router 6

The challenge I encountered was accessing the Meteor.user() in React Router's route loader property.

Alternatively, instead of querying the server, I'm picking up the user id from localStorage. The absence of it indicates the user is logged off or never logged in. Until Meteor kicks off, no sensitive data is displayed anyway.

...
{
  path: '/',
  element: <App />,
  errorElement: <ErrorPage />,
  loader: async () => window.localStorage.getItem('Meteor.userId'),
}
...

and in app.tsx:

const userId: UserId | null = useLoaderData()
const navigate = useNavigate()

useEffect(() => {
  if (userId === null) navigate('/login')
}, [userId])