restfulie 0.6.0 → 0.7.0
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.textile +83 -7
- data/Rakefile +98 -13
- data/lib/restfulie/client/base.rb +48 -53
- data/lib/restfulie/client/configuration.rb +69 -0
- data/lib/restfulie/client/http/adapter.rb +487 -0
- data/lib/restfulie/client/http/atom_ext.rb +87 -0
- data/lib/restfulie/client/http/cache.rb +28 -0
- data/lib/restfulie/client/http/error.rb +80 -0
- data/lib/restfulie/client/http/marshal.rb +147 -0
- data/lib/restfulie/client/http.rb +13 -0
- data/lib/restfulie/client.rb +8 -56
- data/lib/restfulie/common/builder/builder_base.rb +58 -0
- data/lib/restfulie/common/builder/helpers.rb +22 -0
- data/lib/restfulie/common/builder/marshalling/atom.rb +197 -0
- data/lib/restfulie/common/builder/marshalling/base.rb +12 -0
- data/lib/restfulie/common/builder/marshalling/json.rb +2 -0
- data/lib/restfulie/common/builder/marshalling.rb +16 -0
- data/lib/restfulie/common/builder/rules/collection_rule.rb +10 -0
- data/lib/restfulie/common/builder/rules/link.rb +20 -0
- data/lib/restfulie/common/builder/rules/links.rb +9 -0
- data/lib/restfulie/common/builder/rules/member_rule.rb +8 -0
- data/lib/restfulie/common/builder/rules/namespace.rb +25 -0
- data/lib/restfulie/common/builder/rules/rules_base.rb +76 -0
- data/lib/restfulie/common/builder.rb +16 -0
- data/lib/restfulie/common/errors.rb +9 -0
- data/lib/restfulie/{logger.rb → common/logger.rb} +3 -5
- data/lib/restfulie/common/representation/atom.rb +48 -0
- data/lib/restfulie/common/representation/generic.rb +33 -0
- data/lib/restfulie/common/representation/xml.rb +24 -0
- data/lib/restfulie/common/representation.rb +10 -0
- data/lib/restfulie/common.rb +23 -0
- data/lib/restfulie/server/action_controller/base.rb +31 -0
- data/lib/restfulie/server/action_controller/params_parser.rb +62 -0
- data/lib/restfulie/server/action_controller/restful_responder.rb +39 -0
- data/lib/restfulie/server/action_controller/routing/restful_route.rb +14 -0
- data/lib/restfulie/server/action_controller/routing.rb +12 -0
- data/lib/restfulie/server/action_controller.rb +15 -0
- data/lib/restfulie/server/action_view/helpers.rb +45 -0
- data/lib/restfulie/server/action_view/template_handlers/tokamak.rb +15 -0
- data/lib/restfulie/server/action_view/template_handlers.rb +13 -0
- data/lib/restfulie/server/action_view.rb +8 -0
- data/lib/restfulie/server/configuration.rb +21 -0
- data/lib/restfulie/server/core_ext/array.rb +45 -0
- data/lib/restfulie/server/core_ext.rb +1 -0
- data/lib/restfulie/server/restfulie_controller.rb +1 -17
- data/lib/restfulie/server.rb +15 -0
- data/lib/restfulie.rb +4 -72
- data/lib/vendor/atom/configuration.rb +24 -0
- data/lib/vendor/atom/pub.rb +250 -0
- data/lib/vendor/atom/xml/parser.rb +373 -0
- data/lib/vendor/atom.rb +771 -0
- metadata +94 -33
- data/lib/restfulie/client/atom_media_type.rb +0 -75
- data/lib/restfulie/client/cache.rb +0 -103
- data/lib/restfulie/client/entry_point.rb +0 -94
- data/lib/restfulie/client/extensions/http.rb +0 -116
- data/lib/restfulie/client/helper.rb +0 -28
- data/lib/restfulie/client/instance.rb +0 -158
- data/lib/restfulie/client/request_execution.rb +0 -321
- data/lib/restfulie/client/state.rb +0 -36
- data/lib/restfulie/media_type.rb +0 -143
- data/lib/restfulie/media_type_control.rb +0 -115
- data/lib/restfulie/media_type_defaults.rb +0 -51
- data/lib/restfulie/server/atom_media_type.rb +0 -115
- data/lib/restfulie/server/base.rb +0 -91
- data/lib/restfulie/server/controller.rb +0 -122
- data/lib/restfulie/server/instance.rb +0 -102
- data/lib/restfulie/server/marshalling.rb +0 -47
- data/lib/restfulie/server/opensearch/description.rb +0 -54
- data/lib/restfulie/server/opensearch.rb +0 -18
- data/lib/restfulie/server/transition.rb +0 -93
- data/lib/restfulie/unmarshalling.rb +0 -131
- data/lib/vendor/jeokkarak/hashi.rb +0 -65
- data/lib/vendor/jeokkarak/jeokkarak.rb +0 -81
@@ -0,0 +1,147 @@
|
|
1
|
+
#Request builder for marshalled data that unmarshalls content after receiving it.
|
2
|
+
module Restfulie::Client::HTTP
|
3
|
+
|
4
|
+
module ResponseHolder
|
5
|
+
attr_accessor :response
|
6
|
+
|
7
|
+
def respond_to?(symbol)
|
8
|
+
respond_to_rel?(symbol.to_s) || super(symbol)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
# whether this response contains specific relations
|
13
|
+
def respond_to_rel?(rel)
|
14
|
+
links.any? { |link| link.rel==rel }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
module RequestMarshaller
|
20
|
+
include ::Restfulie::Client::HTTP::RequestBuilder
|
21
|
+
|
22
|
+
# accepts a series of media types by default
|
23
|
+
def initialize
|
24
|
+
@acceptable_mediatypes = "application/atom+xml"
|
25
|
+
end
|
26
|
+
|
27
|
+
@@representations = {
|
28
|
+
'application/atom+xml' => ::Restfulie::Common::Representation::Atom,
|
29
|
+
'application/xml' => ::Restfulie::Common::Representation::XmlD
|
30
|
+
}
|
31
|
+
def self.register_representation(media_type,representation)
|
32
|
+
@@representations[media_type] = representation
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.content_type_for(media_type)
|
36
|
+
content_type = media_type.split(';')[0] # [/(.*?);/, 1]
|
37
|
+
type = @@representations[content_type]
|
38
|
+
type ? type.new : nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def accepts(media_types)
|
42
|
+
@acceptable_mediatypes = media_types
|
43
|
+
@default_representation = @@representations[media_types]
|
44
|
+
raise "Undefined representation for #{media_types}" unless @default_representation
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def raw
|
49
|
+
@raw = true
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
#Executes super and unmarshalls it
|
54
|
+
def request!(method, path, *args)
|
55
|
+
representation = do_conneg_and_choose_representation(method, path, *args)
|
56
|
+
if representation
|
57
|
+
if has_payload?(method, path, *args)
|
58
|
+
payload = get_payload(method, path, *args)
|
59
|
+
rel = self.respond_to?(:rel) ? self.rel : ""
|
60
|
+
payload = representation.marshal(payload, rel)
|
61
|
+
args = set_marshalled_payload(method, path, payload, *args)
|
62
|
+
end
|
63
|
+
args = add_representation_headers(method, path, representation, *args)
|
64
|
+
end
|
65
|
+
response = super(method, path, *args)
|
66
|
+
parse_response(response, representation)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# parses the http response.
|
72
|
+
# first checks if its a 201, redirecting to the resource location.
|
73
|
+
# otherwise check if its a raw request, returning the content itself.
|
74
|
+
# finally, tries to parse the content with a mediatype handler or returns the response itself.
|
75
|
+
def parse_response(response, representation)
|
76
|
+
if response.code == 201
|
77
|
+
location = response.headers['location']
|
78
|
+
Restfulie.at(location).accepts(@acceptable_mediatypes).get!
|
79
|
+
elsif @raw
|
80
|
+
response
|
81
|
+
elsif !response.body.empty?
|
82
|
+
representation = RequestMarshaller.content_type_for(response.headers['content-type']) || Restfulie::Common::Representation::Generic.new
|
83
|
+
unmarshalled = representation.unmarshal(response.body)
|
84
|
+
unmarshalled.extend(ResponseHolder)
|
85
|
+
unmarshalled.response = response
|
86
|
+
unmarshalled
|
87
|
+
else
|
88
|
+
response
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def content_type_for(media_type)
|
93
|
+
@@representations[media_type.split(';')[0]].new # [/(.*?);/, 1]
|
94
|
+
end
|
95
|
+
|
96
|
+
def do_conneg_and_choose_representation(method, path, *args)
|
97
|
+
#TODO make a request to server (conneg)
|
98
|
+
representation = @default_representation || @default_representation = @@representations['application/atom+xml']
|
99
|
+
representation.new
|
100
|
+
end
|
101
|
+
|
102
|
+
def has_payload?(method, path, *args)
|
103
|
+
[:put,:post].include?(method)
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_payload(method, path, *args)
|
107
|
+
args.extract_options! #remove header
|
108
|
+
args.shift #payload
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_marshalled_payload(method, path, payload, *args)
|
112
|
+
headers = args.extract_options!
|
113
|
+
args.shift #old payload
|
114
|
+
args << payload << headers
|
115
|
+
args
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_representation_headers(method, path, representation, *args)
|
119
|
+
headers = args.extract_options!
|
120
|
+
args << representation.headers[method]
|
121
|
+
args
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
#=This class includes RequestBuilder module.
|
127
|
+
class RequestMarshallerExecutor
|
128
|
+
include RequestMarshaller
|
129
|
+
|
130
|
+
# * <tt> host (e.g. 'http://restfulie.com') </tt>
|
131
|
+
# * <tt> default_headers (e.g. {'Cache-control' => 'no-cache'} ) </tt>
|
132
|
+
def initialize(host, default_headers = {})
|
133
|
+
self.host=host
|
134
|
+
self.default_headers=default_headers
|
135
|
+
end
|
136
|
+
|
137
|
+
def at(path)
|
138
|
+
@path = path
|
139
|
+
self
|
140
|
+
end
|
141
|
+
def path
|
142
|
+
@path
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
data/lib/restfulie/client.rb
CHANGED
@@ -1,60 +1,12 @@
|
|
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
|
-
#
|
1
|
+
require 'restfulie/common'
|
17
2
|
|
18
|
-
|
3
|
+
module Restfulie::Client; end
|
19
4
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
require
|
26
|
-
require 'restfulie/client/base'
|
27
|
-
require 'restfulie/client/entry_point'
|
28
|
-
require 'restfulie/client/helper'
|
29
|
-
require 'restfulie/client/instance'
|
30
|
-
require 'restfulie/client/request_execution'
|
31
|
-
require 'restfulie/client/state'
|
32
|
-
require 'restfulie/client/cache'
|
33
|
-
require 'restfulie/unmarshalling'
|
34
|
-
|
35
|
-
module Restfulie
|
36
|
-
|
37
|
-
class << self
|
38
|
-
attr_accessor :cache_provider
|
39
|
-
end
|
40
|
-
|
41
|
-
# Extends your class to support restfulie-client side's code.
|
42
|
-
# This will extends Restfulie::Client::Base methods as class methods,
|
43
|
-
# Restfulie::Client::Instance as instance methods and Restfulie::Unmarshalling as class methods.
|
44
|
-
def uses_restfulie
|
45
|
-
extend Restfulie::Client::Base
|
46
|
-
include Restfulie::Client::Instance
|
47
|
-
extend Restfulie::Unmarshalling
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
Object.extend Restfulie
|
53
|
-
|
54
|
-
include ActiveSupport::CoreExtensions::Hash
|
55
|
-
|
56
|
-
class Hashi::CustomHash
|
57
|
-
uses_restfulie
|
5
|
+
%w(
|
6
|
+
http
|
7
|
+
configuration
|
8
|
+
base
|
9
|
+
).each do |file|
|
10
|
+
require "restfulie/client/#{file}"
|
58
11
|
end
|
59
12
|
|
60
|
-
Restfulie.cache_provider = Restfulie::BasicCache.new
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Restfulie::Common::Builder::Base
|
2
|
+
attr_accessor :rules_blocks
|
3
|
+
attr_accessor :object
|
4
|
+
|
5
|
+
# TODO: Configurable
|
6
|
+
OPTIONS_DEFAULT = {
|
7
|
+
:eagerload => true,
|
8
|
+
:default_rule => true
|
9
|
+
}
|
10
|
+
|
11
|
+
def initialize(object, rules_blocks = [], options = {})
|
12
|
+
@options = OPTIONS_DEFAULT.merge(options)
|
13
|
+
@object = object
|
14
|
+
@rules_blocks = rules_blocks
|
15
|
+
end
|
16
|
+
|
17
|
+
# Remove to_json from ActiveSupport in the class
|
18
|
+
# I want my own to_json
|
19
|
+
undef_method :to_json if respond_to?(:to_json)
|
20
|
+
|
21
|
+
def respond_to?(symbol, include_private = false)
|
22
|
+
!marshalling_class(symbol).nil? || super
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(symbol, *args)
|
26
|
+
unless (marshalling = marshalling_class(symbol)).nil?
|
27
|
+
return builder(marshalling, *args)
|
28
|
+
end
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def builder(marshalling, options = {})
|
35
|
+
@object.class.ancestors.include?(Enumerable) ? builder_collection(marshalling, options) : builder_member(marshalling, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def builder_member(marshalling, options = {})
|
39
|
+
marshalling.new(@object, rules_blocks).builder_member(@options.merge(options))
|
40
|
+
end
|
41
|
+
|
42
|
+
def builder_collection(marshalling, options = {})
|
43
|
+
marshalling.new(@object, rules_blocks).builder_collection(@options.merge(options))
|
44
|
+
end
|
45
|
+
|
46
|
+
def marshalling_class(method)
|
47
|
+
if marshalling_name = method.to_s.match(/to_(.*)/)
|
48
|
+
marshalling = marshalling_name[1].downcase.capitalize.to_sym
|
49
|
+
if Restfulie::Common::Builder::Marshalling.const_defined?(marshalling)
|
50
|
+
begin
|
51
|
+
Restfulie::Common::Builder::Marshalling.const_get(marshalling)
|
52
|
+
rescue NameError
|
53
|
+
raise Restfulie::Common::Error::UndefinedMarshallingError.new("Marshalling #{marshalling} not found.")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Restfulie::Common::Builder::Helpers
|
2
|
+
|
3
|
+
def describe_member(member, options = {}, &block)
|
4
|
+
create_builder(member, options, &block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def describe_collection(collection, options = {}, &block)
|
8
|
+
create_builder(collection, options, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Helper to create objects link
|
12
|
+
def link(*args)
|
13
|
+
Restfulie::Common::Builder::Rules::Link.new(*args)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def create_builder(object, options, &block)
|
19
|
+
Restfulie::Common::Builder::Base.new(object, block_given? ? [block] : [], options)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
class Restfulie::Common::Builder::Marshalling::Atom < Restfulie::Common::Builder::Marshalling::Base
|
2
|
+
include ActionController::UrlWriter
|
3
|
+
include Restfulie::Common::Builder::Helpers
|
4
|
+
include Restfulie::Common::Error
|
5
|
+
|
6
|
+
ATOM_ATTRIBUTES = [
|
7
|
+
:title, :id, :updated, # Required
|
8
|
+
:author, :links, # Recommended
|
9
|
+
:category, :contributor, :rights # Optional
|
10
|
+
]
|
11
|
+
|
12
|
+
ATOM_ATTRIBUTES_ENTRY = ATOM_ATTRIBUTES + [
|
13
|
+
:content, :summary, # Required
|
14
|
+
:published, :source # Optional
|
15
|
+
]
|
16
|
+
|
17
|
+
ATOM_ATTRIBUTES_FEED = ATOM_ATTRIBUTES + [
|
18
|
+
:generator, :icon, :logo, :subtitle # Optional
|
19
|
+
]
|
20
|
+
|
21
|
+
ATTRIBUTES_ALREADY_IN_ATOM_SPEC = [
|
22
|
+
"id", "created_at", "updated_at", "title"
|
23
|
+
]
|
24
|
+
|
25
|
+
def initialize(object, rules)
|
26
|
+
@object = object
|
27
|
+
@rules = rules
|
28
|
+
end
|
29
|
+
|
30
|
+
def builder_collection(options = {})
|
31
|
+
builder_feed(@object, @rules, options).to_xml
|
32
|
+
end
|
33
|
+
|
34
|
+
def builder_member(options = {})
|
35
|
+
builder_entry(@object, @rules, options).to_xml
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def builder_feed(objects, rules_blocks, options = {})
|
41
|
+
rule = Restfulie::Common::Builder::CollectionRule.new(rules_blocks)
|
42
|
+
|
43
|
+
rule.blocks.unshift(default_collection_rule) if options[:default_rule]
|
44
|
+
rule.metaclass.send(:attr_accessor, *ATOM_ATTRIBUTES_FEED)
|
45
|
+
rule.apply(objects, options)
|
46
|
+
|
47
|
+
atom = ::Atom::Feed.new do |feed|
|
48
|
+
# Set values
|
49
|
+
(ATOM_ATTRIBUTES_FEED - [:links]).each do |field|
|
50
|
+
feed.send("#{field}=".to_sym, rule.send(field)) unless rule.send(field).nil?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Namespaces
|
54
|
+
builder_namespaces(rule.namespaces, feed)
|
55
|
+
|
56
|
+
# Transitions
|
57
|
+
rule.links.each do |link|
|
58
|
+
atom_link = {:rel => link.rel, :href => link.href, :type => (link.type || 'application/atom+xml')}
|
59
|
+
feed.links << ::Atom::Link.new(atom_link) unless atom_link[:href].nil?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Entries
|
63
|
+
options.delete(:values)
|
64
|
+
member_options = options.merge(rule.members_options || {})
|
65
|
+
objects.each do |member|
|
66
|
+
feed.entries << builder_entry(member, rule.members_blocks || [], member_options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: Validate of the required fields
|
72
|
+
def default_collection_rule
|
73
|
+
Proc.new do |collection_rule, objects, options|
|
74
|
+
# Passed values
|
75
|
+
(options[:values] || {}).each { |key, value| collection_rule.send("#{key}=".to_sym, value)}
|
76
|
+
|
77
|
+
# Default values
|
78
|
+
collection_rule.id ||= options[:self]
|
79
|
+
collection_rule.title ||= "#{objects.first.class.to_s.pluralize.demodulize} feed"
|
80
|
+
collection_rule.updated ||= updated_collection(objects)
|
81
|
+
|
82
|
+
# Transitions
|
83
|
+
collection_rule.links << link(:rel => :self, :href => options[:self]) unless options[:self].nil?
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def builder_entry(object, rules_blocks, options = {})
|
88
|
+
rule = Restfulie::Common::Builder::MemberRule.new(rules_blocks)
|
89
|
+
options = namespace_enhance(options)
|
90
|
+
|
91
|
+
rule.blocks.unshift(default_member_rule) if options[:default_rule]
|
92
|
+
rule.metaclass.send(:attr_accessor, *ATOM_ATTRIBUTES_ENTRY)
|
93
|
+
rule.apply(object, options)
|
94
|
+
|
95
|
+
atom = ::Atom::Entry.new do |entry|
|
96
|
+
# Set values
|
97
|
+
(ATOM_ATTRIBUTES_ENTRY - [:links]).each do |field|
|
98
|
+
entry.send("#{field}=".to_sym, rule.send(field)) unless rule.send(field).nil?
|
99
|
+
end
|
100
|
+
|
101
|
+
# Namespaces
|
102
|
+
builder_namespaces(rule.namespaces, entry)
|
103
|
+
|
104
|
+
# Transitions
|
105
|
+
rule.links.each do |link|
|
106
|
+
atom_link = {:rel => link.rel, :href => link.href, :type => link.type}
|
107
|
+
|
108
|
+
# Self
|
109
|
+
if link.href.nil?
|
110
|
+
if link.rel == "self"
|
111
|
+
path = object
|
112
|
+
else
|
113
|
+
association = object.class.reflect_on_all_associations.find { |a| a.name.to_s == link.rel }
|
114
|
+
path = (association.macro == :has_many) ? [object, association.name] : object.send(association.name) unless association.nil?
|
115
|
+
end
|
116
|
+
atom_link[:href] = polymorphic_url(path, :host => host) rescue nil
|
117
|
+
atom_link[:type] = link.type || 'application/atom+xml'
|
118
|
+
end
|
119
|
+
|
120
|
+
entry.links << ::Atom::Link.new(atom_link) unless atom_link[:href].nil?
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def default_member_rule
|
126
|
+
Proc.new do |member_rule, object, options|
|
127
|
+
# Passed values
|
128
|
+
(options[:values] || {}).each { |key, value| set_attribute(member_rule, key, value) }
|
129
|
+
|
130
|
+
# Default values
|
131
|
+
member_rule.id ||= polymorphic_url(object, :host => host) rescue nil
|
132
|
+
member_rule.title ||= object.respond_to?(:title) && !object.title.nil? ? object.title : "Entry about #{object.class.to_s.demodulize}"
|
133
|
+
member_rule.updated ||= object.updated_at if object.respond_to?(:updated_at)
|
134
|
+
|
135
|
+
# Namespace
|
136
|
+
unless options[:namespace].nil?
|
137
|
+
member_rule.namespace(object, options[:namespace][:uri], options[:namespace])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def updated_collection(objects)
|
143
|
+
objects.map { |item| item.updated_at if item.respond_to?(:updated_at) }.compact.max || Time.now
|
144
|
+
end
|
145
|
+
|
146
|
+
def builder_namespaces(namespaces, atom)
|
147
|
+
kclass = atom.class
|
148
|
+
namespaces.each do |ns|
|
149
|
+
register_namespace(ns.namespace, ns.uri, kclass)
|
150
|
+
ns.each do |key, value|
|
151
|
+
unless ATTRIBUTES_ALREADY_IN_ATOM_SPEC.include?(key.to_s)
|
152
|
+
register_element(ns.namespace, key, kclass)
|
153
|
+
atom.send("#{ns.namespace}_#{key}=".to_sym, value)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def host
|
160
|
+
# TODO: If we split restfulie into 2 separate gems, we may need not to use Restfulie::Server
|
161
|
+
# inside Restfulie::Common
|
162
|
+
Restfulie::Server::Configuration.host
|
163
|
+
end
|
164
|
+
|
165
|
+
def register_namespace(namespace, uri, klass)
|
166
|
+
klass.add_extension_namespace(namespace, uri) unless klass.known_namespaces.include? uri
|
167
|
+
end
|
168
|
+
|
169
|
+
def register_element(namespace, attribute, klass)
|
170
|
+
attribute = "#{namespace}:#{attribute}"
|
171
|
+
klass.element(attribute) if element_unregistered?(attribute, klass)
|
172
|
+
end
|
173
|
+
|
174
|
+
def element_unregistered?(element, klass)
|
175
|
+
klass.element_specs.select { |k,v| v.name == element }.empty?
|
176
|
+
end
|
177
|
+
|
178
|
+
# TODO : Move error handling to class rule, maybe?
|
179
|
+
def set_attribute(rule, attribute, value)
|
180
|
+
begin
|
181
|
+
rule.send("#{attribute}=".to_sym, value)
|
182
|
+
rescue NoMethodError
|
183
|
+
raise AtomMarshallingError.new("Attribute #{attribute} unsupported in Atom #{rule_type_name(rule)}.")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def rule_type_name(rule)
|
188
|
+
rule.kind_of?(Restfulie::Common::Builder::MemberRule) ? 'Entry' : 'Feed'
|
189
|
+
end
|
190
|
+
|
191
|
+
def namespace_enhance(options)
|
192
|
+
if !options[:namespace].nil? && options[:namespace].kind_of?(String)
|
193
|
+
options[:namespace] = { :uri => options[:namespace], :eager_load => true }
|
194
|
+
end
|
195
|
+
options
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Restfulie::Common::Builder::Marshalling::Base
|
2
|
+
def initialize(*args)
|
3
|
+
end
|
4
|
+
|
5
|
+
def builder_member(options = {})
|
6
|
+
"#{self.class.to_s.demodulize} Marshalling not implemented"
|
7
|
+
end
|
8
|
+
|
9
|
+
def builder_collection(options = {})
|
10
|
+
"#{self.class.to_s.demodulize} Marshalling not implemented"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Restfulie::Common::Builder::Marshalling
|
2
|
+
class << self
|
3
|
+
def add_autoload_path(path)
|
4
|
+
if File.directory?(path)
|
5
|
+
Dir["#{path}/*.rb"].each do |file|
|
6
|
+
marshalling_class = File.basename(file, ".rb").downcase.classify.to_sym
|
7
|
+
self.autoload(marshalling_class, file) if !self.const_defined?(marshalling_class) && self.autoload?(marshalling_class).nil?
|
8
|
+
end
|
9
|
+
else
|
10
|
+
raise Restfulie::Common::Error::MarshallingError.new("#{path} is not a path.")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
self.add_autoload_path(File.join(File.dirname(__FILE__), 'marshalling'))
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Restfulie::Common::Builder::CollectionRule < Restfulie::Common::Builder::Rules::Base
|
2
|
+
attr_reader :members_blocks
|
3
|
+
attr_reader :members_options
|
4
|
+
|
5
|
+
def describe_members(options = {}, &block)
|
6
|
+
@members_blocks = block_given? ? [block] : []
|
7
|
+
@members_options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Restfulie::Common::Builder::Rules::Link
|
2
|
+
# Required
|
3
|
+
attr_accessor :href
|
4
|
+
|
5
|
+
# Optional
|
6
|
+
attr_accessor :rel, :type
|
7
|
+
attr_accessor :hreflang, :title, :length
|
8
|
+
|
9
|
+
# TODO: Inline resource and collection support
|
10
|
+
def initialize(options = {})
|
11
|
+
options = { :rel => options } unless options.kind_of?(Hash)
|
12
|
+
options.each do |k, i|
|
13
|
+
self.send("#{k}=".to_sym, i)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def rel=(value)
|
18
|
+
@rel = value.kind_of?(String) ? value : value.to_s
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Restfulie::Common::Builder::Rules::Namespace < Hash
|
2
|
+
attr_reader :namespace
|
3
|
+
attr_reader :uri
|
4
|
+
|
5
|
+
def initialize(ns, uri, *args)
|
6
|
+
@namespace = ns.to_sym
|
7
|
+
self.uri = uri
|
8
|
+
super(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def uri=(value)
|
12
|
+
raise Restfulie::Common::Error::NameSpaceError.new('Namespace can not be blank uri.') if value.blank?
|
13
|
+
@uri = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(symbol, *args)
|
17
|
+
if ((key = symbol.to_s.match(/(.*)=/)) && args.size >= 1)
|
18
|
+
self[key[1].to_sym] = args.first
|
19
|
+
elsif(keys.include?(symbol))
|
20
|
+
self[symbol]
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|