roar 1.0.2 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/ISSUE_TEMPLATE.md +20 -0
- data/.travis.yml +16 -11
- data/CHANGES.markdown +86 -57
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +7 -4
- data/LICENSE +1 -1
- data/README.markdown +133 -255
- data/Rakefile +3 -1
- data/examples/example.rb +0 -0
- data/examples/example_server.rb +0 -0
- data/lib/roar/client.rb +8 -3
- data/lib/roar/decorator.rb +2 -2
- data/lib/roar/http_verbs.rb +0 -16
- data/lib/roar/hypermedia.rb +30 -56
- data/lib/roar/json/collection.rb +10 -2
- data/lib/roar/json/hal.rb +74 -83
- data/lib/roar/json.rb +5 -5
- data/lib/roar/version.rb +1 -1
- data/lib/roar/xml.rb +1 -1
- data/lib/roar.rb +3 -3
- data/roar.gemspec +7 -5
- data/test/client_test.rb +1 -1
- data/test/coercion_feature_test.rb +7 -2
- data/test/decorator_test.rb +17 -7
- data/test/hal_json_test.rb +101 -94
- data/test/hypermedia_feature_test.rb +13 -31
- data/test/hypermedia_test.rb +26 -92
- data/test/{decorator_client_test.rb → integration/decorator_client_test.rb} +5 -4
- data/test/{faraday_http_transport_test.rb → integration/faraday_http_transport_test.rb} +1 -0
- data/test/{http_verbs_test.rb → integration/http_verbs_test.rb} +3 -2
- data/test/integration/json_collection_test.rb +35 -0
- data/test/{net_http_transport_test.rb → integration/net_http_transport_test.rb} +1 -0
- data/test/integration/runner.rb +2 -3
- data/test/integration/server.rb +6 -0
- data/test/json_representer_test.rb +2 -29
- data/test/lonely_test.rb +1 -2
- data/test/ssl_client_certs_test.rb +1 -1
- data/test/test_helper.rb +21 -3
- data/test/xml_representer_test.rb +6 -5
- metadata +21 -37
- data/gemfiles/Gemfile.representable-1.7 +0 -6
- data/gemfiles/Gemfile.representable-1.8 +0 -6
- data/gemfiles/Gemfile.representable-2.0 +0 -5
- data/gemfiles/Gemfile.representable-2.1 +0 -5
- data/gemfiles/Gemfile.representable-head +0 -6
- data/lib/roar/json/collection_json.rb +0 -208
- data/lib/roar/json/json_api.rb +0 -233
- data/test/collection_json_test.rb +0 -132
- data/test/hal_links_test.rb +0 -31
- data/test/json_api_test.rb +0 -451
- data/test/lib/runner.rb +0 -134
data/Rakefile
CHANGED
@@ -7,6 +7,8 @@ task :default => [:test]
|
|
7
7
|
|
8
8
|
Rake::TestTask.new(:test) do |test|
|
9
9
|
test.libs << 'test'
|
10
|
-
test.test_files = FileList
|
10
|
+
test.test_files = FileList.new('test/**/*_test.rb') do |fl|
|
11
|
+
fl.exclude('test/integration/**') if RUBY_VERSION < '2.2.2'
|
12
|
+
end
|
11
13
|
test.verbose = true
|
12
14
|
end
|
data/examples/example.rb
CHANGED
File without changes
|
data/examples/example_server.rb
CHANGED
File without changes
|
data/lib/roar/client.rb
CHANGED
@@ -2,7 +2,7 @@ require "roar/http_verbs"
|
|
2
2
|
|
3
3
|
module Roar
|
4
4
|
|
5
|
-
# Mix in HttpVerbs.
|
5
|
+
# Mix in HttpVerbs.
|
6
6
|
module Client
|
7
7
|
include HttpVerbs
|
8
8
|
|
@@ -28,12 +28,17 @@ module Roar
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def to_hash(options={})
|
31
|
-
options[:links] ||= false
|
31
|
+
# options[:links] ||= false
|
32
|
+
options[:user_options] ||= {}
|
33
|
+
options[:user_options][:links] ||= false
|
34
|
+
|
32
35
|
super(options)
|
33
36
|
end
|
34
37
|
|
35
38
|
def to_xml(options={}) # sorry, but i'm not even sure if anyone uses this module.
|
36
|
-
options[:
|
39
|
+
options[:user_options] ||= {}
|
40
|
+
options[:user_options][:links] ||= false
|
41
|
+
|
37
42
|
super(options)
|
38
43
|
end
|
39
44
|
end
|
data/lib/roar/decorator.rb
CHANGED
@@ -4,8 +4,8 @@ require 'representable/decorator'
|
|
4
4
|
class Roar::Decorator < Representable::Decorator
|
5
5
|
module HypermediaConsumer
|
6
6
|
def links=(arr)
|
7
|
-
|
8
|
-
represented.instance_variable_set :@links, links
|
7
|
+
super
|
8
|
+
represented.instance_variable_set :@links, self.links
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
data/lib/roar/http_verbs.rb
CHANGED
@@ -68,21 +68,5 @@ module Roar
|
|
68
68
|
def http
|
69
69
|
transport_engine.new
|
70
70
|
end
|
71
|
-
|
72
|
-
def handle_deprecated_args(body, *args) # TODO: remove in 1.0.
|
73
|
-
options = args.first
|
74
|
-
|
75
|
-
if args.size > 1
|
76
|
-
warn %{DEPRECATION WARNING: #get, #post, #put, #delete and #patch no longer accept positional arguments. Please call them as follows:
|
77
|
-
get(uri: "http://localhost/songs", as: "application/json")
|
78
|
-
post(uri: "http://localhost/songs", as: "application/json")
|
79
|
-
Thank you and have a beautiful day.}
|
80
|
-
options = {:uri => args[0], :as => args[1]} if args.size == 2
|
81
|
-
options = {:uri => args[0], :as => args[2]}
|
82
|
-
end
|
83
|
-
|
84
|
-
options[:body] = body
|
85
|
-
options
|
86
|
-
end
|
87
71
|
end
|
88
72
|
end
|
data/lib/roar/hypermedia.rb
CHANGED
@@ -33,33 +33,26 @@ module Roar
|
|
33
33
|
#
|
34
34
|
# model.to_json(:id => 1)
|
35
35
|
module Hypermedia
|
36
|
-
# links= [Hyperlink, Hyperlink] is where parsing happens.
|
37
36
|
def self.included(base)
|
38
37
|
base.extend ClassMethods
|
39
38
|
end
|
40
39
|
|
41
|
-
|
42
|
-
|
40
|
+
# public API: #links (only helpful in clients, though).
|
41
|
+
attr_writer :links # this is called in parsing when Hyperlinks are deserialized.
|
42
|
+
def links # this is _not_ called by rendering as we go via ::links_config.
|
43
|
+
tuples = (@links||[]).collect { |link| [link.rel, link] }
|
44
|
+
# tuples.to_h
|
45
|
+
::Hash[tuples] # TODO: tuples.to_h when dropping < 2.1.
|
43
46
|
end
|
44
47
|
|
45
|
-
attr_reader :links # this is only useful after parsing.
|
46
|
-
|
47
|
-
|
48
|
-
module LinkConfigsMethod
|
49
|
-
def link_configs # we could store the ::link configs in links Definition.
|
50
|
-
representable_attrs[:links] ||= Representable::Inheritable::Array.new
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
include LinkConfigsMethod
|
55
|
-
|
56
48
|
private
|
57
49
|
# Create hypermedia links for this instance by invoking their blocks.
|
58
50
|
# This is called in links: getter: {}.
|
59
51
|
def prepare_links!(options)
|
60
|
-
return [] if options[:links] == false
|
52
|
+
return [] if (options[:user_options] || {})[:links] == false
|
61
53
|
|
62
|
-
|
54
|
+
link_configs = representable_attrs["links"].link_configs
|
55
|
+
compile_links_for(link_configs, options)
|
63
56
|
end
|
64
57
|
|
65
58
|
def compile_links_for(configs, *args)
|
@@ -72,41 +65,12 @@ module Roar
|
|
72
65
|
end
|
73
66
|
|
74
67
|
def prepare_link_for(href, options)
|
75
|
-
options = options.merge(href.is_a?(::Hash) ? href : {:
|
68
|
+
options = options.merge(href.is_a?(::Hash) ? href : {href: href})
|
76
69
|
Hyperlink.new(options)
|
77
70
|
end
|
78
71
|
|
79
|
-
def run_link_block(block,
|
80
|
-
instance_exec(
|
81
|
-
end
|
82
|
-
|
83
|
-
|
84
|
-
# LinkCollection keeps an array of Hyperlinks to be rendered (setup in #prepare_links!)
|
85
|
-
# or parsed (array is passed to #links= which transforms it into a LinkCollection).
|
86
|
-
# It is implemented as a hash and keys links by their rel value.
|
87
|
-
#
|
88
|
-
# {"self" => <Hyperlink ..>, ..}
|
89
|
-
class LinkCollection < ::Hash
|
90
|
-
# The only way to create is LinkCollection[<Hyperlink>, <Hyperlink>]
|
91
|
-
def self.[](*arr)
|
92
|
-
super(arr.collect { |link| [link.rel, link] })
|
93
|
-
end
|
94
|
-
|
95
|
-
def [](rel)
|
96
|
-
super(rel.to_s)
|
97
|
-
end
|
98
|
-
|
99
|
-
# Iterating links. Block parameters: |link| or |rel, link|.
|
100
|
-
# This is used Hash::HashBinding#serialize.
|
101
|
-
def each(&block)
|
102
|
-
return values.each(&block) if block.arity == 1
|
103
|
-
super(&block)
|
104
|
-
end
|
105
|
-
|
106
|
-
def collect(&block) # TODO: remove me when we drop representable 2.0.x support!
|
107
|
-
return values.collect(&block) if block.arity == 1
|
108
|
-
super(&block)
|
109
|
-
end
|
72
|
+
def run_link_block(block, options)
|
73
|
+
instance_exec(options[:user_options], &block)
|
110
74
|
end
|
111
75
|
|
112
76
|
|
@@ -122,23 +86,34 @@ module Roar
|
|
122
86
|
# The block is executed in instance context, so you may call properties or other accessors.
|
123
87
|
# Note that you're free to put decider logic into #link blocks, too.
|
124
88
|
def link(options, &block)
|
125
|
-
|
89
|
+
heritage.record(:link, options, &block)
|
90
|
+
|
91
|
+
links_dfn = create_links_definition! # this assures the links are rendered at the right position.
|
126
92
|
|
127
93
|
options = {:rel => options} unless options.is_a?(::Hash)
|
128
|
-
link_configs << [options, block]
|
129
|
-
end
|
130
94
|
|
131
|
-
|
95
|
+
links_dfn.link_configs << [options, block]
|
96
|
+
end
|
132
97
|
|
133
98
|
private
|
134
99
|
# Add a :links Definition to the representable_attrs so they get rendered/parsed.
|
135
100
|
def create_links_definition!
|
136
|
-
|
101
|
+
dfn = definitions["links"] and return dfn # only create it once.
|
137
102
|
|
138
103
|
options = links_definition_options
|
139
|
-
options.merge!(:
|
104
|
+
options.merge!(getter: ->(opts) { prepare_links!(opts) })
|
105
|
+
|
106
|
+
dfn = build_definition(:links, options)
|
107
|
+
|
108
|
+
|
109
|
+
dfn.extend(DefinitionOptions)
|
110
|
+
dfn
|
111
|
+
end
|
112
|
+
end
|
140
113
|
|
141
|
-
|
114
|
+
module DefinitionOptions
|
115
|
+
def link_configs
|
116
|
+
@link_configs ||= []
|
142
117
|
end
|
143
118
|
end
|
144
119
|
|
@@ -172,7 +147,6 @@ module Roar
|
|
172
147
|
attrs.inject({}) { |hsh, kv| hsh[kv.first.to_s] = kv.last; hsh }.tap do |hsh|
|
173
148
|
hsh["rel"] = hsh["rel"].to_s if hsh["rel"]
|
174
149
|
end
|
175
|
-
# raise "Hyperlink without rel doesn't work!" unless @attrs["rel"]
|
176
150
|
end
|
177
151
|
end
|
178
152
|
end
|
data/lib/roar/json/collection.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require 'roar/json'
|
2
2
|
|
3
|
-
Roar::JSON
|
3
|
+
module Roar::JSON
|
4
|
+
module Collection
|
5
|
+
include Roar::JSON
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.send :include, Representable::Hash::Collection
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/roar/json/hal.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
require
|
1
|
+
require "roar/json"
|
2
|
+
require "representable/json/collection"
|
3
|
+
require "representable/json/hash"
|
2
4
|
|
3
5
|
module Roar
|
4
6
|
module JSON
|
@@ -46,8 +48,8 @@ module Roar
|
|
46
48
|
base.class_eval do
|
47
49
|
include Roar::JSON
|
48
50
|
include Links # overwrites #links_definition_options.
|
49
|
-
extend ClassMethods # overwrites #links_definition_options, again.
|
50
51
|
include Resources
|
52
|
+
include LinksReader # gives us Decorator#links => {self=>< >}
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
@@ -56,8 +58,9 @@ module Roar
|
|
56
58
|
super.tap do |hash|
|
57
59
|
embedded = {}
|
58
60
|
representable_attrs.find_all do |dfn|
|
59
|
-
|
60
|
-
embedded
|
61
|
+
name = dfn[:as] ? dfn[:as].(nil) : dfn.name # DISCUSS: should we simplify that in Representable?
|
62
|
+
next unless dfn[:embedded] and fragment = hash.delete(name)
|
63
|
+
embedded[name] = fragment
|
61
64
|
end
|
62
65
|
|
63
66
|
hash["_embedded"] = embedded if embedded.any?
|
@@ -71,38 +74,7 @@ module Roar
|
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|
74
|
-
module ClassMethods
|
75
|
-
def links_definition_options
|
76
|
-
super.merge(:as => :_links)
|
77
|
-
end
|
78
|
-
end
|
79
77
|
|
80
|
-
class LinkCollection < Hypermedia::LinkCollection
|
81
|
-
def initialize(array_rels, *args)
|
82
|
-
super(*args)
|
83
|
-
@array_rels = array_rels.map(&:to_s)
|
84
|
-
end
|
85
|
-
|
86
|
-
def is_array?(rel)
|
87
|
-
@array_rels.include?(rel.to_s)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
# Including this module in your representer will render and parse your embedded hyperlinks
|
92
|
-
# following the HAL specification: http://stateless.co/hal_specification.html
|
93
|
-
#
|
94
|
-
# module SongRepresenter
|
95
|
-
# include Roar::JSON
|
96
|
-
# include Roar::JSON::HAL::Links
|
97
|
-
#
|
98
|
-
# link :self { "http://self" }
|
99
|
-
# end
|
100
|
-
#
|
101
|
-
# Renders to
|
102
|
-
#
|
103
|
-
# {"links":{"self":{"href":"http://self"}}}
|
104
|
-
#
|
105
|
-
# Note that the HAL::Links module alone doesn't prepend an underscore to +links+. Use the JSON::HAL module for that.
|
106
78
|
module Links
|
107
79
|
def self.included(base)
|
108
80
|
base.extend ClassMethods # ::links_definition_options
|
@@ -111,83 +83,84 @@ module Roar
|
|
111
83
|
end
|
112
84
|
|
113
85
|
module InstanceMethods
|
86
|
+
def _links
|
87
|
+
links
|
88
|
+
end
|
89
|
+
|
114
90
|
private
|
115
91
|
def prepare_link_for(href, options)
|
116
|
-
return super(href, options) unless options[:array] #
|
92
|
+
return super(href, options) unless options[:array] # returns Hyperlink.
|
117
93
|
|
118
|
-
|
119
|
-
LinkArray.new(list, options[:rel])
|
120
|
-
end
|
121
|
-
|
122
|
-
# TODO: move to LinksDefinition.
|
123
|
-
def link_array_rels
|
124
|
-
link_configs.collect { |cfg| cfg.first[:array] ? cfg.first[:rel] : nil }.compact
|
94
|
+
ArrayLink.new(options[:rel], href.collect { |opts| Hypermedia::Hyperlink.new(opts) })
|
125
95
|
end
|
126
96
|
end
|
127
97
|
|
128
98
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
values :extend => lambda { |item, *|
|
134
|
-
item.is_a?(Array) ? LinkArrayRepresenter : Roar::JSON::HyperlinkRepresenter },
|
135
|
-
:instance => lambda { |fragment, *| fragment.is_a?(LinkArray) ? fragment : Roar::Hypermedia::Hyperlink.new
|
136
|
-
}
|
99
|
+
class SingleLink
|
100
|
+
class Representer < Representable::Decorator
|
101
|
+
include Representable::JSON::Hash
|
137
102
|
|
138
|
-
|
139
|
-
|
140
|
-
|
103
|
+
def to_hash(*)
|
104
|
+
hash = super
|
105
|
+
{hash.delete("rel").to_s => hash}
|
141
106
|
end
|
142
107
|
end
|
143
|
-
|
144
|
-
|
145
|
-
def from_hash(hash, *args)
|
146
|
-
hash.each { |k,v| hash[k] = LinkArray.new(v, k) if is_array?(k) }
|
147
|
-
|
148
|
-
hsh = super(hash) # this is where :class and :extend do the work.
|
149
|
-
|
150
|
-
hsh.each { |k, v| v.merge!(:rel => k) }
|
151
|
-
hsh.values # links= expects [Hyperlink, Hyperlink]
|
152
|
-
end
|
153
108
|
end
|
154
109
|
|
155
|
-
|
156
|
-
|
157
|
-
def initialize(elems, rel)
|
158
|
-
super(elems)
|
110
|
+
class ArrayLink < Array
|
111
|
+
def initialize(rel, links)
|
159
112
|
@rel = rel
|
113
|
+
super(links)
|
160
114
|
end
|
161
|
-
|
162
115
|
attr_reader :rel
|
163
116
|
|
164
|
-
|
165
|
-
|
117
|
+
|
118
|
+
# [Hyperlink, Hyperlink]
|
119
|
+
class Representer < Representable::Decorator
|
120
|
+
include Representable::JSON::Collection
|
121
|
+
|
122
|
+
items extend: SingleLink::Representer,
|
123
|
+
class: Roar::Hypermedia::Hyperlink
|
124
|
+
|
125
|
+
def to_hash(*)
|
126
|
+
links = super.flat_map(&:values) # [{"self"=>{"href": ..}}, ..]
|
127
|
+
|
128
|
+
{ represented.rel.to_s => links } # {"self"=>[{"lang"=>"en", "href"=>"http://en.hit"}, {"lang"=>"de", "href"=>"http://de.hit"}]}
|
129
|
+
end
|
166
130
|
end
|
167
131
|
end
|
168
132
|
|
169
|
-
|
170
|
-
|
133
|
+
|
134
|
+
# Represents all links for "_links": [Hyperlink, [Hyperlink, Hyperlink]]
|
135
|
+
class Representer < Representable::Decorator # links could be a simple collection property.
|
171
136
|
include Representable::JSON::Collection
|
172
137
|
|
173
|
-
|
174
|
-
|
138
|
+
# render: decorates represented.links with ArrayLink::R or SingleLink::R and calls #to_hash.
|
139
|
+
# parse: instantiate either Array or Hypermedia instance, decorate respectively, call #from_hash.
|
140
|
+
items decorator: ->(options) { options[:input].is_a?(Array) ? ArrayLink::Representer : SingleLink::Representer },
|
141
|
+
class: ->(options) { options[:input].is_a?(Array) ? Array : Hypermedia::Hyperlink }
|
142
|
+
|
143
|
+
def to_hash(options)
|
144
|
+
super.inject({}) { |links, hash| links.merge!(hash) } # [{ rel=>{}, rel=>[{}, {}] }]
|
145
|
+
end
|
175
146
|
|
176
|
-
def
|
177
|
-
|
178
|
-
|
147
|
+
def from_hash(hash, *args)
|
148
|
+
collection = hash.collect do |rel, value| # "self" => [{"href": "//"}, ] or "self" => {"href": "//"}
|
149
|
+
value.is_a?(Array) ? value.collect { |link| link.merge("rel"=>rel) } : value.merge("rel"=>rel)
|
179
150
|
end
|
151
|
+
|
152
|
+
super(collection) # [{rel=>self, href=>//}, ..] or {rel=>self, href=>//}
|
180
153
|
end
|
181
154
|
end
|
182
155
|
|
183
156
|
|
184
157
|
module ClassMethods
|
185
158
|
def links_definition_options
|
186
|
-
# property :links_array,
|
187
159
|
{
|
188
|
-
:
|
189
|
-
:
|
190
|
-
:
|
160
|
+
# collection: false,
|
161
|
+
:as => :_links,
|
162
|
+
decorator: Links::Representer,
|
163
|
+
instance: ->(*) { Array.new }, # defined in InstanceMethods as this is executed in represented context.
|
191
164
|
:exec_context => :decorator,
|
192
165
|
}
|
193
166
|
end
|
@@ -199,7 +172,7 @@ module Roar
|
|
199
172
|
# {:lang => "de", :href => "http://de.hit"}]
|
200
173
|
# end
|
201
174
|
def links(options, &block)
|
202
|
-
options = {:rel => options} if options.is_a?(Symbol)
|
175
|
+
options = {:rel => options} if options.is_a?(Symbol) || options.is_a?(String)
|
203
176
|
options[:array] = true
|
204
177
|
link(options, &block)
|
205
178
|
end
|
@@ -217,6 +190,24 @@ module Roar
|
|
217
190
|
end
|
218
191
|
end
|
219
192
|
end
|
193
|
+
|
194
|
+
# This is only helpful in client mode. It shouldn't be used per default.
|
195
|
+
module LinksReader
|
196
|
+
def links
|
197
|
+
return unless @links
|
198
|
+
tuples = @links.collect do |link|
|
199
|
+
if link.is_a?(Array)
|
200
|
+
next unless link.any?
|
201
|
+
[link.first.rel, link]
|
202
|
+
else
|
203
|
+
[link.rel, link]
|
204
|
+
end
|
205
|
+
end.compact
|
206
|
+
|
207
|
+
# tuples.to_h
|
208
|
+
::Hash[tuples] # TODO: tuples.to_h when dropping < 2.1.
|
209
|
+
end
|
210
|
+
end
|
220
211
|
end
|
221
212
|
end
|
222
213
|
end
|
data/lib/roar/json.rb
CHANGED
@@ -37,10 +37,10 @@ module Roar
|
|
37
37
|
def links_definition_options
|
38
38
|
# FIXME: this doesn't belong into the generic JSON representer.
|
39
39
|
{
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
40
|
+
class: Hypermedia::Hyperlink,
|
41
|
+
decorator: HyperlinkDecorator,
|
42
|
+
collection: true,
|
43
|
+
exec_context: :decorator
|
44
44
|
}
|
45
45
|
end
|
46
46
|
end
|
@@ -48,7 +48,7 @@ module Roar
|
|
48
48
|
|
49
49
|
require "representable/json/hash"
|
50
50
|
# Represents a hyperlink in standard roar+json hash representation.
|
51
|
-
|
51
|
+
class HyperlinkDecorator < Representable::Decorator
|
52
52
|
include Representable::JSON::Hash
|
53
53
|
end
|
54
54
|
end
|
data/lib/roar/version.rb
CHANGED
data/lib/roar/xml.rb
CHANGED
@@ -35,10 +35,10 @@ module Roar
|
|
35
35
|
# FIXME: this doesn't belong into the generic XML representer.
|
36
36
|
{
|
37
37
|
:as => :link,
|
38
|
-
:collection => true,
|
39
38
|
:class => Hypermedia::Hyperlink,
|
40
39
|
:extend => XML::HyperlinkRepresenter,
|
41
40
|
:exec_context => :decorator,
|
41
|
+
collection: true
|
42
42
|
} # TODO: merge with JSON.
|
43
43
|
end
|
44
44
|
end
|
data/lib/roar.rb
CHANGED
data/roar.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.authors = ["Nick Sutterer"]
|
9
9
|
s.email = ["apotonick@gmail.com"]
|
10
|
-
s.homepage = "http://
|
10
|
+
s.homepage = "http://trailblazer.to/gems/roar"
|
11
11
|
s.summary = %q{Parse and render REST API documents using representers.}
|
12
12
|
s.description = %q{Object-oriented representers help you defining nested REST API documents which can then be rendered and parsed using one and the same concept.}
|
13
13
|
s.license = 'MIT'
|
@@ -17,14 +17,16 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.
|
20
|
+
s.required_ruby_version = '>= 1.9.3'
|
21
21
|
|
22
|
-
s.
|
22
|
+
s.add_runtime_dependency "representable", "~> 3.0"
|
23
|
+
|
24
|
+
s.add_development_dependency "rake"
|
23
25
|
s.add_development_dependency "test_xml", "0.1.6"
|
24
|
-
s.add_development_dependency
|
26
|
+
s.add_development_dependency 'minitest', '>= 5.10'
|
25
27
|
s.add_development_dependency "sinatra"
|
26
28
|
s.add_development_dependency "sinatra-contrib"
|
27
29
|
s.add_development_dependency "virtus", ">= 1.0.0"
|
28
30
|
s.add_development_dependency "faraday"
|
29
|
-
s.add_development_dependency "
|
31
|
+
s.add_development_dependency "multi_json"
|
30
32
|
end
|
data/test/client_test.rb
CHANGED
@@ -30,7 +30,7 @@ class ClientTest < MiniTest::Spec
|
|
30
30
|
# since this is considered dangerous, we test the mutuable options.
|
31
31
|
it "adds links: false to options" do
|
32
32
|
song.to_hash(options = {})
|
33
|
-
options.must_equal({:
|
33
|
+
options.must_equal(user_options: {links: false})
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -1,14 +1,17 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
require 'roar/coercion'
|
3
|
+
require 'roar/decorator'
|
3
4
|
|
4
5
|
class CoercionFeatureTest < MiniTest::Spec
|
5
6
|
describe "Coercion" do
|
6
|
-
class
|
7
|
+
class SongDecorator < Roar::Decorator
|
7
8
|
include Roar::JSON
|
8
9
|
include Roar::Coercion
|
9
10
|
|
10
11
|
property :composed_at, :type => DateTime, :default => "May 12th, 2012"
|
12
|
+
end
|
11
13
|
|
14
|
+
class ImmigrantSong
|
12
15
|
attr_accessor :composed_at
|
13
16
|
def composed_at=(v) # in ruby 2.2, #label= is not there, all at sudden. what *is* that?
|
14
17
|
@composed_at = v
|
@@ -16,7 +19,9 @@ class CoercionFeatureTest < MiniTest::Spec
|
|
16
19
|
end
|
17
20
|
|
18
21
|
it "coerces into the provided type" do
|
19
|
-
song
|
22
|
+
song = ImmigrantSong.new
|
23
|
+
decorator = SongDecorator.new(song)
|
24
|
+
decorator.from_json("{\"composed_at\":\"November 18th, 1983\"}")
|
20
25
|
assert_equal DateTime.parse("Fri, 18 Nov 1983 00:00:00 +0000"), song.composed_at
|
21
26
|
end
|
22
27
|
end
|
data/test/decorator_test.rb
CHANGED
@@ -27,11 +27,15 @@ class DecoratorTest < MiniTest::Spec
|
|
27
27
|
describe "JSON" do
|
28
28
|
let (:decorator_class) { rpr_mod = rpr
|
29
29
|
Class.new(Roar::Decorator) do
|
30
|
+
include Roar::JSON
|
31
|
+
include Roar::Hypermedia
|
32
|
+
|
30
33
|
include rpr_mod
|
31
34
|
end }
|
32
35
|
let (:decorator) { decorator_class.new(model) }
|
33
36
|
|
34
|
-
it "
|
37
|
+
it "xxxrendering links works" do
|
38
|
+
pp decorator.send(:representable_attrs)
|
35
39
|
decorator.to_hash.must_equal({"links"=>[{"rel"=>"self", "href"=>"http://self"}]})
|
36
40
|
end
|
37
41
|
|
@@ -42,7 +46,7 @@ class DecoratorTest < MiniTest::Spec
|
|
42
46
|
|
43
47
|
it "does not set links on represented" do
|
44
48
|
decorator_class.new(model_with_links).from_hash("links"=>[{:rel=>:self, :href=>"http://self"}])
|
45
|
-
model_with_links.links.
|
49
|
+
model_with_links.links.must_be_nil
|
46
50
|
end
|
47
51
|
|
48
52
|
class ConsumingDecorator < Roar::Decorator
|
@@ -63,10 +67,10 @@ class DecoratorTest < MiniTest::Spec
|
|
63
67
|
decorator.from_hash("links"=>[{:rel=>:self, :href=>"http://percolator"}])
|
64
68
|
|
65
69
|
# links are always set on decorator instance.
|
66
|
-
decorator
|
70
|
+
decorator.links["self"].must_equal(link(:rel=>:self, :href=>"http://percolator"))
|
67
71
|
|
68
72
|
# and propagated to represented with HypermediaConsumer.
|
69
|
-
model_with_links.links[
|
73
|
+
model_with_links.links["self"].must_equal(link(:rel=>:self, :href=>"http://percolator"))
|
70
74
|
end
|
71
75
|
end
|
72
76
|
end
|
@@ -78,6 +82,8 @@ class DecoratorTest < MiniTest::Spec
|
|
78
82
|
end
|
79
83
|
let (:decorator_class) { rpr_mod = rpr
|
80
84
|
Class.new(Roar::Decorator) do
|
85
|
+
include Roar::XML
|
86
|
+
include Roar::Hypermedia
|
81
87
|
include rpr_mod
|
82
88
|
self.representation_wrap = :song
|
83
89
|
end
|
@@ -97,10 +103,13 @@ class DecoratorTest < MiniTest::Spec
|
|
97
103
|
|
98
104
|
describe "JSON::HAL" do
|
99
105
|
representer_for([Roar::JSON::HAL]) do
|
106
|
+
# feature Roar::JSON::HAL
|
100
107
|
link(:self) { "http://self" }
|
101
108
|
end
|
102
109
|
let (:decorator_class) { rpr_mod = rpr
|
103
110
|
Class.new(Roar::Decorator) do
|
111
|
+
include Roar::JSON::HAL
|
112
|
+
|
104
113
|
include rpr_mod
|
105
114
|
end
|
106
115
|
}
|
@@ -116,15 +125,16 @@ class DecoratorTest < MiniTest::Spec
|
|
116
125
|
end
|
117
126
|
|
118
127
|
describe "Decorator::HypermediaClient" do
|
119
|
-
let (:
|
128
|
+
let (:decorator_class) { rpr_mod = rpr
|
120
129
|
Class.new(Roar::Decorator) do
|
130
|
+
include Roar::JSON::HAL
|
121
131
|
include rpr_mod
|
122
132
|
include Roar::Decorator::HypermediaConsumer
|
123
133
|
end }
|
124
134
|
|
125
135
|
it "propagates links to represented" do
|
126
|
-
|
127
|
-
model_with_links.links[
|
136
|
+
decorator_class.new(model_with_links).from_hash("_links"=>{"self"=>{:href=>"http://self"}})
|
137
|
+
model_with_links.links["self"].must_equal(link(:rel=>"self", :href=>"http://self"))
|
128
138
|
end
|
129
139
|
end
|
130
140
|
end
|