wadl 0.1.2 → 0.1.3.1
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.
- data/README +1 -1
- data/Rakefile +1 -1
- data/TODO +2 -0
- data/examples/README +2 -0
- data/lib/wadl.rb +24 -1234
- data/lib/wadl/address.rb +193 -0
- data/lib/wadl/application.rb +71 -0
- data/lib/wadl/cheap_schema.rb +343 -0
- data/lib/wadl/documentation.rb +41 -0
- data/lib/wadl/fault.rb +48 -0
- data/lib/wadl/fault_format.rb +67 -0
- data/lib/wadl/has_docs.rb +49 -0
- data/lib/wadl/http_method.rb +108 -0
- data/lib/wadl/link.rb +40 -0
- data/lib/wadl/option.rb +40 -0
- data/lib/wadl/param.rb +134 -0
- data/lib/wadl/representation_container.rb +47 -0
- data/lib/wadl/representation_format.rb +73 -0
- data/lib/wadl/request_format.rb +68 -0
- data/lib/wadl/resource.rb +163 -0
- data/lib/wadl/resource_and_address.rb +108 -0
- data/lib/wadl/resource_container.rb +58 -0
- data/lib/wadl/resource_type.rb +44 -0
- data/lib/wadl/resources.rb +44 -0
- data/lib/wadl/response.rb +36 -0
- data/lib/wadl/response_format.rb +108 -0
- data/lib/wadl/uri_parts.rb +63 -0
- data/lib/wadl/version.rb +1 -1
- data/lib/wadl/xml_representation.rb +64 -0
- data/test/test_wadl.rb +7 -0
- metadata +43 -8
data/lib/wadl/address.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# #
|
4
|
+
# A component of wadl, the super cheap Ruby WADL client. #
|
5
|
+
# #
|
6
|
+
# Copyright (C) 2006-2008 Leonard Richardson #
|
7
|
+
# Copyright (C) 2010 Jens Wille #
|
8
|
+
# #
|
9
|
+
# Authors: #
|
10
|
+
# Leonard Richardson <leonardr@segfault.org> (Original author) #
|
11
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
12
|
+
# #
|
13
|
+
# wadl is free software; you can redistribute it and/or modify it under the #
|
14
|
+
# terms of the GNU General Public License as published by the Free Software #
|
15
|
+
# Foundation; either version 3 of the License, or (at your option) any later #
|
16
|
+
# version. #
|
17
|
+
# #
|
18
|
+
# wadl is distributed in the hope that it will be useful, but WITHOUT ANY #
|
19
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
20
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
21
|
+
# details. #
|
22
|
+
# #
|
23
|
+
# You should have received a copy of the GNU General Public License along #
|
24
|
+
# with wadl. If not, see <http://www.gnu.org/licenses/>. #
|
25
|
+
# #
|
26
|
+
###############################################################################
|
27
|
+
#++
|
28
|
+
|
29
|
+
require 'wadl'
|
30
|
+
|
31
|
+
module WADL
|
32
|
+
|
33
|
+
# The Address class keeps track of the user's path through a resource
|
34
|
+
# graph. Values for WADL parameters may be specified at any time using
|
35
|
+
# the bind method. An Address cannot be turned into a URI and header
|
36
|
+
# set until all required parameters have been bound to values.
|
37
|
+
#
|
38
|
+
# An Address object is built up through calls to Resource#address
|
39
|
+
|
40
|
+
class Address
|
41
|
+
|
42
|
+
attr_reader :path_fragments, :query_vars, :headers,
|
43
|
+
:path_params, :query_params, :header_params
|
44
|
+
|
45
|
+
def self.embedded_param_names(fragment)
|
46
|
+
fragment.scan(/\{(.+?)\}/).flatten
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(path_fragments = [], query_vars = [], headers = {},
|
50
|
+
path_params = {}, query_params = {}, header_params = {})
|
51
|
+
@path_fragments, @query_vars, @headers = path_fragments, query_vars, headers
|
52
|
+
@path_params, @query_params, @header_params = path_params, query_params, header_params
|
53
|
+
end
|
54
|
+
|
55
|
+
# Perform a deep copy.
|
56
|
+
def deep_copy
|
57
|
+
Address.new(
|
58
|
+
_deep_copy_array(@path_fragments),
|
59
|
+
_deep_copy_array(@query_vars),
|
60
|
+
_deep_copy_hash(@headers),
|
61
|
+
@path_params.dup,
|
62
|
+
@query_params.dup,
|
63
|
+
@header_params.dup
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
"Address:\n" <<
|
69
|
+
" Path fragments: #{@path_fragments.inspect}\n" <<
|
70
|
+
" Query variables: #{@query_vars.inspect}\n" <<
|
71
|
+
" Header variables: #{@headers.inspect}\n" <<
|
72
|
+
" Unbound path parameters: #{@path_params.inspect}\n" <<
|
73
|
+
" Unbound query parameters: #{@query_params.inspect}\n" <<
|
74
|
+
" Unbound header parameters: #{@header_params.inspect}\n"
|
75
|
+
end
|
76
|
+
|
77
|
+
alias_method :inspect, :to_s
|
78
|
+
|
79
|
+
# Binds some or all of the unbound variables in this address to values.
|
80
|
+
def bind!(args = {})
|
81
|
+
path_var_values = args[:path] || {}
|
82
|
+
query_var_values = args[:query] || {}
|
83
|
+
header_var_values = args[:headers] || {}
|
84
|
+
|
85
|
+
# Bind variables found in the path fragments.
|
86
|
+
path_params_to_delete = []
|
87
|
+
|
88
|
+
path_fragments.each { |fragment|
|
89
|
+
if fragment.respond_to?(:to_str)
|
90
|
+
# This fragment is a string which might contain {} substitutions.
|
91
|
+
# Make any substitutions available to the provided path variables.
|
92
|
+
self.class.embedded_param_names(fragment).each { |param_name|
|
93
|
+
value = path_var_values[param_name] || path_var_values[param_name.to_sym]
|
94
|
+
|
95
|
+
value = if param = path_params[param_name]
|
96
|
+
path_params_to_delete << param
|
97
|
+
param % value
|
98
|
+
else
|
99
|
+
Param.default.format(value, param_name)
|
100
|
+
end
|
101
|
+
|
102
|
+
fragment.gsub!("{#{param_name}}", value)
|
103
|
+
}
|
104
|
+
else
|
105
|
+
# This fragment is an array of Param objects (style 'matrix'
|
106
|
+
# or 'plain') which may be bound to strings. As substitutions
|
107
|
+
# happen, the array will become a mixed array of Param objects
|
108
|
+
# and strings.
|
109
|
+
fragment.each_with_index { |param, i|
|
110
|
+
if param.respond_to?(:name)
|
111
|
+
name = param.name
|
112
|
+
|
113
|
+
value = path_var_values[name] || path_var_values[name.to_sym]
|
114
|
+
value = param % value
|
115
|
+
fragment[i] = value if value
|
116
|
+
|
117
|
+
path_params_to_delete << param
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
}
|
122
|
+
|
123
|
+
# Delete any embedded path parameters that are now bound from
|
124
|
+
# our list of unbound parameters.
|
125
|
+
path_params_to_delete.each { |p| path_params.delete(p.name) }
|
126
|
+
|
127
|
+
# Bind query variable values to query parameters
|
128
|
+
query_var_values.each { |name, value|
|
129
|
+
param = query_params.delete(name.to_s)
|
130
|
+
query_vars << param % value if param
|
131
|
+
}
|
132
|
+
|
133
|
+
# Bind header variables to header parameters
|
134
|
+
header_var_values.each { |name, value|
|
135
|
+
param = header_params.delete(name.to_s)
|
136
|
+
headers[name] = param % value if param
|
137
|
+
}
|
138
|
+
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
def uri(args = {})
|
143
|
+
obj, uri = deep_copy.bind!(args), ''
|
144
|
+
|
145
|
+
# Build the path
|
146
|
+
obj.path_fragments.flatten.each { |fragment|
|
147
|
+
if fragment.respond_to?(:to_str)
|
148
|
+
embedded_param_names = self.class.embedded_param_names(fragment)
|
149
|
+
|
150
|
+
unless embedded_param_names.empty?
|
151
|
+
raise ArgumentError, %Q{Missing a value for required path parameter "#{embedded_param_names[0]}"!}
|
152
|
+
end
|
153
|
+
|
154
|
+
unless fragment.empty?
|
155
|
+
uri << '/' unless uri.empty? || uri =~ /\/\z/
|
156
|
+
uri << fragment
|
157
|
+
end
|
158
|
+
elsif fragment.required?
|
159
|
+
# This is a required Param that was never bound to a value.
|
160
|
+
raise ArgumentError, %Q{Missing a value for required path parameter "#{fragment.name}"!}
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
164
|
+
# Hunt for required unbound query parameters.
|
165
|
+
obj.query_params.each { |name, value|
|
166
|
+
if value.required?
|
167
|
+
raise ArgumentError, %Q{Missing a value for required query parameter "#{value.name}"!}
|
168
|
+
end
|
169
|
+
}
|
170
|
+
|
171
|
+
# Hunt for required unbound header parameters.
|
172
|
+
obj.header_params.each { |name, value|
|
173
|
+
if value.required?
|
174
|
+
raise ArgumentError, %Q{Missing a value for required header parameter "#{value.name}"!}
|
175
|
+
end
|
176
|
+
}
|
177
|
+
|
178
|
+
URIParts.new(uri, obj.query_vars, obj.headers)
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def _deep_copy_hash(h)
|
184
|
+
h.inject({}) { |h, (k, v)| h[k] = v && v.dup; h }
|
185
|
+
end
|
186
|
+
|
187
|
+
def _deep_copy_array(a)
|
188
|
+
a.inject([]) { |a, e| a << (e && e.dup) }
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# #
|
4
|
+
# A component of wadl, the super cheap Ruby WADL client. #
|
5
|
+
# #
|
6
|
+
# Copyright (C) 2006-2008 Leonard Richardson #
|
7
|
+
# Copyright (C) 2010 Jens Wille #
|
8
|
+
# #
|
9
|
+
# Authors: #
|
10
|
+
# Leonard Richardson <leonardr@segfault.org> (Original author) #
|
11
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
12
|
+
# #
|
13
|
+
# wadl is free software; you can redistribute it and/or modify it under the #
|
14
|
+
# terms of the GNU General Public License as published by the Free Software #
|
15
|
+
# Foundation; either version 3 of the License, or (at your option) any later #
|
16
|
+
# version. #
|
17
|
+
# #
|
18
|
+
# wadl is distributed in the hope that it will be useful, but WITHOUT ANY #
|
19
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
20
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
21
|
+
# details. #
|
22
|
+
# #
|
23
|
+
# You should have received a copy of the GNU General Public License along #
|
24
|
+
# with wadl. If not, see <http://www.gnu.org/licenses/>. #
|
25
|
+
# #
|
26
|
+
###############################################################################
|
27
|
+
#++
|
28
|
+
|
29
|
+
require 'rexml/document'
|
30
|
+
require 'wadl'
|
31
|
+
|
32
|
+
module WADL
|
33
|
+
|
34
|
+
class Application < HasDocs
|
35
|
+
|
36
|
+
in_document 'application'
|
37
|
+
has_one Resources
|
38
|
+
has_many HTTPMethod, RepresentationFormat, FaultFormat
|
39
|
+
|
40
|
+
def self.from_wadl(wadl)
|
41
|
+
wadl = wadl.read if wadl.respond_to?(:read)
|
42
|
+
doc = REXML::Document.new(wadl)
|
43
|
+
|
44
|
+
application = from_element(nil, doc.root, need_finalization = [])
|
45
|
+
need_finalization.each { |x| x.finalize_creation }
|
46
|
+
|
47
|
+
application
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_resource(symbol, *args, &block)
|
51
|
+
resource_list.find_resource(symbol, *args, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def resource(symbol)
|
55
|
+
resource_list.resource(symbol)
|
56
|
+
end
|
57
|
+
|
58
|
+
def find_resource_by_path(symbol, *args, &block)
|
59
|
+
resource_list.find_resource_by_path(symbol, *args, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def finalize_creation
|
63
|
+
resource_list.resources.each { |r|
|
64
|
+
define_singleton(r, :id, 'resource_list.find_resource')
|
65
|
+
define_singleton(r, :path, 'resource_list.find_resource_by_path')
|
66
|
+
} if resource_list
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
#--
|
2
|
+
###############################################################################
|
3
|
+
# #
|
4
|
+
# A component of wadl, the super cheap Ruby WADL client. #
|
5
|
+
# #
|
6
|
+
# Copyright (C) 2006-2008 Leonard Richardson #
|
7
|
+
# Copyright (C) 2010 Jens Wille #
|
8
|
+
# #
|
9
|
+
# Authors: #
|
10
|
+
# Leonard Richardson <leonardr@segfault.org> (Original author) #
|
11
|
+
# Jens Wille <jens.wille@uni-koeln.de> #
|
12
|
+
# #
|
13
|
+
# wadl is free software; you can redistribute it and/or modify it under the #
|
14
|
+
# terms of the GNU General Public License as published by the Free Software #
|
15
|
+
# Foundation; either version 3 of the License, or (at your option) any later #
|
16
|
+
# version. #
|
17
|
+
# #
|
18
|
+
# wadl is distributed in the hope that it will be useful, but WITHOUT ANY #
|
19
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
|
20
|
+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more #
|
21
|
+
# details. #
|
22
|
+
# #
|
23
|
+
# You should have received a copy of the GNU General Public License along #
|
24
|
+
# with wadl. If not, see <http://www.gnu.org/licenses/>. #
|
25
|
+
# #
|
26
|
+
###############################################################################
|
27
|
+
#++
|
28
|
+
|
29
|
+
require 'wadl'
|
30
|
+
|
31
|
+
module WADL
|
32
|
+
|
33
|
+
# A cheap way of defining an XML schema as Ruby classes and then parsing
|
34
|
+
# documents into instances of those classes.
|
35
|
+
|
36
|
+
class CheapSchema
|
37
|
+
|
38
|
+
@may_be_reference = false
|
39
|
+
@contents_are_mixed_data = false
|
40
|
+
|
41
|
+
ATTRIBUTES = %w[names members collections required_attributes attributes]
|
42
|
+
|
43
|
+
class << self
|
44
|
+
|
45
|
+
attr_reader(*ATTRIBUTES)
|
46
|
+
|
47
|
+
def init
|
48
|
+
@names, @members, @collections = {}, {}, {}
|
49
|
+
@required_attributes, @attributes = [], []
|
50
|
+
end
|
51
|
+
|
52
|
+
def inherit(from)
|
53
|
+
init
|
54
|
+
|
55
|
+
ATTRIBUTES.each { |attr|
|
56
|
+
value = from.send(attr)
|
57
|
+
instance_variable_set("@#{attr}", value.dup) if value
|
58
|
+
}
|
59
|
+
|
60
|
+
%w[may_be_reference contents_are_mixed_data].each { |attr|
|
61
|
+
instance_variable_set("@#{attr}", from.instance_variable_get("@#{attr}"))
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def inherited(klass)
|
66
|
+
klass.inherit(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def may_be_reference?
|
70
|
+
@may_be_reference
|
71
|
+
end
|
72
|
+
|
73
|
+
def in_document(element_name)
|
74
|
+
@names[:element] = element_name
|
75
|
+
@names[:member] = element_name
|
76
|
+
@names[:collection] = element_name + 's'
|
77
|
+
end
|
78
|
+
|
79
|
+
def as_collection(collection_name)
|
80
|
+
@names[:collection] = collection_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def as_member(member_name)
|
84
|
+
@names[:member] = member_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def contents_are_mixed_data
|
88
|
+
@contents_are_mixed_data = true
|
89
|
+
end
|
90
|
+
|
91
|
+
def has_one(*classes)
|
92
|
+
classes.each { |klass|
|
93
|
+
@members[klass.names[:element]] = klass
|
94
|
+
dereferencing_instance_accessor(klass.names[:member])
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def has_many(*classes)
|
99
|
+
classes.each { |klass|
|
100
|
+
@collections[klass.names[:element]] = klass
|
101
|
+
|
102
|
+
collection_name = klass.names[:collection]
|
103
|
+
dereferencing_instance_accessor(collection_name)
|
104
|
+
|
105
|
+
# Define a method for finding a specific element of this
|
106
|
+
# collection.
|
107
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
108
|
+
def find_#{klass.names[:element]}(*args, &block)
|
109
|
+
block ||= begin
|
110
|
+
name = args.shift.to_s
|
111
|
+
lambda { |match| match.matches?(name) }
|
112
|
+
end
|
113
|
+
|
114
|
+
auto_dereference = args.shift
|
115
|
+
auto_dereference = true if auto_dereference.nil?
|
116
|
+
|
117
|
+
match = #{collection_name}.find { |match|
|
118
|
+
block[match] || (
|
119
|
+
#{klass}.may_be_reference? &&
|
120
|
+
auto_dereference &&
|
121
|
+
block[match.dereference]
|
122
|
+
)
|
123
|
+
}
|
124
|
+
|
125
|
+
match && auto_dereference ? match.dereference : match
|
126
|
+
end
|
127
|
+
EOT
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
def dereferencing_instance_accessor(*symbols)
|
132
|
+
define_dereferencing_accessors(symbols,
|
133
|
+
'd, v = dereference, :@%s; ' <<
|
134
|
+
'd.instance_variable_get(v) if d.instance_variable_defined?(v)',
|
135
|
+
'dereference.instance_variable_set(:@%s, value)'
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
def dereferencing_attr_accessor(*symbols)
|
140
|
+
define_dereferencing_accessors(symbols,
|
141
|
+
'dereference.attributes["%s"]',
|
142
|
+
'dereference.attributes["%s"] = value'
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
def has_attributes(*names)
|
147
|
+
has_required_or_attributes(names, @attributes)
|
148
|
+
end
|
149
|
+
|
150
|
+
def has_required(*names)
|
151
|
+
has_required_or_attributes(names, @required_attributes)
|
152
|
+
end
|
153
|
+
|
154
|
+
def may_be_reference
|
155
|
+
@may_be_reference = true
|
156
|
+
|
157
|
+
find_method_name = "find_#{names[:element]}"
|
158
|
+
|
159
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
160
|
+
def dereference
|
161
|
+
return self unless href = attributes['href']
|
162
|
+
|
163
|
+
unless @referenced
|
164
|
+
p = self
|
165
|
+
|
166
|
+
until @referenced || !p
|
167
|
+
begin
|
168
|
+
p = p.parent
|
169
|
+
end until !p || p.respond_to?(:#{find_method_name})
|
170
|
+
|
171
|
+
@referenced = p.#{find_method_name}(href, false) if p
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
dereference_with_context(@referenced) if @referenced
|
176
|
+
end
|
177
|
+
EOT
|
178
|
+
end
|
179
|
+
|
180
|
+
# Turn an XML element into an instance of this class.
|
181
|
+
def from_element(parent, element, need_finalization)
|
182
|
+
attributes = element.attributes
|
183
|
+
|
184
|
+
me = new
|
185
|
+
me.parent = parent
|
186
|
+
|
187
|
+
@collections.each { |name, klass|
|
188
|
+
me.instance_variable_set("@#{klass.names[:collection]}", [])
|
189
|
+
}
|
190
|
+
|
191
|
+
if may_be_reference? and href = attributes['href']
|
192
|
+
# Handle objects that are just references to other objects
|
193
|
+
# somewhere above this one in the hierarchy
|
194
|
+
href = href.dup
|
195
|
+
href.sub!(/\A#/, '') or warn "Warning: HREF #{href} should be ##{href}"
|
196
|
+
|
197
|
+
me.attributes['href'] = href
|
198
|
+
else
|
199
|
+
# Handle this element's attributes
|
200
|
+
@required_attributes.each { |name|
|
201
|
+
name = name.to_s
|
202
|
+
|
203
|
+
raise ArgumentError, %Q{Missing required attribute "#{name}" in element: #{element}} unless attributes[name]
|
204
|
+
|
205
|
+
me.attributes[name] = attributes[name]
|
206
|
+
me.index_key = attributes[name] if name == @index_attribute
|
207
|
+
}
|
208
|
+
|
209
|
+
@attributes.each { |name|
|
210
|
+
name = name.to_s
|
211
|
+
|
212
|
+
me.attributes[name] = attributes[name]
|
213
|
+
me.index_key = attributes[name] if name == @index_attribute
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
# Handle this element's children.
|
218
|
+
if @contents_are_mixed_data
|
219
|
+
me.instance_variable_set(:@contents, element.children)
|
220
|
+
else
|
221
|
+
element.each_element { |child|
|
222
|
+
if klass = @members[child.name] || @collections[child.name]
|
223
|
+
object = klass.from_element(me, child, need_finalization)
|
224
|
+
|
225
|
+
if klass == @members[child.name]
|
226
|
+
instance_variable_name = "@#{klass.names[:member]}"
|
227
|
+
|
228
|
+
if me.instance_variable_defined?(instance_variable_name)
|
229
|
+
raise "#{name} can only have one #{klass.name}, but several were specified in element: #{element}"
|
230
|
+
end
|
231
|
+
|
232
|
+
me.instance_variable_set(instance_variable_name, object)
|
233
|
+
else
|
234
|
+
me.instance_variable_get("@#{klass.names[:collection]}") << object
|
235
|
+
end
|
236
|
+
end
|
237
|
+
}
|
238
|
+
end
|
239
|
+
|
240
|
+
need_finalization << me if me.respond_to?(:finalize_creation)
|
241
|
+
|
242
|
+
me
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
def define_dereferencing_accessors(symbols, getter, setter)
|
248
|
+
symbols.each { |name|
|
249
|
+
name = name.to_s
|
250
|
+
|
251
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1 unless name =~ /\W/
|
252
|
+
def #{name}; #{getter % name}; end
|
253
|
+
def #{name}=(value); #{setter % name}; end
|
254
|
+
EOT
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
def has_required_or_attributes(names, var)
|
259
|
+
names.each { |name|
|
260
|
+
var << name
|
261
|
+
@index_attribute ||= name.to_s
|
262
|
+
name == :href ? attr_accessor(name) : dereferencing_attr_accessor(name)
|
263
|
+
}
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
attr_accessor :index_key, :href, :parent
|
269
|
+
attr_reader :attributes
|
270
|
+
|
271
|
+
def initialize
|
272
|
+
@attributes, @contents, @referenced = {}, nil, nil
|
273
|
+
end
|
274
|
+
|
275
|
+
# This object is a reference to another object. This method returns
|
276
|
+
# an object that acts like the other object, but also contains any
|
277
|
+
# neccessary context about this object. See the ResourceAndAddress
|
278
|
+
# implementation, in which a dereferenced resource contains
|
279
|
+
# information about the parent of the resource that referenced it
|
280
|
+
# (otherwise, there's no way to build the URI).
|
281
|
+
def dereference_with_context(referent)
|
282
|
+
referent
|
283
|
+
end
|
284
|
+
|
285
|
+
# A null implementation so that foo.dereference will always return the
|
286
|
+
# "real" object.
|
287
|
+
def dereference
|
288
|
+
self
|
289
|
+
end
|
290
|
+
|
291
|
+
# Returns whether or not the given name matches this object.
|
292
|
+
# By default, checks the index key for this class.
|
293
|
+
def matches?(name)
|
294
|
+
index_key == name
|
295
|
+
end
|
296
|
+
|
297
|
+
def to_s(indent = 0)
|
298
|
+
klass = self.class
|
299
|
+
|
300
|
+
i = ' ' * indent
|
301
|
+
s = "#{i}#{klass.name}\n"
|
302
|
+
|
303
|
+
if klass.may_be_reference? and href = attributes['href']
|
304
|
+
s << "#{i} href=#{href}\n"
|
305
|
+
else
|
306
|
+
[klass.required_attributes, klass.attributes].each { |list|
|
307
|
+
list.each { |attr|
|
308
|
+
val = attributes[attr.to_s]
|
309
|
+
s << "#{i} #{attr}=#{val}\n" if val
|
310
|
+
}
|
311
|
+
}
|
312
|
+
|
313
|
+
klass.members.each_value { |member_class|
|
314
|
+
o = send(member_class.names[:member])
|
315
|
+
s << o.to_s(indent + 1) if o
|
316
|
+
}
|
317
|
+
|
318
|
+
klass.collections.each_value { |collection_class|
|
319
|
+
c = send(collection_class.names[:collection])
|
320
|
+
|
321
|
+
if c && !c.empty?
|
322
|
+
s << "#{i} Collection of #{c.size} #{collection_class.name}(s)\n"
|
323
|
+
c.each { |o| s << o.to_s(indent + 2) }
|
324
|
+
end
|
325
|
+
}
|
326
|
+
|
327
|
+
if @contents && !@contents.empty?
|
328
|
+
sep = '-' * 80
|
329
|
+
s << "#{sep}\n#{@contents.join(' ')}\n#{sep}\n"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
s
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
# Simple backport for Ruby <= 1.8.5
|
341
|
+
class Object # :nodoc:
|
342
|
+
def instance_variable_defined?(sym); instance_eval("defined?(#{sym})"); end
|
343
|
+
end unless Object.method_defined?(:instance_variable_defined?)
|