roar 1.0.2 → 1.1.1
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.
- 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
|