Designing a regex test feature with Stimulus pt 2

In part 1 we built a Stimulus controller that tests a regex, however, there is a problem with that solution. Ruby and JavaScript regex implementations are slightly different. In our case, we want users to verify their regex using Ruby’s regex implementation. To do that, we need to evaluate the regex on the server side.

1. Adding a Rails endpoint

In order to evaluate the regex on the server side, we need to expose an endpoint so that Stimulus can call the Rails application. To do that, we need to generate a Rails controller:

1
bin/rails g controller regex_test create

And we need to setup a new route, which we’ll call regex_test:

1
resources :regex_test, only: :create

2. Importing UJS

Now that we have a Rails endpoint, we’d like to call it from the Stimulus controller. JavaScript has a number of libraries for making API calls. We’re going to use UJS because it handles CSRF tokens for us automatically.

To use UJS, we need to import it inside the Stimulus controller:

1
import Rails from "@rails/ujs";

However, UJS will only work if we import the JavaScript module into the codebase. With Rails 7 we can use Import Maps to include JavaScript modules without needing webpack or yarn.

Import Maps has the concept of ‘pinning’ packages. We can pin UJS to include it into the project.

1
bin/importmap pin @rails/ujs

3. Calling Rails from JavaScript

With UJS imported we can use it to call Rails. Here’s a basic POST request:

1
2
3
4
5
6
7
Rails.ajax({
  type: 'POST',
  url: `/regex_test`,
  success: (data) => {
    // do something
  }
})

This will make a request to the Rails endpoint but it won’t send any params. To do that, we need to pass in a data object. Unfortunately, UJS doesn’t support JSON so we’ll have to convert a JavaScript object into a string of params.

An object can be built easily using the regex and exampleString Stimulus targets:

1
2
3
4
const data = {
  regex: this.regexTarget.value,
  exampleString: this.exampleStringTarget.value
}

Then we can use URLSearchParams to convert the object into a string:

1
2
3
4
5
6
7
8
Rails.ajax({
  type: 'POST',
  url: `/regex_test`,
  data: new URLSearchParams(data).toString(),
  success: (data) => {
    // do something
  }
})

4. Adding regex matching logic to the Rails controller

In the Rails controller we can use the params to evaluate the regex, using Regexp#match? to run the regex against the example string:

1
Regexp.new(params["regex"]).match?(params["exampleString"])

Then we can return the result, in a simple JSON object, to use on the client side:

1
2
3
4
5
6
7
class RegexTestController < ApplicationController
  def create
    render json: {
      match: Regexp.new(params["regex"]).match?(params["exampleString"])
    }, status: 200
  end
end

5. Using the Rails response in the Stimulus controller

Now that the Rails controller is returning a result, we can use it in the Stimulus controller. We can pull out match from the response object and use the value to set the color of the form field:

1
this.exampleStringTarget.style.borderColor = data.match ? "green" : "red"

Here’s how the Stimulus controller looks now:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Controller } from "@hotwired/stimulus"

import Rails from "@rails/ujs";

export default class extends Controller {
  static targets = [ "regex", "exampleString" ]

  test() {
    const data = {
      regex: this.regexTarget.value,
      exampleString: this.exampleStringTarget.value
    }

    Rails.ajax({
      type: 'POST',
      url: `/regex_test`,
      data: new URLSearchParams(data).toString(),
      success: (data) => {
        this.exampleStringTarget.style.borderColor = data.match ? "green" : "red"
      }
    })
  }
}

That’s the end of part 2. If we refresh the browser and test out the form we should see that it’s working as before but evaluating the regex based on Ruby’s regex implementation instead of JavaScript’s.