restfulie 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/lib/restfulie/client/atom_media_type.rb +19 -2
- data/lib/restfulie/client/base.rb +36 -31
- data/lib/restfulie/client/cache.rb +103 -0
- data/lib/restfulie/client/entry_point.rb +19 -2
- data/lib/restfulie/client/extensions/http.rb +116 -0
- data/lib/restfulie/client/helper.rb +17 -0
- data/lib/restfulie/client/instance.rb +138 -114
- data/lib/restfulie/client/request_execution.rb +255 -65
- data/lib/restfulie/client/state.rb +18 -1
- data/lib/restfulie/client.rb +29 -4
- data/lib/restfulie/logger.rb +13 -0
- data/lib/restfulie/media_type.rb +25 -6
- data/lib/restfulie/media_type_control.rb +40 -1
- data/lib/restfulie/media_type_defaults.rb +19 -2
- data/lib/restfulie/server/atom_media_type.rb +18 -5
- data/lib/restfulie/server/base.rb +18 -1
- data/lib/restfulie/server/controller.rb +73 -20
- data/lib/restfulie/server/instance.rb +96 -44
- data/lib/restfulie/server/marshalling.rb +16 -0
- data/lib/restfulie/server/opensearch/description.rb +54 -0
- data/lib/restfulie/server/opensearch.rb +18 -0
- data/lib/restfulie/server/restfulie_controller.rb +44 -1
- data/lib/restfulie/server/transition.rb +32 -1
- data/lib/restfulie/unmarshalling.rb +84 -42
- data/lib/restfulie.rb +27 -2
- data/lib/vendor/jeokkarak/hashi.rb +23 -19
- data/lib/vendor/jeokkarak/jeokkarak.rb +16 -0
- metadata +18 -4
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
|
|
5
5
|
require 'spec/rake/spectask'
|
6
6
|
|
7
7
|
GEM = "restfulie"
|
8
|
-
GEM_VERSION = "0.
|
8
|
+
GEM_VERSION = "0.6.0"
|
9
9
|
SUMMARY = "Hypermedia aware resource based library in ruby (client side) and ruby on rails (server side)."
|
10
10
|
AUTHOR = "Guilherme Silveira, Caue Guerra"
|
11
11
|
EMAIL = "guilherme.silveira@caelum.com.br"
|
@@ -18,7 +18,7 @@ spec = Gem::Specification.new do |s|
|
|
18
18
|
s.summary = SUMMARY
|
19
19
|
s.require_paths = ['lib']
|
20
20
|
s.files = FileList['lib/**/*.rb', '[A-Z]*'].to_a
|
21
|
-
|
21
|
+
s.add_dependency("ratom", [">= 0.6.3"])
|
22
22
|
# s.add_dependency("jeokkarak", [">= 1.0.3"])
|
23
23
|
|
24
24
|
# s.add_dependency(%q<rubigen>, [">= 1.3.4"])
|
@@ -1,3 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
1
18
|
module Restfulie
|
2
19
|
|
3
20
|
# TODO break media type registering for DECODING and ENCODING appart, so we can have two files
|
@@ -23,7 +40,7 @@ module Restfulie
|
|
23
40
|
# retrieves the nth element from an atom feed
|
24
41
|
def [](position)
|
25
42
|
|
26
|
-
hash = entry[position].content.
|
43
|
+
hash = entry[position].content.internal_hash
|
27
44
|
hash = hash.dup
|
28
45
|
hash.delete("type")
|
29
46
|
result = Restfulie::MediaType::DefaultMediaTypeDecoder.from_hash(hash)
|
@@ -36,7 +53,7 @@ module Restfulie
|
|
36
53
|
private
|
37
54
|
|
38
55
|
def add_links_to(result, entry)
|
39
|
-
links = entry.link.
|
56
|
+
links = entry.link.internal_hash
|
40
57
|
links = [links] if links.kind_of? Hash
|
41
58
|
self_definition = self_from(links)
|
42
59
|
links << {:rel => "destroy", :method => "delete", :href => self_definition["href"]} unless self_definition.nil?
|
@@ -1,3 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
1
18
|
class Hash
|
2
19
|
def to_object(body)
|
3
20
|
if keys.length>1
|
@@ -15,46 +32,34 @@ module Restfulie
|
|
15
32
|
|
16
33
|
# will execute some action in a specific URI
|
17
34
|
def self.at(uri)
|
18
|
-
Client::RequestExecution.new(nil).at uri
|
35
|
+
Client::RequestExecution.new(nil, nil).at uri
|
19
36
|
end
|
20
|
-
|
37
|
+
|
21
38
|
module Client
|
22
|
-
module
|
39
|
+
module Config
|
40
|
+
BASIC_MAPPING = { :delete => Net::HTTP::Delete, :put => Net::HTTP::Put, :get => Net::HTTP::Get, :post => Net::HTTP::Post}
|
41
|
+
DEFAULTS = { :destroy => Net::HTTP::Delete, :delete => Net::HTTP::Delete, :cancel => Net::HTTP::Delete,
|
42
|
+
:refresh => Net::HTTP::Get, :reload => Net::HTTP::Get, :show => Net::HTTP::Get, :latest => Net::HTTP::Get, :self => Net::HTTP::Get}
|
23
43
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
return
|
30
|
-
|
31
|
-
raise UnsupportedContentType.new("unsupported content type '#{res.content_type}' '#{res.code}'") unless res.content_type=="application/xml"
|
32
|
-
|
33
|
-
# TODO this method should use the RequestExecution process to parse the content type and body
|
34
|
-
# TODO add default html parser: do nothin
|
35
|
-
|
36
|
-
body = res.body
|
37
|
-
return {} if body.empty?
|
38
|
-
|
39
|
-
hash = Hash.from_xml body
|
40
|
-
hash.to_object(body)
|
41
|
-
|
44
|
+
def self.self_retrieval
|
45
|
+
[:latest, :refresh, :reload, :self]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.requisition_method_for(overriden_option,name)
|
49
|
+
return BASIC_MAPPING[overriden_option.to_sym] if overriden_option
|
50
|
+
DEFAULTS[name.to_sym] || Net::HTTP::Post
|
42
51
|
end
|
43
52
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
:refresh => Net::HTTP::Get, :reload => Net::HTTP::Get, :show => Net::HTTP::Get, :latest => Net::HTTP::Get, :self => Net::HTTP::Get}
|
53
|
+
end
|
54
|
+
|
55
|
+
module Base
|
48
56
|
|
49
|
-
return basic_mapping[overriden_option.to_sym] if overriden_option
|
50
|
-
defaults[name.to_sym] || Net::HTTP::Post
|
51
|
-
end
|
52
|
-
|
53
57
|
def is_self_retrieval?(name)
|
54
58
|
name = name.to_sym if name.kind_of? String
|
55
|
-
|
59
|
+
Restfulie::Client::Config.self_retrieval.include? name
|
56
60
|
end
|
57
|
-
|
61
|
+
|
58
62
|
end
|
63
|
+
|
59
64
|
end
|
60
65
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
# Basic cache implementation for restfulie.
|
19
|
+
#
|
20
|
+
# It uses the request headers and uri to store it in memory.
|
21
|
+
# This cache might not be optimal for long running clients, which should use a memcached based one.
|
22
|
+
# Use Restfulie.cache_provider to change the provider
|
23
|
+
class Restfulie::BasicCache
|
24
|
+
|
25
|
+
def put(url, req, response)
|
26
|
+
if Restfulie::Cache::Restrictions.may_cache?(req, response)
|
27
|
+
Restfulie.logger.debug "caching #{url} #{req} #{response}"
|
28
|
+
cache[key_for(url, req)] = response
|
29
|
+
end
|
30
|
+
response
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(url, req)
|
34
|
+
|
35
|
+
response = cache[key_for(url, req)]
|
36
|
+
return nil if response.nil?
|
37
|
+
|
38
|
+
if response.has_expired_cache?
|
39
|
+
remove(key_for(url, req))
|
40
|
+
else
|
41
|
+
Restfulie.logger.debug "RETURNING cache #{url} #{req}"
|
42
|
+
response
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# removes all elements from the cache
|
48
|
+
def clear
|
49
|
+
cache.clear
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def remove(what)
|
55
|
+
@cache.delete(what)
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def cache
|
60
|
+
@cache ||= {}
|
61
|
+
end
|
62
|
+
|
63
|
+
def key_for(url, req)
|
64
|
+
[url, req.class]
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
# Fake cache that does not cache anything
|
70
|
+
# Use Restfulie.cache_provider = Restfulie::FakeCache.new
|
71
|
+
class Restfulie::FakeCache
|
72
|
+
|
73
|
+
def put(url, req, response)
|
74
|
+
response
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(url, req)
|
78
|
+
end
|
79
|
+
|
80
|
+
def clear
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
module Restfulie::Cache
|
86
|
+
module Restrictions
|
87
|
+
|
88
|
+
class << self
|
89
|
+
|
90
|
+
# checks whether this request verb and its cache headers allow caching
|
91
|
+
def may_cache?(request,response)
|
92
|
+
may_cache_method?(request) && response.may_cache?
|
93
|
+
end
|
94
|
+
|
95
|
+
# only Post and Get requests are cacheable so far
|
96
|
+
def may_cache_method?(verb)
|
97
|
+
verb.kind_of?(Net::HTTP::Post) || verb.kind_of?(Net::HTTP::Get)
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -1,3 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
1
18
|
module Restfulie
|
2
19
|
module Client
|
3
20
|
module Base
|
@@ -22,12 +39,12 @@ module Restfulie
|
|
22
39
|
|
23
40
|
# retrieves a resource form a specific uri
|
24
41
|
def from_web(uri, options = {})
|
25
|
-
RequestExecution.new(self).at(uri).get(options)
|
42
|
+
RequestExecution.new(self, nil).at(uri).get(options)
|
26
43
|
end
|
27
44
|
|
28
45
|
private
|
29
46
|
def remote_post(content)
|
30
|
-
RequestExecution.new(self).at(entry_point_for.create.uri).post(content)
|
47
|
+
RequestExecution.new(self, nil).at(entry_point_for.create.uri).post(content)
|
31
48
|
end
|
32
49
|
|
33
50
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require 'time'
|
19
|
+
|
20
|
+
# an extesion to http responses
|
21
|
+
module Restfulie::Client::HTTPResponse
|
22
|
+
|
23
|
+
attr_accessor :previous
|
24
|
+
|
25
|
+
# determines if this response code was successful (according to http specs: 200~299)
|
26
|
+
def is_successful?
|
27
|
+
code.to_i >= 200 && code.to_i <= 299
|
28
|
+
end
|
29
|
+
|
30
|
+
# determines if this response code was successful (according to http specs: 100~199)
|
31
|
+
def is_informational?
|
32
|
+
code.to_i >= 100 && code.to_i <= 199
|
33
|
+
end
|
34
|
+
|
35
|
+
# determines if this response code was successful (according to http specs: 300~399)
|
36
|
+
def is_redirection?
|
37
|
+
code.to_i >= 300 && code.to_i <= 399
|
38
|
+
end
|
39
|
+
|
40
|
+
# determines if this response code was successful (according to http specs: 400~499)
|
41
|
+
def is_client_error?
|
42
|
+
code.to_i >= 400 && code.to_i <= 499
|
43
|
+
end
|
44
|
+
|
45
|
+
# determines if this response code was successful (according to http specs: 500~599)
|
46
|
+
def is_server_error?
|
47
|
+
code.to_i >= 500 && code.to_i <= 599
|
48
|
+
end
|
49
|
+
|
50
|
+
def etag
|
51
|
+
self['Etag']
|
52
|
+
end
|
53
|
+
|
54
|
+
def last_modified
|
55
|
+
self['Last-Modified']
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
module Restfulie::Client::HTTPResponse
|
61
|
+
|
62
|
+
def cache_max_age
|
63
|
+
val = header_value_from('Cache-control', /^\s*max-age=(\d+)/)
|
64
|
+
if val
|
65
|
+
val.to_i
|
66
|
+
else
|
67
|
+
0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def header_value_from(header, expression)
|
72
|
+
h = value_for(get_fields(header)[0], expression)
|
73
|
+
return nil if h.nil?
|
74
|
+
h.match(expression)[1]
|
75
|
+
end
|
76
|
+
|
77
|
+
def has_expired_cache?
|
78
|
+
return true if self['Date'].nil?
|
79
|
+
Time.now > Time.rfc2822(self['Date']) + cache_max_age.seconds
|
80
|
+
end
|
81
|
+
|
82
|
+
# checks if the header's max-age is available and no no-store if available.
|
83
|
+
def may_cache?
|
84
|
+
may_cache_field?(get_fields('Cache-control'))
|
85
|
+
end
|
86
|
+
|
87
|
+
def may_cache_field?(field)
|
88
|
+
return false if field.nil?
|
89
|
+
|
90
|
+
if field.kind_of? Array
|
91
|
+
field.each do |f|
|
92
|
+
return false if !may_cache_field?(f)
|
93
|
+
end
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
|
97
|
+
max_age_header = value_for(field, /^max-age=(\d+)/)
|
98
|
+
return false if max_age_header.nil?
|
99
|
+
max_age = max_age_header[1]
|
100
|
+
|
101
|
+
return false if value_for(field, /^no-store/)
|
102
|
+
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
# extracts the header value for an specific expression, which can be located at the start or in the middle
|
107
|
+
# of the expression
|
108
|
+
def value_for(value, expression)
|
109
|
+
value.split(",").find { |obj| obj.strip =~ expression }
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
class Net::HTTPResponse
|
115
|
+
include Restfulie::Client::HTTPResponse
|
116
|
+
end
|
@@ -1,3 +1,20 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
1
18
|
module Restfulie
|
2
19
|
module Client
|
3
20
|
module Helper
|
@@ -1,134 +1,158 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009 Caelum - www.caelum.com.br/opensource
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
module Restfulie::Client::Instance
|
4
19
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# which content-type generated this data
|
11
|
-
attr_accessor :_came_from
|
12
|
-
|
13
|
-
def invoke_remote_transition(name, options, block)
|
14
|
-
|
15
|
-
method = self.class.requisition_method_for options[:method], name
|
16
|
-
|
17
|
-
state = self._possible_states[name]
|
18
|
-
url = URI.parse(state["href"] || state[:href])
|
19
|
-
req = method.new(url.path)
|
20
|
-
req.body = options[:data] if options[:data]
|
21
|
-
add_request_headers(req, name)
|
22
|
-
|
23
|
-
response = Net::HTTP.new(url.host, url.port).request(req)
|
20
|
+
# list of possible states to access
|
21
|
+
def existing_relations
|
22
|
+
@existing_relations ||= {}
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
# which content-type generated this data
|
26
|
+
attr_reader :_came_from
|
27
|
+
|
28
|
+
# prepares a new request
|
29
|
+
def request
|
30
|
+
Restfulie::Client::RequestExecution.new(self.class, self)
|
31
|
+
end
|
32
|
+
|
33
|
+
# parse arguments from a transition invocation (or relation)
|
34
|
+
# it will receive either zero, one or two args, if there are two args, return them
|
35
|
+
# if there is one hash arg, its the options, add a data = nil
|
36
|
+
# if there is one arg (not a hash), its the data, add a options = {}
|
37
|
+
# if there are no args, data is nil and options = {}
|
38
|
+
def parse_args_from_transition(args)
|
39
|
+
data = nil
|
40
|
+
if args.nil? || args.size==0
|
41
|
+
options = {}
|
42
|
+
elsif args.size==1
|
43
|
+
if args[0].kind_of?(Hash)
|
44
|
+
options = args[0]
|
45
|
+
else
|
46
|
+
data = args[0]
|
47
|
+
options = {}
|
35
48
|
end
|
49
|
+
elsif args.size==2
|
50
|
+
data = args[0]
|
51
|
+
options = args[1] || {}
|
52
|
+
end
|
53
|
+
[data, options]
|
54
|
+
end
|
55
|
+
|
56
|
+
def invoke_remote_transition(name, args, block = nil)
|
57
|
+
|
58
|
+
data, options = parse_args_from_transition(args)
|
59
|
+
|
60
|
+
method = Restfulie::Client::Config.requisition_method_for options[:method], name
|
61
|
+
state = self.existing_relations[name]
|
62
|
+
|
63
|
+
request = Restfulie::Client::RequestExecution.new(self.class, self).at(state["href"] || state[:href]).with(options[:headers])
|
64
|
+
request.do method, name, data
|
36
65
|
|
37
|
-
|
66
|
+
end
|
38
67
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
self.extend Restfulie::Client::State
|
48
|
-
end
|
68
|
+
# inserts all links from this object as can_xxx and xxx methods
|
69
|
+
def add_transitions(links)
|
70
|
+
links.each do |t|
|
71
|
+
self.existing_relations[t["rel"] || t[:rel]] = t
|
72
|
+
self.add_state(t)
|
73
|
+
end
|
74
|
+
self.extend Restfulie::Client::State
|
75
|
+
end
|
49
76
|
|
77
|
+
# adds the specific information for one state change or related resource
|
78
|
+
def add_state(transition)
|
79
|
+
name = transition["rel"] || transition[:rel]
|
80
|
+
|
81
|
+
# TODO: wrong, should be instance_eval
|
82
|
+
self.class.module_eval do
|
50
83
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
# TODO: wrong, should be instance_eval
|
55
|
-
self.class.module_eval do
|
56
|
-
|
57
|
-
def temp_method(options = {}, &block)
|
58
|
-
self.invoke_remote_transition(Restfulie::Client::Helper.current_method, options, block)
|
59
|
-
end
|
60
|
-
|
61
|
-
alias_method name, :temp_method
|
62
|
-
undef :temp_method
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# returns a list of extended fields for this instance.
|
67
|
-
# extended fields are those unknown to this model but kept in a hash
|
68
|
-
# to allow forward-compatibility.
|
69
|
-
def extended_fields
|
70
|
-
@hash ||= {}
|
71
|
-
@hash
|
84
|
+
def temp_method(*args, &block)
|
85
|
+
self.invoke_remote_transition(Restfulie::Client::Helper.current_method, args, block)
|
72
86
|
end
|
87
|
+
|
88
|
+
alias_method name, :temp_method
|
89
|
+
undef :temp_method
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# returns a list of extended fields for this instance.
|
94
|
+
# extended fields are those unknown to this model but kept in a hash
|
95
|
+
# to allow forward-compatibility.
|
96
|
+
def extended_fields
|
97
|
+
@extended_fields ||= {}
|
98
|
+
@extended_fields
|
99
|
+
end
|
73
100
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
101
|
+
def method_missing(name, *args)
|
102
|
+
name = name.to_s if name.kind_of? Symbol
|
103
|
+
|
104
|
+
if name[-1,1] == "="
|
105
|
+
extended_fields[name.chop] = args[0]
|
106
|
+
elsif name[-1,1] == "?"
|
107
|
+
found = extended_fields[name.chop]
|
108
|
+
return super(name,args) if found.nil?
|
109
|
+
parse(found)
|
110
|
+
else
|
111
|
+
found = extended_fields[name]
|
112
|
+
return super(name,args) if found.nil?
|
113
|
+
parse(transform(found))
|
114
|
+
end
|
88
115
|
|
89
|
-
|
116
|
+
end
|
90
117
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
118
|
+
def respond_to?(sym)
|
119
|
+
extended_fields[sym.to_s].nil? ? super(sym) : true
|
120
|
+
end
|
95
121
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
104
|
-
end
|
105
|
-
super(values)
|
122
|
+
# redefines attribute definition allowing the invocation of method_missing
|
123
|
+
# when an attribute does not exist
|
124
|
+
def attributes=(values)
|
125
|
+
values.each do |key, value|
|
126
|
+
unless attributes.include? key
|
127
|
+
method_missing("#{key}=", value)
|
128
|
+
values.delete key
|
106
129
|
end
|
130
|
+
end
|
131
|
+
super(values)
|
132
|
+
end
|
107
133
|
|
108
134
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
end
|
115
|
-
end
|
135
|
+
# serializes the extended fields with the existing fields
|
136
|
+
def to_xml(options={})
|
137
|
+
super(options) do |xml|
|
138
|
+
extended_fields.each do |key,value|
|
139
|
+
xml.tag! key, value
|
116
140
|
end
|
141
|
+
end
|
142
|
+
end
|
117
143
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
144
|
+
private
|
145
|
+
|
146
|
+
# transforms a value in a custom hash
|
147
|
+
def transform(value)
|
148
|
+
return CustomHash.new(value) if value.kind_of?(Hash) || value.kind_of?(Array)
|
149
|
+
value
|
150
|
+
end
|
151
|
+
|
152
|
+
def parse(val)
|
153
|
+
raise "undefined method: '#{val}'" if val.nil?
|
154
|
+
val
|
155
|
+
end
|
130
156
|
|
131
157
|
|
132
|
-
end
|
133
|
-
end
|
134
158
|
end
|