roar-rails 0.1.0 → 0.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.
- data/.travis.yml +1 -0
- data/CHANGES.markdown +4 -0
- data/Gemfile +3 -1
- data/README.markdown +44 -1
- data/gemfiles/Gemfile.rails3-2 +4 -1
- data/gemfiles/Gemfile.rails4-0 +4 -4
- data/lib/roar-rails.rb +1 -1
- data/lib/roar/rails/controller_additions.rb +9 -0
- data/lib/roar/rails/hal.rb +6 -0
- data/lib/roar/rails/responder.rb +48 -7
- data/lib/roar/rails/version.rb +1 -1
- data/roar-rails.gemspec +0 -1
- data/test/json_hal_renderer_test.rb +35 -0
- data/test/responder_test.rb +139 -7
- data/test/test_helper.rb +1 -3
- metadata +4 -18
data/.travis.yml
CHANGED
data/CHANGES.markdown
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
h2. 0.1.1
|
2
|
+
|
3
|
+
* Added the `represented_formats: [...]` option to be passed to `#respond_with` for either suppressing roar-rails from extending/decorating models when rendering or to fine-tune so this will only happen on white-listed formats as `:hal`. This can also be set globally using `config.representer.represented_formats`.
|
4
|
+
|
1
5
|
h2. 0.1.0
|
2
6
|
|
3
7
|
* `ActiveRecord::Relation` is now detected as a collection and the appropriate representer should be found.
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -145,6 +145,16 @@ Same goes with `#consume!`, passing options to `from_json`.
|
|
145
145
|
consume! Singer.new, :current_user => current_user
|
146
146
|
```
|
147
147
|
|
148
|
+
Note: If you pass in options to a representer, you must process them youself. For rendering, use `:getter` in the representer.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
property :username, getter: lambda { |args| args[:current_user].name }
|
152
|
+
```
|
153
|
+
|
154
|
+
That'll render the `current_user`'s name as the `username` property.
|
155
|
+
|
156
|
+
More docs about passing and processing option can be found [here](https://github.com/apotonick/representable/#passing-options).
|
157
|
+
|
148
158
|
|
149
159
|
## URL Helpers
|
150
160
|
|
@@ -168,6 +178,39 @@ config.representer.default_url_options = {:host => "127.0.0.1:3000"}
|
|
168
178
|
|
169
179
|
Attention: If you are using representers from a gem your Rails URL helpers might not work in these modules. This is due to a [loading order problem](https://groups.google.com/forum/?fromgroups#!topic/rubyonrails-core/5tG5unZ8jDQ) in Rails. As a workaround, don't require the representers in the gem but load them as lately as possible, usually it works when you require in the controller. We are working on fixing that problem.
|
170
180
|
|
181
|
+
## Representing Formats Exclusively
|
182
|
+
|
183
|
+
By default, roar-rails will extend/decorate any model passed to `respond_with` for any request format. When adding roar-rails to a legacy project, you might want to restrict roar-rails' representing and fall back to the old behavior for certain formats. This can be configured both globally and on a per action basis.
|
184
|
+
|
185
|
+
To restrict representing globally to a particular format you can set the `config.representer.represented_formats` in your environment's configuration to an array of formats. For example the following will only represent hal and json requests.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
config.representer.represented_formats = [:hal, :json]
|
189
|
+
```
|
190
|
+
|
191
|
+
The global configuration (or lack thereof) can be overridden by supplying the `:represented_formats` array when calling `respond_with`. The following will only represent `@resource` for the hal format in the `show` action. For any other format, it will expose the resource using Rails' old behavior.
|
192
|
+
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
class MyController < ApplicationController
|
196
|
+
def show
|
197
|
+
...
|
198
|
+
respond_with @resource, :represented_formats => [:hal]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
You can entirely suppress roar-rails in `respond_with` by passing in an empty array.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
class MyController < ApplicationController
|
207
|
+
def show
|
208
|
+
...
|
209
|
+
respond_with @resource, :represented_formats => []
|
210
|
+
end
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
171
214
|
## Testing
|
172
215
|
|
173
216
|
## Autoloading
|
@@ -183,4 +226,4 @@ Put your representers in `app/representers` and they will be autoloaded by Rails
|
|
183
226
|
|
184
227
|
## License
|
185
228
|
|
186
|
-
Roar-rails is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
229
|
+
Roar-rails is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
data/gemfiles/Gemfile.rails3-2
CHANGED
data/gemfiles/Gemfile.rails4-0
CHANGED
@@ -3,10 +3,10 @@ source "http://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in roar-rails.gemspec
|
4
4
|
gemspec :path => '../'
|
5
5
|
|
6
|
-
gem 'railties', '~> 4.0.0
|
7
|
-
gem 'actionpack', '~> 4.0.0
|
6
|
+
gem 'railties', '~> 4.0.0'
|
7
|
+
gem 'actionpack', '~> 4.0.0'
|
8
8
|
|
9
|
-
gem 'activemodel', '~> 4.0.0
|
10
|
-
gem 'activerecord', '~> 4.0.0
|
9
|
+
gem 'activemodel', '~> 4.0.0'
|
10
|
+
gem 'activerecord', '~> 4.0.0'
|
11
11
|
|
12
12
|
gem 'roar', ">= 0.11.17"
|
data/lib/roar-rails.rb
CHANGED
@@ -51,6 +51,15 @@ module Roar::Rails
|
|
51
51
|
request.body.read
|
52
52
|
end
|
53
53
|
|
54
|
+
def render_to_body(options)
|
55
|
+
if res = options[formats.first] and res.is_a?(Roar::Rails::Responder::Response)
|
56
|
+
response.content_type = res.content_type
|
57
|
+
return res.body
|
58
|
+
end
|
59
|
+
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
54
63
|
|
55
64
|
class RepresenterComputer < Hash
|
56
65
|
def add(format, opts)
|
data/lib/roar/rails/responder.rb
CHANGED
@@ -1,23 +1,54 @@
|
|
1
1
|
module Roar::Rails
|
2
2
|
module Responder
|
3
3
|
def display(model, *args)
|
4
|
-
if
|
5
|
-
|
6
|
-
return super
|
7
|
-
end
|
4
|
+
if represent_format?
|
5
|
+
handle_lonely_collection!(model) and return super # :represent_items_with
|
8
6
|
|
9
|
-
|
7
|
+
model = prepare_model_for(format, model, options)
|
8
|
+
end
|
10
9
|
|
11
|
-
super
|
10
|
+
super(create_response(model), *args) # AC::Responder: this calls controller.render which calls render_to_body which calls renderer[:json] which calls model.to_json.
|
12
11
|
end
|
13
12
|
|
14
13
|
private
|
15
|
-
|
14
|
+
|
15
|
+
def default_represented_formats
|
16
|
+
Rails.application.config.representer.represented_formats
|
17
|
+
end
|
18
|
+
|
19
|
+
# First check for user option overrides, then check for defaults, then trigger
|
20
|
+
# previous behavior and always represent for the requested format.
|
21
|
+
def represent_format?
|
22
|
+
formats = Array((options[:represented_formats] || default_represented_formats || [format]))
|
23
|
+
formats.include?(format)
|
24
|
+
end
|
25
|
+
|
26
|
+
def resourceful?
|
27
|
+
# FIXME: find out if we have a representer? what about old behaviour?
|
28
|
+
#resource.respond_to?("to_#{format}")
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def create_response(model)
|
33
|
+
render_method = "to_#{format}"
|
34
|
+
media_format = Mime[format]
|
35
|
+
Response.new(model.send(render_method, options), media_format)
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle_lonely_collection!(collection)
|
39
|
+
return false unless representer = options.delete(:represent_items_with)
|
40
|
+
|
41
|
+
setup_items_with(collection, representer) # convenience API, not recommended since it's missing hypermedia.
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def setup_items_with(collection, representer)
|
16
46
|
collection.map! do |mdl| # DISCUSS: i don't like changing the method argument here.
|
17
47
|
representer.prepare(mdl).to_hash(options) # FIXME: huh? and what about XML?
|
18
48
|
end
|
19
49
|
end
|
20
50
|
|
51
|
+
|
21
52
|
module PrepareModel
|
22
53
|
def prepare_model_for(format, model, options) # overwritten in VersionStrategy/3.0.
|
23
54
|
controller.prepare_model_for(format, model, options)
|
@@ -25,5 +56,15 @@ module Roar::Rails
|
|
25
56
|
end
|
26
57
|
include PrepareModel
|
27
58
|
include VersionStrategy
|
59
|
+
|
60
|
+
|
61
|
+
class Response
|
62
|
+
attr_reader :content_type, :body # DISCUSS: how to add more header fields? #headers?
|
63
|
+
|
64
|
+
def initialize(body, content_type)
|
65
|
+
@body = body
|
66
|
+
@content_type = content_type
|
67
|
+
end
|
68
|
+
end
|
28
69
|
end
|
29
70
|
end
|
data/lib/roar/rails/version.rb
CHANGED
data/roar-rails.gemspec
CHANGED
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
s.add_runtime_dependency "roar", "~> 0.11.13"
|
22
|
-
s.add_runtime_dependency "representable", "~> 1.4"
|
23
22
|
s.add_runtime_dependency "test_xml", ">= 0.1.6" # TODO: remove dependency as most people don't use XML.
|
24
23
|
s.add_runtime_dependency "actionpack"
|
25
24
|
s.add_runtime_dependency "railties", ">= 3.0.0"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
Mime::Type.register 'application/json+hal', :hal
|
4
|
+
class HalRendererTest < ActionController::TestCase
|
5
|
+
include Roar::Rails::TestCase
|
6
|
+
|
7
|
+
class SingersController < ActionController::Base
|
8
|
+
module HalSingerRepresenter
|
9
|
+
include Roar::Representer::JSON::HAL
|
10
|
+
|
11
|
+
property :name
|
12
|
+
link(:self) { "http://#{name}" }
|
13
|
+
end
|
14
|
+
|
15
|
+
include Roar::Rails::ControllerAdditions
|
16
|
+
represents :hal, :entity => HalSingerRepresenter
|
17
|
+
|
18
|
+
def show
|
19
|
+
singer = Musician.new("Bumi")
|
20
|
+
respond_with singer
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
tests SingersController
|
25
|
+
|
26
|
+
test "should render correctly in response to a application/json+hal request" do
|
27
|
+
get :show, :id => "bumi", :format => :hal
|
28
|
+
assert_body '{"name":"Bumi","_links":{"self":{"href":"http://Bumi"}}}'
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should have a content_type of application/json+hal" do
|
32
|
+
get :show, :id => "bumi", :format => :hal
|
33
|
+
assert_equal response.content_type, 'application/json+hal'
|
34
|
+
end
|
35
|
+
end
|
data/test/responder_test.rb
CHANGED
@@ -45,6 +45,117 @@ class ResponderTest < ActionController::TestCase
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
class JsonResponseTest < ResponderTest
|
49
|
+
SingerRepresenter = ::SingerRepresenter
|
50
|
+
class SingersController < BaseController
|
51
|
+
end
|
52
|
+
tests SingersController
|
53
|
+
|
54
|
+
test "set Content-type to json" do
|
55
|
+
get do
|
56
|
+
singer = Singer.new("Bumi")
|
57
|
+
respond_with singer
|
58
|
+
end
|
59
|
+
|
60
|
+
@response.body.must_equal "{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://roar.apotomo.de/singers/Bumi\"}]}"
|
61
|
+
@response.headers["Content-Type"].must_match "application/json"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class SuppressingRepresenterForFormatTest < ResponderTest
|
66
|
+
class SingersController < BaseController
|
67
|
+
end
|
68
|
+
tests SingersController
|
69
|
+
|
70
|
+
test "returns non-represented json of model by falling back to Rails default responding when supressed in respond_with" do
|
71
|
+
singer = Singer.new('Bumi')
|
72
|
+
|
73
|
+
get do
|
74
|
+
respond_with singer, :represented_formats => []
|
75
|
+
end
|
76
|
+
|
77
|
+
assert_equal bumi_json, @response.body
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class ProvidingRepresenterForFormatTest < ResponderTest
|
82
|
+
SingerRepresenter = ::SingerRepresenter
|
83
|
+
class SingersController < BaseController
|
84
|
+
represents :json, :entity => SingerRepresenter
|
85
|
+
end
|
86
|
+
tests SingersController
|
87
|
+
|
88
|
+
test "returns represented json of model when set in respond_with" do
|
89
|
+
singer = Singer.new('Bumi')
|
90
|
+
|
91
|
+
get do
|
92
|
+
respond_with singer, :represented_formats => [:json]
|
93
|
+
end
|
94
|
+
|
95
|
+
assert_equal %{{"name":"Bumi","links":[{"rel":"self","href":"http://roar.apotomo.de/singers/Bumi"}]}}, @response.body
|
96
|
+
end
|
97
|
+
|
98
|
+
test "return represented json model by using configured default" do
|
99
|
+
singer = Singer.new('Bumi')
|
100
|
+
|
101
|
+
Rails.application.config.representer.represented_formats = [:json]
|
102
|
+
get do
|
103
|
+
respond_with singer
|
104
|
+
end
|
105
|
+
Rails.application.config.representer.represented_formats = nil
|
106
|
+
|
107
|
+
assert_equal %{{"name":"Bumi","links":[{"rel":"self","href":"http://roar.apotomo.de/singers/Bumi"}]}}, @response.body
|
108
|
+
end
|
109
|
+
|
110
|
+
test "return non-represented json model by falling back to Rails default responding when supressed in the configuration" do
|
111
|
+
singer = Singer.new('Bumi')
|
112
|
+
|
113
|
+
Rails.application.config.representer.represented_formats = []
|
114
|
+
get do
|
115
|
+
respond_with singer
|
116
|
+
end
|
117
|
+
Rails.application.config.representer.represented_formats = nil
|
118
|
+
|
119
|
+
assert_equal bumi_json, @response.body
|
120
|
+
end
|
121
|
+
|
122
|
+
test "represented_formats passed to respond_with override global directive" do
|
123
|
+
singer = Singer.new('Bumi')
|
124
|
+
|
125
|
+
Rails.application.config.representer.represented_formats = [:hal]
|
126
|
+
get do
|
127
|
+
respond_with singer, :represented_formats => [:json]
|
128
|
+
end
|
129
|
+
Rails.application.config.representer.represented_formats = nil
|
130
|
+
|
131
|
+
assert_equal %{{"name":"Bumi","links":[{"rel":"self","href":"http://roar.apotomo.de/singers/Bumi"}]}}, @response.body
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class XmlResponseTest < ResponderTest
|
136
|
+
module SingerRepresenter
|
137
|
+
include Roar::Representer::XML
|
138
|
+
|
139
|
+
property :name
|
140
|
+
self.representation_wrap = :singer
|
141
|
+
end
|
142
|
+
|
143
|
+
class SingersController < BaseController
|
144
|
+
respond_to :xml
|
145
|
+
end
|
146
|
+
tests SingersController
|
147
|
+
|
148
|
+
test "set Content-type to xml" do
|
149
|
+
get :xml do
|
150
|
+
singer = Singer.new("Bumi")
|
151
|
+
respond_with singer
|
152
|
+
end
|
153
|
+
|
154
|
+
@response.body.must_equal_xml '<singer><name>Bumi</name></singer>'
|
155
|
+
@response.headers["Content-Type"].must_match "application/xml"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
48
159
|
class UnconfiguredControllerTest < ResponderTest
|
49
160
|
SingersRepresenter = ::SingersRepresenter
|
50
161
|
SingerRepresenter = ::SingerRepresenter
|
@@ -60,7 +171,7 @@ class ResponderTest < ActionController::TestCase
|
|
60
171
|
respond_with singer
|
61
172
|
end
|
62
173
|
|
63
|
-
@response.body.must_equal
|
174
|
+
@response.body.must_equal "{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://roar.apotomo.de/singers/Bumi\"}]}"
|
64
175
|
end
|
65
176
|
|
66
177
|
test "responder finds SingersRepresenter for collections by convention" do
|
@@ -95,7 +206,7 @@ class ResponderTest < ActionController::TestCase
|
|
95
206
|
respond_with singer, :represent_with => SingerRepresenter
|
96
207
|
end
|
97
208
|
|
98
|
-
|
209
|
+
@response.body.must_equal "{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://roar.apotomo.de/singers/Bumi\"}]}"
|
99
210
|
end
|
100
211
|
|
101
212
|
test "responder uses passed representer for collection" do
|
@@ -130,7 +241,7 @@ class ResponderTest < ActionController::TestCase
|
|
130
241
|
respond_with singer
|
131
242
|
end
|
132
243
|
|
133
|
-
@response.body.must_equal
|
244
|
+
@response.body.must_equal "{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://roar.apotomo.de/singers/Bumi\"}]}"
|
134
245
|
end
|
135
246
|
|
136
247
|
test "responder uses configured representer for collection" do
|
@@ -224,21 +335,37 @@ class ResponderTest < ActionController::TestCase
|
|
224
335
|
end
|
225
336
|
end
|
226
337
|
|
338
|
+
class FallbackTest < ResponderTest
|
339
|
+
class MusicianController < BaseController
|
340
|
+
represents :json, Object
|
341
|
+
end
|
227
342
|
|
343
|
+
tests MusicianController
|
228
344
|
|
229
|
-
|
345
|
+
test "passes options to entity representer" do
|
346
|
+
get do
|
347
|
+
render :text => "Rendered template"
|
348
|
+
end
|
349
|
+
|
350
|
+
@response.body.must_equal("Rendered template")
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
|
356
|
+
def get(format=:json, &block)
|
230
357
|
@controller.instance_eval do
|
231
358
|
@block = block
|
232
359
|
end
|
233
360
|
|
234
|
-
super :execute, :format =>
|
361
|
+
super :execute, :format => format
|
235
362
|
end
|
236
363
|
|
237
|
-
def put(body="", &block)
|
364
|
+
def put(body="", format=:json, &block)
|
238
365
|
@controller.instance_eval do
|
239
366
|
@block = block
|
240
367
|
end
|
241
|
-
super :execute, body, :format =>
|
368
|
+
super :execute, body, :format => format
|
242
369
|
end
|
243
370
|
|
244
371
|
def singer(name="Bumi")
|
@@ -249,4 +376,9 @@ class ResponderTest < ActionController::TestCase
|
|
249
376
|
def singers
|
250
377
|
[singer("Bumi"), singer("Bjork"), singer("Sinead")]
|
251
378
|
end
|
379
|
+
|
380
|
+
def bumi_json
|
381
|
+
return "[\"Bumi\"]" if Roar::Rails.rails3_0?
|
382
|
+
"{\"name\":\"Bumi\"}"
|
383
|
+
end
|
252
384
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roar-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-07-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: roar
|
@@ -27,22 +27,6 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 0.11.13
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
|
-
name: representable
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
|
-
requirements:
|
35
|
-
- - ~>
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: '1.4'
|
38
|
-
type: :runtime
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '1.4'
|
46
30
|
- !ruby/object:Gem::Dependency
|
47
31
|
name: test_xml
|
48
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -206,6 +190,7 @@ files:
|
|
206
190
|
- gemfiles/Gemfile.rails4-0
|
207
191
|
- lib/roar-rails.rb
|
208
192
|
- lib/roar/rails/controller_additions.rb
|
193
|
+
- lib/roar/rails/hal.rb
|
209
194
|
- lib/roar/rails/rails3_0_strategy.rb
|
210
195
|
- lib/roar/rails/rails3_1_strategy.rb
|
211
196
|
- lib/roar/rails/railtie.rb
|
@@ -254,6 +239,7 @@ files:
|
|
254
239
|
- test/dummy/script/rails
|
255
240
|
- test/dummy/tmp/app/cells/blog/post/latest.html.erb
|
256
241
|
- test/dummy/tmp/app/cells/blog/post_cell.rb
|
242
|
+
- test/json_hal_renderer_test.rb
|
257
243
|
- test/representer_computer_test.rb
|
258
244
|
- test/representer_test.rb
|
259
245
|
- test/responder_test.rb
|