1 # Copyright 2010 Google Inc.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
19 require 'addressable/uri'
20 require 'addressable/template'
22 require 'google/inflection'
23 require 'google/api_client/errors'
28 def initialize(api, api_name, api_version, discovery_document)
29 # This constructor is super-long, but hard to break up due to the
30 # unavoidable dependence on closures and execution context.
32 @discovery_document = discovery_document
34 Google::INFLECTOR.camelize(api_name)
36 Google::INFLECTOR.camelize(api_version).gsub('.', '_')
37 @schema_name = @discovery_document['id']
38 if Google::APIClient::Schema.const_defined?(api_name_string)
39 @api_name = Google::APIClient::Schema.const_get(api_name_string)
41 @api_name = Google::APIClient::Schema.const_set(
42 api_name_string, Class.new
45 if @api_name.const_defined?(api_version_string)
46 @api_version = @api_name.const_get(api_version_string)
48 @api_version = @api_name.const_set(api_version_string, Class.new)
50 if @api_version.const_defined?(@schema_name)
51 @schema_class = @api_version.const_get(@schema_name)
54 @schema_class = @api_version.const_set(
56 Class.new(APIObject) do |klass|
57 discovery_document['properties'].each do |(k, v)|
58 property_name = Google::INFLECTOR.underscore(k)
59 define_method(:schema) { schema }
60 define_method(property_name + '_property') do
65 define_string_property(property_name, k, v)
67 define_boolean_property(property_name, k, v)
69 define_number_property(property_name, k, v)
71 define_object_property(property_name, k, v)
73 # Either type 'any' or we don't know what this is,
74 # default to anything goes.
75 define_any_property(property_name, k, v)
92 # Returns a <code>String</code> representation of the resource's state.
94 # @return [String] The resource's state, as a <code>String</code>.
97 "#<%s:%#0x CLASS:%s>",
98 self.class.to_s, self.object_id, self.schema_class.name
104 def self.define_string_property(property_name, key, schema)
105 define_method(property_name) do
106 self[key] ||= schema['default']
107 if schema['format'] == 'byte' && self[key] != nil
108 Base64.decode64(self[key])
109 elsif schema['format'] == 'date-time' && self[key] != nil
110 Time.parse(self[key])
111 elsif schema['format'] =~ /^u?int(32|64)$/ && self[key] != nil
117 define_method(property_name + '=') do |value|
118 if schema['format'] == 'byte'
119 self[key] = Base64.encode64(value)
120 elsif schema['format'] == 'date-time'
121 if value.respond_to?(:to_str)
122 value = Time.parse(value.to_str)
123 elsif !value.respond_to?(:xmlschema)
125 "Could not obtain RFC 3339 timestamp from #{value.class}."
127 self[key] = value.xmlschema
128 elsif schema['format'] =~ /^u?int(32|64)$/
129 self[key] = value.to_s
130 elsif value.respond_to?(:to_str)
131 self[key] = value.to_str
132 elsif value.kind_of?(Symbol)
133 self[key] = value.to_s
136 "Expected String or Symbol, got #{value.class}."
141 def self.define_boolean_property(property_name, key, schema)
142 define_method(property_name) do
143 self[key] ||= schema['default']
144 case self[key].to_s.downcase
145 when 'true', 'yes', 'y', 'on', '1'
147 when 'false', 'no', 'n', 'off', '0'
153 "Expected boolean, got #{self[key].class}."
156 define_method(property_name + '=') do |value|
157 case value.to_s.downcase
158 when 'true', 'yes', 'y', 'on', '1'
160 when 'false', 'no', 'n', 'off', '0'
165 raise TypeError, "Expected boolean, got #{value.class}."
170 def self.define_number_property(property_name, key, schema)
171 define_method(property_name) do
172 self[key] ||= schema['default']
173 if self[key] != nil && !self[key].respond_to?(:to_f)
175 "Expected Float, got #{self[key].class}."
176 elsif self[key] != nil && self[key].respond_to?(:to_f)
182 define_method(property_name + '=') do |value|
186 case schema['format']
187 when 'double', 'float'
188 if value.respond_to?(:to_f)
189 self[key] = value.to_f
192 "Expected String or Symbol, got #{value.class}."
196 "Unexpected type format for number: #{schema['format']}."
202 def self.define_object_property(property_name, key, schema)
204 # Do we treat this differently from any?
205 self.define_any_property(property_name, key, schema)
208 def self.define_any_property(property_name, key, schema)
209 define_method(property_name) do
210 self[k] || v['default']
212 define_method(property_name + '=') do |value|
226 return @data[key] = value