<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yet Run</title>
    <description>The latest articles on DEV Community by Yet Run (@yetrun).</description>
    <link>https://dev.to/yetrun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1059993%2F11df41ca-7ced-4bb8-8a0a-e1b930a42677.png</url>
      <title>DEV Community: Yet Run</title>
      <link>https://dev.to/yetrun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yetrun"/>
    <language>en</language>
    <item>
      <title>A new gem to generate Swagger Doc in Rails</title>
      <dc:creator>Yet Run</dc:creator>
      <pubDate>Thu, 06 Apr 2023 08:01:14 +0000</pubDate>
      <link>https://dev.to/yetrun/a-new-gem-to-generate-swagger-doc-in-rails-2ek7</link>
      <guid>https://dev.to/yetrun/a-new-gem-to-generate-swagger-doc-in-rails-2ek7</guid>
      <description>&lt;p&gt;In reality, there are many gems that add parameter validation to Rails, and many add Swagger document generation, but there are very few that can support parameter validation, return value rendering, and Swagger document generation at the same time, and these capabilities are tightly combined. Almost gone.&lt;/p&gt;

&lt;p&gt;Do you guys have these confusions during team development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lack of a more detailed API documentation, some do not even have documentation.&lt;/li&gt;
&lt;li&gt;Documentation and implementation are often inconsistent. It is clearly stated in the document that there is this field, but it is found that there is no such field when it is actually called, or vice versa.&lt;/li&gt;
&lt;li&gt;Documentation is difficult to implement specifications. For example, the fields of the parameter set and the fields of the return value need to be distinguished, and the fields of the return value in different scenarios should also be distinguished. Therefore, in reality, it is often only possible to throw out a large and comprehensive field table in the API document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If so, you should really take a look at my gem: &lt;a href="https://github.com/yetrun/web-frame"&gt;meta-api&lt;/a&gt;, it is born to solve these problems. We regard API documentation as a contract that both front-end and back-end developers need to abide by. Not only front-end call errors need to be reported, but back-end usage errors also need to be reported.&lt;/p&gt;

&lt;h2&gt;
  
  
  First glance
&lt;/h2&gt;

&lt;p&gt;*Reminder: *Visit the repository &lt;a href="https://github.com/yetrun/web-frame-rails-example"&gt;web-frame-rails-example&lt;/a&gt; to see an example project. You can experience it now, or come back to it at any time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;

&lt;p&gt;To use &lt;code&gt;meta-api&lt;/code&gt; with Rails, follow the steps below.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first step,&lt;/strong&gt; add in Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'meta-api'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then execute &lt;code&gt;bundle install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second step,&lt;/strong&gt; Create a file in the &lt;code&gt;config/initializers&lt;/code&gt; directory, such as &lt;code&gt;meta_rails_plugin.rb&lt;/code&gt;, and write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'meta/rails'&lt;/span&gt;

&lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3,&lt;/strong&gt; Add under &lt;code&gt;application_controller.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApplicationController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;API&lt;/span&gt;
  &lt;span class="c1"&gt;# Import macro commands&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Plugin&lt;/span&gt;

  &lt;span class="c1"&gt;# Handle parameter validation errors, the following is just an example, please write according to actual needs&lt;/span&gt;
  &lt;span class="n"&gt;rescue_from&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Errors&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ParameterInvalid&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;status: :bad_request&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the above steps are completed, you can write macro commands in Rails to define parameters and return values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write an example
&lt;/h3&gt;

&lt;p&gt;Let's write a &lt;code&gt;UsersController&lt;/code&gt; as an example of using macro commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="s1"&gt;'Create User'&lt;/span&gt;
     &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="ss"&gt;param: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;
         &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'age'&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
     &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="ss"&gt;expose: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
         &lt;span class="ss"&gt;expose: &lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'ID'&lt;/span&gt;
         &lt;span class="n"&gt;expose&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;
         &lt;span class="n"&gt;expose&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'age'&lt;/span&gt;
       &lt;span class="k"&gt;end&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;
     &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params_on_schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
     &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json_on_schema: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;status: &lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above first uses the &lt;code&gt;route&lt;/code&gt; command to define a &lt;code&gt;POST /users&lt;/code&gt; route, and provides a code block, all parameter definitions and return value definitions are in this code block. When all these definitions are in place, the actual execution will bring about two effects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;params_on_schema&lt;/code&gt;: It returns the parameters parsed according to the definition, for example, if you pass such a parameter:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"foo"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;It will help you filter out unnecessary fields and do appropriate type conversion, so that the parameters actually obtained in the application become (via &lt;code&gt;params_on_schema&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This way you can safely pass it to the &lt;code&gt;User.create!&lt;/code&gt; method without worrying about any errors.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;json_on_schema&lt;/code&gt;: The object passed through it will be parsed through the return value definition, because there are field filtering, type conversion and data verification, the backend can control which fields are returned to the front end, how to return them, and get reminder etc. Say your users table contains the following fields:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; id, name, age, password, created, updated
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;By definition, it will only return the following fields:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; id, name, age
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Generate Swagger documentation
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Everything is centered on generating Swagger documentation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you want to generate documentation, follow my steps.&lt;/p&gt;

&lt;p&gt;The first step is to figure out where to put your Swagger documents? For example, I create a new interface to return Swagger documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SwaggerController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
     &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eager_load!&lt;/span&gt; &lt;span class="c1"&gt;# All controller constants need to be loaded early&lt;/span&gt;
     &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Plugin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_swagger_doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="no"&gt;ApplicationController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="ss"&gt;info: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="ss"&gt;title: &lt;/span&gt;&lt;span class="s1"&gt;'Web API Sample Project'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="ss"&gt;version: &lt;/span&gt;&lt;span class="s1"&gt;'current'&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="ss"&gt;servers: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;url: &lt;/span&gt;&lt;span class="s1"&gt;'http://localhost:9292'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'Web API Sample Project'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second step is to configure a route for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#config/routes.rb&lt;/span&gt;

&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'/swagger_doc'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'swagger#index'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third step is to start the application to view the effect of the Swagger document, enter in the browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000/swagger_doc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Swagger documentation in JSON format comes into view.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are an old man using Swagger documentation, you should know what to do next. If you don't know, here's how to render a Swagger document:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add cross-domain support for the application:&lt;/li&gt;
&lt;/ol&gt;


&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Gemfile&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'rack-cors'&lt;/span&gt;

&lt;span class="c1"&gt;# config/initializers/cors.rb&lt;/span&gt;
&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert_before&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Cors&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="n"&gt;allow&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="n"&gt;origins&lt;/span&gt; &lt;span class="s1"&gt;'openapi.yet.run'&lt;/span&gt;
&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;headers: :any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;methods: :all&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If you are using Google Chrome browser, you need to do some extra settings to support cross domain of localhost domain name. Or you need to hang it under a normal domain name.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open &lt;a href="http://openapi.yet.run/playground"&gt;http://openapi.yet.run/playground&lt;/a&gt; and enter &lt;code&gt;http://localhost:3000/swagger_doc&lt;/code&gt; in the input box.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, all the initial test experience is completed, the front-end can obtain a well-defined document, and the back-end is also strictly implemented according to this document. Back-end developers don't need to do extra work, it just defines the parameters, defines the return value, and then, everything is complete.&lt;/p&gt;

&lt;h2&gt;
  
  
  Respond to different fields in different occasions
&lt;/h2&gt;

&lt;p&gt;This chapter provides a more adequate topic, how &lt;code&gt;meta-api&lt;/code&gt; handles field distinctions in different situations. Different occasions, such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Some fields are only used as parameters, and some fields are only used as return values.&lt;/li&gt;
&lt;li&gt;When used as a return value, some interfaces return fields &lt;code&gt;a, b, c&lt;/code&gt;, while some interfaces return fields &lt;code&gt;a, b, c, d&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Same as 2 when used as parameters, some interfaces use fields &lt;code&gt;a, b, c&lt;/code&gt;, and some interfaces use fields &lt;code&gt;a, b, c, d&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;How to handle the semantic distinction between &lt;code&gt;PUT&lt;/code&gt; and &lt;code&gt;PATCH&lt;/code&gt; requests in HTTP.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here &lt;code&gt;meta-api&lt;/code&gt; plug-in provides the concept of entity, we can put fields into entity, and then refer to it in interface. Entities are only defined once, without any repeated definitions, and most importantly, the documentation and your definitions can always be consistent. Please read on!&lt;/p&gt;

&lt;h3&gt;
  
  
  Use entities to simplify the definition of parameters and return values
&lt;/h3&gt;

&lt;p&gt;We summarize the parameters and return values ​​in the above example into one entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'ID'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;param: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'age'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then both parameter and return value definitions can refer to this entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:post&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
   &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="ss"&gt;param: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;required: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;UserEntity&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;expose&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;UserEntity&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that in the entity definition, the &lt;code&gt;id&lt;/code&gt; attribute has a &lt;code&gt;param: false&lt;/code&gt; option, which means that this field can only be used as a return value and not as a parameter. This is consistent with the previous definition.&lt;/p&gt;

&lt;p&gt;In addition to restricting a field to be used only as a return value, it can also be restricted to be used only as a parameter. Add a &lt;code&gt;password&lt;/code&gt; field as a parameter in &lt;code&gt;UserEntity&lt;/code&gt;, which can be defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="c1"&gt;# omit other fields&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;description: &lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;render: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;In short, only write the entity once, and use it as a parameter and a return value at the same time. *&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to return different fields according to different scenarios
&lt;/h3&gt;

&lt;p&gt;In the actual application of the interface, the field categories returned by different scenarios are often different, and those interface implementations that return the same fields regardless of the occasion are wrong. The different exit scenarios mentioned here, for example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The basic fields are returned on the list page, and more fields are returned on the details page.&lt;/li&gt;
&lt;li&gt;Ordinary users can only see part of the fields when viewing, and administrators can view all fields when viewing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These can be distinguished by the scope mechanism provided by &lt;code&gt;meta-api&lt;/code&gt;. I only use the list page interface as an example.&lt;/p&gt;

&lt;p&gt;Suppose the &lt;code&gt;/articles&lt;/code&gt; interface returns a list of articles, and each article only needs to return the &lt;code&gt;title&lt;/code&gt; field; the &lt;code&gt;/articles/:id&lt;/code&gt; interface needs to return article details, and each article needs to return the &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;content&lt;/code&gt; fields . Defining two entities would be cumbersome and confusing, just define one entity and use the &lt;code&gt;scope:&lt;/code&gt; option to differentiate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;
   &lt;span class="ss"&gt;property: &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'full'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this time, the list page interface and detail page interface are implemented in the following way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="ss"&gt;expose: &lt;/span&gt;&lt;span class="n"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'array'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;ArticleEntity&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
     &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
     &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json_on_schema: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'articles'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;articles&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/articles/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="ss"&gt;expose: &lt;/span&gt;&lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'object'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;ArticleEntity&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
     &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
     &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json_on_schema: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'article'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'full'&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only change from the regular implementation is this line of code used when rendering data in the &lt;code&gt;show&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json_on_schema: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'article'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'full'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It passes an option &lt;code&gt;scope: 'full'&lt;/code&gt;, which tells the entity to also render the field marked as &lt;code&gt;full&lt;/code&gt; by &lt;code&gt;scope&lt;/code&gt; (that is, the &lt;code&gt;content&lt;/code&gt; field).&lt;/p&gt;

&lt;p&gt;In addition to passing specific options when calling, there is another trick here, using the &lt;strong&gt;locking&lt;/strong&gt; technique provided by &lt;code&gt;meta-api&lt;/code&gt; to lock the referenced entity on specific options.&lt;/p&gt;

&lt;p&gt;Let's modify the definition and implementation of the &lt;code&gt;show&lt;/code&gt; method in the above example, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticlesController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/articles/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:get&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;expose&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'object'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;ArticleEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'full'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show&lt;/span&gt;
     &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
     &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json_on_schema: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'article'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;article&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two changes here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When referencing an entity, we do &lt;code&gt;ArticleEntity.locked(scope: 'full')&lt;/code&gt; on the entity, which returns the new locked entity.&lt;/li&gt;
&lt;li&gt;When rendering data, the option &lt;code&gt;scope: 'full'&lt;/code&gt; does not need to be passed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't underestimate this small change. When we lock the entity when defining it, we actually define the scope of the entity at the interface design stage. My point is that design takes precedence over implementation, so I prefer the second option. In addition, locking will also affect the generation of documents. Entities in documents will only render the fields that are locked &lt;code&gt;scope&lt;/code&gt;, and will not generate other fields that will not be returned. This is more friendly to the front end when viewing documents.&lt;/p&gt;

&lt;p&gt;We add a new unused field inside &lt;code&gt;ArticleEntity&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="ss"&gt;:title&lt;/span&gt;
   &lt;span class="ss"&gt;property: &lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'full'&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'foo'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;ArticleEntity.locked(scope: 'full')&lt;/code&gt; will not only return the fields &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt; when it is implemented, but also only the fields &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;content&lt;/code&gt; will appear when the document is rendered.&lt;/p&gt;

&lt;p&gt;**The idea of this section is: only need to write the entity once, and use it in different occasions. **This idea is also the idea of the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock on parameters: distinguish different occasions on parameters
&lt;/h3&gt;

&lt;p&gt;Like the return value, parameters also need to modify different fields according to different occasions. We define a parameter entity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExampleEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;
   &lt;span class="ss"&gt;property: &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;
   &lt;span class="ss"&gt;property: &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'master'&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is set with two scopes, and the fields that can be modified are different in the two occasions. When we implement, we can use locking technology when defining parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Admin::UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/admin/users'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:put&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;ExampleEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'admin'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:put&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;ExampleEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;scope: &lt;/span&gt;&lt;span class="s1"&gt;'master'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;example&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is both responsive in implementation and documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock on parameters: handle missing parameters
&lt;/h3&gt;

&lt;p&gt;First declare an HTTP-related background. For missing parameters, HTTP provides two semantic methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;PUT&lt;/code&gt; request needs to provide a complete entity, including all fields. If a field is missing, it will be treated as if the field passed a &lt;code&gt;nil&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;PATCH&lt;/code&gt; request only provides the fields that need to be updated. If a field is missing, it will indicate that this field does not need to be updated.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What is involved here is how to deal with missing values in parameters. In other words, for entities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;
   &lt;span class="ss"&gt;property: &lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user request only passes &lt;code&gt;{ "name": "Jim" }&lt;/code&gt;, what will call &lt;code&gt;params_on_schema&lt;/code&gt; return? yes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;still&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jim"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woolen cloth?&lt;/p&gt;

&lt;p&gt;The difference between the two is that the former sets missing values to &lt;code&gt;nil&lt;/code&gt;, while the latter discards missing values. Note that these two kinds of data are passed as parameters to the &lt;code&gt;user.update!&lt;/code&gt; method, and their effects are different.&lt;/p&gt;

&lt;p&gt;The way to control this effect is also by locking setting the &lt;code&gt;discard_missing:&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UsersController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
   &lt;span class="c1"&gt;# PUT effect, by default, the parameter always returns the complete entity&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/users/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:put&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ss"&gt;:user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;ref: &lt;/span&gt;&lt;span class="no"&gt;UserEntity&lt;/span&gt; &lt;span class="c1"&gt;# or UserEntity.locked(discard_missing: false), equivalent&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;replace&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;

   &lt;span class="c1"&gt;# PATCH effect, parameters discard unpassed fields&lt;/span&gt;
   &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/users/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:patch&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
     &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
       &lt;span class="n"&gt;param&lt;/span&gt; &lt;span class="ss"&gt;:ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;UserEntity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nf"&gt;locked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;discard_missing: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
   &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;
   &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;Summary: You only need to write the entity once, and use it on different occasions. *&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dive into details
&lt;/h2&gt;

&lt;p&gt;Finally, let's talk about some details about &lt;code&gt;meta-api&lt;/code&gt;. It has basic parameter verification, default value and conversion logic, for detailed content, please refer to &lt;a href="https://github.com/yetrun/web-frame/blob/master/docs/%E6%95%99%%20E7%A8%8B.md"&gt;tutorial&lt;/a&gt; relevant part. If there is an unfriendly part in the document, you are welcome to submit an ISSUE improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defaults
&lt;/h3&gt;

&lt;p&gt;You can provide default values for parameters (or fields within entities), like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  data verification
&lt;/h3&gt;

&lt;p&gt;There are some built-in data validators such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:birth_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="sr"&gt;/\d\d\d\d-\d\d-\d\d/&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or custom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;birthday&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="ss"&gt;validate: &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;JsonSchema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'The date format is incorrect'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=~&lt;/span&gt; &lt;span class="sr"&gt;/\d\d\d\d-\d\d-\d\d/&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  enumeration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserEntity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Entity&lt;/span&gt;
   &lt;span class="n"&gt;property&lt;/span&gt; &lt;span class="ss"&gt;:age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s1"&gt;'integer'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;allowable: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# only allow children under 6 years old&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Polymorphism
&lt;/h3&gt;

&lt;p&gt;Really, it has the ability to handle polymorphism, as provided in the Swagger documentation. I don't want to explain it here, but please believe that it really has the ability to handle polymorphism.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final remarks
&lt;/h2&gt;

&lt;p&gt;Say nothing, my project address is currently at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="http://github.com/yetrun/web-frame"&gt;http://github.com/yetrun/web-frame&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All comments and suggestions are useful to me.&lt;/p&gt;

&lt;p&gt;If you encounter bugs, or want new features, please file &lt;a href="https://github.com/yetrun/web-frame/issues"&gt;ISSUE&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If this project is useful to you, please give it a star, it is much appreciated.&lt;/p&gt;

&lt;p&gt;If you are interested in contributing to this project, a PR is welcome.&lt;/p&gt;

&lt;p&gt;My hope is that plugins for Rails will be provided in a way that doesn't interfere with your existing projects and will help with documentation.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>swagger</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
