Fixed some YARD documentation issues and changed how the user-agent is built.
[arvados.git] / lib / google / api_client / discovery / schema.rb
index 884f3dc6f3414ba9935cc73b419b626bbcc15d11..9421be1e1ca43b99b3cfa970a0c11e85b4b2c41c 100644 (file)
 # limitations under the License.
 
 
+require 'time'
 require 'json'
+require 'base64'
+require 'autoparse'
 require 'addressable/uri'
 require 'addressable/template'
 
 require 'google/inflection'
 require 'google/api_client/errors'
 
+
 module Google
   class APIClient
-    class Schema
-      def initialize(api, api_name, api_version, discovery_document)
-        # This constructor is super-long, but hard to break up due to the
+    module Schema
+      def self.parse(api, schema_data)
+        # This method is super-long, but hard to break up due to the
         # unavoidable dependence on closures and execution context.
-        @api = api
-        @discovery_document = discovery_document
-        api_name_string =
-          Google::INFLECTOR.camelize(api_name)
-        api_version_string =
-          Google::INFLECTOR.camelize(api_version).gsub('.', '_')
-        @schema_name = @discovery_document['id']
-        if Google::APIClient::Schema.const_defined?(api_name_string)
-          @api_name = Google::APIClient::Schema.const_get(api_name_string)
-        else
-          @api_name = Google::APIClient::Schema.const_set(
-            api_name_string, Class.new
-          )
-        end
-        if @api_name.const_defined?(api_version_string)
-          @api_version = @api_name.const_get(api_version_string)
-        else
-          @api_version = @api_name.const_set(api_version_string, Class.new)
-        end
-        if @api_version.const_defined?(@schema_name)
-          @schema_class = @api_version.const_get(@schema_name)
-        else
-          @schema_class = @api_version.const_set(
-            @schema_name,
-            Class.new(APIObject) do |klass|
-              discovery_document['properties'].each do |(k, v)|
-                property_name = Google::INFLECTOR.underscore(k)
-                define_method(property_name + '_property') do
-                  v
-                end
-                case v['type']
-                when 'string'
-                  define_method(property_name) do
-                    self[k] || v['default']
-                  end
-                  define_method(property_name + '=') do |value|
-                    if value.respond_to?(:to_str)
-                      self[k] = value.to_str
-                    elsif value.kind_of?(Symbol)
-                      self[k] = value.to_s
-                    else
-                      raise TypeError,
-                        "Expected String or Symbol, got #{value.class}."
-                    end
-                  end
-                else
-                  # TODO(bobaman):
-                  # Implement the remainder of the types.
+        schema_name = schema_data['id']
+
+        # Due to an oversight, schema IDs may not be URI references.
+        # TODO(bobaman): Remove this code once this has been resolved.
+        schema_uri = (
+          api.document_base +
+          (schema_name[0..0] != '#' ? '#' + schema_name : schema_name)
+        )
+        # puts schema_uri
 
-                  # Don't know what this is, default to anything goes.
-                  define_method(property_name) do
-                    self[k] || v['default']
-                  end
-                  define_method(property_name + '=') do |value|
-                    self[k] = value
-                  end
-                end
+        # Due to an oversight, schema IDs may not be URI references.
+        # TODO(bobaman): Remove this whole lambda once this has been resolved.
+        reformat_references = lambda do |data|
+          # This code is not particularly efficient due to recursive traversal
+          # and excess object creation, but this hopefully shouldn't be an
+          # issue since it should only be called only once per schema per
+          # process.
+          if data.kind_of?(Hash) && data['$ref']
+            reference = data['$ref']
+            reference = '#' + reference if reference[0..0] != '#'
+            data.merge({
+              '$ref' => reference
+            })
+          elsif data.kind_of?(Hash)
+            data.inject({}) do |accu, (key, value)|
+              if value.kind_of?(Hash)
+                accu[key] = reformat_references.call(value)
+              else
+                accu[key] = value
               end
+              accu
             end
-          )
+          else
+            data
+          end
         end
-      end
-
-      def schema_name
-        return @schema_name
-      end
-
-      def schema_class
-        return @schema_class
-      end
+        schema_data = reformat_references.call(schema_data)
+        # puts schema_data.inspect
 
-      ##
-      # Returns a <code>String</code> representation of the resource's state.
-      #
-      # @return [String] The resource's state, as a <code>String</code>.
-      def inspect
-        sprintf(
-          "#<%s:%#0x CLASS:%s>",
-          self.class.to_s, self.object_id, self.schema_class.name
-        )
-      end
-    end
-
-    class APIObject
-      def initialize(data)
-        @data = data
-      end
-
-      def [](key)
-        return @data[key]
-      end
+        if schema_name
+          api_name_string =
+            Google::INFLECTOR.camelize(api.name)
+          api_version_string =
+            Google::INFLECTOR.camelize(api.version).gsub('.', '_')
+          if Google::APIClient::Schema.const_defined?(api_name_string)
+            api_name = Google::APIClient::Schema.const_get(api_name_string)
+          else
+            api_name = Google::APIClient::Schema.const_set(
+              api_name_string, Module.new
+            )
+          end
+          if api_name.const_defined?(api_version_string)
+            api_version = api_name.const_get(api_version_string)
+          else
+            api_version = api_name.const_set(api_version_string, Module.new)
+          end
+          if api_version.const_defined?(schema_name)
+            schema_class = api_version.const_get(schema_name)
+          end
+        end
 
-      def []=(key, value)
-        return @data[key] = value
+        # It's possible the schema has already been defined. If so, don't
+        # redefine it. This means that reloading a schema which has already
+        # been loaded into memory is not possible.
+        unless schema_class
+          schema_class = AutoParse.generate(schema_data, :uri => schema_uri)
+          if schema_name
+            api_version.const_set(schema_name, schema_class)
+          end
+        end
+        return schema_class
       end
     end
   end