restfulie 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|