roar-rails 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|