Custom initializer when subclassing Dry::Struct

I’ve been using dry-struct in most of my Ruby projects. This time around, I had to do something a little bit unusual: I have a subclass that requires less information than its parent. So here is how I wrote my custom initializer when subclassing Dry::Struct.

The Problem

The solution seems simple: the subclass should simply not send part of the information to its base constructor. But Dry::Struct was built in a way that we can’t do that as easily. The first thing I tried was the following:

Line 21 works correctly, but this is not what I wanted to achieve. I wanted to not have to specify X when creating the object, like the line 22. When doing so, I got greeted with a: in `block in resolve_missing_keys': :X is missing in Hash input (Dry::Types::MissingKeyError).

After investigating the callstack, I figured that the validation was not done in the initializer, but in the class’s new function.

The Solution

Armed with the information that the validation is done in the new function, I tried something else. The following is the version I ended up with:

The magic is on line 15: instead of defining an initializer, I overload the new function of my class. This allows me the same control as an initializer but does this before Dry::Struct validations.

Now, is this the best solution … probably not. But following the documentation, I could not find any other ways to do it.

Using SemanticLogger with Highline in Ruby

I started using Ruby again for a command-line project of mine. In order to do all that I needed to do, I had to find a configurable library for logging (kinda like log4net and friends) and another one to ask various input from the command line. Therefore, I ended up using SemanticLogger with Highline. I ended up having a few issues, here is how I fixed them.

The problem

The first input I did, after showing a few debug information, ended up a mangled mess looking like:

You can clearly see that the output is intertwined with the input selection. This is really annoying and needed fixing.

The associated code did not look anything weird, even a simple test like the following produces the issue:

The only explanation for me was that the output from the logger was done in an asynchronous way, either some kind of buffering or simply done in a background thread. After checking a bit more  Highline’s homepage, I found the answer:

Logging is performed in a separate thread so as not to slow down the application whilst logging to one or more destinations.

The solution

After looking through more guides from the official site, I finally found what I needed: a way to make SemanticLogger log in the current thread.

There are multiple ways of enabling this feature, I decided to go with the code-based approach: add  ::SemanticLogger.sync! before any of my appenders got created.

Doing so, my final solution looks like:

And my output is finally as expected:

Interacting with Google Tasks using Ruby

On the first iteration of a project of mine, I wanted to access Google Tasks using Ruby. Therefore, I did a little bit of tinkering with using the Google Tasks API using Ruby. In this post, I will go over your project’s setup, the Google backend setup and the basic usage of the API.

Project Setup

All of the Google Tasks APIs are basically REST endpoints. One could simply call all these HTTP endpoints manually, but this would be painful. I decided to use google-api-client to do all the heavy lifting. In order to include it in your project, simply add the following to your Gemfile:

Google API Setup

In order to access Google Tasks using Ruby, you will need to use Google’s OAuth. Google’s OAuth uses what they call client secrets file. These files contain all the information needed to access the selected services.

To get a client secrets file, you can follow my other post.

Handling the user’s credentials

The way the Google APIs work, an URL will be provided to you. Opening that URL and grabbing the authentication token is all on you. Therefore, there is three consideration when handling the user’s credential:

  1. Storing the credentials (so that you don’t ask access every time);
  2. Showing the consent webpage;
  3. Grabbing the authentication token;

For this quick test, I decided to use a file storage for the token. The FileTokenStore token store will simply use a YAML file to store the user’s tokens. To show the consent webpage, I went with launchy. This cross-platform library will handle opening URLs in the default browser and such. Finally, I decided to basically do nothing to grab the token: I expect the user to paste it on the command line.

Initializing the Google Tasks API

Now that you have the user’s credentials, you can initialize the API you want to use. In my case, this is the task API. In order to do so, simply create a new instance of the service you want to use (Google::Apis::TasksV1::TasksService) and set its authorization member to the credentials you got from the client.

Interacting with Google Tasks using Ruby

Creating a task list

The tasklist is basically a grouping of tasks. It is the root concept of Google Tasks. Creating one is quite straightforward:

Finding a task list

The Google Tasks API only allows you to search by tasklist ID, not a title. This ID is automatically generated and therefore not really easy to use. The easiest way to find a task list is to list all task lists and find the one you are looking for in there:

Creating a task

Creating a simple task, at the top of the task list is quite straightforward:

Finding a task

Just like finding a task list, finding a task by its title can’t be done directly through the API. In order to do so, you need to iterate over the tasks and check them:

Updating a task

Once you have your hand on the task you want to update, simply update the field you want to update and call update_task:

Completing a task

Completing a task is done by setting its status to “completed” and updating the task:

Handling a large number of return values

Google’s Task API uses pagination in order to handle a large number of return values. By default, a maximum of 100 return values will be returned by calls to list_tasklists and list_tasks. The return values of these two APIs will contain a next_page_token value. You can then use this token in a subsequent call to the API specifying the optional argument page_token in order to get another set of return values.

Here is a quick example of handling of pagination with the list_tasks call. Do note that in this example, I set the max_results value in the call so that I do not have to create 100+ tasks.

Sources