roar 0.11.4 → 0.11.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.markdown +5 -1
- data/Gemfile +1 -1
- data/README.textile +22 -21
- data/lib/roar/representer.rb +1 -1
- data/lib/roar/representer/feature/client.rb +3 -3
- data/lib/roar/representer/feature/coercion.rb +1 -1
- data/lib/roar/representer/feature/http_verbs.rb +8 -9
- data/lib/roar/representer/feature/hypermedia.rb +83 -63
- data/lib/roar/representer/json.rb +12 -12
- data/lib/roar/representer/json/hal.rb +98 -25
- data/lib/roar/representer/transport/faraday.rb +5 -11
- data/lib/roar/representer/transport/net_http.rb +5 -5
- data/lib/roar/representer/xml.rb +11 -11
- data/lib/roar/version.rb +1 -1
- data/roar.gemspec +2 -2
- data/test/client_test.rb +2 -2
- data/test/coercion_feature_test.rb +2 -2
- data/test/dummy/app/controllers/albums_controller.rb +6 -6
- data/test/dummy/app/models/album.rb +1 -1
- data/test/dummy/app/representers/representer/xml/album.rb +3 -3
- data/test/dummy/app/representers/representer/xml/song.rb +1 -1
- data/test/dummy/config/boot.rb +1 -1
- data/test/fake_server.rb +14 -15
- data/test/faraday_http_transport_test.rb +25 -10
- data/test/hal_json_test.rb +73 -31
- data/test/http_verbs_feature_test.rb +12 -12
- data/test/hypermedia_feature_test.rb +38 -145
- data/test/hypermedia_test.rb +100 -0
- data/test/integration_test.rb +22 -22
- data/test/json_representer_test.rb +31 -31
- data/test/net_http_transport_test.rb +24 -10
- data/test/order_representers.rb +11 -11
- data/test/rails/controller_methods_test.rb +25 -25
- data/test/rails/rails_representer_methods_test.rb +3 -3
- data/test/representer_test.rb +6 -6
- data/test/test_helper.rb +33 -4
- data/test/xml_representer_test.rb +47 -47
- metadata +3 -3
- data/lib/roar/rails.rb +0 -21
@@ -9,48 +9,48 @@ module Roar
|
|
9
9
|
base.class_eval do
|
10
10
|
include Representer
|
11
11
|
include Representable::JSON
|
12
|
-
|
12
|
+
|
13
13
|
extend ClassMethods
|
14
14
|
include InstanceMethods # otherwise Representable overrides our #to_json.
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
module InstanceMethods
|
19
19
|
def to_hash(*args)
|
20
20
|
before_serialize(*args)
|
21
21
|
super
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def from_json(document, options={})
|
25
25
|
document = '{}' if document.nil? or document.empty?
|
26
|
-
|
26
|
+
|
27
27
|
super
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
# Generic entry-point for rendering.
|
31
31
|
def serialize(*args)
|
32
32
|
to_json(*args)
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def deserialize(*args)
|
36
36
|
from_json(*args)
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
|
-
|
39
|
+
|
40
|
+
|
41
41
|
module ClassMethods
|
42
42
|
def deserialize(*args)
|
43
43
|
from_json(*args)
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
# TODO: move to instance method, or remove?
|
47
47
|
def links_definition_options
|
48
|
-
[:
|
48
|
+
[:links_array, {:from => :links, :class => Feature::Hypermedia::Hyperlink, :extend => HyperlinkRepresenter, :collection => true}]
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
require "representable/json/hash"
|
53
|
-
# Represents a hyperlink in standard roar+json hash representation.
|
53
|
+
# Represents a hyperlink in standard roar+json hash representation.
|
54
54
|
module HyperlinkRepresenter
|
55
55
|
include Representable::JSON::Hash
|
56
56
|
end
|
@@ -8,10 +8,10 @@ module Roar::Representer
|
|
8
8
|
# :embedded => true option.
|
9
9
|
#
|
10
10
|
# Example:
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# module OrderRepresenter
|
13
13
|
# include Roar::Representer::JSON::HAL
|
14
|
-
#
|
14
|
+
#
|
15
15
|
# property :id
|
16
16
|
# collection :items, :class => Item, :extend => ItemRepresenter, :embedded => true
|
17
17
|
#
|
@@ -32,33 +32,48 @@ module Roar::Representer
|
|
32
32
|
include Resources
|
33
33
|
end
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
module Resources
|
37
37
|
# Write the property to the +_embedded+ hash when it's a resource.
|
38
38
|
def compile_fragment(bin, doc)
|
39
39
|
return super unless bin.options[:embedded]
|
40
40
|
super(bin, doc[:_embedded] ||= {})
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
def uncompile_fragment(bin, doc)
|
44
44
|
return super unless bin.options[:embedded]
|
45
45
|
super(bin, doc["_embedded"] || {})
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
module ClassMethods
|
50
50
|
def links_definition_options
|
51
|
-
super.tap { |options| options[1].merge!(
|
51
|
+
super.tap { |options| options[1].merge!(
|
52
|
+
:from => :_links,
|
53
|
+
:extend => HAL::Links::LinkCollectionRepresenter,
|
54
|
+
:instance => lambda { |hsh| LinkCollection.new(link_array_rels) }) # defined in InstanceMethods as this is executed in represented context.
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class LinkCollection < Feature::Hypermedia::LinkCollection
|
60
|
+
def initialize(array_rels, *args)
|
61
|
+
super(*args)
|
62
|
+
@array_rels = array_rels.map(&:to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
def is_array?(rel)
|
66
|
+
@array_rels.include?(rel.to_s)
|
52
67
|
end
|
53
68
|
end
|
54
|
-
|
69
|
+
|
55
70
|
# Including this module in your representer will render and parse your embedded hyperlinks
|
56
71
|
# following the HAL specification: http://stateless.co/hal_specification.html
|
57
72
|
#
|
58
73
|
# module SongRepresenter
|
59
74
|
# include Roar::Representer::JSON
|
60
75
|
# include Roar::Representer::JSON::HAL::Links
|
61
|
-
#
|
76
|
+
#
|
62
77
|
# link :self { "http://self" }
|
63
78
|
# end
|
64
79
|
#
|
@@ -68,40 +83,98 @@ module Roar::Representer
|
|
68
83
|
#
|
69
84
|
# Note that the HAL::Links module alone doesn't prepend an underscore to +links+. Use the JSON::HAL module for that.
|
70
85
|
module Links
|
71
|
-
# TODO: allow more attributes besides :href in Hyperlink.
|
72
86
|
def self.included(base)
|
73
87
|
base.class_eval do
|
74
88
|
include Roar::Representer::Feature::Hypermedia
|
89
|
+
include InstanceMethods
|
75
90
|
extend Links::ClassMethods
|
76
91
|
end
|
77
92
|
end
|
78
93
|
|
94
|
+
module InstanceMethods
|
95
|
+
private
|
96
|
+
def prepare_link_for(href, options)
|
97
|
+
return super(href, options) unless options[:array] # TODO: remove :array and use special instan
|
98
|
+
|
99
|
+
list = href.collect { |opts| Feature::Hypermedia::Hyperlink.new(opts.merge!(:rel => options[:rel])) }
|
100
|
+
LinkArray.new(list)
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: move to LinksDefinition.
|
104
|
+
def link_array_rels
|
105
|
+
links_definition.collect { |cfg| cfg.first[:array] ? cfg.first[:rel] : nil }.compact
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
79
109
|
|
110
|
+
require 'representable/json/hash'
|
80
111
|
module LinkCollectionRepresenter
|
81
|
-
include JSON
|
82
|
-
|
83
|
-
|
84
|
-
{
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
112
|
+
include Representable::JSON::Hash
|
113
|
+
|
114
|
+
values :extend => lambda { |item| item.is_a?(Array) ? LinkArrayRepresenter : Roar::Representer::JSON::HyperlinkRepresenter },
|
115
|
+
:class => lambda { |hsh| hsh.is_a?(LinkArray) ? nil : Roar::Representer::Feature::Hypermedia::Hyperlink }
|
116
|
+
|
117
|
+
def to_hash(options)
|
118
|
+
super.tap do |hsh| # TODO: cool: super(:exclude => [:rel]).
|
119
|
+
hsh.each { |k,v| v.delete(:rel) }
|
89
120
|
end
|
90
121
|
end
|
91
122
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
123
|
+
|
124
|
+
def from_hash(hash, options={})
|
125
|
+
hash.each { |k,v| hash[k] = LinkArray.new(v) if is_array?(k) }
|
126
|
+
|
127
|
+
hsh = super(hash) # this is where :class and :extend do the work.
|
128
|
+
|
129
|
+
hsh.each { |k, v| v.rel = k }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class LinkArray < Array
|
134
|
+
def rel
|
135
|
+
first.rel
|
136
|
+
end
|
137
|
+
|
138
|
+
def rel=(rel)
|
139
|
+
each { |lnk| lnk.rel = rel }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
require 'representable/json/collection'
|
144
|
+
module LinkArrayRepresenter
|
145
|
+
include Representable::JSON::Collection
|
146
|
+
|
147
|
+
items :extend => Roar::Representer::JSON::HyperlinkRepresenter,
|
148
|
+
:class => Roar::Representer::Feature::Hypermedia::Hyperlink
|
149
|
+
|
150
|
+
def to_hash(*)
|
151
|
+
super.tap do |ary|
|
152
|
+
ary.each { |lnk| rel = lnk.delete(:rel) }
|
96
153
|
end
|
97
|
-
self
|
98
154
|
end
|
99
155
|
end
|
100
|
-
|
101
|
-
|
156
|
+
|
157
|
+
|
102
158
|
module ClassMethods
|
159
|
+
# TODO: remove Links support in non-HAL.
|
103
160
|
def links_definition_options
|
104
|
-
super.tap { |options|
|
161
|
+
super.tap { |options|
|
162
|
+
options[0] = :links
|
163
|
+
options[1] = {:class => Feature::Hypermedia::LinkCollection, :extend => LinkCollectionRepresenter, :from => :_links}
|
164
|
+
}
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
# Use this to define link arrays. It accepts the shared rel attribute and an array of options per link object.
|
169
|
+
#
|
170
|
+
# links :self do
|
171
|
+
# [{:lang => "en", :href => "http://en.hit"},
|
172
|
+
# {:lang => "de", :href => "http://de.hit"}]
|
173
|
+
# end
|
174
|
+
def links(options, &block)
|
175
|
+
options = {:rel => options} if options.is_a?(Symbol)
|
176
|
+
options[:array] = true # FIXME: we need to save this information somewhere.
|
177
|
+
link(options, &block)
|
105
178
|
end
|
106
179
|
end
|
107
180
|
end
|
@@ -1,9 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'faraday'
|
3
|
-
rescue LoadError
|
4
|
-
puts 'You must add faraday as a dependency to use the FaradayTransport'
|
5
|
-
end
|
6
|
-
require 'logger'
|
1
|
+
require 'faraday'
|
7
2
|
|
8
3
|
module Roar
|
9
4
|
module Representer
|
@@ -23,15 +18,15 @@ module Roar
|
|
23
18
|
end
|
24
19
|
|
25
20
|
def post_uri(uri, body, as)
|
26
|
-
build_connection(uri, as).post
|
21
|
+
build_connection(uri, as).post(nil, body)
|
27
22
|
end
|
28
23
|
|
29
24
|
def put_uri(uri, body, as)
|
30
|
-
build_connection(uri, as).put
|
25
|
+
build_connection(uri, as).put(nil, body)
|
31
26
|
end
|
32
27
|
|
33
28
|
def patch_uri(uri, body, as)
|
34
|
-
build_connection(uri, as).patch
|
29
|
+
build_connection(uri, as).patch(nil, body)
|
35
30
|
end
|
36
31
|
|
37
32
|
def delete_uri(uri, as)
|
@@ -43,9 +38,8 @@ module Roar
|
|
43
38
|
def build_connection(uri, as)
|
44
39
|
::Faraday::Connection.new(
|
45
40
|
:url => uri,
|
46
|
-
:headers => { :accept => as }
|
41
|
+
:headers => { :accept => as, :content_type => as }
|
47
42
|
) do |builder|
|
48
|
-
# builder.use Faraday::Response::Logger, Logger.new('faraday.log')
|
49
43
|
builder.use ::Faraday::Response::RaiseError
|
50
44
|
builder.adapter ::Faraday.default_adapter
|
51
45
|
end
|
@@ -11,23 +11,23 @@ module Roar
|
|
11
11
|
def get_uri(uri, as)
|
12
12
|
do_request(Net::HTTP::Get, uri, as)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def post_uri(uri, body, as)
|
16
16
|
do_request(Net::HTTP::Post, uri, as, body)
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def put_uri(uri, body, as)
|
20
20
|
do_request(Net::HTTP::Put, uri, as, body)
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def patch_uri(uri, body, as)
|
24
24
|
do_request(Net::HTTP::Patch, uri, as, body)
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def delete_uri(uri, as)
|
28
28
|
do_request(Net::HTTP::Delete, uri, as)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
private
|
32
32
|
def do_request(what, uri, as, body="")
|
33
33
|
# DISCUSS: can this be made easier?
|
data/lib/roar/representer/xml.rb
CHANGED
@@ -16,42 +16,42 @@ module Roar
|
|
16
16
|
include InstanceMethods # otherwise Representable overrides our #to_xml.
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
module InstanceMethods
|
21
21
|
def to_xml(*args)
|
22
22
|
before_serialize(*args)
|
23
23
|
super
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
# Generic entry-point for rendering.
|
27
27
|
def serialize(*args)
|
28
28
|
to_xml(*args)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def deserialize(*args)
|
32
32
|
from_xml(*args)
|
33
33
|
end
|
34
34
|
end
|
35
|
-
|
36
|
-
|
35
|
+
|
36
|
+
|
37
37
|
module ClassMethods
|
38
38
|
include Representable::XML::ClassMethods
|
39
|
-
|
39
|
+
|
40
40
|
def links_definition_options
|
41
|
-
[:
|
41
|
+
[:links_array, {:from => :link, :class => Feature::Hypermedia::Hyperlink, :collection => true, :extend => XML::HyperlinkRepresenter}]
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
# Generic entry-point for parsing.
|
45
45
|
def deserialize(*args)
|
46
46
|
from_xml(*args)
|
47
47
|
end
|
48
48
|
end
|
49
|
-
|
50
|
-
|
49
|
+
|
50
|
+
|
51
51
|
require 'representable/xml/hash'
|
52
52
|
module HyperlinkRepresenter
|
53
53
|
include Representable::XML::AttributeHash
|
54
|
-
|
54
|
+
|
55
55
|
self.representation_wrap = :link
|
56
56
|
end
|
57
57
|
end
|
data/lib/roar/version.rb
CHANGED
data/roar.gemspec
CHANGED
@@ -18,9 +18,9 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
|
-
|
21
|
+
|
22
22
|
s.add_runtime_dependency "representable", ">= 1.2.8"
|
23
|
-
|
23
|
+
|
24
24
|
s.add_development_dependency "rake"
|
25
25
|
s.add_development_dependency "test_xml"
|
26
26
|
s.add_development_dependency "minitest", ">= 2.8.1"
|
data/test/client_test.rb
CHANGED
@@ -9,10 +9,10 @@ class ClientTest < MiniTest::Spec
|
|
9
9
|
property :name
|
10
10
|
property :band
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
@song = Object.new.extend(@representer)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
it "should add accessors" do
|
17
17
|
@song.extend Roar::Representer::Feature::Client
|
18
18
|
@song.name = "Social Suicide"
|
@@ -6,10 +6,10 @@ class CoercionFeatureTest < MiniTest::Spec
|
|
6
6
|
class ImmigrantSong
|
7
7
|
include Roar::Representer::JSON
|
8
8
|
include Roar::Representer::Feature::Coercion
|
9
|
-
|
9
|
+
|
10
10
|
property :composed_at, :type => DateTime, :default => "May 12th, 2012"
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
it "coerces into the provided type" do
|
14
14
|
song = ImmigrantSong.new.from_json("{\"composed_at\":\"November 18th, 1983\"}")
|
15
15
|
assert_equal DateTime.parse("Fri, 18 Nov 1983 00:00:00 +0000"), song.composed_at
|
@@ -2,26 +2,26 @@ require 'roar/rails'
|
|
2
2
|
|
3
3
|
class AlbumsController < ActionController::Base
|
4
4
|
include Roar::Rails::ControllerMethods
|
5
|
-
|
5
|
+
|
6
6
|
respond_to :xml
|
7
7
|
represents Album
|
8
|
-
|
8
|
+
|
9
9
|
def show
|
10
10
|
@album = Album.find(params[:id])
|
11
11
|
respond_with @album
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def create
|
15
15
|
@album = Album.create(representation)
|
16
|
-
|
16
|
+
|
17
17
|
respond_with @album
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def update
|
21
21
|
@album = Album.find(params[:id])
|
22
22
|
@album.songs.delete_all # make PUT behave REST-compliant.
|
23
23
|
@album.update_attributes(representation)
|
24
|
-
|
24
|
+
|
25
25
|
respond_with @album
|
26
26
|
end
|
27
27
|
end
|