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.
18 require 'addressable/uri'
19 require 'addressable/template'
21 require 'google/inflection'
22 require 'google/api_client/errors'
27 def initialize(api, api_name, api_version, discovery_document)
28 # This constructor is super-long, but hard to break up due to the
29 # unavoidable dependence on closures and execution context.
31 @discovery_document = discovery_document
33 Google::INFLECTOR.camelize(api_name)
35 Google::INFLECTOR.camelize(api_version).gsub('.', '_')
36 @schema_name = @discovery_document['id']
37 if Google::APIClient::Schema.const_defined?(api_name_string)
38 @api_name = Google::APIClient::Schema.const_get(api_name_string)
40 @api_name = Google::APIClient::Schema.const_set(
41 api_name_string, Class.new
44 if @api_name.const_defined?(api_version_string)
45 @api_version = @api_name.const_get(api_version_string)
47 @api_version = @api_name.const_set(api_version_string, Class.new)
49 if @api_version.const_defined?(@schema_name)
50 @schema_class = @api_version.const_get(@schema_name)
53 @schema_class = @api_version.const_set(
55 Class.new(APIObject) do |klass|
56 discovery_document['properties'].each do |(k, v)|
57 property_name = Google::INFLECTOR.underscore(k)
58 define_method(:schema) { schema }
59 define_method(property_name + '_property') do
64 define_string_property(property_name, k, v)
66 define_boolean_property(property_name, k, v)
68 define_number_property(property_name, k, v)
70 define_object_property(property_name, k, v)
72 # Either type 'any' or we don't know what this is,
73 # default to anything goes.
74 define_any_property(property_name, k, v)
91 # Returns a <code>String</code> representation of the resource's state.
93 # @return [String] The resource's state, as a <code>String</code>.
96 "#<%s:%#0x CLASS:%s>",
97 self.class.to_s, self.object_id, self.schema_class.name
103 def self.define_string_property(property_name, key, schema)
104 define_method(property_name) do
105 self[key] ||= schema['default']
106 if schema['format'] == 'byte' && self[key] != nil
107 Base64.decode64(self[key])
108 elsif schema['format'] == 'date-time' && self[key] != nil
109 Time.parse(self[key])
110 elsif schema['format'] =~ /^u?int(32|64)$/ && self[key] != nil
116 define_method(property_name + '=') do |value|
117 if schema['format'] == 'byte'
118 self[key] = Base64.encode64(value)
119 elsif schema['format'] == 'date-time'
120 if value.respond_to?(:to_str)
121 value = Time.parse(value.to_str)
122 elsif !value.respond_to?(:xmlschema)
124 "Could not obtain RFC 3339 timestamp from #{value.class}."
126 self[key] = value.xmlschema
127 elsif schema['format'] =~ /^u?int(32|64)$/
128 self[key] = value.to_s
129 elsif value.respond_to?(:to_str)
130 self[key] = value.to_str
131 elsif value.kind_of?(Symbol)
132 self[key] = value.to_s
135 "Expected String or Symbol, got #{value.class}."
140 def self.define_boolean_property(property_name, key, schema)
141 define_method(property_name) do
142 self[key] ||= schema['default']
143 case self[key].to_s.downcase
144 when 'true', 'yes', 'y', 'on', '1'
146 when 'false', 'no', 'n', 'off', '0'
152 "Expected boolean, got #{self[key].class}."
155 define_method(property_name + '=') do |value|
156 case value.to_s.downcase
157 when 'true', 'yes', 'y', 'on', '1'
159 when 'false', 'no', 'n', 'off', '0'
164 raise TypeError, "Expected boolean, got #{value.class}."
169 def self.define_number_property(property_name, key, schema)
170 define_method(property_name) do
171 self[key] ||= schema['default']
172 if self[key] != nil && !self[key].respond_to?(:to_f)
174 "Expected Float, got #{self[key].class}."
175 elsif self[key] != nil && self[key].respond_to?(:to_f)
181 define_method(property_name + '=') do |value|
185 case schema['format']
186 when 'double', 'float'
187 if value.respond_to?(:to_f)
188 self[key] = value.to_f
191 "Expected String or Symbol, got #{value.class}."
195 "Unexpected type format for number: #{schema['format']}."
201 def self.define_object_property(property_name, key, schema)
203 # Do we treat this differently from any?
204 self.define_any_property(property_name, key, schema)
207 def self.define_any_property(property_name, key, schema)
208 define_method(property_name) do
209 self[k] || v['default']
211 define_method(property_name + '=') do |value|
225 return @data[key] = value