{"id":237,"date":"2024-04-07T08:00:00","date_gmt":"2024-04-07T12:00:00","guid":{"rendered":"https:\/\/gerardomiranda.dev\/blog\/?p=237"},"modified":"2024-04-07T12:44:32","modified_gmt":"2024-04-07T16:44:32","slug":"unlocking-the-potential-of-ruby-on-rails-custom-generators","status":"publish","type":"post","link":"https:\/\/gerardomiranda.dev\/blog\/unlocking-the-potential-of-ruby-on-rails-custom-generators\/","title":{"rendered":"Unlocking the potential of Ruby on Rails custom generators"},"content":{"rendered":"\n<p>In the last weeks I have been using <a href=\"https:\/\/github.com\/felangel\/mason\" title=\"\">mason <\/a>to generate some boiler plate code for my personal project, may have to write about it some other time. So the idea of templating was sitting in my head at all times.<\/p>\n\n\n\n<p>Then at my job I got assigned a task, a very common task actually, a third party integration. So my first thought was, I can use mason to generate some templates and make all our data processors consistent. Very excited, I went to ask for permission to create a new repository where I was already imagining filling it up with bricks (bricks are templates that mason uses to create your code). But the lead dev asked &#8220;doesn&#8217;t Rails have a feature for that?&#8221;.<\/p>\n\n\n\n<p>At first I was thinking, &#8220;oh no! they don&#8217;t get it&#8221;. But after the initial reaction, it actually sound like a pretty good idea, so I went on to investigate about Rails generators, to see if we could use them for this case. The official docs didn&#8217;t have a very complete example, so I started looking for some blog posts and the API reference as well. I didn&#8217;t find any comprehensive guide, so I went on to experiment with it, I can show you a bit of the work I accomplished with it (not the real thing obviously) in case you need a more concrete example of how to create Rails generators.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Preparing<\/h2>\n\n\n\n<p>First let&#8217;s build something simple, like a service for getting animal pictures, there we are going to have options to show pictures from different kinds of animals.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">rails g controller AnimalPictures index<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Since cats are my favorite animals we will start with pictures of cats, lets add an image_tag and a button to retrieve the cat picture. Don&#8217;t forget to add the route for our post method get_cat, and in the controller we are going to retrieve a cute cat image I found with google.<\/p>\n\n\n\n<p><strong>animal_pictures\/index.html.erb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"ERB (Embedded Ruby)\" data-shcb-language-slug=\"erb\"><span><code class=\"hljs language-erb shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>AnimalPictures#index<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span><\/span><\/span><span class=\"ruby\"> <span class=\"hljs-keyword\">unless<\/span> @image_url.<span class=\"hljs-literal\">nil<\/span>? <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"ruby\"><span class=\"xml\">    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> image_tag @image_url, <span class=\"hljs-symbol\">size:<\/span><span class=\"hljs-string\">\"512x512\"<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"ruby\"><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"ruby\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span><\/span><\/span><span class=\"ruby\"> <span class=\"hljs-keyword\">end<\/span><\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"ruby\"><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"ruby\"><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"ruby\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> button_to <span class=\"hljs-string\">\"Cats\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_cat<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">ERB (Embedded Ruby)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">erb<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>config\/routes.rb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\">get <span class=\"hljs-string\">'animal_pictures\/index'<\/span>\npost <span class=\"hljs-string\">'animal_pictures\/get_cat'<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>animal_pictures_controller.rb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AnimalPicturesController<\/span> &lt; ApplicationController<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">index<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    @image_url = params&#91;<span class=\"hljs-symbol\">:image_url<\/span>]\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_cat<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    redirect_to <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:index<\/span>, <span class=\"hljs-symbol\">image_url:<\/span> <span class=\"hljs-string\">'https:\/\/i.natgeofe.com\/n\/548467d8-c5f1-4551-9f58-6817a8d2c45e\/NationalGeographic_2572187_square.jpg'<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With this we get a button that when pressed will show the picture, but wait&#8230; it always shows the same picture. To remedy that we will build a little client to call an <a href=\"https:\/\/developers.thecatapi.com\/\" title=\"\">API<\/a> that will provide a different picture every time:<\/p>\n\n\n\n<p><strong>app\/services\/cats\/client.rb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-class\"><span class=\"hljs-keyword\">module<\/span> <span class=\"hljs-title\">Cats<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Client<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">include<\/span> HTTParty\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    base_uri <span class=\"hljs-string\">'https:\/\/api.thecatapi.com'<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">initialize<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_picture_url<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>      response = <span class=\"hljs-keyword\">self<\/span><span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>.<span class=\"hljs-title\">get<\/span>('\/<span class=\"hljs-title\">v1<\/span>\/<span class=\"hljs-title\">images<\/span>\/<span class=\"hljs-title\">search<\/span>')<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      response.parsed_response&#91;<span class=\"hljs-number\">0<\/span>]&#91;<span class=\"hljs-string\">'url'<\/span>]\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And now we modify our get_cat method to use this API<\/p>\n\n\n\n<p><strong>animal_pictures_controller.rb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_cat<\/span><\/span>\n  client = Cats::Client.new\n  redirect_to <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:index<\/span>, <span class=\"hljs-symbol\">image_url:<\/span> client.get_picture_url\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div style=\"height:75px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Building a basic generator<\/h2>\n\n\n\n<p>Our cat picture fetching service is now working, but remember the service was suposed to be for animals not only cats. Let&#8217;s add the option to retrieve dog pictures. But before that, you will notice, the client is probably something we will repeat a lot so it is a good candidate for making a generator (Finally!).<\/p>\n\n\n\n<p>Let&#8217;s generate a generator:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">rails generate generator AnimalApiClient<\/code><\/span><\/pre>\n\n\n<p>This will generate some files under lib\/generators. First we are going to create a file in the templates folder and call it client.rb.tt where we are going to copy the code from the cats client and make a few modifications, removing some specific data and using the class_name method to name our module. Think of this like something similar to the erb files that render our views. Check all the <a href=\"https:\/\/api.rubyonrails.org\/classes\/Rails\/Generators\/NamedBase.html\" title=\"\">methods<\/a> provided by the NamedBased class that you can use to generate the file.<\/p>\n\n\n\n<p><strong>lib\/generators\/animal_api_client\/templates\/client.rb.tt<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">module<\/span> &lt;%= <span class=\"hljs-title\">class_name<\/span> %&gt;<\/span>\n\n  <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Client<\/span><\/span>\n    <span class=\"hljs-keyword\">include<\/span> HTTParty\n\n    base_uri <span class=\"hljs-string\">''<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">initialize<\/span><\/span>\n    <span class=\"hljs-keyword\">end<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_picture_url<\/span><\/span>\n      response = <span class=\"hljs-keyword\">self<\/span><span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>.<span class=\"hljs-title\">get<\/span>('')<\/span>\n      <span class=\"hljs-comment\"># return the url<\/span>\n    <span class=\"hljs-keyword\">end<\/span>\n\n  <span class=\"hljs-keyword\">end<\/span>\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That is going to be our template, and now let&#8217;s move on to the actual generator that is going to use this template to generate our new clients.<\/p>\n\n\n\n<p><strong>lib\/generators\/animal_api_client\/animal_api_client_generator.rb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AnimalApiClientGenerator<\/span> &lt; Rails::Generators::<span class=\"hljs-title\">NamedBase<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>  source_root File.expand_path(<span class=\"hljs-string\">'templates'<\/span>, __dir_<span class=\"hljs-number\">_<\/span>)\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">create_client_file<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    template <span class=\"hljs-string\">\"client.rb\"<\/span>, File.join(<span class=\"hljs-string\">\"app\/services\"<\/span>, file_name, <span class=\"hljs-string\">\"client.rb\"<\/span>)\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can define all the methods you want in the generator class, they will be called in the order they are defined. <\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Using the generator<\/h4>\n\n\n\n<p>We can now call the generator to create new clients, let&#8217;s create a new client, this time to retrieve picture of dogs:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">rails g animal_api_client dogs<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That will create the file app\/services\/dogs\/client.rb with the following content:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-class\"><span class=\"hljs-keyword\">module<\/span> <span class=\"hljs-title\">Dogs<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Client<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">include<\/span> HTTParty\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    base_uri <span class=\"hljs-string\">''<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">initialize<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_picture_url<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>      response = <span class=\"hljs-keyword\">self<\/span><span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>.<span class=\"hljs-title\">get<\/span>('')<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-comment\"># return the url<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">end<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we just have to update the base_uri, the endpoint and return the appropriate data, we will use <a href=\"https:\/\/shibe.online\/\" title=\"\">shibe API<\/a>. Next don&#8217;t forget to add the route, the button in the view and the method in the controller:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-comment\"># config\/routes.rb<\/span>\npost <span class=\"hljs-string\">'animal_pictures\/get_dog'<\/span>\n\n<span class=\"hljs-comment\"># app\/views\/animal_pictures\/index.html.erb<\/span>\n&lt;%= button_to <span class=\"hljs-string\">\"Dogs\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_dog<\/span> %&gt;\n\n<span class=\"hljs-comment\"># app\/controllers\/animal_pictures_controller.rb<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_dog<\/span><\/span>\n  client = Dogs::Client.new\n  redirect_to <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:index<\/span>, <span class=\"hljs-symbol\">image_url:<\/span> client.get_picture_url\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div style=\"height:75px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Beyond the basics<\/h2>\n\n\n\n<p>Our project now can retrieve pictures of cats and dogs. And although replicating the dogs part was easier, I still have to modify another 3 files with repetitive code! We can make it even easier. Let&#8217;s add something else to our generator, we will insert some code into our existing files<\/p>\n\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">route<\/h4>\n\n\n\n<p>You can add routes to your routes.rb file using the <a href=\"https:\/\/api.rubyonrails.org\/classes\/Rails\/Generators\/Actions.html#method-i-route\" title=\"\">route <\/a>method. In this case we will add a post route using the singular_table_name method to name the endpoint:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">add_routes<\/span><\/span>\n  route <span class=\"hljs-string\">\"post 'animal_pictures\/get_<span class=\"hljs-subst\">#{singular_table_name}<\/span>'\"<\/span>\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">inject_into_class<\/h4>\n\n\n\n<p>For the code  in the controller we are going to take another approach, we will use the <a href=\"https:\/\/www.rubydoc.info\/gems\/thor\/Thor\/Actions#inject_into_class-instance_method\" title=\"\">inject_into_class <\/a>method, that puts whatever content we provide just after the class declaration, then again we will be using the singular_table_name and class_name helper methods<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">add_to_controller<\/span><\/span>\n  inject_into_class <span class=\"hljs-string\">\"app\/controllers\/animal_pictures_controller.rb\"<\/span>, AnimalPicturesController, <span class=\"hljs-string\">&lt;&lt;~RUBY.indent(2)\n    def get_<span class=\"hljs-subst\">#{singular_table_name}<\/span>\n      client = <span class=\"hljs-subst\">#{class_name }<\/span>::Client.new\n      redirect_to action: :index, image_url: client.get_picture_url\n    end\n  RUBY<\/span>\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">insert_into_file<\/h4>\n\n\n\n<p>Finally for our view we will use the <a href=\"https:\/\/www.rubydoc.info\/gems\/thor\/Thor\/Actions#insert_into_file-instance_method\" title=\"\">insert_into_file<\/a> method, this will allow us to specify an anchor for our content with the before: or after: options. In our case to facilitate our work we are going to modify our view and wrap our buttons in a div with a unique id, this div will serve as the anchor.<\/p>\n\n\n\n<p><strong>app\/views\/animal_pictures\/index.html.erb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"ERB (Embedded Ruby)\" data-shcb-language-slug=\"erb\"><span><code class=\"hljs language-erb\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"actions\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> button_to <span class=\"hljs-string\">\"Cats\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_cat<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> button_to <span class=\"hljs-string\">\"Dogs\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_dog<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">ERB (Embedded Ruby)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">erb<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now let&#8217;s write the method in our generator:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">add_to_view<\/span><\/span>\n  insert_into_file <span class=\"hljs-string\">\"app\/views\/animal_pictures\/index.html.erb\"<\/span>, <span class=\"hljs-symbol\">after:<\/span> <span class=\"hljs-string\">'&lt;div id=\"actions\"&gt;'<\/span> <span class=\"hljs-keyword\">do<\/span> <span class=\"hljs-string\">&lt;&lt;~ERB.indent(4)\n\n      &lt;%= button_to \"<span class=\"hljs-subst\">#{class_name}<\/span>\", action: :get_<span class=\"hljs-subst\">#{singular_table_name}<\/span> %&gt;\n    ERB<\/span>\n  <span class=\"hljs-keyword\">end<\/span>\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This code will insert the button right after the opening div tag. <\/p>\n\n\n\n<p>Let&#8217;s run our generator again, this time we will be fetching <a href=\"https:\/\/random-d.uk\/api\" title=\"\">duck pictures<\/a><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">rails g animal_api_client ducks<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We will have this result:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Ruby\" data-shcb-language-slug=\"ruby\"><span><code class=\"hljs language-ruby\"><span class=\"hljs-comment\"># config\/routes.rb<\/span>\nRails.application.routes.draw <span class=\"hljs-keyword\">do<\/span>\n  post <span class=\"hljs-string\">'animal_pictures\/get_duck'<\/span>\n  get <span class=\"hljs-string\">'animal_pictures\/index'<\/span>\n  post <span class=\"hljs-string\">'animal_pictures\/get_cat'<\/span>\n  post <span class=\"hljs-string\">'animal_pictures\/get_dog'<\/span>\n  <span class=\"hljs-comment\"># For details on the DSL available within this file, see https:\/\/guides.rubyonrails.org\/routing.html<\/span>\n<span class=\"hljs-keyword\">end<\/span>\n\n<span class=\"hljs-comment\"># app\/controllers\/animal_pictures_controller.rb<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AnimalPicturesController<\/span> &lt; ApplicationController<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_duck<\/span><\/span>\n    client = Ducks::Client.new\n    redirect_to <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:index<\/span>, <span class=\"hljs-symbol\">image_url:<\/span> client.get_picture_url\n  <span class=\"hljs-keyword\">end<\/span>\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">index<\/span><\/span>\n    @image_url = params&#91;<span class=\"hljs-symbol\">:image_url<\/span>]\n  <span class=\"hljs-keyword\">end<\/span>\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_cat<\/span><\/span>\n    client = Cats::Client.new\n    redirect_to <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:index<\/span>, <span class=\"hljs-symbol\">image_url:<\/span> client.get_picture_url\n  <span class=\"hljs-keyword\">end<\/span>\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_dog<\/span><\/span>\n    client = Dogs::Client.new\n    redirect_to <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:index<\/span>, <span class=\"hljs-symbol\">image_url:<\/span> client.get_picture_url\n  <span class=\"hljs-keyword\">end<\/span>\n\n<span class=\"hljs-keyword\">end<\/span>\n\n<span class=\"hljs-comment\"># app\/services\/ducks\/client.rb<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">module<\/span> <span class=\"hljs-title\">Ducks<\/span><\/span>\n\n  <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Client<\/span><\/span>\n    <span class=\"hljs-keyword\">include<\/span> HTTParty\n\n    base_uri <span class=\"hljs-string\">''<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">initialize<\/span><\/span>\n    <span class=\"hljs-keyword\">end<\/span>\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">get_picture_url<\/span><\/span>\n      response = <span class=\"hljs-keyword\">self<\/span><span class=\"hljs-class\">.<span class=\"hljs-keyword\">class<\/span>.<span class=\"hljs-title\">get<\/span>('')<\/span>\n      <span class=\"hljs-comment\"># return the url<\/span>\n    <span class=\"hljs-keyword\">end<\/span>\n\n  <span class=\"hljs-keyword\">end<\/span>\n<span class=\"hljs-keyword\">end<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Ruby<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">ruby<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>app\/views\/animal_pictures\/index.html.erb<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"ERB (Embedded Ruby)\" data-shcb-language-slug=\"erb\"><span><code class=\"hljs language-erb\"><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>AnimalPictures#index<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span><\/span><\/span><span class=\"ruby\"> <span class=\"hljs-keyword\">unless<\/span> @image_url.<span class=\"hljs-literal\">nil<\/span>? <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> image_tag @image_url, <span class=\"hljs-symbol\">size:<\/span><span class=\"hljs-string\">\"512x512\"<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%<\/span><\/span><\/span><span class=\"ruby\"> <span class=\"hljs-keyword\">end<\/span><\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"actions\"<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> button_to <span class=\"hljs-string\">\"Ducks\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_duck<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> button_to <span class=\"hljs-string\">\"Cats\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_cat<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">%=<\/span><\/span><\/span><span class=\"ruby\"> button_to <span class=\"hljs-string\">\"Dogs\"<\/span>, <span class=\"hljs-symbol\">action:<\/span> <span class=\"hljs-symbol\">:get_dog<\/span> <\/span><span class=\"xml\"><span class=\"hljs-tag\">%&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">ERB (Embedded Ruby)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">erb<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we only have to add the specifics for the API we are going to use and it will be READY!.<\/p>\n\n\n\n<div style=\"height:75px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Final thoughts<\/h2>\n\n\n\n<p>I hope this helped you know the capabilities of the Rails generators, especially when you have an application that requires a lot of integrations, or new features that have a very similar starting point. This may save you a lot of time. Complete code is in my <a href=\"https:\/\/github.com\/gerardo-m\/rails_test_labhttps:\/\/github.com\/gerardo-m\/rails_test_lab\" title=\"\">repo<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the last weeks I have been using mason to generate some boiler plate code for my personal project, may have to write about it some other time. So the idea of templating was sitting in my head at all times. Then at my job I got assigned a task, a very common task actually, a third party integration. So [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":165,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[24],"tags":[98,94],"class_list":["post-237","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-learning","tag-api","tag-ruby-on-rails-en"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/posts\/237","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/comments?post=237"}],"version-history":[{"count":15,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/posts\/237\/revisions"}],"predecessor-version":[{"id":259,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/posts\/237\/revisions\/259"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/media\/165"}],"wp:attachment":[{"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/media?parent=237"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/categories?post=237"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gerardomiranda.dev\/blog\/wp-json\/wp\/v2\/tags?post=237"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}