roar 0.12.9 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +8 -10
- data/CHANGES.markdown +24 -2
- data/Gemfile +5 -2
- data/README.markdown +132 -28
- data/TODO.markdown +9 -7
- data/examples/example.rb +2 -0
- data/examples/example_server.rb +19 -3
- data/gemfiles/Gemfile.representable-2.0 +5 -0
- data/gemfiles/Gemfile.representable-2.1 +5 -0
- data/lib/roar.rb +1 -1
- data/lib/roar/client.rb +38 -0
- data/lib/roar/{representer/feature/coercion.rb → coercion.rb} +2 -1
- data/lib/roar/decorator.rb +3 -11
- data/lib/roar/http_verbs.rb +88 -0
- data/lib/roar/hypermedia.rb +174 -0
- data/lib/roar/json.rb +55 -0
- data/lib/roar/json/collection.rb +3 -0
- data/lib/roar/{representer/json → json}/collection_json.rb +20 -20
- data/lib/roar/{representer/json → json}/hal.rb +33 -31
- data/lib/roar/json/hash.rb +3 -0
- data/lib/roar/json/json_api.rb +208 -0
- data/lib/roar/representer.rb +3 -36
- data/lib/roar/transport/faraday.rb +49 -0
- data/lib/roar/transport/net_http.rb +57 -0
- data/lib/roar/transport/net_http/request.rb +72 -0
- data/lib/roar/version.rb +1 -1
- data/lib/roar/xml.rb +54 -0
- data/roar.gemspec +5 -4
- data/test/client_test.rb +3 -3
- data/test/coercion_feature_test.rb +6 -3
- data/test/collection_json_test.rb +8 -10
- data/test/decorator_test.rb +27 -15
- data/test/faraday_http_transport_test.rb +13 -15
- data/test/hal_json_test.rb +16 -16
- data/test/hal_links_test.rb +3 -3
- data/test/http_verbs_test.rb +17 -22
- data/test/hypermedia_feature_test.rb +23 -45
- data/test/hypermedia_test.rb +11 -23
- data/test/integration/band_representer.rb +2 -2
- data/test/integration/runner.rb +4 -3
- data/test/integration/server.rb +13 -2
- data/test/integration/ssl_server.rb +1 -1
- data/test/json_api_test.rb +336 -0
- data/test/json_representer_test.rb +16 -12
- data/test/lib/runner.rb +134 -0
- data/test/lonely_test.rb +9 -0
- data/test/net_http_transport_test.rb +4 -4
- data/test/representer_test.rb +2 -2
- data/test/{lib/roar/representer/transport/net_http/request_test.rb → ssl_client_certs_test.rb} +43 -5
- data/test/test_helper.rb +12 -5
- data/test/xml_representer_test.rb +26 -166
- metadata +49 -29
- data/gemfiles/Gemfile.representable-1.7 +0 -6
- data/gemfiles/Gemfile.representable-1.8 +0 -6
- data/lib/roar/representer/feature/client.rb +0 -39
- data/lib/roar/representer/feature/http_verbs.rb +0 -95
- data/lib/roar/representer/feature/hypermedia.rb +0 -175
- data/lib/roar/representer/json.rb +0 -67
- data/lib/roar/representer/transport/faraday.rb +0 -50
- data/lib/roar/representer/transport/net_http.rb +0 -59
- data/lib/roar/representer/transport/net_http/request.rb +0 -75
- data/lib/roar/representer/xml.rb +0 -61
@@ -1,39 +0,0 @@
|
|
1
|
-
require "roar/representer/feature/http_verbs"
|
2
|
-
|
3
|
-
module Roar
|
4
|
-
# Automatically add accessors for properties and collections. Also mixes in HttpVerbs.
|
5
|
-
module Representer
|
6
|
-
module Feature
|
7
|
-
module Client
|
8
|
-
include HttpVerbs
|
9
|
-
|
10
|
-
def self.extended(base)
|
11
|
-
base.instance_eval do
|
12
|
-
representable_attrs.each do |attr|
|
13
|
-
next unless attr.instance_of? Representable::Definition # ignore hyperlinks etc for now.
|
14
|
-
name = attr.name
|
15
|
-
|
16
|
-
# TODO: could anyone please make this better?
|
17
|
-
instance_eval %Q{
|
18
|
-
def #{name}=(v)
|
19
|
-
@#{name} = v
|
20
|
-
end
|
21
|
-
|
22
|
-
def #{name}
|
23
|
-
@#{name}
|
24
|
-
end
|
25
|
-
}
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
# DISCUSS: should we just override #serialize here? otherwise if you later include Hypermedia, it'll run before that method.
|
31
|
-
def before_serialize(options={})
|
32
|
-
options[:links] ||= false
|
33
|
-
|
34
|
-
super(options)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
require 'roar/representer/transport/net_http'
|
2
|
-
|
3
|
-
module Roar
|
4
|
-
# Gives HTTP-power to representers. They can serialize, send, process and deserialize HTTP-requests.
|
5
|
-
module Representer
|
6
|
-
module Feature
|
7
|
-
module HttpVerbs
|
8
|
-
|
9
|
-
class << self
|
10
|
-
attr_accessor :transport_engine
|
11
|
-
|
12
|
-
def included(base)
|
13
|
-
base.extend ClassMethods
|
14
|
-
end
|
15
|
-
end
|
16
|
-
self.transport_engine = ::Roar::Representer::Transport::NetHTTP
|
17
|
-
|
18
|
-
|
19
|
-
module ClassMethods
|
20
|
-
# GETs +url+ with +format+ and returns deserialized represented object.
|
21
|
-
def get(*args)
|
22
|
-
new.get(*args)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
attr_writer :transport_engine
|
28
|
-
def transport_engine
|
29
|
-
@transport_engine || HttpVerbs.transport_engine
|
30
|
-
end
|
31
|
-
|
32
|
-
# Serializes the object, POSTs it to +url+ with +format+, deserializes the returned document
|
33
|
-
# and updates properties accordingly.
|
34
|
-
def post(*args, &block)
|
35
|
-
options = handle_deprecated_args(serialize, *args)
|
36
|
-
response = http.post_uri(options, &block)
|
37
|
-
handle_response(response)
|
38
|
-
end
|
39
|
-
|
40
|
-
# GETs +url+ with +format+, deserializes the returned document and updates properties accordingly.
|
41
|
-
def get(*args, &block)
|
42
|
-
response = http.get_uri(*args, &block)
|
43
|
-
handle_response(response)
|
44
|
-
end
|
45
|
-
|
46
|
-
# Serializes the object, PUTs it to +url+ with +format+, deserializes the returned document
|
47
|
-
# and updates properties accordingly.
|
48
|
-
def put(*args, &block)
|
49
|
-
options = handle_deprecated_args(serialize, *args)
|
50
|
-
response = http.put_uri(options, &block)
|
51
|
-
handle_response(response)
|
52
|
-
self
|
53
|
-
end
|
54
|
-
|
55
|
-
def patch(*args, &block)
|
56
|
-
options = handle_deprecated_args(serialize, *args)
|
57
|
-
response = http.patch_uri(options, &block)
|
58
|
-
handle_response(response)
|
59
|
-
self
|
60
|
-
end
|
61
|
-
|
62
|
-
def delete(*args, &block)
|
63
|
-
http.delete_uri(*args, &block)
|
64
|
-
self
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
def handle_response(response)
|
69
|
-
document = response.body
|
70
|
-
deserialize(document)
|
71
|
-
end
|
72
|
-
|
73
|
-
def http
|
74
|
-
transport_engine.new
|
75
|
-
end
|
76
|
-
|
77
|
-
def handle_deprecated_args(body, *args) # TODO: remove in 1.0.
|
78
|
-
options = args.first
|
79
|
-
|
80
|
-
if args.size > 1
|
81
|
-
warn %{DEPRECATION WARNING: #get, #post, #put, #delete and #patch no longer accept positional arguments. Please call them as follows:
|
82
|
-
get(uri: "http://localhost/songs", as: "application/json")
|
83
|
-
post(uri: "http://localhost/songs", as: "application/json")
|
84
|
-
Thank you and have a beautiful day.}
|
85
|
-
options = {:uri => args[0], :as => args[1]} if args.size == 2
|
86
|
-
options = {:uri => args[0], :as => args[2]}
|
87
|
-
end
|
88
|
-
|
89
|
-
options[:body] = body
|
90
|
-
options
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
@@ -1,175 +0,0 @@
|
|
1
|
-
module Roar
|
2
|
-
module Representer
|
3
|
-
module Feature
|
4
|
-
# Define hypermedia links in your representations.
|
5
|
-
#
|
6
|
-
# Example:
|
7
|
-
#
|
8
|
-
# class Order
|
9
|
-
# include Roar::Representer::JSON
|
10
|
-
#
|
11
|
-
# property :id
|
12
|
-
#
|
13
|
-
# link :self do
|
14
|
-
# "http://orders/#{id}"
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# If you want more attributes, just pass a hash to #link.
|
18
|
-
#
|
19
|
-
# link :rel => :next, :title => "Next, please!" do
|
20
|
-
# "http://orders/#{id}"
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# If you need dynamic attributes, the block can return a hash.
|
24
|
-
#
|
25
|
-
# link :preview do
|
26
|
-
# {:href => image.url, :title => image.name}
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# Sometimes you need values from outside when the representation links are rendered. Just pass them
|
30
|
-
# to the render method, they will be available as block parameters.
|
31
|
-
#
|
32
|
-
# link :self do |opts|
|
33
|
-
# "http://orders/#{opts[:id]}"
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# model.to_json(:id => 1)
|
37
|
-
module Hypermedia
|
38
|
-
def self.included(base)
|
39
|
-
base.extend ClassMethods
|
40
|
-
base.extend InheritableArray
|
41
|
-
end
|
42
|
-
|
43
|
-
def before_serialize(options={})
|
44
|
-
super(options) # Representer::Base
|
45
|
-
prepare_links!(options) unless options[:links] == false # DISCUSS: doesn't work when links are already setup (e.g. from #deserialize).
|
46
|
-
end
|
47
|
-
|
48
|
-
attr_writer :links
|
49
|
-
|
50
|
-
def links
|
51
|
-
@links ||= LinkCollection.new
|
52
|
-
end
|
53
|
-
|
54
|
-
def links_array
|
55
|
-
links.values # FIXME: move to LinkCollection#to_a.
|
56
|
-
end
|
57
|
-
|
58
|
-
def links_array=(ary)
|
59
|
-
# FIXME: move to LinkCollection
|
60
|
-
self.links= LinkCollection.new
|
61
|
-
ary.each { |lnk| links.add(lnk) }
|
62
|
-
end
|
63
|
-
|
64
|
-
module LinkConfigsMethod
|
65
|
-
def link_configs
|
66
|
-
representable_attrs.inheritable_array(:links)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
include LinkConfigsMethod
|
71
|
-
|
72
|
-
private
|
73
|
-
def links_definition_options
|
74
|
-
# TODO: this method is never called.
|
75
|
-
[:links_array, {:as => :link, :class => Feature::Hypermedia::Hyperlink, :collection => true,
|
76
|
-
:decorator_scope => true}] # TODO: merge with JSON.
|
77
|
-
end
|
78
|
-
|
79
|
-
# Setup hypermedia links by invoking their blocks. Usually called by #serialize.
|
80
|
-
def prepare_links!(*args)
|
81
|
-
# TODO: move this method to _links or something so it doesn't need to be called in #serialize.
|
82
|
-
compile_links_for(link_configs, *args).each do |lnk|
|
83
|
-
links.add(lnk) # TODO: move to LinkCollection.new.
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def compile_links_for(configs, *args)
|
88
|
-
configs.collect do |config|
|
89
|
-
options, block = config.first, config.last
|
90
|
-
href = run_link_block(block, *args) or next
|
91
|
-
|
92
|
-
prepare_link_for(href, options)
|
93
|
-
end.compact # FIXME: make this less ugly.
|
94
|
-
end
|
95
|
-
|
96
|
-
def prepare_link_for(href, options)
|
97
|
-
options = options.merge(href.is_a?(Hash) ? href : {:href => href})
|
98
|
-
Hyperlink.new(options)
|
99
|
-
end
|
100
|
-
|
101
|
-
def run_link_block(block, *args)
|
102
|
-
instance_exec(*args, &block)
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
class LinkCollection < Hash
|
107
|
-
# DISCUSS: make Link#rel return string always.
|
108
|
-
def [](rel)
|
109
|
-
self.fetch(rel.to_s, nil)
|
110
|
-
end
|
111
|
-
|
112
|
-
def add(link)
|
113
|
-
self[link.rel.to_s] = link
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
module ClassMethods
|
119
|
-
# Declares a hypermedia link in the document.
|
120
|
-
#
|
121
|
-
# Example:
|
122
|
-
#
|
123
|
-
# link :self do
|
124
|
-
# "http://orders/#{id}"
|
125
|
-
# end
|
126
|
-
#
|
127
|
-
# The block is executed in instance context, so you may call properties or other accessors.
|
128
|
-
# Note that you're free to put decider logic into #link blocks, too.
|
129
|
-
def link(options, &block)
|
130
|
-
options = {:rel => options} unless options.is_a?(Hash)
|
131
|
-
create_links_definition # this assures the links are rendered at the right position.
|
132
|
-
link_configs << [options, block]
|
133
|
-
end
|
134
|
-
|
135
|
-
include LinkConfigsMethod
|
136
|
-
|
137
|
-
private
|
138
|
-
def create_links_definition
|
139
|
-
return if representable_attrs.find { |d| d.is_a?(LinksDefinition) }
|
140
|
-
|
141
|
-
options = links_definition_options # TODO: remove in 1.0.
|
142
|
-
if Roar.representable_1_8?
|
143
|
-
opt = options.last
|
144
|
-
opt[:exec_context] = :decorator
|
145
|
-
opt.delete(:decorator_scope)
|
146
|
-
end
|
147
|
-
|
148
|
-
representable_attrs << LinksDefinition.new(*options)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
class LinksDefinition < Representable::Definition
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
require "ostruct"
|
157
|
-
# An abstract hypermedia link with arbitrary attributes.
|
158
|
-
class Hyperlink < OpenStruct
|
159
|
-
include Enumerable
|
160
|
-
|
161
|
-
def each(*args, &block)
|
162
|
-
marshal_dump.each(*args, &block)
|
163
|
-
end
|
164
|
-
|
165
|
-
# FIXME: do we need this method any longer?
|
166
|
-
def replace(hash)
|
167
|
-
# #marshal_load requires symbol keys: http://apidock.com/ruby/v1_9_3_125/OpenStruct/marshal_load
|
168
|
-
marshal_load(hash.inject({}) { |h, (k,v)| h[k.to_sym] = v; h })
|
169
|
-
self
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'roar/representer'
|
2
|
-
require 'roar/representer/feature/hypermedia'
|
3
|
-
require 'representable/json'
|
4
|
-
|
5
|
-
module Roar
|
6
|
-
require 'representable/version'
|
7
|
-
def self.representable_1_8? # TODO: remove me in 1.0.
|
8
|
-
Representable::VERSION =~ /^1.8/
|
9
|
-
end
|
10
|
-
|
11
|
-
module Representer
|
12
|
-
module JSON
|
13
|
-
def self.included(base)
|
14
|
-
base.class_eval do
|
15
|
-
include Representer
|
16
|
-
include Representable::JSON
|
17
|
-
|
18
|
-
extend ClassMethods
|
19
|
-
include InstanceMethods # otherwise Representable overrides our #to_json.
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
module InstanceMethods
|
24
|
-
def to_hash(*args)
|
25
|
-
before_serialize(*args)
|
26
|
-
super
|
27
|
-
end
|
28
|
-
|
29
|
-
def from_json(document, options={})
|
30
|
-
document = '{}' if document.nil? or document.empty?
|
31
|
-
|
32
|
-
super
|
33
|
-
end
|
34
|
-
|
35
|
-
# Generic entry-point for rendering.
|
36
|
-
def serialize(*args)
|
37
|
-
to_json(*args)
|
38
|
-
end
|
39
|
-
|
40
|
-
def deserialize(*args)
|
41
|
-
from_json(*args)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
module ClassMethods
|
47
|
-
def deserialize(*args)
|
48
|
-
from_json(*args)
|
49
|
-
end
|
50
|
-
|
51
|
-
# TODO: move to instance method, or remove?
|
52
|
-
def links_definition_options
|
53
|
-
# FIXME: this doesn't belong into the generic JSON representer.
|
54
|
-
[:links_array, {:as => :links, :class => Feature::Hypermedia::Hyperlink, :extend => HyperlinkRepresenter, :collection => true,
|
55
|
-
:decorator_scope => true}]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
require "representable/json/hash"
|
61
|
-
# Represents a hyperlink in standard roar+json hash representation.
|
62
|
-
module HyperlinkRepresenter
|
63
|
-
include Representable::JSON::Hash
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
|
3
|
-
module Roar
|
4
|
-
module Representer
|
5
|
-
module Transport
|
6
|
-
# Advanced implementation of the HTTP verbs with the Faraday HTTP library
|
7
|
-
# (which can, in turn, use adapters based on Net::HTTP or libcurl)
|
8
|
-
#
|
9
|
-
# Depending on how the Faraday middleware stack is configured, this
|
10
|
-
# Transport can support features such as HTTP error code handling,
|
11
|
-
# redirects, etc.
|
12
|
-
#
|
13
|
-
# @see http://rubydoc.info/gems/faraday/file/README.md Faraday README
|
14
|
-
class Faraday
|
15
|
-
|
16
|
-
def get_uri(uri, as, *args)
|
17
|
-
build_connection(uri, as).get
|
18
|
-
end
|
19
|
-
|
20
|
-
def post_uri(uri, body, as, *args)
|
21
|
-
build_connection(uri, as).post(nil, body)
|
22
|
-
end
|
23
|
-
|
24
|
-
def put_uri(uri, body, as, *args)
|
25
|
-
build_connection(uri, as).put(nil, body)
|
26
|
-
end
|
27
|
-
|
28
|
-
def patch_uri(uri, body, as, *args)
|
29
|
-
build_connection(uri, as).patch(nil, body)
|
30
|
-
end
|
31
|
-
|
32
|
-
def delete_uri(uri, as, *args)
|
33
|
-
build_connection(uri, as).delete
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def build_connection(uri, as)
|
39
|
-
::Faraday::Connection.new(
|
40
|
-
:url => uri,
|
41
|
-
:headers => { :accept => as, :content_type => as }
|
42
|
-
) do |builder|
|
43
|
-
builder.use ::Faraday::Response::RaiseError
|
44
|
-
builder.adapter ::Faraday.default_adapter
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|