angus-remote 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/angus-remote.rb +4 -0
- data/lib/angus/remote/builder.rb +204 -0
- data/lib/angus/remote/client.rb +79 -0
- data/lib/angus/remote/exceptions.rb +50 -0
- data/lib/angus/remote/http/multipart.rb +54 -0
- data/lib/angus/remote/http/multipart_methods/multipart_base.rb +36 -0
- data/lib/angus/remote/http/multipart_methods/multipart_post.rb +11 -0
- data/lib/angus/remote/http/multipart_methods/multipart_put.rb +11 -0
- data/lib/angus/remote/http/query_params.rb +53 -0
- data/lib/angus/remote/message.rb +14 -0
- data/lib/angus/remote/proxy_client.rb +58 -0
- data/lib/angus/remote/proxy_client_utils.rb +70 -0
- data/lib/angus/remote/remote_response.rb +44 -0
- data/lib/angus/remote/representation.rb +18 -0
- data/lib/angus/remote/response/builder.rb +308 -0
- data/lib/angus/remote/response/hash.rb +47 -0
- data/lib/angus/remote/response/serializer.rb +43 -0
- data/lib/angus/remote/service_directory.rb +217 -0
- data/lib/angus/remote/utils.rb +119 -0
- data/lib/angus/remote/version.rb +4 -2
- data/lib/angus/unmarshalling.rb +33 -0
- data/spec/angus/remote/builder_spec.rb +105 -0
- data/spec/angus/remote/client_spec.rb +75 -0
- data/spec/angus/remote/http/multipart_methods/multipart_base_spec.rb +36 -0
- data/spec/angus/remote/http/multipart_spec.rb +120 -0
- data/spec/angus/remote/http/query_params_spec.rb +28 -0
- data/spec/angus/remote/proxy_client_utils_spec.rb +102 -0
- data/spec/angus/remote/response/builder_spec.rb +69 -0
- data/spec/angus/remote/service_directory_spec.rb +76 -0
- data/spec/angus/remote/utils_spec.rb +204 -0
- metadata +192 -32
- data/.gitignore +0 -17
- data/Gemfile +0 -4
- data/LICENSE.txt +0 -22
- data/README.md +0 -29
- data/Rakefile +0 -1
- data/angus-remote.gemspec +0 -23
- data/lib/angus/remote.rb +0 -7
@@ -0,0 +1,53 @@
|
|
1
|
+
module Http
|
2
|
+
module QueryParams
|
3
|
+
|
4
|
+
# @return <String> This hash as a query string
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# { :name => "Bob",
|
8
|
+
# :address => {
|
9
|
+
# :street => '111 Ruby Ave.',
|
10
|
+
# :city => 'Ruby Central',
|
11
|
+
# :phones => ['111-111-1111', '222-222-2222']
|
12
|
+
# }
|
13
|
+
# }.to_params
|
14
|
+
# #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave."
|
15
|
+
def self.to_params(hash)
|
16
|
+
params = hash.map { |k,v| normalize_param(k,v) }.join
|
17
|
+
params.chop! # trailing &
|
18
|
+
params
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param key<Object> The key for the param.
|
22
|
+
# @param value<Object> The value for the param.
|
23
|
+
#
|
24
|
+
# @return <String> This key value pair as a param
|
25
|
+
#
|
26
|
+
# @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
|
27
|
+
def self.normalize_param(key, value)
|
28
|
+
param = ''
|
29
|
+
stack = []
|
30
|
+
|
31
|
+
if value.is_a?(Array)
|
32
|
+
param << value.map { |element| normalize_param("#{key}[]", element) }.join
|
33
|
+
elsif value.is_a?(Hash)
|
34
|
+
stack << [key,value]
|
35
|
+
else
|
36
|
+
param << "#{key}=#{URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&"
|
37
|
+
end
|
38
|
+
|
39
|
+
stack.each do |parent, hash|
|
40
|
+
hash.each do |k, v|
|
41
|
+
if v.is_a?(Hash)
|
42
|
+
stack << ["#{parent}[#{k}]", v]
|
43
|
+
else
|
44
|
+
param << normalize_param("#{parent}[#{k}]", v)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
param
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'persistent_http'
|
3
|
+
|
4
|
+
require_relative 'exceptions'
|
5
|
+
require_relative 'proxy_client_utils'
|
6
|
+
|
7
|
+
module Angus
|
8
|
+
module Remote
|
9
|
+
|
10
|
+
# A client for service invocation when proxing requests.
|
11
|
+
class ProxyClient
|
12
|
+
|
13
|
+
DEFAULT_TIMEOUT = 10
|
14
|
+
|
15
|
+
def initialize(url, timeout = DEFAULT_TIMEOUT)
|
16
|
+
url = url[0..-2] if url[-1] == '/'
|
17
|
+
|
18
|
+
@connection = PersistentHTTP.new(
|
19
|
+
:pool_size => 4,
|
20
|
+
:pool_timeout => 10,
|
21
|
+
:warn_timeout => 0.25,
|
22
|
+
:force_retry => false,
|
23
|
+
:url => url,
|
24
|
+
|
25
|
+
:read_timeout => timeout,
|
26
|
+
:open_timeout => timeout
|
27
|
+
)
|
28
|
+
|
29
|
+
@api_base_path = @connection.default_path
|
30
|
+
end
|
31
|
+
|
32
|
+
# Makes a request to the service
|
33
|
+
#
|
34
|
+
def make_request(method, path, query, headers = {}, body = nil)
|
35
|
+
full_path = @api_base_path + path
|
36
|
+
|
37
|
+
request = ProxyClientUtils.build_request(method, full_path, query, headers, body)
|
38
|
+
|
39
|
+
begin
|
40
|
+
response = @connection.request(request)
|
41
|
+
|
42
|
+
from_headers = ProxyClientUtils.normalize_headers(
|
43
|
+
ProxyClientUtils.filter_response_headers(response.to_hash)
|
44
|
+
)
|
45
|
+
|
46
|
+
[response.code.to_i, from_headers, [response.body]]
|
47
|
+
rescue Errno::ECONNREFUSED
|
48
|
+
raise RemoteConnectionError.new(self.class.base_uri)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"#<#{self.class}:#{object_id}>"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
require_relative 'exceptions'
|
5
|
+
require_relative 'http/query_params'
|
6
|
+
|
7
|
+
module Angus
|
8
|
+
module Remote
|
9
|
+
|
10
|
+
module ProxyClientUtils
|
11
|
+
|
12
|
+
ALLOWED_RESPONSE_HEADERS = ['content-type']
|
13
|
+
|
14
|
+
def self.build_request(method, path, query, headers = {}, body = nil)
|
15
|
+
uri = URI(path)
|
16
|
+
uri.query = query
|
17
|
+
|
18
|
+
full_uri = uri.to_s
|
19
|
+
|
20
|
+
request = case method.to_s.downcase
|
21
|
+
when 'get'
|
22
|
+
Net::HTTP::Get.new(full_uri)
|
23
|
+
when 'post'
|
24
|
+
Net::HTTP::Post.new(full_uri)
|
25
|
+
when 'put'
|
26
|
+
Net::HTTP::Put.new(full_uri)
|
27
|
+
when 'delete'
|
28
|
+
Net::HTTP::Delete.new(full_uri)
|
29
|
+
else
|
30
|
+
raise MethodArgumentError.new(method)
|
31
|
+
end
|
32
|
+
|
33
|
+
headers.each do |k, v|
|
34
|
+
request[k] = v
|
35
|
+
end
|
36
|
+
|
37
|
+
request.body = body
|
38
|
+
|
39
|
+
request
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.filter_response_headers(headers)
|
43
|
+
headers.select { |h, v| ALLOWED_RESPONSE_HEADERS.include?(h) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Converts any header value that is an array to its first value.
|
47
|
+
#
|
48
|
+
# @param [Hash] header
|
49
|
+
#
|
50
|
+
# @return [Hash]
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# normalize_headers({'content-type'=>['application/json;charset=utf-8']})
|
54
|
+
#
|
55
|
+
# -> {'content-type'=>'application/json;charset=utf-8'}
|
56
|
+
def self.normalize_headers(headers)
|
57
|
+
normalized = headers.map do |h, v|
|
58
|
+
if v.is_a?(Array)
|
59
|
+
[h, v.first]
|
60
|
+
else
|
61
|
+
[h, v]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Hash[normalized]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'response/hash'
|
2
|
+
|
3
|
+
module Angus
|
4
|
+
module Remote
|
5
|
+
|
6
|
+
# A service's response
|
7
|
+
#
|
8
|
+
# Acts as an array to store information at HTTP level, like the status_code
|
9
|
+
class RemoteResponse
|
10
|
+
include Angus::Remote::Response::Hash
|
11
|
+
|
12
|
+
attr_accessor :status
|
13
|
+
attr_accessor :status_code
|
14
|
+
attr_accessor :messages
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@http_response_info = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def []=(key, value)
|
21
|
+
@http_response_info[key] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](key)
|
25
|
+
@http_response_info[key]
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"#<#{self.class}:#{object_id}>"
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_hash
|
33
|
+
{
|
34
|
+
:http_status_code => @http_response_info[:status_code],
|
35
|
+
:body => @http_response_info[:body],
|
36
|
+
:service_name => @http_response_info[:service_name],
|
37
|
+
:operation_name => @http_response_info[:operation_name],
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'response/hash'
|
2
|
+
|
3
|
+
module Angus
|
4
|
+
module Remote
|
5
|
+
|
6
|
+
class Representation
|
7
|
+
include Angus::Remote::Response::Hash
|
8
|
+
|
9
|
+
attr_accessor :elements
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@http_response_info = {}
|
13
|
+
@elements = {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,308 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
require 'date'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require_relative '../message'
|
6
|
+
require_relative '../remote_response'
|
7
|
+
|
8
|
+
require_relative 'hash'
|
9
|
+
|
10
|
+
require_relative '../service_directory'
|
11
|
+
|
12
|
+
|
13
|
+
# TODO: move to another gem and possibly change its name
|
14
|
+
require_relative '../../unmarshalling'
|
15
|
+
|
16
|
+
module Angus
|
17
|
+
module Remote
|
18
|
+
module Response
|
19
|
+
|
20
|
+
module Builder
|
21
|
+
|
22
|
+
# Builds a Response
|
23
|
+
#
|
24
|
+
# The r parameter should contain in the body, encoded as json, the values / objects
|
25
|
+
# specified in the operation response metadata
|
26
|
+
#
|
27
|
+
# @param [Integer] status_code HTTP status_code
|
28
|
+
# @param body [String] HTTP body
|
29
|
+
# @param service_name [String] Name of the service that the response belongs to
|
30
|
+
# @param version [String] Version of the service that the response belongs to
|
31
|
+
# @param operation_namespace [String] Namespace of the operation that the response belongs to
|
32
|
+
# @param operation_name [String] Name of the operation that the response belongs to
|
33
|
+
#
|
34
|
+
# @return A Response object that responds to the methods:
|
35
|
+
# - status
|
36
|
+
# - messages
|
37
|
+
#
|
38
|
+
# Also, provides one method for each value / object / array returned
|
39
|
+
def self.build(status_code, body, service_code_name, version, operation_namespace,
|
40
|
+
operation_code_name)
|
41
|
+
service_definition = Angus::Remote::ServiceDirectory.service_definition(
|
42
|
+
service_code_name, version
|
43
|
+
)
|
44
|
+
|
45
|
+
representations = service_definition.representations
|
46
|
+
glossary = service_definition.glossary
|
47
|
+
|
48
|
+
operation_definition = service_definition.operation_definition(operation_namespace,
|
49
|
+
operation_code_name)
|
50
|
+
|
51
|
+
json_response = JSON(body)
|
52
|
+
|
53
|
+
response_class = build_response_class(operation_definition.name)
|
54
|
+
|
55
|
+
response = response_class.new
|
56
|
+
|
57
|
+
# TODO use constants
|
58
|
+
response[:status_code] = status_code
|
59
|
+
response[:body] = body
|
60
|
+
response[:service_code_name] = service_code_name
|
61
|
+
response[:service_version] = version
|
62
|
+
response[:operation_namespace] = operation_namespace
|
63
|
+
response[:operation_code_name] = operation_code_name
|
64
|
+
|
65
|
+
response.status = json_response['status']
|
66
|
+
response.messages = self.build_messages(json_response['messages'])
|
67
|
+
|
68
|
+
representations_hash = self.representations_hash(representations)
|
69
|
+
glossary_terms_hash = glossary.terms_hash
|
70
|
+
|
71
|
+
operation_definition.response_elements.each do |element|
|
72
|
+
self.build_response_method(json_response, response_class, response,
|
73
|
+
representations_hash, glossary_terms_hash, element)
|
74
|
+
end
|
75
|
+
|
76
|
+
response
|
77
|
+
end
|
78
|
+
|
79
|
+
# Builds the methods for each value / object / array
|
80
|
+
#
|
81
|
+
# The r parameter should contain in the body, encoded as json, the values / objects
|
82
|
+
# specified in the operation response metadata
|
83
|
+
def self.build_response_method(json_response, response_class, response,
|
84
|
+
representations_hash, glossary_terms_hash, element)
|
85
|
+
if (json_response.has_key?(element.name))
|
86
|
+
hash_value = json_response[element.name]
|
87
|
+
elsif (element.required == false)
|
88
|
+
hash_value = element.default
|
89
|
+
else
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
object_value = nil
|
94
|
+
|
95
|
+
if element.type && representations_hash.include?(element.type)
|
96
|
+
object_value = self.build_from_representation(hash_value, element.type,
|
97
|
+
representations_hash, glossary_terms_hash)
|
98
|
+
elsif element.elements_type
|
99
|
+
object_value = self.build_collection_from_representation(hash_value,
|
100
|
+
element.elements_type,
|
101
|
+
representations_hash,
|
102
|
+
glossary_terms_hash)
|
103
|
+
elsif element.type && element.type.to_sym == :variable
|
104
|
+
object_value = self.build_from_variable_fields(hash_value)
|
105
|
+
elsif element.type
|
106
|
+
begin
|
107
|
+
object_value = Angus::Unmarshalling.unmarshal_scalar(hash_value,
|
108
|
+
element.type.to_sym)
|
109
|
+
rescue ArgumentError
|
110
|
+
object_value = nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Don't apply the glossary to response elements
|
115
|
+
response.elements[element.name] = object_value
|
116
|
+
|
117
|
+
# Instead, apply the glossary to method names
|
118
|
+
getter_method_name = self.apply_glossary(element.name, glossary_terms_hash)
|
119
|
+
|
120
|
+
response_class.send :define_method, getter_method_name do
|
121
|
+
object_value
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Builds a Response based on a service's response
|
126
|
+
#
|
127
|
+
# The remote_response parameter should contain in the body,
|
128
|
+
# encoded as json, the values / objects
|
129
|
+
# specified in the operation response metadata.
|
130
|
+
#
|
131
|
+
# @param [Http] remote_response HTTP response object, must respond to methods :body and :code
|
132
|
+
# @param [String] service_name Name of the invoked service
|
133
|
+
# @param version [String] Version of the invoked service
|
134
|
+
# @param operation_namespace [String] Namespace of the invoked operation
|
135
|
+
# @param operation_name [String] Name of the invoked operation
|
136
|
+
#
|
137
|
+
# @return (see #build)
|
138
|
+
def self.build_from_remote_response(remote_response, service_code_name, version,
|
139
|
+
operation_namespace, operation_code_name)
|
140
|
+
|
141
|
+
status_code = remote_response.code
|
142
|
+
body = remote_response.body
|
143
|
+
|
144
|
+
self.build(status_code, body, service_code_name, version, operation_namespace,
|
145
|
+
operation_code_name)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Searches for a short name in the glossary and returns the corresponding long name
|
149
|
+
#
|
150
|
+
# If name is not a short name, returns the name
|
151
|
+
#
|
152
|
+
# @param [String] name
|
153
|
+
# @param [Hash<String, GlossaryTerm>] glossary_terms_hash, where keys are short names
|
154
|
+
#
|
155
|
+
# @return long name
|
156
|
+
def self.apply_glossary(name, glossary_terms_hash)
|
157
|
+
if glossary_terms_hash.include?(name)
|
158
|
+
name = glossary_terms_hash[name].long_name
|
159
|
+
end
|
160
|
+
|
161
|
+
return name
|
162
|
+
end
|
163
|
+
|
164
|
+
# Build a response class for the operation
|
165
|
+
#
|
166
|
+
# @param [String] operation_name the name of the operation
|
167
|
+
#
|
168
|
+
# @return [Class] A class client, that inherits from {Angus::Remote::Response}
|
169
|
+
def self.build_response_class(operation_name)
|
170
|
+
response_class = Class.new(Angus::Remote::RemoteResponse)
|
171
|
+
response_class.class_eval <<-END
|
172
|
+
def self.name
|
173
|
+
"#<Response_#{operation_name.gsub(' ', '_')}>"
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.to_s
|
177
|
+
name
|
178
|
+
end
|
179
|
+
END
|
180
|
+
|
181
|
+
response_class
|
182
|
+
end
|
183
|
+
|
184
|
+
# Receives an array of messages and returns an array of Message objects
|
185
|
+
def self.build_messages(messages)
|
186
|
+
messages ||= []
|
187
|
+
|
188
|
+
messages.map do |m|
|
189
|
+
message = Angus::Remote::Message.new
|
190
|
+
message.description = m['dsc']
|
191
|
+
message.key = m['key']
|
192
|
+
message.level = m['level']
|
193
|
+
|
194
|
+
message
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Receives a hash, a type and an array of representations and
|
199
|
+
# build an object that has one method for each attribute of the type.
|
200
|
+
def self.build_from_representation(hash_value, type, representations, glossary_terms_hash)
|
201
|
+
return nil if hash_value.nil?
|
202
|
+
|
203
|
+
representation_class = Class.new do
|
204
|
+
include Angus::Remote::Response::Hash
|
205
|
+
end
|
206
|
+
|
207
|
+
representation_object = representation_class.new
|
208
|
+
|
209
|
+
if representations.include?(type)
|
210
|
+
representation = representations[type]
|
211
|
+
return nil if representation.nil?
|
212
|
+
representation.fields.each do |field|
|
213
|
+
field_raw_value = hash_value[field.name]
|
214
|
+
field_value = nil
|
215
|
+
unless field_raw_value.nil? && field.required == false
|
216
|
+
if field.type && representations.include?(field.type)
|
217
|
+
field_value = self.build_from_representation(field_raw_value, field.type,
|
218
|
+
representations,
|
219
|
+
glossary_terms_hash)
|
220
|
+
elsif field.elements_type
|
221
|
+
field_value = self.build_collection_from_representation(field_raw_value,
|
222
|
+
field.elements_type,
|
223
|
+
representations,
|
224
|
+
glossary_terms_hash)
|
225
|
+
elsif field.type && field.type.to_sym == :variable
|
226
|
+
field_value = self.build_from_variable_fields(field_raw_value)
|
227
|
+
elsif field.type
|
228
|
+
field_value = Angus::Unmarshalling.unmarshal_scalar(field_raw_value,
|
229
|
+
field.type.to_sym)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Don't apply the glossary to response elements
|
234
|
+
representation_object.elements[field.name] = field_value
|
235
|
+
|
236
|
+
# Instead, apply the glossary to method names
|
237
|
+
getter_method_name = self.apply_glossary(field.name, glossary_terms_hash)
|
238
|
+
|
239
|
+
representation_class.send :define_method, field.name.to_sym do
|
240
|
+
field_value
|
241
|
+
end
|
242
|
+
end
|
243
|
+
else
|
244
|
+
if type.to_sym == :variable
|
245
|
+
representation_object = self.build_from_variable_fields(hash_value)
|
246
|
+
else
|
247
|
+
begin
|
248
|
+
representation_object = Angus::Unmarshalling.unmarshal_scalar(hash_value,
|
249
|
+
type.to_sym)
|
250
|
+
rescue ArgumentError
|
251
|
+
representation_object = nil
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
return representation_object
|
258
|
+
end
|
259
|
+
|
260
|
+
# Builds an array of objects that corresponds to the received type
|
261
|
+
def self.build_collection_from_representation(value_array, type, representations,
|
262
|
+
glossary_terms_hash)
|
263
|
+
collection = []
|
264
|
+
|
265
|
+
value_array.each do |raw_value|
|
266
|
+
collection << build_from_representation(raw_value, type, representations,
|
267
|
+
glossary_terms_hash)
|
268
|
+
end
|
269
|
+
|
270
|
+
collection
|
271
|
+
end
|
272
|
+
|
273
|
+
# Builds an object from variable fields
|
274
|
+
def self.build_from_variable_fields(variable_fields_hash)
|
275
|
+
|
276
|
+
return nil if variable_fields_hash.nil?
|
277
|
+
|
278
|
+
representation_class = Class.new do
|
279
|
+
include Angus::Remote::Response::Hash
|
280
|
+
end
|
281
|
+
|
282
|
+
representation_object = representation_class.new
|
283
|
+
|
284
|
+
variable_fields_hash.each do |key_name, field_value|
|
285
|
+
representation_object.elements[key_name] = field_value
|
286
|
+
representation_class.send :define_method, key_name.to_sym do
|
287
|
+
field_value
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
representation_object
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
# Receives an array of representations and returns a hash of representations where
|
296
|
+
# the keys are the representation's names and the values are the representations
|
297
|
+
def self.representations_hash(representations)
|
298
|
+
hash = {}
|
299
|
+
representations.each do |representation|
|
300
|
+
hash[representation.name] = representation
|
301
|
+
end
|
302
|
+
hash
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|