roar-rails 0.0.14 → 0.0.15
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/CHANGES.markdown +6 -0
- data/Gemfile +4 -0
- data/README.markdown +12 -3
- data/gemfiles/Gemfile.rails3-0 +1 -0
- data/gemfiles/Gemfile.rails3-2 +1 -0
- data/gemfiles/Gemfile.rails4-0 +1 -0
- data/lib/roar-rails.rb +1 -1
- data/lib/roar/rails/controller_additions.rb +39 -22
- data/lib/roar/rails/version.rb +1 -1
- data/test/dummy/app/controllers/bands_controller.rb +10 -0
- data/test/dummy/app/representers/band_representer.rb +10 -0
- data/test/dummy/config/environments/test.rb +2 -2
- data/test/dummy/config/routes.rb +1 -0
- data/test/representer_computer_test.rb +73 -0
- data/test/representer_test.rb +15 -3
- data/test/responder_test.rb +19 -98
- metadata +5 -2
data/CHANGES.markdown
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
h2. 0.0.14
|
2
|
+
|
3
|
+
* Moved logic to infer representer names from `ControllerAdditions` to `RepresenterComputer` class.
|
4
|
+
* Representer names passed to `::represents` are now constantized at runtime where they are actually needed, only. This fixes a bug where you were required to provide a `SingersRepresenter` (for collections) everywhere even when you just want to represent singular resources.
|
5
|
+
* You can now pass strings to `::represents` as representer names.
|
6
|
+
|
1
7
|
h2. 0.0.13
|
2
8
|
|
3
9
|
* Allow passing user options to both `#respond_with` and `#consume!`.
|
data/Gemfile
CHANGED
data/README.markdown
CHANGED
@@ -45,7 +45,7 @@ end
|
|
45
45
|
|
46
46
|
### Represents Configuration
|
47
47
|
|
48
|
-
If you don't want to use conventions or pass representers you can configure them on the class level using
|
48
|
+
If you don't want to use conventions or pass representers you can configure them on the class level using `::represents`. This will also call `respond_to` for you.
|
49
49
|
|
50
50
|
```ruby
|
51
51
|
class SingersController < ApplicationController
|
@@ -53,13 +53,16 @@ class SingersController < ApplicationController
|
|
53
53
|
```
|
54
54
|
This will use the `MusicianRepresenter` for models and `MusiciansRepresenter` for representing collections.
|
55
55
|
|
56
|
-
Note that
|
56
|
+
Note that `::represents` also allows fine-tuning.
|
57
57
|
|
58
58
|
```ruby
|
59
59
|
class SingersController < ApplicationController
|
60
60
|
represents :json, :entity => MusicianRepresenter, :collection => MusicianCollectionRepresenter
|
61
61
|
```
|
62
62
|
|
63
|
+
You might pass strings as representer names to `::represents`, they will be constantized at run-time when needed.
|
64
|
+
|
65
|
+
|
63
66
|
### Old API Support
|
64
67
|
|
65
68
|
If you don't want to write a dedicated representer for a collection of items (highly recommended, thou) but rather use a representer for each item, use the `:represent_items_with` option.
|
@@ -110,16 +113,22 @@ consume!(singer, :represent_with => MusicianRepresenter)
|
|
110
113
|
|
111
114
|
## Using Decorators
|
112
115
|
|
113
|
-
If you prefer roar's decorator approach over extend, just go for it. roar-rails will figure out automatically which represent strategy to use.
|
116
|
+
If you prefer roar's decorator approach over extend, just go for it. roar-rails will figure out automatically which represent strategy to use. Be sure to use roar >= 0.11.17.
|
114
117
|
|
115
118
|
```ruby
|
116
119
|
class SingerRepresenter < Roar::Decorator
|
117
120
|
include Roar::Representer::JSON
|
118
121
|
|
119
122
|
property :name
|
123
|
+
|
124
|
+
link :self do
|
125
|
+
singer_url(represented)
|
126
|
+
end
|
120
127
|
end
|
121
128
|
```
|
122
129
|
|
130
|
+
In decorators' link blocks you currently have to use `represented` to get the actual represented model (this is `self` in module representers).
|
131
|
+
|
123
132
|
## Passing Options
|
124
133
|
|
125
134
|
Both rendering and consuming support passing user options to the representer.
|
data/gemfiles/Gemfile.rails3-0
CHANGED
data/gemfiles/Gemfile.rails3-2
CHANGED
data/gemfiles/Gemfile.rails4-0
CHANGED
data/lib/roar-rails.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "roar/rails/version"
|
2
2
|
require "roar/representer"
|
3
|
+
require "roar/decorator"
|
3
4
|
require "roar/rails/railtie"
|
4
5
|
|
5
6
|
module Roar::Representer
|
@@ -10,7 +11,6 @@ module Roar::Representer
|
|
10
11
|
autoload("HAL", "roar/representer/json/hal")
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
14
|
module Feature
|
15
15
|
autoload("Hypermedia", "roar/representer/feature/hypermedia")
|
16
16
|
end
|
@@ -8,7 +8,7 @@ module Roar::Rails
|
|
8
8
|
included do
|
9
9
|
extend Hooks::InheritableAttribute
|
10
10
|
inheritable_attr :represents_options
|
11
|
-
self.represents_options ||=
|
11
|
+
self.represents_options ||= RepresenterComputer.new
|
12
12
|
end
|
13
13
|
|
14
14
|
|
@@ -17,20 +17,8 @@ module Roar::Rails
|
|
17
17
|
Class.new(super).send :include, Roar::Rails::Responder
|
18
18
|
end
|
19
19
|
|
20
|
-
def add_representer_suffix(prefix)
|
21
|
-
"#{prefix}Representer"
|
22
|
-
end
|
23
|
-
|
24
20
|
def represents(format, options)
|
25
|
-
|
26
|
-
model = options
|
27
|
-
options = {
|
28
|
-
:entity => add_representer_suffix(model.name).constantize,
|
29
|
-
:collection => add_representer_suffix(model.name.pluralize).constantize
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
represents_options[format] = options
|
21
|
+
represents_options.add(format,options)
|
34
22
|
respond_to format
|
35
23
|
end
|
36
24
|
end
|
@@ -51,7 +39,7 @@ module Roar::Rails
|
|
51
39
|
|
52
40
|
# Central entry-point for finding the appropriate representer.
|
53
41
|
def representer_for(format, model, options={})
|
54
|
-
options.delete(:represent_with) ||
|
42
|
+
options.delete(:represent_with) || self.class.represents_options.for(format, model, controller_path)
|
55
43
|
end
|
56
44
|
|
57
45
|
private
|
@@ -63,15 +51,44 @@ module Roar::Rails
|
|
63
51
|
request.body.read
|
64
52
|
end
|
65
53
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
54
|
+
|
55
|
+
class RepresenterComputer < Hash
|
56
|
+
def add(format, opts)
|
57
|
+
# FIXME: use controller_path here as well!
|
58
|
+
# by pre-computing the representer name we allow "one-step inheritance": if B doesn't call ::represents it "inherits" A's settings.
|
59
|
+
unless opts.is_a?(Hash)
|
60
|
+
model = opts
|
61
|
+
opts = {
|
62
|
+
:entity => add_representer_suffix(model.name),
|
63
|
+
:collection => add_representer_suffix(model.name.pluralize)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
self[format] = opts
|
71
68
|
end
|
72
69
|
|
73
|
-
|
74
|
-
|
70
|
+
def for(*args)
|
71
|
+
name = name_for(*args) or return
|
72
|
+
|
73
|
+
return name if name.is_a?(Module) # i hate is_a? but this is really handy here.
|
74
|
+
name.constantize
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def name_for(format, model, controller_path) # DISCUSS: should we pass and process options here?
|
79
|
+
if self[format.to_sym].blank? # TODO: test to_sym?
|
80
|
+
model_name = model.class.name
|
81
|
+
model_name = controller_path.camelize if model.kind_of?(Array)
|
82
|
+
return add_representer_suffix(model_name).constantize
|
83
|
+
end
|
84
|
+
|
85
|
+
return self[format][:collection] if model.kind_of?(Array)
|
86
|
+
self[format][:entity]
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_representer_suffix(prefix)
|
90
|
+
"#{prefix}Representer"
|
91
|
+
end
|
75
92
|
end
|
76
93
|
end
|
77
94
|
end
|
data/lib/roar/rails/version.rb
CHANGED
@@ -29,6 +29,6 @@ Dummy::Application.configure do
|
|
29
29
|
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
30
30
|
# like if you have constraints or database-specific column types
|
31
31
|
# config.active_record.schema_format = :sql
|
32
|
-
|
33
|
-
config.representer.default_url_options = {:host => "
|
32
|
+
|
33
|
+
config.representer.default_url_options = {:host => "roar.apotomo.de"}
|
34
34
|
end
|
data/test/dummy/config/routes.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module ObjectRepresenter
|
4
|
+
end
|
5
|
+
module ObjectsRepresenter
|
6
|
+
end
|
7
|
+
|
8
|
+
class RepresenterComputerTest < MiniTest::Spec
|
9
|
+
let (:subject) { Roar::Rails::ControllerAdditions::RepresenterComputer.new }
|
10
|
+
|
11
|
+
describe "nothing configured" do
|
12
|
+
|
13
|
+
|
14
|
+
it "uses model class" do
|
15
|
+
subject.for(:json, Singer.new, "bands").must_equal SingerRepresenter
|
16
|
+
end
|
17
|
+
|
18
|
+
it "uses plural controller name when collection" do
|
19
|
+
subject.for(:json, [Singer.new], "objects").must_equal ObjectsRepresenter
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "represents :json, Singer" do
|
24
|
+
before { subject.add(:json, Object) }
|
25
|
+
|
26
|
+
it "uses defined class for item" do
|
27
|
+
subject.for(:json, Singer.new, "bands").must_equal ObjectRepresenter
|
28
|
+
end
|
29
|
+
|
30
|
+
it "uses plural name when collection" do
|
31
|
+
subject.for(:json, [], "bands").must_equal ObjectsRepresenter
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "represents :json, :entity => SingerRepresenter" do
|
36
|
+
before { subject.add(:json, :entity => ObjectRepresenter) }
|
37
|
+
|
38
|
+
it "returns :entity representer constant" do
|
39
|
+
subject.for(:json, Singer.new, "bands").must_equal ObjectRepresenter
|
40
|
+
end
|
41
|
+
|
42
|
+
it "doesn't infer collection representer" do
|
43
|
+
subject.for(:json, [], "bands").must_equal nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "represents :json, :entity => SingerRepresenter, :collection => SingersRepresenter" do
|
48
|
+
before { subject.add(:json, :entity => ObjectRepresenter,
|
49
|
+
:collection => SingersRepresenter) }
|
50
|
+
|
51
|
+
it "returns :entity representer constant" do
|
52
|
+
subject.for(:json, Singer.new, "bands").must_equal ObjectRepresenter
|
53
|
+
end
|
54
|
+
|
55
|
+
it "doesn't infer collection representer" do
|
56
|
+
subject.for(:json, [], "bands").must_equal SingersRepresenter
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#add" do
|
61
|
+
it "doesn't constantize" do
|
62
|
+
subject.add(:json, :entity => "ObjectRepresenter")
|
63
|
+
subject.send(:name_for, :json, Object.new, "bands").must_equal "ObjectRepresenter"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#for" do
|
68
|
+
it "constantizes strings" do
|
69
|
+
subject.add(:json, :entity => "ObjectRepresenter")
|
70
|
+
subject.for(:json, Object.new, "bands").must_equal ObjectRepresenter
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/test/representer_test.rb
CHANGED
@@ -7,12 +7,12 @@ class RepresenterTest < ActionController::TestCase
|
|
7
7
|
|
8
8
|
test "representers can use URL helpers" do
|
9
9
|
get :show, :id => "bumi"
|
10
|
-
assert_body "{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://
|
10
|
+
assert_body "{\"name\":\"Bumi\",\"links\":[{\"rel\":\"self\",\"href\":\"http://roar.apotomo.de/singers/Bumi\"}]}"
|
11
11
|
end
|
12
12
|
|
13
13
|
test "it works with uninitialized config.representer.default_url_options" do
|
14
14
|
url_options = Rails.application.config.representer.default_url_options
|
15
|
-
|
15
|
+
|
16
16
|
begin
|
17
17
|
Rails.application.config.representer.default_url_options = nil
|
18
18
|
assert_raises RuntimeError, ArgumentError do
|
@@ -21,7 +21,19 @@ class RepresenterTest < ActionController::TestCase
|
|
21
21
|
assert $!.message =~ /Missing host to link to/
|
22
22
|
rescue
|
23
23
|
ensure
|
24
|
-
Rails.application.config.representer.default_url_options = url_options
|
24
|
+
Rails.application.config.representer.default_url_options = url_options
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
|
+
|
29
|
+
|
30
|
+
class DecoratorTest < ActionController::TestCase
|
31
|
+
include Roar::Rails::TestCase
|
32
|
+
|
33
|
+
tests BandsController
|
34
|
+
|
35
|
+
test "it renders URLs using the decorator" do
|
36
|
+
get :show, :id => 1, :format => :json
|
37
|
+
assert_body "{\"name\":\"Bodyjar\",\"links\":[{\"rel\":\"self\",\"href\":\"http://roar.apotomo.de/bands/Bodyjar\"}]}"
|
38
|
+
end
|
39
|
+
end
|
data/test/responder_test.rb
CHANGED
@@ -11,96 +11,6 @@ module SingersRepresenter
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
module ObjectRepresenter
|
15
|
-
end
|
16
|
-
module ObjectsRepresenter
|
17
|
-
end
|
18
|
-
|
19
|
-
class RepresentsTest < MiniTest::Spec
|
20
|
-
class SingersController
|
21
|
-
end
|
22
|
-
|
23
|
-
before do
|
24
|
-
@controller = Class.new do
|
25
|
-
include Roar::Rails::ControllerAdditions
|
26
|
-
end.new
|
27
|
-
end
|
28
|
-
|
29
|
-
describe "representer_for" do
|
30
|
-
describe "nothing configured" do
|
31
|
-
before do
|
32
|
-
@controller = class ::SingersController
|
33
|
-
include Roar::Rails::ControllerAdditions
|
34
|
-
self
|
35
|
-
end.new
|
36
|
-
end
|
37
|
-
|
38
|
-
it "uses model class" do
|
39
|
-
assert_equal SingerRepresenter, @controller.representer_for(:json, Singer.new)
|
40
|
-
end
|
41
|
-
|
42
|
-
it "uses plural controller name when collection" do
|
43
|
-
assert_equal SingersRepresenter, @controller.representer_for(:json, [])
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
describe "represents :json, Singer" do
|
48
|
-
before do
|
49
|
-
@controller = class ::WhateverController < ActionController::Base
|
50
|
-
include Roar::Rails::ControllerAdditions
|
51
|
-
represents :json, Object
|
52
|
-
self
|
53
|
-
end.new
|
54
|
-
end
|
55
|
-
|
56
|
-
it "uses defined class for item" do
|
57
|
-
assert_equal ObjectRepresenter, @controller.representer_for(:json, Singer.new)
|
58
|
-
end
|
59
|
-
|
60
|
-
it "uses plural name when collection" do
|
61
|
-
assert_equal ObjectsRepresenter, @controller.representer_for(:json, [])
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
|
66
|
-
describe "represents :json, :entity => SingerRepresenter" do
|
67
|
-
before do
|
68
|
-
@controller = class ::FooController < ActionController::Base
|
69
|
-
include Roar::Rails::ControllerAdditions
|
70
|
-
represents :json, :entity => "ObjectRepresenter"
|
71
|
-
self
|
72
|
-
end.new
|
73
|
-
end
|
74
|
-
|
75
|
-
it "returns :entity representer name" do
|
76
|
-
assert_equal "ObjectRepresenter", @controller.representer_for(:json, Singer.new)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "doesn't infer collection representer" do
|
80
|
-
assert_equal nil, @controller.representer_for(:json, [])
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
describe "represents :json, :entity => SingerRepresenter, :collection => SingersRepresenter" do
|
85
|
-
before do
|
86
|
-
@controller = class ::BooController < ActionController::Base
|
87
|
-
include Roar::Rails::ControllerAdditions
|
88
|
-
represents :json, :entity => "ObjectRepresenter", :collection => "SingersRepresenter"
|
89
|
-
self
|
90
|
-
end.new
|
91
|
-
end
|
92
|
-
|
93
|
-
it "uses defined class for item" do
|
94
|
-
assert_equal "ObjectRepresenter", @controller.representer_for(:json, Singer.new)
|
95
|
-
end
|
96
|
-
|
97
|
-
it "uses defined class when collection" do
|
98
|
-
assert_equal "SingersRepresenter", @controller.representer_for(:json, [])
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
14
|
|
105
15
|
class ResponderTest < ActionController::TestCase
|
106
16
|
include Roar::Rails::TestCase
|
@@ -114,15 +24,26 @@ class ResponderTest < ActionController::TestCase
|
|
114
24
|
end
|
115
25
|
end
|
116
26
|
|
117
|
-
class UniqueRepresentsOptionsTest <
|
27
|
+
class UniqueRepresentsOptionsTest < MiniTest::Spec
|
118
28
|
class One < BaseController
|
119
29
|
represents :json, Object
|
120
30
|
end
|
121
31
|
class Two < BaseController
|
122
32
|
represents :json, Singer
|
123
33
|
end
|
124
|
-
|
125
|
-
|
34
|
+
|
35
|
+
it "each subclass of a roar-augmented controller can represent different things" do
|
36
|
+
One.represents_options.wont_equal Two.represents_options
|
37
|
+
end
|
38
|
+
|
39
|
+
it "does not share RepresenterComputer instances when inheriting" do
|
40
|
+
Class.new(One) do
|
41
|
+
represents :json, Singer
|
42
|
+
end.represents_options.wont_equal One.represents_options
|
43
|
+
end
|
44
|
+
|
45
|
+
it "inherits when subclass doesn't call ::represents" do
|
46
|
+
Class.new(One).represents_options.must_equal One.represents_options
|
126
47
|
end
|
127
48
|
end
|
128
49
|
|
@@ -139,7 +60,7 @@ class ResponderTest < ActionController::TestCase
|
|
139
60
|
respond_with singer
|
140
61
|
end
|
141
62
|
|
142
|
-
|
63
|
+
@response.body.must_equal singer.to_json
|
143
64
|
end
|
144
65
|
|
145
66
|
test "responder finds SingersRepresenter for collections by convention" do
|
@@ -209,7 +130,7 @@ class ResponderTest < ActionController::TestCase
|
|
209
130
|
respond_with singer
|
210
131
|
end
|
211
132
|
|
212
|
-
|
133
|
+
@response.body.must_equal singer.to_json
|
213
134
|
end
|
214
135
|
|
215
136
|
test "responder uses configured representer for collection" do
|
@@ -259,13 +180,13 @@ class ResponderTest < ActionController::TestCase
|
|
259
180
|
|
260
181
|
class PassingUserOptionsTest < ResponderTest
|
261
182
|
# FIXME: should be in generic roar-rails test.
|
262
|
-
module
|
183
|
+
module DynamicSingerRepresenter
|
263
184
|
include Roar::Representer::JSON
|
264
185
|
property :name, :setter => lambda { |val, opts| self.name = "#{opts[:title]} #{val}" },
|
265
186
|
:getter => lambda { |opts| "#{opts[:title]} #{name}" }
|
266
187
|
end
|
267
188
|
class MusicianController < BaseController
|
268
|
-
represents :json, :entity =>
|
189
|
+
represents :json, :entity => DynamicSingerRepresenter, :collection => SingersRepresenter
|
269
190
|
end
|
270
191
|
|
271
192
|
tests MusicianController
|
@@ -281,7 +202,7 @@ class ResponderTest < ActionController::TestCase
|
|
281
202
|
|
282
203
|
test "passes options to explicit collection representer" do
|
283
204
|
get do
|
284
|
-
respond_with [Singer.new("Bumi"), Singer.new("Iggy")], :title => "Mr.", :represent_items_with =>
|
205
|
+
respond_with [Singer.new("Bumi"), Singer.new("Iggy")], :title => "Mr.", :represent_items_with => DynamicSingerRepresenter
|
285
206
|
end
|
286
207
|
|
287
208
|
@response.body.must_equal("[{\"name\":\"Mr. Bumi\"},{\"name\":\"Mr. Iggy\"}]")
|
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.0.
|
4
|
+
version: 0.0.15
|
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-05-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: roar
|
@@ -186,8 +186,10 @@ files:
|
|
186
186
|
- test/consume_test.rb
|
187
187
|
- test/dummy/Rakefile
|
188
188
|
- test/dummy/app/controllers/application_controller.rb
|
189
|
+
- test/dummy/app/controllers/bands_controller.rb
|
189
190
|
- test/dummy/app/controllers/singers_controller.rb
|
190
191
|
- test/dummy/app/helpers/application_helper.rb
|
192
|
+
- test/dummy/app/representers/band_representer.rb
|
191
193
|
- test/dummy/app/representers/singer_alias_representer.rb
|
192
194
|
- test/dummy/app/representers/singer_representer.rb
|
193
195
|
- test/dummy/app/views/layouts/application.html.erb
|
@@ -219,6 +221,7 @@ files:
|
|
219
221
|
- test/dummy/script/rails
|
220
222
|
- test/dummy/tmp/app/cells/blog/post/latest.html.erb
|
221
223
|
- test/dummy/tmp/app/cells/blog/post_cell.rb
|
224
|
+
- test/representer_computer_test.rb
|
222
225
|
- test/representer_test.rb
|
223
226
|
- test/responder_test.rb
|
224
227
|
- test/test_case_test.rb
|