In an effort to learn a little bit about Ruby and Rails, I decided a fun project would be to attempt generating a YAML file that aligns to the OpenAPI v3.0 specification that I could import into Postman and test routes without manually having to do things via the Rails console and/or the consuming frontend application.
As this relates to what could be important information, I am going to redact as much as possible.
For context - I am normally writing in JS, Python, Swift, Rust (if I don't need C) or Golang these days. I knew jack shit about Ruby coming into this (other than general basics).
To see the full project, head to the GitHub repo.
What I wanted to achieve:
factory-bot(the latter two which I ran out of time for).
Initially I wanted to take the output of
rails routes and manipulate it from there, but for the sake of time I cut it back.
At first, I also wanted to resolve all the controller functions and generate a placeholder for the final important request body to Postman, but I soon realised this wouldn't be so feasible with the file structure and changing approach to code.
To get my head around Rails, I did the following:
I made a decision on what I considered the important parts of the application. I took the initial output that I would get from
rails routes and did some manual cleansing.
The initial output looked like so (what a mess with the whitespace, I know):
While I could have written the code to cleanse this back, I decided to do some manual culling.
First, I echo the routes into a file to work with ie
bin/rails routes > routes.txt.
Next, I identified that
URI Pattern and
Controller#Action as the pieces of information I wanted, so I ended up using some regex cleaning in VSCode (use whatever) using
.+?(?=GET|DELETE|POST|PUT|PATCH) to replace with
'' to trim the start and manually cut out the few lines left that were not aligned to the format I wanted (first line, three others related to GraphQL that I could manually add once to Postman anyways).
I did what I always do here and went to
awesome GitHub repo for the language and looked at the suggested CLI libraries. I just went with
Slop as it seemed super basic. There were a few good options.
I generated the
main.rb file as the app entrypoint and legit only use
Slop to parse for
--file for a path the
After that, the application itself is basically to call a file helper to parse the file and get it into an array of
RouteInfo structs and then a second phase to take that array and mold is to the
The file helper part is quite straight forward. I rescued any errors and propagated the error to identify where the methods would fail.
The OpenAPI helper was a little more complex because a) I stopped being so bothered and b) I was splitting up structs into smaller pieces. There are some helper methods there that I would prefer to be abstracted elsewhere but I wanted them as lambdas and it wasn't so straight forward for this Ruby n00b. I used a
deep_merge Gem and I didn't really think through a nicer way to start immutable. This might not be a good decision.
Where possible, I used the
dry-struct libraries to learn how the worked and help with valiation of the classes. Unsure if I used these effectively, and I basically converted using the
attributes method straight away but it was nice for catching type errors (which I really appreciated).
There are some comments prefixed with
# ! to indicate things that I was a bit unhappy with the decision, but are likely just the result of lack of real-world Ruby experience.
I had an example OpenAPI v3 specification YAML file that I tried before writing all of this see how that worked when importing to Postman:
This set as my basis for the structs and design of hashes and how I would write. The
success lambda when folding at the end of the
main.rb OpenAPI class monad did the conversion of symbols to strings and writing of the YAML file. The resulting file looked like so:
The above is omitting about 7000 lines. All 900+ route were successfully mapped. The initial home route did end up an empty string, so there is always work to do.
Because there was no easy way to find the models and copy their properties over, any REST verb other than
GET just received a default JSON body of
id. This means any call needs to be manually updated when using, but this is actually just so much better than nothing anyway.
This part was the delightful part.
Once imported, you can
Generate Collection on Postman and it would bring in all 900 requests.
After they were in, there was still two smaller pieces of work to be done. I had to update the default variables in Postman and set
Setting the baseUrl value for all requests
As for the requests made, I ended up jumping into the UI and watching the request information sent to the
API when creating a team. I copied that info, found the correct request (Postman laid out the folders so nice!) and then BAM successfully managed to create a team from Postman.
Updating the create team with the request body from Chrome DevTools
This is a great way to do some validation from outside of the Rails ecosystem while waiting on the UI to catch up.
While I didn't get to explore RSpec and the others, I considered this as mission success!
dry-rbgems? Is using the
do notationthe preferred way? Can I compose without having to grab the return values from genetors and forwarding on the returned monad?
do notationis the preferred way?
ActiveModelsupport for defining schemas.
Resultmonads I saw being used in the work controllers don't use an
eithermethod to fold the monad - wondering if there is a reason for raising HTTP errors instead of propagating a
Failuremonad and folding?
hash.transform_keys(&:to_s)work? I saw it as an option for converting hash keys but had no success, so I borrowed a dumb hack to extend the
Hashclass from Stack Overflow that I hate.
rescueblocks - is this an anti-pattern? A lot of
unlessetc so maybe all errors are handled inline?
irb(maybe my misunderstanding between hashes and constructors?):
The post-mortem of this is that I at least got my feet wet with a lot of the different parts of Ruby and the gems we use. I am definitely up for any feedback anyone has on what is written as I am sure there were some anti-patterns here.
To see the full project, head to the GitHub repo.