sp-duh 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/LICENSE +661 -0
 - data/README.md +2 -0
 - data/Rakefile +32 -0
 - data/config/i18n/i18n.xlsx +0 -0
 - data/config/initializers/active_record/connection_adapters_postgre_sql_adapter.rb +165 -0
 - data/config/initializers/active_record/migration_without_transaction.rb +4 -0
 - data/config/initializers/active_record/migrator.rb +34 -0
 - data/config/initializers/rails/generators.rb +13 -0
 - data/config/jsonapi/settings.yml +14 -0
 - data/config/locales/pt.yml +15 -0
 - data/lib/generators/accounting_migration/accounting_migration_generator.rb +10 -0
 - data/lib/generators/accounting_migration/templates/migration.rb +42 -0
 - data/lib/generators/accounting_payroll_migration/accounting_payroll_migration_generator.rb +10 -0
 - data/lib/generators/accounting_payroll_migration/templates/migration.rb +73 -0
 - data/lib/generators/sharded_migration/sharded_migration_generator.rb +10 -0
 - data/lib/generators/sharded_migration/templates/migration.rb +45 -0
 - data/lib/sp-duh.rb +32 -0
 - data/lib/sp/duh.rb +180 -0
 - data/lib/sp/duh/adapters/pg/text_decoder/json.rb +15 -0
 - data/lib/sp/duh/adapters/pg/text_encoder/json.rb +15 -0
 - data/lib/sp/duh/db/transfer/backup.rb +71 -0
 - data/lib/sp/duh/db/transfer/restore.rb +89 -0
 - data/lib/sp/duh/engine.rb +35 -0
 - data/lib/sp/duh/exceptions.rb +70 -0
 - data/lib/sp/duh/i18n/excel_loader.rb +26 -0
 - data/lib/sp/duh/jsonapi/adapters/base.rb +168 -0
 - data/lib/sp/duh/jsonapi/adapters/db.rb +36 -0
 - data/lib/sp/duh/jsonapi/adapters/raw_db.rb +77 -0
 - data/lib/sp/duh/jsonapi/configuration.rb +167 -0
 - data/lib/sp/duh/jsonapi/doc/apidoc_documentation_format_generator.rb +286 -0
 - data/lib/sp/duh/jsonapi/doc/generator.rb +32 -0
 - data/lib/sp/duh/jsonapi/doc/schema_catalog_helper.rb +97 -0
 - data/lib/sp/duh/jsonapi/doc/victor_pinus_metadata_format_parser.rb +374 -0
 - data/lib/sp/duh/jsonapi/exceptions.rb +56 -0
 - data/lib/sp/duh/jsonapi/model/base.rb +25 -0
 - data/lib/sp/duh/jsonapi/model/concerns/attributes.rb +94 -0
 - data/lib/sp/duh/jsonapi/model/concerns/model.rb +42 -0
 - data/lib/sp/duh/jsonapi/model/concerns/persistence.rb +221 -0
 - data/lib/sp/duh/jsonapi/model/concerns/serialization.rb +59 -0
 - data/lib/sp/duh/jsonapi/parameters.rb +44 -0
 - data/lib/sp/duh/jsonapi/resource_publisher.rb +28 -0
 - data/lib/sp/duh/jsonapi/service.rb +110 -0
 - data/lib/sp/duh/migrations.rb +47 -0
 - data/lib/sp/duh/migrations/migrator.rb +41 -0
 - data/lib/sp/duh/repl.rb +193 -0
 - data/lib/sp/duh/version.rb +25 -0
 - data/lib/tasks/db_utils.rake +98 -0
 - data/lib/tasks/doc.rake +27 -0
 - data/lib/tasks/i18n.rake +23 -0
 - data/lib/tasks/oauth.rake +29 -0
 - data/lib/tasks/transfer.rake +48 -0
 - data/lib/tasks/xls2jrxml.rake +15 -0
 - data/test/jsonapi/server.rb +67 -0
 - data/test/tasks/test.rake +10 -0
 - metadata +170 -0
 
| 
         @@ -0,0 +1,286 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SP
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Duh
         
     | 
| 
      
 3 
     | 
    
         
            +
                module JSONAPI
         
     | 
| 
      
 4 
     | 
    
         
            +
                  module Doc
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                    class ApidocDocumentationFormatGenerator
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                      def generate(resource_parser, version, doc_folder_path)
         
     | 
| 
      
 9 
     | 
    
         
            +
                        @version = version
         
     | 
| 
      
 10 
     | 
    
         
            +
                        # First create an index of all resources (for relationships linking)
         
     | 
| 
      
 11 
     | 
    
         
            +
                        @resources = {}
         
     | 
| 
      
 12 
     | 
    
         
            +
                        resource_parser.each do |resource|
         
     | 
| 
      
 13 
     | 
    
         
            +
                          next if get_resource_data(resource, :scope) != :public
         
     | 
| 
      
 14 
     | 
    
         
            +
                          @resources[get_resource_name(resource)] = get_resource_link(resource)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        end
         
     | 
| 
      
 16 
     | 
    
         
            +
                        _log "Generating Apidoc documentation for version #{version} in folder #{doc_folder_path}", "JSONAPI::Doc::Generator"
         
     | 
| 
      
 17 
     | 
    
         
            +
                        resource_parser.each do |resource|
         
     | 
| 
      
 18 
     | 
    
         
            +
                          next if get_resource_data(resource, :scope) != :public
         
     | 
| 
      
 19 
     | 
    
         
            +
                          generate_documentation(resource, doc_folder_path)
         
     | 
| 
      
 20 
     | 
    
         
            +
                        end
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      private
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                        def generate_documentation(resource, folder_path)
         
     | 
| 
      
 26 
     | 
    
         
            +
                          readonly = (get_resource_data(resource, :readonly) == true)
         
     | 
| 
      
 27 
     | 
    
         
            +
                          _log "   Generating documentation for resource #{resource}", "JSONAPI::Doc::Generator"
         
     | 
| 
      
 28 
     | 
    
         
            +
                          File.open(File.join(folder_path, "#{get_resource_name(resource)}.js"), File::CREAT | File::TRUNC | File::RDWR) do |f|
         
     | 
| 
      
 29 
     | 
    
         
            +
                            wrap_in_comments(f) { append_lines(f, get_post_documentation(resource)) } if !readonly
         
     | 
| 
      
 30 
     | 
    
         
            +
                            wrap_in_comments(f) { append_lines(f, get_get_documentation(resource)) }
         
     | 
| 
      
 31 
     | 
    
         
            +
                            wrap_in_comments(f) { append_lines(f, get_patch_documentation(resource)) } if !readonly
         
     | 
| 
      
 32 
     | 
    
         
            +
                            wrap_in_comments(f) { append_lines(f, get_delete_documentation(resource)) } if !readonly
         
     | 
| 
      
 33 
     | 
    
         
            +
                            wrap_in_comments(f) { append_lines(f, get_get_documentation(resource, false)) }
         
     | 
| 
      
 34 
     | 
    
         
            +
                          end
         
     | 
| 
      
 35 
     | 
    
         
            +
                        end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                        def get_resource_link(resource)
         
     | 
| 
      
 38 
     | 
    
         
            +
                          group = get_resource_data(resource, :group).gsub(' ', '_')
         
     | 
| 
      
 39 
     | 
    
         
            +
                          get_action_name = "#{('Get_' + get_resource_name(resource)).camelcase}"
         
     | 
| 
      
 40 
     | 
    
         
            +
                          link = '#api-' + "#{group}-#{get_action_name}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                          link
         
     | 
| 
      
 42 
     | 
    
         
            +
                        end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                        def wrap_in_comments(f) ; write_header(f) ; yield ; write_footer(f) ; end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                        def get_get_documentation(resource, single = true)
         
     | 
| 
      
 47 
     | 
    
         
            +
                          documentation = get_api_method_header(resource, :get, single) + get_api_method_params(resource, :get, single) + get_attribute_list(resource, single) + get_api_method_success_example(resource, :get, single)
         
     | 
| 
      
 48 
     | 
    
         
            +
                          documentation.compact!
         
     | 
| 
      
 49 
     | 
    
         
            +
                          documentation
         
     | 
| 
      
 50 
     | 
    
         
            +
                        end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                        def get_post_documentation(resource)
         
     | 
| 
      
 53 
     | 
    
         
            +
                          documentation = get_api_method_header(resource, :post) + get_api_method_params(resource, :post) + get_attribute_list(resource) + get_api_method_success_example(resource, :post)
         
     | 
| 
      
 54 
     | 
    
         
            +
                          documentation.compact!
         
     | 
| 
      
 55 
     | 
    
         
            +
                          documentation
         
     | 
| 
      
 56 
     | 
    
         
            +
                        end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                        def get_patch_documentation(resource)
         
     | 
| 
      
 59 
     | 
    
         
            +
                          documentation = get_api_method_header(resource, :patch) + get_api_method_params(resource, :patch) + get_attribute_list(resource) + get_api_method_success_example(resource, :patch)
         
     | 
| 
      
 60 
     | 
    
         
            +
                          documentation.compact!
         
     | 
| 
      
 61 
     | 
    
         
            +
                          documentation
         
     | 
| 
      
 62 
     | 
    
         
            +
                        end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                        def get_delete_documentation(resource)
         
     | 
| 
      
 65 
     | 
    
         
            +
                          documentation = get_api_method_header(resource, :delete) + get_api_method_params(resource, :delete) + get_api_method_success_example(resource, :delete)
         
     | 
| 
      
 66 
     | 
    
         
            +
                          documentation.compact!
         
     | 
| 
      
 67 
     | 
    
         
            +
                          documentation
         
     | 
| 
      
 68 
     | 
    
         
            +
                        end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                        def get_api_method_header(resource, method, single = true)
         
     | 
| 
      
 71 
     | 
    
         
            +
                          data = []
         
     | 
| 
      
 72 
     | 
    
         
            +
                          resource_name = get_resource_name(resource)
         
     | 
| 
      
 73 
     | 
    
         
            +
                          resource_description = get_resource_data(resource, :description)
         
     | 
| 
      
 74 
     | 
    
         
            +
                          resource_description = [ resource_name.titlecase ] if resource_description.blank?
         
     | 
| 
      
 75 
     | 
    
         
            +
                          method_title = "#{get_human_method(method).capitalize} #{uncapitalize(resource_description.first)} #{(single ? '' : 'list')}"
         
     | 
| 
      
 76 
     | 
    
         
            +
                          case
         
     | 
| 
      
 77 
     | 
    
         
            +
                            when method.to_sym.in?([ :patch, :delete ]) || (method.to_sym == :get && single)
         
     | 
| 
      
 78 
     | 
    
         
            +
                              url = "/#{resource_name}/:id"
         
     | 
| 
      
 79 
     | 
    
         
            +
                            else
         
     | 
| 
      
 80 
     | 
    
         
            +
                              url = "/#{resource_name}"
         
     | 
| 
      
 81 
     | 
    
         
            +
                          end
         
     | 
| 
      
 82 
     | 
    
         
            +
                          data << "@api {#{method}} #{url} #{method_title}"
         
     | 
| 
      
 83 
     | 
    
         
            +
                          data << "@apiVersion #{@version}"
         
     | 
| 
      
 84 
     | 
    
         
            +
                          data << "@apiName #{(method.to_s + '_' + resource_name + (single ? '' : '_list')).camelcase}"
         
     | 
| 
      
 85 
     | 
    
         
            +
                          data << "@apiGroup #{get_resource_data(resource, :group)}"
         
     | 
| 
      
 86 
     | 
    
         
            +
                          data << "@apiDescription #{method_title}"
         
     | 
| 
      
 87 
     | 
    
         
            +
                          resource_description.each_with_index do |d, i|
         
     | 
| 
      
 88 
     | 
    
         
            +
                            next if i == 0
         
     | 
| 
      
 89 
     | 
    
         
            +
                            data << d
         
     | 
| 
      
 90 
     | 
    
         
            +
                          end
         
     | 
| 
      
 91 
     | 
    
         
            +
                          data
         
     | 
| 
      
 92 
     | 
    
         
            +
                        end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                        def get_attribute_type_name_and_description(a)
         
     | 
| 
      
 95 
     | 
    
         
            +
                          data = ""
         
     | 
| 
      
 96 
     | 
    
         
            +
                          type = nil
         
     | 
| 
      
 97 
     | 
    
         
            +
                          if !a[:catalog].nil?
         
     | 
| 
      
 98 
     | 
    
         
            +
                            type = get_type(a[:catalog])
         
     | 
| 
      
 99 
     | 
    
         
            +
                            data += "{#{type}} "
         
     | 
| 
      
 100 
     | 
    
         
            +
                          end
         
     | 
| 
      
 101 
     | 
    
         
            +
                          description = (a[:description] || []).join('. ')
         
     | 
| 
      
 102 
     | 
    
         
            +
                          if a[:association]
         
     | 
| 
      
 103 
     | 
    
         
            +
                            if type && @resources[type.gsub('[]','')]
         
     | 
| 
      
 104 
     | 
    
         
            +
                              description = '<a href="' + @resources[type.gsub('[]','')] + '">' + description + '</a>'
         
     | 
| 
      
 105 
     | 
    
         
            +
                            end
         
     | 
| 
      
 106 
     | 
    
         
            +
                          end
         
     | 
| 
      
 107 
     | 
    
         
            +
                          data += "#{a[:name]} #{description}"
         
     | 
| 
      
 108 
     | 
    
         
            +
                          data
         
     | 
| 
      
 109 
     | 
    
         
            +
                        end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                        def get_api_method_params(resource, method, single = true)
         
     | 
| 
      
 112 
     | 
    
         
            +
                          a = get_resource_data(resource, :id)
         
     | 
| 
      
 113 
     | 
    
         
            +
                          id_param = (a.nil? ? -1 : a.to_i)
         
     | 
| 
      
 114 
     | 
    
         
            +
                          params = []
         
     | 
| 
      
 115 
     | 
    
         
            +
                          case
         
     | 
| 
      
 116 
     | 
    
         
            +
                            when method.to_sym.in?([ :post, :patch, :delete ]) || (method.to_sym == :get && single)
         
     | 
| 
      
 117 
     | 
    
         
            +
                              if resource[:attributes]
         
     | 
| 
      
 118 
     | 
    
         
            +
                                resource[:attributes].each_with_index do |a, i|
         
     | 
| 
      
 119 
     | 
    
         
            +
                                  next if a[:readonly] == true
         
     | 
| 
      
 120 
     | 
    
         
            +
                                  next if i == id_param && method.to_sym == :post
         
     | 
| 
      
 121 
     | 
    
         
            +
                                  data = "@apiParam "
         
     | 
| 
      
 122 
     | 
    
         
            +
                                  params << "@apiParam " + get_attribute_type_name_and_description(a)
         
     | 
| 
      
 123 
     | 
    
         
            +
                                  break if i == id_param && method.to_sym.in?([ :get, :delete ])
         
     | 
| 
      
 124 
     | 
    
         
            +
                                end
         
     | 
| 
      
 125 
     | 
    
         
            +
                              end
         
     | 
| 
      
 126 
     | 
    
         
            +
                          end
         
     | 
| 
      
 127 
     | 
    
         
            +
                          params = params + get_api_method_param_example(resource, method) if method.to_sym.in?([ :post, :patch ])
         
     | 
| 
      
 128 
     | 
    
         
            +
                          params
         
     | 
| 
      
 129 
     | 
    
         
            +
                        end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                        def get_attribute_list(resource, single = true)
         
     | 
| 
      
 132 
     | 
    
         
            +
                          if resource[:attributes]
         
     | 
| 
      
 133 
     | 
    
         
            +
                            resource[:attributes].map do |a|
         
     | 
| 
      
 134 
     | 
    
         
            +
                              "@apiSuccess " + get_attribute_type_name_and_description(a)
         
     | 
| 
      
 135 
     | 
    
         
            +
                            end
         
     | 
| 
      
 136 
     | 
    
         
            +
                          else
         
     | 
| 
      
 137 
     | 
    
         
            +
                            []
         
     | 
| 
      
 138 
     | 
    
         
            +
                          end
         
     | 
| 
      
 139 
     | 
    
         
            +
                        end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                        def get_api_method_param_example(resource, method)
         
     | 
| 
      
 142 
     | 
    
         
            +
                          data = [ "@apiParamExample {json} Request body example", "" ]
         
     | 
| 
      
 143 
     | 
    
         
            +
                          data = data + get_api_method_json(resource, method)
         
     | 
| 
      
 144 
     | 
    
         
            +
                          data
         
     | 
| 
      
 145 
     | 
    
         
            +
                        end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
                        def get_api_method_success_example(resource, method, single = true)
         
     | 
| 
      
 148 
     | 
    
         
            +
                          data = [ "@apiSuccessExample {json} Success response example", "HTTP/1.1 200 OK", "" ]
         
     | 
| 
      
 149 
     | 
    
         
            +
                          case
         
     | 
| 
      
 150 
     | 
    
         
            +
                            when method.to_sym.in?([ :get, :patch, :post ])
         
     | 
| 
      
 151 
     | 
    
         
            +
                              data = data + get_api_method_json(resource, :get, single)
         
     | 
| 
      
 152 
     | 
    
         
            +
                          end
         
     | 
| 
      
 153 
     | 
    
         
            +
                          data
         
     | 
| 
      
 154 
     | 
    
         
            +
                        end
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
                        def uncapitalize(text)
         
     | 
| 
      
 157 
     | 
    
         
            +
                          words = text.split(' ')
         
     | 
| 
      
 158 
     | 
    
         
            +
                          words[0] = words[0].downcase
         
     | 
| 
      
 159 
     | 
    
         
            +
                          words.join(' ')
         
     | 
| 
      
 160 
     | 
    
         
            +
                        end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                        def get_human_method(m) ; m.to_sym == :post ? :create : (m.to_sym == :patch ? :update : m.to_sym) ; end
         
     | 
| 
      
 163 
     | 
    
         
            +
                        def get_resource_name(r) ; get_resource_data(r, :name) ; end
         
     | 
| 
      
 164 
     | 
    
         
            +
                        def get_resource_data(r, name) ; r[:resource][name] ; end
         
     | 
| 
      
 165 
     | 
    
         
            +
                        def get_attribute(r, i) ; r[:attributes][i] if r[:attributes] ; end
         
     | 
| 
      
 166 
     | 
    
         
            +
                        def get_attribute_data(r, i, name) ; get_attribute(r, i)[name] ; end
         
     | 
| 
      
 167 
     | 
    
         
            +
                        def get_type(r) ; r['format_type'].gsub(/character varying\(\d+\)/, 'text').gsub(/character/, 'text').gsub(' without time zone', '').gsub(' with time zone', '') ; end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                        def get_api_method_json(resource, method, single = true)
         
     | 
| 
      
 170 
     | 
    
         
            +
                          json = [ '{' ]
         
     | 
| 
      
 171 
     | 
    
         
            +
                          case
         
     | 
| 
      
 172 
     | 
    
         
            +
                            when method.to_sym.in?([ :post, :patch ]) || (method.to_sym == :get && single)
         
     | 
| 
      
 173 
     | 
    
         
            +
                              json << '  "data": {'
         
     | 
| 
      
 174 
     | 
    
         
            +
                              json = json + get_resource_json(resource, method.to_sym != :post, !method.to_sym.in?([ :post, :patch ]))
         
     | 
| 
      
 175 
     | 
    
         
            +
                              json << '  }'
         
     | 
| 
      
 176 
     | 
    
         
            +
                            when method.to_sym == :get && !single
         
     | 
| 
      
 177 
     | 
    
         
            +
                              json << '  "data": [{'
         
     | 
| 
      
 178 
     | 
    
         
            +
                              json = json + get_resource_json(resource)
         
     | 
| 
      
 179 
     | 
    
         
            +
                              json << '  }]'
         
     | 
| 
      
 180 
     | 
    
         
            +
                          end
         
     | 
| 
      
 181 
     | 
    
         
            +
                          json << '}'
         
     | 
| 
      
 182 
     | 
    
         
            +
                          json
         
     | 
| 
      
 183 
     | 
    
         
            +
                        end
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                        def get_resource_json(resource, include_id = true, include_readonly = true)
         
     | 
| 
      
 186 
     | 
    
         
            +
                          json = []
         
     | 
| 
      
 187 
     | 
    
         
            +
                          json << '    "type": "' + get_resource_name(resource) + '",'
         
     | 
| 
      
 188 
     | 
    
         
            +
                          id_index = get_resource_data(resource, :id).to_i
         
     | 
| 
      
 189 
     | 
    
         
            +
                          json << '    "id": "' + get_default_value_for_attribute(resource, id_index) + '",' if include_id
         
     | 
| 
      
 190 
     | 
    
         
            +
                          json << '    "attributes": {'
         
     | 
| 
      
 191 
     | 
    
         
            +
                          resource[:attributes].each_with_index do |a, i|
         
     | 
| 
      
 192 
     | 
    
         
            +
                            next if i == id_index
         
     | 
| 
      
 193 
     | 
    
         
            +
                            next if a[:readonly] == true && !include_readonly
         
     | 
| 
      
 194 
     | 
    
         
            +
                            next if a[:association]
         
     | 
| 
      
 195 
     | 
    
         
            +
                            json << '      "' + a[:name].to_s + '": ' + get_default_value_for_attribute(resource, i) + ','
         
     | 
| 
      
 196 
     | 
    
         
            +
                          end
         
     | 
| 
      
 197 
     | 
    
         
            +
                          delete_comma(json)
         
     | 
| 
      
 198 
     | 
    
         
            +
                          json << '    }'
         
     | 
| 
      
 199 
     | 
    
         
            +
                          has_associations = false
         
     | 
| 
      
 200 
     | 
    
         
            +
                          resource[:attributes].each_with_index do |a, i|
         
     | 
| 
      
 201 
     | 
    
         
            +
                            next if !a[:association]
         
     | 
| 
      
 202 
     | 
    
         
            +
                            next if a[:readonly] == true && !include_readonly
         
     | 
| 
      
 203 
     | 
    
         
            +
                            if !has_associations
         
     | 
| 
      
 204 
     | 
    
         
            +
                              add_comma(json)
         
     | 
| 
      
 205 
     | 
    
         
            +
                              json << '    "relationships": {'
         
     | 
| 
      
 206 
     | 
    
         
            +
                              has_associations = true
         
     | 
| 
      
 207 
     | 
    
         
            +
                            end
         
     | 
| 
      
 208 
     | 
    
         
            +
                            json = json + get_association_json(resource, i, a)
         
     | 
| 
      
 209 
     | 
    
         
            +
                          end
         
     | 
| 
      
 210 
     | 
    
         
            +
                          if has_associations
         
     | 
| 
      
 211 
     | 
    
         
            +
                            delete_comma(json)
         
     | 
| 
      
 212 
     | 
    
         
            +
                            json << '    }'
         
     | 
| 
      
 213 
     | 
    
         
            +
                          end
         
     | 
| 
      
 214 
     | 
    
         
            +
                          json
         
     | 
| 
      
 215 
     | 
    
         
            +
                        end
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                        def add_comma(lines) ; lines[lines.length-1] = lines[lines.length-1] + ',' ; end
         
     | 
| 
      
 218 
     | 
    
         
            +
                        def delete_comma(lines) ; lines[lines.length-1] = lines[lines.length-1][0..lines[lines.length-1].length-2] ; end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                        def get_association_json(resource, i, attribute)
         
     | 
| 
      
 221 
     | 
    
         
            +
                          json = []
         
     | 
| 
      
 222 
     | 
    
         
            +
                          json << '      "' + attribute[:name] + '": {'
         
     | 
| 
      
 223 
     | 
    
         
            +
                          if attribute[:association] == :'to-many'
         
     | 
| 
      
 224 
     | 
    
         
            +
                            json << '        "data": ['
         
     | 
| 
      
 225 
     | 
    
         
            +
                            json << '          {'
         
     | 
| 
      
 226 
     | 
    
         
            +
                            json << '            "type": "' + get_type(attribute[:catalog]).sub('[]', '') + '",'
         
     | 
| 
      
 227 
     | 
    
         
            +
                            json << '            "id": ' + get_default_value_for_attribute(resource, i)
         
     | 
| 
      
 228 
     | 
    
         
            +
                            json << '          },'
         
     | 
| 
      
 229 
     | 
    
         
            +
                            json << '          {'
         
     | 
| 
      
 230 
     | 
    
         
            +
                            json << '            "type": "' + get_type(attribute[:catalog]).sub('[]', '') + '",'
         
     | 
| 
      
 231 
     | 
    
         
            +
                            json << '            "id": ' + (get_default_value_for_attribute(resource, i).to_i + 1).to_s
         
     | 
| 
      
 232 
     | 
    
         
            +
                            json << '          }'
         
     | 
| 
      
 233 
     | 
    
         
            +
                            json << '        ],'
         
     | 
| 
      
 234 
     | 
    
         
            +
                          else
         
     | 
| 
      
 235 
     | 
    
         
            +
                            json << '        "data": {'
         
     | 
| 
      
 236 
     | 
    
         
            +
                            json << '          "type": "' + get_type(attribute[:catalog]) + '",'
         
     | 
| 
      
 237 
     | 
    
         
            +
                            json << '          "id": ' + get_default_value_for_attribute(resource, i)
         
     | 
| 
      
 238 
     | 
    
         
            +
                            json << '        },'
         
     | 
| 
      
 239 
     | 
    
         
            +
                          end
         
     | 
| 
      
 240 
     | 
    
         
            +
                          delete_comma(json)
         
     | 
| 
      
 241 
     | 
    
         
            +
                          json << '      },'
         
     | 
| 
      
 242 
     | 
    
         
            +
                          json
         
     | 
| 
      
 243 
     | 
    
         
            +
                        end
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                        def get_default_value_for_attribute(r, i)
         
     | 
| 
      
 246 
     | 
    
         
            +
                          a = get_attribute(r, i)
         
     | 
| 
      
 247 
     | 
    
         
            +
                          if a.nil?
         
     | 
| 
      
 248 
     | 
    
         
            +
                            'null'
         
     | 
| 
      
 249 
     | 
    
         
            +
                          else
         
     | 
| 
      
 250 
     | 
    
         
            +
                            if a[:name].to_sym == :id || a[:association]
         
     | 
| 
      
 251 
     | 
    
         
            +
                              return "1"
         
     | 
| 
      
 252 
     | 
    
         
            +
                            else
         
     | 
| 
      
 253 
     | 
    
         
            +
                              default = a[:example]
         
     | 
| 
      
 254 
     | 
    
         
            +
                              if !default.nil? && !a[:catalog].nil?
         
     | 
| 
      
 255 
     | 
    
         
            +
                                default = default.gsub('"','').gsub("'", '')
         
     | 
| 
      
 256 
     | 
    
         
            +
                                type = get_type(a[:catalog])
         
     | 
| 
      
 257 
     | 
    
         
            +
                                case type
         
     | 
| 
      
 258 
     | 
    
         
            +
                                when 'boolean'
         
     | 
| 
      
 259 
     | 
    
         
            +
                                  default = default
         
     | 
| 
      
 260 
     | 
    
         
            +
                                when 'integer', 'bigint'
         
     | 
| 
      
 261 
     | 
    
         
            +
                                  default = default
         
     | 
| 
      
 262 
     | 
    
         
            +
                                when 'numeric', 'decimal', 'float'
         
     | 
| 
      
 263 
     | 
    
         
            +
                                  default = default
         
     | 
| 
      
 264 
     | 
    
         
            +
                                else
         
     | 
| 
      
 265 
     | 
    
         
            +
                                  default = '"' + default + '"'
         
     | 
| 
      
 266 
     | 
    
         
            +
                                end
         
     | 
| 
      
 267 
     | 
    
         
            +
                              end
         
     | 
| 
      
 268 
     | 
    
         
            +
                              if default.nil?
         
     | 
| 
      
 269 
     | 
    
         
            +
                                default = 'null'
         
     | 
| 
      
 270 
     | 
    
         
            +
                              end
         
     | 
| 
      
 271 
     | 
    
         
            +
                              default
         
     | 
| 
      
 272 
     | 
    
         
            +
                            end
         
     | 
| 
      
 273 
     | 
    
         
            +
                          end
         
     | 
| 
      
 274 
     | 
    
         
            +
                        end
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
                        def write_header(f) ; f.puts '/**' ; end
         
     | 
| 
      
 277 
     | 
    
         
            +
                        def append_lines(f, lines) ; lines.each { |l| append_line(f, l) } ; end
         
     | 
| 
      
 278 
     | 
    
         
            +
                        def append_line(f, line = nil) ; f.puts ' * ' + line.to_s ; end
         
     | 
| 
      
 279 
     | 
    
         
            +
                        def write_footer(f) ; f.puts '*/' ; end
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                    end
         
     | 
| 
      
 282 
     | 
    
         
            +
             
     | 
| 
      
 283 
     | 
    
         
            +
                  end
         
     | 
| 
      
 284 
     | 
    
         
            +
                end
         
     | 
| 
      
 285 
     | 
    
         
            +
              end
         
     | 
| 
      
 286 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'sp/duh/jsonapi/doc/victor_pinus_metadata_format_parser'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'sp/duh/jsonapi/doc/apidoc_documentation_format_generator'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module SP
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Duh
         
     | 
| 
      
 6 
     | 
    
         
            +
                module JSONAPI
         
     | 
| 
      
 7 
     | 
    
         
            +
                  module Doc
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    class Generator
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                      def initialize(pg_connection)
         
     | 
| 
      
 12 
     | 
    
         
            +
                        @pg_connection = pg_connection
         
     | 
| 
      
 13 
     | 
    
         
            +
                      end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                      def generate(resource_publisher, version, doc_folder_path = File.join(Dir.pwd, 'apidoc'))
         
     | 
| 
      
 16 
     | 
    
         
            +
                        # Load the JSONAPI resources from the given publishers
         
     | 
| 
      
 17 
     | 
    
         
            +
                        @parser = SP::Duh::JSONAPI::Doc::VictorPinusMetadataFormatParser.new(@pg_connection)
         
     | 
| 
      
 18 
     | 
    
         
            +
                        @parser.parse(resource_publisher)
         
     | 
| 
      
 19 
     | 
    
         
            +
                        # Generate the resources documentation
         
     | 
| 
      
 20 
     | 
    
         
            +
                        @generator = SP::Duh::JSONAPI::Doc::ApidocDocumentationFormatGenerator.new
         
     | 
| 
      
 21 
     | 
    
         
            +
                        @generator.generate(@parser, version, doc_folder_path)
         
     | 
| 
      
 22 
     | 
    
         
            +
                        # Regenerate the documentation site
         
     | 
| 
      
 23 
     | 
    
         
            +
                        _log "Generating the documentation site in #{doc_folder_path}", "JSONAPI::Doc::Generator"
         
     | 
| 
      
 24 
     | 
    
         
            +
                        `(cd #{doc_folder_path} && apidoc)`
         
     | 
| 
      
 25 
     | 
    
         
            +
                      end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module SP
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Duh
         
     | 
| 
      
 3 
     | 
    
         
            +
                module JSONAPI
         
     | 
| 
      
 4 
     | 
    
         
            +
                  module Doc
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                    class SchemaCatalogHelper
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                      def initialize(pg_connection)
         
     | 
| 
      
 9 
     | 
    
         
            +
                        @pg_connection = pg_connection
         
     | 
| 
      
 10 
     | 
    
         
            +
                        @default_schema = get_db_first_sharded_schema()
         
     | 
| 
      
 11 
     | 
    
         
            +
                        clear_settings
         
     | 
| 
      
 12 
     | 
    
         
            +
                      end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                      def clear_settings ; @settings = {} ; @attrs = nil ; end
         
     | 
| 
      
 15 
     | 
    
         
            +
                      def add_setting(setting, value) ; @settings[setting.to_sym] = value ; end
         
     | 
| 
      
 16 
     | 
    
         
            +
                      def get_settings
         
     | 
| 
      
 17 
     | 
    
         
            +
                        @settings.merge({
         
     | 
| 
      
 18 
     | 
    
         
            +
                          schema: (@settings[:use_schema] ? @default_schema : 'public')
         
     | 
| 
      
 19 
     | 
    
         
            +
                        })
         
     | 
| 
      
 20 
     | 
    
         
            +
                      end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      def get_attribute(name)
         
     | 
| 
      
 23 
     | 
    
         
            +
                        if @attrs.nil?
         
     | 
| 
      
 24 
     | 
    
         
            +
                          if get_settings[:table_name].nil?
         
     | 
| 
      
 25 
     | 
    
         
            +
                            @attrs = get_db_function_attribute_definitions(get_settings[:schema], get_settings[:function_name])
         
     | 
| 
      
 26 
     | 
    
         
            +
                          else
         
     | 
| 
      
 27 
     | 
    
         
            +
                            @attrs = get_db_table_attribute_definitions(get_settings[:schema], get_settings[:table_name])
         
     | 
| 
      
 28 
     | 
    
         
            +
                          end
         
     | 
| 
      
 29 
     | 
    
         
            +
                        end
         
     | 
| 
      
 30 
     | 
    
         
            +
                        @attrs[name.to_s]
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                      private
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                        def get_db_first_sharded_schema
         
     | 
| 
      
 36 
     | 
    
         
            +
                          result = @pg_connection.exec %q[
         
     | 
| 
      
 37 
     | 
    
         
            +
                            SELECT
         
     | 
| 
      
 38 
     | 
    
         
            +
                              c.schema_name
         
     | 
| 
      
 39 
     | 
    
         
            +
                            FROM
         
     | 
| 
      
 40 
     | 
    
         
            +
                              public.companies c
         
     | 
| 
      
 41 
     | 
    
         
            +
                            WHERE
         
     | 
| 
      
 42 
     | 
    
         
            +
                              c.use_sharded_company = true
         
     | 
| 
      
 43 
     | 
    
         
            +
                            LIMIT 1
         
     | 
| 
      
 44 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 45 
     | 
    
         
            +
                          result[0]['schema_name']
         
     | 
| 
      
 46 
     | 
    
         
            +
                        end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                        def get_db_table_attribute_definitions(schema, table_name)
         
     | 
| 
      
 49 
     | 
    
         
            +
                          return {} if schema.nil? || table_name.nil?
         
     | 
| 
      
 50 
     | 
    
         
            +
                          result = @pg_connection.exec %Q[
         
     | 
| 
      
 51 
     | 
    
         
            +
                            SELECT
         
     | 
| 
      
 52 
     | 
    
         
            +
                              t.tablename::TEXT AS object_name,
         
     | 
| 
      
 53 
     | 
    
         
            +
                              a.attname,
         
     | 
| 
      
 54 
     | 
    
         
            +
                              pg_catalog.format_type(a.atttypid, a.atttypmod),
         
     | 
| 
      
 55 
     | 
    
         
            +
                              (SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid) for 128) FROM pg_catalog.pg_attrdef d WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef) AS default_value,
         
     | 
| 
      
 56 
     | 
    
         
            +
                              a.attnotnull
         
     | 
| 
      
 57 
     | 
    
         
            +
                            FROM pg_catalog.pg_attribute a
         
     | 
| 
      
 58 
     | 
    
         
            +
                              JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
         
     | 
| 
      
 59 
     | 
    
         
            +
                              JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
         
     | 
| 
      
 60 
     | 
    
         
            +
                              JOIN pg_catalog.pg_tables t ON c.oid = (t.schemaname || '.' || t.tablename)::regclass::oid
         
     | 
| 
      
 61 
     | 
    
         
            +
                            WHERE a.attnum > 0
         
     | 
| 
      
 62 
     | 
    
         
            +
                              AND NOT a.attisdropped
         
     | 
| 
      
 63 
     | 
    
         
            +
                              AND n.nspname = '#{schema}'
         
     | 
| 
      
 64 
     | 
    
         
            +
                              AND t.tablename = '#{table_name}'
         
     | 
| 
      
 65 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 66 
     | 
    
         
            +
                          definitions = {}
         
     | 
| 
      
 67 
     | 
    
         
            +
                          result.each { |a| definitions.merge!({ a['attname'] => a })  }
         
     | 
| 
      
 68 
     | 
    
         
            +
                          definitions
         
     | 
| 
      
 69 
     | 
    
         
            +
                        end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                        def get_db_function_attribute_definitions(schema, function_name)
         
     | 
| 
      
 72 
     | 
    
         
            +
                          return {} if schema.nil? || function_name.nil?
         
     | 
| 
      
 73 
     | 
    
         
            +
                          result = @pg_connection.exec %Q[
         
     | 
| 
      
 74 
     | 
    
         
            +
                            SELECT
         
     | 
| 
      
 75 
     | 
    
         
            +
                              trim(split_part(regexp_replace(p.argument, E'^OUT ', ''), ' ', 1)) AS attname,
         
     | 
| 
      
 76 
     | 
    
         
            +
                              trim(split_part(regexp_replace(p.argument, E'^OUT ', ''), ' ', 2)) AS format_type
         
     | 
| 
      
 77 
     | 
    
         
            +
                            FROM (
         
     | 
| 
      
 78 
     | 
    
         
            +
                              SELECT
         
     | 
| 
      
 79 
     | 
    
         
            +
                                trim(unnest(regexp_split_to_array(pg_catalog.pg_get_function_arguments(p.oid), E','))) as argument
         
     | 
| 
      
 80 
     | 
    
         
            +
                              FROM pg_catalog.pg_proc p
         
     | 
| 
      
 81 
     | 
    
         
            +
                                   LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
         
     | 
| 
      
 82 
     | 
    
         
            +
                              WHERE p.proname ~ '^(#{function_name})$'
         
     | 
| 
      
 83 
     | 
    
         
            +
                                AND n.nspname ~ '^(#{schema})$'
         
     | 
| 
      
 84 
     | 
    
         
            +
                            ) p
         
     | 
| 
      
 85 
     | 
    
         
            +
                            WHERE
         
     | 
| 
      
 86 
     | 
    
         
            +
                              CASE WHEN p.argument ~ '^OUT' THEN true ELSE false END = true
         
     | 
| 
      
 87 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 88 
     | 
    
         
            +
                          definitions = {}
         
     | 
| 
      
 89 
     | 
    
         
            +
                          result.each { |a| definitions.merge!({ a['attname'] => a })  }
         
     | 
| 
      
 90 
     | 
    
         
            +
                          definitions
         
     | 
| 
      
 91 
     | 
    
         
            +
                        end
         
     | 
| 
      
 92 
     | 
    
         
            +
                    end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,374 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'sp/duh/jsonapi/doc/schema_catalog_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SP
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Duh
         
     | 
| 
      
 5 
     | 
    
         
            +
                module JSONAPI
         
     | 
| 
      
 6 
     | 
    
         
            +
                  module Doc
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                    class VictorPinusMetadataFormatParser
         
     | 
| 
      
 9 
     | 
    
         
            +
                      include Enumerable
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                      def resources ; @resources || [] ; end
         
     | 
| 
      
 12 
     | 
    
         
            +
                      def resource_names ; resources.map { |r| r.keys.first } ; end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                      def initialize(pg_connection)
         
     | 
| 
      
 15 
     | 
    
         
            +
                        @pg_connection = pg_connection
         
     | 
| 
      
 16 
     | 
    
         
            +
                        @schema_helper = SchemaCatalogHelper.new(pg_connection)
         
     | 
| 
      
 17 
     | 
    
         
            +
                      end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      def parse(publisher, public_only = true)
         
     | 
| 
      
 20 
     | 
    
         
            +
                        @public_only = public_only
         
     | 
| 
      
 21 
     | 
    
         
            +
                        begin
         
     | 
| 
      
 22 
     | 
    
         
            +
                          publisher = publisher.constantize if publisher.is_a?(String)
         
     | 
| 
      
 23 
     | 
    
         
            +
                          raise Exceptions::InvalidResourcePublisherError.new(publisher: publisher.name) if !publisher.include?(ResourcePublisher)
         
     | 
| 
      
 24 
     | 
    
         
            +
                          @publisher = publisher
         
     | 
| 
      
 25 
     | 
    
         
            +
                        rescue StandardError => e
         
     | 
| 
      
 26 
     | 
    
         
            +
                          raise Exceptions::InvalidResourcePublisherError.new(publisher: publisher.is_a?(String) ? publisher : publisher.name)
         
     | 
| 
      
 27 
     | 
    
         
            +
                        end
         
     | 
| 
      
 28 
     | 
    
         
            +
                        @resources = []
         
     | 
| 
      
 29 
     | 
    
         
            +
                        add_resources_from_folder(publisher.jsonapi_resources_root)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        @resources
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                      def each(&block)
         
     | 
| 
      
 34 
     | 
    
         
            +
                        @resources.each do |resource|
         
     | 
| 
      
 35 
     | 
    
         
            +
                          parsed_resource = parse_resource(resource)
         
     | 
| 
      
 36 
     | 
    
         
            +
                          block.call(parsed_resource) if !parsed_resource.nil?
         
     | 
| 
      
 37 
     | 
    
         
            +
                        end
         
     | 
| 
      
 38 
     | 
    
         
            +
                      end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      private
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                        def add_resources_from_folder(folder_name)
         
     | 
| 
      
 43 
     | 
    
         
            +
                          @resources ||= []
         
     | 
| 
      
 44 
     | 
    
         
            +
                          # First load resources at the root folder
         
     | 
| 
      
 45 
     | 
    
         
            +
                          Dir.glob(File.join(folder_name, '*.yml')) do |configuration_file|
         
     | 
| 
      
 46 
     | 
    
         
            +
                            add_resources_from_file(configuration_file)
         
     | 
| 
      
 47 
     | 
    
         
            +
                          end
         
     | 
| 
      
 48 
     | 
    
         
            +
                          # Then load resources at the inner folders
         
     | 
| 
      
 49 
     | 
    
         
            +
                          Dir.glob(File.join(folder_name, '*', '*.yml')) do |configuration_file|
         
     | 
| 
      
 50 
     | 
    
         
            +
                            add_resources_from_file(configuration_file)
         
     | 
| 
      
 51 
     | 
    
         
            +
                          end
         
     | 
| 
      
 52 
     | 
    
         
            +
                          @resources
         
     | 
| 
      
 53 
     | 
    
         
            +
                        end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                        def add_resources_from_file(configuration_file)
         
     | 
| 
      
 56 
     | 
    
         
            +
                          _log "Loading resources from file #{configuration_file}", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 57 
     | 
    
         
            +
                          configuration =  YAML.load_file(configuration_file)
         
     | 
| 
      
 58 
     | 
    
         
            +
                          if configuration.is_a? Hash
         
     | 
| 
      
 59 
     | 
    
         
            +
                            add_resource(configuration, configuration_file)
         
     | 
| 
      
 60 
     | 
    
         
            +
                          else
         
     | 
| 
      
 61 
     | 
    
         
            +
                            if configuration.is_a? Array
         
     | 
| 
      
 62 
     | 
    
         
            +
                              configuration.each { |resource| add_resource(resource, configuration_file) }
         
     | 
| 
      
 63 
     | 
    
         
            +
                            else
         
     | 
| 
      
 64 
     | 
    
         
            +
                              raise Exceptions::InvalidResourceConfigurationError.new(file: configuration_file)
         
     | 
| 
      
 65 
     | 
    
         
            +
                            end
         
     | 
| 
      
 66 
     | 
    
         
            +
                          end
         
     | 
| 
      
 67 
     | 
    
         
            +
                        end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                        def add_resource(resource, configuration_file)
         
     | 
| 
      
 70 
     | 
    
         
            +
                          raise Exceptions::InvalidResourceConfigurationError.new(file: configuration_file) if (resource.keys.count != 1)
         
     | 
| 
      
 71 
     | 
    
         
            +
                          resource_name = resource.keys[0]
         
     | 
| 
      
 72 
     | 
    
         
            +
                          _log "   Loading resource #{resource_name}", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 73 
     | 
    
         
            +
                          processed = false
         
     | 
| 
      
 74 
     | 
    
         
            +
                          @resources.each_with_index do |r, i|
         
     | 
| 
      
 75 
     | 
    
         
            +
                            if r.keys.include?(resource_name)
         
     | 
| 
      
 76 
     | 
    
         
            +
                              @resources[i] = get_resource_index(resource_name, configuration_file)
         
     | 
| 
      
 77 
     | 
    
         
            +
                              processed = true
         
     | 
| 
      
 78 
     | 
    
         
            +
                              break
         
     | 
| 
      
 79 
     | 
    
         
            +
                            end
         
     | 
| 
      
 80 
     | 
    
         
            +
                          end
         
     | 
| 
      
 81 
     | 
    
         
            +
                          @resources << get_resource_index(resource_name, configuration_file) if !processed
         
     | 
| 
      
 82 
     | 
    
         
            +
                        end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                        def get_resource_index(resource_name, configuration_file)
         
     | 
| 
      
 85 
     | 
    
         
            +
                          {
         
     | 
| 
      
 86 
     | 
    
         
            +
                            resource_name.to_sym => {
         
     | 
| 
      
 87 
     | 
    
         
            +
                              group: @publisher.name,
         
     | 
| 
      
 88 
     | 
    
         
            +
                              file: configuration_file
         
     | 
| 
      
 89 
     | 
    
         
            +
                            }
         
     | 
| 
      
 90 
     | 
    
         
            +
                          }
         
     | 
| 
      
 91 
     | 
    
         
            +
                        end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                        def parse_resource(resource)
         
     | 
| 
      
 94 
     | 
    
         
            +
                          resource_name = resource.keys[0].to_s
         
     | 
| 
      
 95 
     | 
    
         
            +
                          resource_file = resource.values[0][:file]
         
     | 
| 
      
 96 
     | 
    
         
            +
                          _log "   Processing resource #{resource_name} in file #{resource_file}", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 97 
     | 
    
         
            +
                          metadata = parse_file(resource_name, resource_file)
         
     | 
| 
      
 98 
     | 
    
         
            +
                          if !metadata.nil?
         
     | 
| 
      
 99 
     | 
    
         
            +
                            metadata[:resource] = {} if !metadata.has_key?(:resource)
         
     | 
| 
      
 100 
     | 
    
         
            +
                            metadata[:resource] = metadata[:resource].merge({
         
     | 
| 
      
 101 
     | 
    
         
            +
                              name: resource_name
         
     | 
| 
      
 102 
     | 
    
         
            +
                            })
         
     | 
| 
      
 103 
     | 
    
         
            +
                            if metadata[:resource][:group].blank?
         
     | 
| 
      
 104 
     | 
    
         
            +
                              # group = /^(?<group>\w+?)_/.match(resource_name)
         
     | 
| 
      
 105 
     | 
    
         
            +
                              # resource_group = (group ? group[:group].capitalize : resource.values[0][:group])
         
     | 
| 
      
 106 
     | 
    
         
            +
                              resource_group = resource_name.capitalize
         
     | 
| 
      
 107 
     | 
    
         
            +
                              metadata[:resource][:group] = resource_group
         
     | 
| 
      
 108 
     | 
    
         
            +
                            end
         
     | 
| 
      
 109 
     | 
    
         
            +
                          else
         
     | 
| 
      
 110 
     | 
    
         
            +
                            _log "   Ignoring PRIVATE resource #{resource_name} in file #{resource_file}", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 111 
     | 
    
         
            +
                          end
         
     | 
| 
      
 112 
     | 
    
         
            +
                          metadata
         
     | 
| 
      
 113 
     | 
    
         
            +
                        end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                        def parse_file(resource, resource_file)
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                          context = nil
         
     | 
| 
      
 118 
     | 
    
         
            +
                          metadata = {}
         
     | 
| 
      
 119 
     | 
    
         
            +
                          indent = nil
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                          lines = File.readlines(resource_file)
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                          table_name = function_name = data_schema = use_schema = nil
         
     | 
| 
      
 124 
     | 
    
         
            +
                          lines.each_with_index do |line, i|
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                            # Ignore empty lines
         
     | 
| 
      
 127 
     | 
    
         
            +
                            next if line.strip.blank?
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                            # Process resource definition beginning
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                            r = get_resource(line, indent)
         
     | 
| 
      
 132 
     | 
    
         
            +
                            # First get the starting line of the resource...
         
     | 
| 
      
 133 
     | 
    
         
            +
                            next if !metadata.has_key?(:resource) && (r.first.nil? || r.first.strip != resource)
         
     | 
| 
      
 134 
     | 
    
         
            +
                            # ... but exit if we reached another resource
         
     | 
| 
      
 135 
     | 
    
         
            +
                            break if metadata.has_key?(:resource) && !r.first.nil? && r.first.strip != resource
         
     | 
| 
      
 136 
     | 
    
         
            +
                            if !r.first.nil? && !metadata.has_key?(:resource)
         
     | 
| 
      
 137 
     | 
    
         
            +
                              context = :resource
         
     | 
| 
      
 138 
     | 
    
         
            +
                              indent = r.last.length
         
     | 
| 
      
 139 
     | 
    
         
            +
                              # Get the resource metadata:
         
     | 
| 
      
 140 
     | 
    
         
            +
                              m, example, tags = get_metadata_for(lines, i, r)
         
     | 
| 
      
 141 
     | 
    
         
            +
                              scope = :private
         
     | 
| 
      
 142 
     | 
    
         
            +
                              scope = :public if 'public'.in?(tags)
         
     | 
| 
      
 143 
     | 
    
         
            +
                              metadata[:resource] = {
         
     | 
| 
      
 144 
     | 
    
         
            +
                                description: m,
         
     | 
| 
      
 145 
     | 
    
         
            +
                                scope: scope
         
     | 
| 
      
 146 
     | 
    
         
            +
                              }
         
     | 
| 
      
 147 
     | 
    
         
            +
                              tags.each do |t|
         
     | 
| 
      
 148 
     | 
    
         
            +
                                case
         
     | 
| 
      
 149 
     | 
    
         
            +
                                when t.start_with?('group')
         
     | 
| 
      
 150 
     | 
    
         
            +
                                  v = t.split('=')
         
     | 
| 
      
 151 
     | 
    
         
            +
                                  metadata[:resource][:group] = v.last.strip
         
     | 
| 
      
 152 
     | 
    
         
            +
                                when t == 'readonly'
         
     | 
| 
      
 153 
     | 
    
         
            +
                                  metadata[:resource][:readonly] = true
         
     | 
| 
      
 154 
     | 
    
         
            +
                                end
         
     | 
| 
      
 155 
     | 
    
         
            +
                              end
         
     | 
| 
      
 156 
     | 
    
         
            +
                            end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                            # Return now if resource is private
         
     | 
| 
      
 159 
     | 
    
         
            +
                            return nil if scope == :private && @public_only
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                            if context == :resource
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                              # Process data structure
         
     | 
| 
      
 164 
     | 
    
         
            +
                              table_name = get_value_of('pg-table', line) if table_name.nil?
         
     | 
| 
      
 165 
     | 
    
         
            +
                              function_name = get_value_of('pg-function', line) if function_name.nil?
         
     | 
| 
      
 166 
     | 
    
         
            +
                              data_schema = get_value_of('pg-schema', line) if data_schema.nil?
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                              use_schema = get_value_of('request-schema', line) if use_schema.nil?
         
     | 
| 
      
 169 
     | 
    
         
            +
                              use_schema = get_value_of('request-sharded-schema', line) if use_schema.nil?
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                              # Process resource attributes
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                              if is_beginning_of_attribute_section?(line)
         
     | 
| 
      
 174 
     | 
    
         
            +
                                context = :attributes
         
     | 
| 
      
 175 
     | 
    
         
            +
                                metadata[:resource] = {} if !metadata.has_key?(:resource)
         
     | 
| 
      
 176 
     | 
    
         
            +
                                @schema_helper.clear_settings
         
     | 
| 
      
 177 
     | 
    
         
            +
                                @schema_helper.add_setting(:schema, data_schema) if !data_schema.nil?
         
     | 
| 
      
 178 
     | 
    
         
            +
                                @schema_helper.add_setting(:table_name, table_name) if !table_name.nil?
         
     | 
| 
      
 179 
     | 
    
         
            +
                                @schema_helper.add_setting(:function_name, function_name) if !function_name.nil?
         
     | 
| 
      
 180 
     | 
    
         
            +
                                @schema_helper.add_setting(:use_schema, use_schema) if !use_schema.nil?
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                                metadata[:resource][:catalog] = {
         
     | 
| 
      
 183 
     | 
    
         
            +
                                  sharded_schema: use_schema == 'true'
         
     | 
| 
      
 184 
     | 
    
         
            +
                                }
         
     | 
| 
      
 185 
     | 
    
         
            +
                                metadata[:resource][:catalog][:schema] = data_schema if !data_schema.nil?
         
     | 
| 
      
 186 
     | 
    
         
            +
                                metadata[:resource][:catalog][:table_name] = table_name if !table_name.nil?
         
     | 
| 
      
 187 
     | 
    
         
            +
                                metadata[:resource][:catalog][:function_name] = function_name if !function_name.nil?
         
     | 
| 
      
 188 
     | 
    
         
            +
                                metadata[:attributes] = []
         
     | 
| 
      
 189 
     | 
    
         
            +
                                next
         
     | 
| 
      
 190 
     | 
    
         
            +
                              end
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
                            end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                            readonly = false
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                            if context == :attributes
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                              readonly = false
         
     | 
| 
      
 199 
     | 
    
         
            +
                              a = get_attribute(line)
         
     | 
| 
      
 200 
     | 
    
         
            +
                              if !a.first.nil?
         
     | 
| 
      
 201 
     | 
    
         
            +
                                metadata[:resource][:id] = metadata[:attributes].length if a.first.strip.to_sym == :id
         
     | 
| 
      
 202 
     | 
    
         
            +
                                # Get the attribute metadata
         
     | 
| 
      
 203 
     | 
    
         
            +
                                description, example, tags = get_metadata_for(lines, i, a)
         
     | 
| 
      
 204 
     | 
    
         
            +
                                readonly = true if 'readonly'.in?(tags)
         
     | 
| 
      
 205 
     | 
    
         
            +
                                metadata[:attributes] << {
         
     | 
| 
      
 206 
     | 
    
         
            +
                                  name: a.first.strip,
         
     | 
| 
      
 207 
     | 
    
         
            +
                                  catalog: @schema_helper.get_attribute(a.first),
         
     | 
| 
      
 208 
     | 
    
         
            +
                                  description: description,
         
     | 
| 
      
 209 
     | 
    
         
            +
                                  example: example,
         
     | 
| 
      
 210 
     | 
    
         
            +
                                  readonly: readonly
         
     | 
| 
      
 211 
     | 
    
         
            +
                                }
         
     | 
| 
      
 212 
     | 
    
         
            +
                              else
         
     | 
| 
      
 213 
     | 
    
         
            +
                                a = get_jsonapi_attribute(line)
         
     | 
| 
      
 214 
     | 
    
         
            +
                                if !a.first.nil?
         
     | 
| 
      
 215 
     | 
    
         
            +
                                  case
         
     | 
| 
      
 216 
     | 
    
         
            +
                                  when a.first.in?([ 'to-one', 'to-many' ])
         
     | 
| 
      
 217 
     | 
    
         
            +
                                    context = a.first.to_sym
         
     | 
| 
      
 218 
     | 
    
         
            +
                                    readonly = false
         
     | 
| 
      
 219 
     | 
    
         
            +
                                    next
         
     | 
| 
      
 220 
     | 
    
         
            +
                                  end
         
     | 
| 
      
 221 
     | 
    
         
            +
                                else
         
     | 
| 
      
 222 
     | 
    
         
            +
                                  column = get_value_of('pg-column', line)
         
     | 
| 
      
 223 
     | 
    
         
            +
                                  metadata[:attributes].last.merge!({
         
     | 
| 
      
 224 
     | 
    
         
            +
                                    catalog: @schema_helper.get_attribute(column)
         
     | 
| 
      
 225 
     | 
    
         
            +
                                  }) if column
         
     | 
| 
      
 226 
     | 
    
         
            +
                                end
         
     | 
| 
      
 227 
     | 
    
         
            +
                              end
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
                            end
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
                            if context == :"to-one" || context == :"to-many"
         
     | 
| 
      
 232 
     | 
    
         
            +
                              a = get_jsonapi_attribute(line)
         
     | 
| 
      
 233 
     | 
    
         
            +
                              if a.first.nil?
         
     | 
| 
      
 234 
     | 
    
         
            +
                                type = get_value_of('resource', line)
         
     | 
| 
      
 235 
     | 
    
         
            +
                                table_name = get_value_of('pg-table', line)
         
     | 
| 
      
 236 
     | 
    
         
            +
                                if !type.nil? || !table_name.nil?
         
     | 
| 
      
 237 
     | 
    
         
            +
                                  type ||= table_name
         
     | 
| 
      
 238 
     | 
    
         
            +
                                  metadata[:attributes].last.merge!({
         
     | 
| 
      
 239 
     | 
    
         
            +
                                    catalog: {
         
     | 
| 
      
 240 
     | 
    
         
            +
                                      'format_type' => (context == :'to-one' ? type : "#{type}[]")
         
     | 
| 
      
 241 
     | 
    
         
            +
                                    }
         
     | 
| 
      
 242 
     | 
    
         
            +
                                  })
         
     | 
| 
      
 243 
     | 
    
         
            +
                                end
         
     | 
| 
      
 244 
     | 
    
         
            +
                              else
         
     | 
| 
      
 245 
     | 
    
         
            +
                                case
         
     | 
| 
      
 246 
     | 
    
         
            +
                                when a.first.in?([ 'to-one', 'to-many' ])
         
     | 
| 
      
 247 
     | 
    
         
            +
                                  context = a.first.to_sym
         
     | 
| 
      
 248 
     | 
    
         
            +
                                  next
         
     | 
| 
      
 249 
     | 
    
         
            +
                                else
         
     | 
| 
      
 250 
     | 
    
         
            +
                                  description, example, tags = get_metadata_for(lines, i, a)
         
     | 
| 
      
 251 
     | 
    
         
            +
                                  readonly = true if 'readonly'.in?(tags)
         
     | 
| 
      
 252 
     | 
    
         
            +
                                  metadata[:attributes] << {
         
     | 
| 
      
 253 
     | 
    
         
            +
                                    name: a.first.strip,
         
     | 
| 
      
 254 
     | 
    
         
            +
                                    association: context,
         
     | 
| 
      
 255 
     | 
    
         
            +
                                    description: description,
         
     | 
| 
      
 256 
     | 
    
         
            +
                                    example: example,
         
     | 
| 
      
 257 
     | 
    
         
            +
                                    readonly: readonly
         
     | 
| 
      
 258 
     | 
    
         
            +
                                  }
         
     | 
| 
      
 259 
     | 
    
         
            +
                                end
         
     | 
| 
      
 260 
     | 
    
         
            +
                              end
         
     | 
| 
      
 261 
     | 
    
         
            +
                            end
         
     | 
| 
      
 262 
     | 
    
         
            +
             
     | 
| 
      
 263 
     | 
    
         
            +
                          end
         
     | 
| 
      
 264 
     | 
    
         
            +
                          _log "   > #{resource} metadata", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 265 
     | 
    
         
            +
                          _log metadata,  "JSONAPI::Doc::Parser"
         
     | 
| 
      
 266 
     | 
    
         
            +
                          metadata
         
     | 
| 
      
 267 
     | 
    
         
            +
                        end
         
     | 
| 
      
 268 
     | 
    
         
            +
             
     | 
| 
      
 269 
     | 
    
         
            +
                        def get_resource(line, indent = nil)
         
     | 
| 
      
 270 
     | 
    
         
            +
                          if indent.nil?
         
     | 
| 
      
 271 
     | 
    
         
            +
                            resource = /^((?<indent>\s*))?(?<name>[a-z]+[0-9|a-z|_]*?):\s*(#(?<meta>.*?))*$/.match(line)
         
     | 
| 
      
 272 
     | 
    
         
            +
                          else
         
     | 
| 
      
 273 
     | 
    
         
            +
                            resource = /^((?<indent>\s{#{indent}}))?(?<name>[a-z]+[0-9|a-z|_]*?):\s*(#(?<meta>.*?))*$/.match(line)
         
     | 
| 
      
 274 
     | 
    
         
            +
                          end
         
     | 
| 
      
 275 
     | 
    
         
            +
                          if resource
         
     | 
| 
      
 276 
     | 
    
         
            +
                            [ resource[:name], resource[:meta], resource[:indent] ]
         
     | 
| 
      
 277 
     | 
    
         
            +
                          else
         
     | 
| 
      
 278 
     | 
    
         
            +
                            [ nil, nil, nil ]
         
     | 
| 
      
 279 
     | 
    
         
            +
                          end
         
     | 
| 
      
 280 
     | 
    
         
            +
                        end
         
     | 
| 
      
 281 
     | 
    
         
            +
                        def get_jsonapi_attribute(line)
         
     | 
| 
      
 282 
     | 
    
         
            +
                          resource = /^(?<name>[a-z]+[0-9|a-z|_|-]*?):\s*(#(?<meta>.*?))*$/.match(line.strip)
         
     | 
| 
      
 283 
     | 
    
         
            +
                          if resource
         
     | 
| 
      
 284 
     | 
    
         
            +
                            [ resource[:name], resource[:meta] ]
         
     | 
| 
      
 285 
     | 
    
         
            +
                          else
         
     | 
| 
      
 286 
     | 
    
         
            +
                            [ nil, nil ]
         
     | 
| 
      
 287 
     | 
    
         
            +
                          end
         
     | 
| 
      
 288 
     | 
    
         
            +
                        end
         
     | 
| 
      
 289 
     | 
    
         
            +
                        def is_beginning_of_attribute_section?(line)
         
     | 
| 
      
 290 
     | 
    
         
            +
                          line = line.strip
         
     | 
| 
      
 291 
     | 
    
         
            +
                          line = line[1..line.length-1] if line.start_with?('#')
         
     | 
| 
      
 292 
     | 
    
         
            +
                          name, meta = get_jsonapi_attribute(line)
         
     | 
| 
      
 293 
     | 
    
         
            +
                          return true if name && name.to_sym == :attributes
         
     | 
| 
      
 294 
     | 
    
         
            +
                        end
         
     | 
| 
      
 295 
     | 
    
         
            +
                        def get_attribute(line)
         
     | 
| 
      
 296 
     | 
    
         
            +
                          attribute = /^(#\s*)?-\s*(?<name>[a-z]+[0-9|a-z|_]*?)(\s*:\s*)?(#(?<meta>.*?))*$/.match(line.strip)
         
     | 
| 
      
 297 
     | 
    
         
            +
                          if attribute
         
     | 
| 
      
 298 
     | 
    
         
            +
                            [ attribute[:name], attribute[:meta] ]
         
     | 
| 
      
 299 
     | 
    
         
            +
                          else
         
     | 
| 
      
 300 
     | 
    
         
            +
                            [ nil, nil ]
         
     | 
| 
      
 301 
     | 
    
         
            +
                          end
         
     | 
| 
      
 302 
     | 
    
         
            +
                        end
         
     | 
| 
      
 303 
     | 
    
         
            +
                        def get_metadata(line)
         
     | 
| 
      
 304 
     | 
    
         
            +
                          line = line.strip
         
     | 
| 
      
 305 
     | 
    
         
            +
                          attribute = get_attribute(line)
         
     | 
| 
      
 306 
     | 
    
         
            +
                          metadata = line.start_with?('#') ? line[1..line.length-1].strip : nil
         
     | 
| 
      
 307 
     | 
    
         
            +
                          tag = /^#\s*\[(?<tag>(\w|=|\s)+?)\]\s*$/.match(line)
         
     | 
| 
      
 308 
     | 
    
         
            +
                          if tag.nil?
         
     | 
| 
      
 309 
     | 
    
         
            +
                            example = /\((ex|Ex|default|Default):\s*(?<eg>.+?)\)/.match(line)
         
     | 
| 
      
 310 
     | 
    
         
            +
                            if metadata && attribute.first.nil? && metadata != 'attributes:'
         
     | 
| 
      
 311 
     | 
    
         
            +
                              [ metadata, example ? example[:eg] : nil, nil ]
         
     | 
| 
      
 312 
     | 
    
         
            +
                            else
         
     | 
| 
      
 313 
     | 
    
         
            +
                              [ nil, nil, nil ]
         
     | 
| 
      
 314 
     | 
    
         
            +
                            end
         
     | 
| 
      
 315 
     | 
    
         
            +
                          else
         
     | 
| 
      
 316 
     | 
    
         
            +
                            [ nil, nil, tag[:tag] ]
         
     | 
| 
      
 317 
     | 
    
         
            +
                          end
         
     | 
| 
      
 318 
     | 
    
         
            +
                        end
         
     | 
| 
      
 319 
     | 
    
         
            +
                        def get_value_of(attribute, line)
         
     | 
| 
      
 320 
     | 
    
         
            +
                          # First try to match a string value, between double quotes
         
     | 
| 
      
 321 
     | 
    
         
            +
                          data = /^#{attribute}:\s*(\"){1}(?<value>.*?)(\"){1}/.match(line.strip)
         
     | 
| 
      
 322 
     | 
    
         
            +
                          # Then try to match a non-string value
         
     | 
| 
      
 323 
     | 
    
         
            +
                          data = /^#{attribute}:\s*(?<value>.*?)(#(?<meta>.*?))*$/.match(line.strip) if data.nil?
         
     | 
| 
      
 324 
     | 
    
         
            +
                          data ? data[:value] : nil
         
     | 
| 
      
 325 
     | 
    
         
            +
                        end
         
     | 
| 
      
 326 
     | 
    
         
            +
             
     | 
| 
      
 327 
     | 
    
         
            +
                        def get_metadata_for(enumerable, index, object)
         
     | 
| 
      
 328 
     | 
    
         
            +
                          return if object.first.nil?
         
     | 
| 
      
 329 
     | 
    
         
            +
                          name = object.first.strip
         
     | 
| 
      
 330 
     | 
    
         
            +
                          data = []
         
     | 
| 
      
 331 
     | 
    
         
            +
                          example = nil
         
     | 
| 
      
 332 
     | 
    
         
            +
                          tags = []
         
     | 
| 
      
 333 
     | 
    
         
            +
                          if object[1].nil?
         
     | 
| 
      
 334 
     | 
    
         
            +
                            each_backwards(enumerable, index) do |line|
         
     | 
| 
      
 335 
     | 
    
         
            +
                              m = get_metadata(line)
         
     | 
| 
      
 336 
     | 
    
         
            +
                              if m.first
         
     | 
| 
      
 337 
     | 
    
         
            +
                                data << m.first
         
     | 
| 
      
 338 
     | 
    
         
            +
                                example = m[1] if m[1]
         
     | 
| 
      
 339 
     | 
    
         
            +
                              else
         
     | 
| 
      
 340 
     | 
    
         
            +
                                if m[2]
         
     | 
| 
      
 341 
     | 
    
         
            +
                                  tags << m[2]
         
     | 
| 
      
 342 
     | 
    
         
            +
                                else
         
     | 
| 
      
 343 
     | 
    
         
            +
                                  break
         
     | 
| 
      
 344 
     | 
    
         
            +
                                end
         
     | 
| 
      
 345 
     | 
    
         
            +
                              end
         
     | 
| 
      
 346 
     | 
    
         
            +
                            end
         
     | 
| 
      
 347 
     | 
    
         
            +
                          else
         
     | 
| 
      
 348 
     | 
    
         
            +
                            data << object[1].strip
         
     | 
| 
      
 349 
     | 
    
         
            +
                          end
         
     | 
| 
      
 350 
     | 
    
         
            +
                          if data.any?
         
     | 
| 
      
 351 
     | 
    
         
            +
                            data.reverse!
         
     | 
| 
      
 352 
     | 
    
         
            +
                            _log "   > #{name} metadata", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 353 
     | 
    
         
            +
                            _log data,  "JSONAPI::Doc::Parser"
         
     | 
| 
      
 354 
     | 
    
         
            +
                          else
         
     | 
| 
      
 355 
     | 
    
         
            +
                            data = nil
         
     | 
| 
      
 356 
     | 
    
         
            +
                            _log "   > #{name} has no metadata", "JSONAPI::Doc::Parser"
         
     | 
| 
      
 357 
     | 
    
         
            +
                          end
         
     | 
| 
      
 358 
     | 
    
         
            +
                          [ data, example, tags ]
         
     | 
| 
      
 359 
     | 
    
         
            +
                        end
         
     | 
| 
      
 360 
     | 
    
         
            +
             
     | 
| 
      
 361 
     | 
    
         
            +
                        def each_backwards(enumerable, index, &block)
         
     | 
| 
      
 362 
     | 
    
         
            +
                          while index > 0 do
         
     | 
| 
      
 363 
     | 
    
         
            +
                             index = index - 1
         
     | 
| 
      
 364 
     | 
    
         
            +
                             next if enumerable[index].strip.blank?
         
     | 
| 
      
 365 
     | 
    
         
            +
                             block.call(enumerable[index])
         
     | 
| 
      
 366 
     | 
    
         
            +
                          end
         
     | 
| 
      
 367 
     | 
    
         
            +
                        end
         
     | 
| 
      
 368 
     | 
    
         
            +
             
     | 
| 
      
 369 
     | 
    
         
            +
                    end
         
     | 
| 
      
 370 
     | 
    
         
            +
             
     | 
| 
      
 371 
     | 
    
         
            +
                  end
         
     | 
| 
      
 372 
     | 
    
         
            +
                end
         
     | 
| 
      
 373 
     | 
    
         
            +
              end
         
     | 
| 
      
 374 
     | 
    
         
            +
            end
         
     |