Permalink
Browse files

Refactored so that the representational layer is a separate object,

allowing us to manipulate the format a little better
  • Loading branch information...
1 parent 4c62727 commit 95efff13f102b4ea640cebf54be8251a2e7c9181 @baphled committed May 13, 2012
View
4 lib/acceptable_model.rb
@@ -49,13 +49,13 @@ def for mime_type
raise MimeTypeNotReckonised.new mime_type if map.nil?
mime = mime_type_lookup mime_type
format = 'to_' + mime
- representation(map[:attributes]).send format.to_sym, :skip_types => true, :root => self.class.to_s.downcase
+ representation(map[:attributes]).send format.to_sym
end
def representation attributes_block
klass = 'AcceptableModel::#{model_object}'.constantize
mapper = RelationshipsMapper.new :model => self, :response_block => self.response_block, :attributes_block => attributes_block, :associations => klass.associations
- mapper.representation
+ Representation.new self.class.to_s.downcase => mapper.representation
end
class << self
View
9 lib/acceptable_model/enumerable.rb
@@ -12,21 +12,22 @@ class Enumerable < Array
#
def for mime_type
mime = self.first.mime_type_lookup mime_type
- class_name = self.first.class.to_s.downcase.pluralize
format = "to_#{mime}".to_sym
- attributes_for(mime_type).send format, :skip_types => true, :root => class_name
+ attributes_for(mime_type).send format
end
def attributes_for mime_type
- collect do |model|
+ attributes = collect do |model|
map = model.version_lookup mime_type
representation model, map[:attributes]
end
+ class_name = self.first.class.to_s.downcase.pluralize
+ Representation.new class_name => attributes
end
def representation model, attributes_block
mapper = RelationshipsMapper.new :model => model, :response_block => model.response_block, :attributes_block => attributes_block, :associations => model.associations
- mapper.representation
+ mapper.representation.to_hash
end
def model_attributes model, attributes
View
4 lib/acceptable_model/relationships_mapper.rb
@@ -27,10 +27,10 @@ def representation
protected
#
- # Calls the attributes_block and passes the model to base on the response on
+ # Gathers the models attributes
#
def attributes
- attributes = attributes_block.call model
+ attributes_block.call model
end
#
View
56 lib/acceptable_model/representation.rb
@@ -0,0 +1,56 @@
+require "builder"
+
+#
+# Gives us more control over the output we receive when automatically
+# generating our HATEOAS responses.
+#
+module AcceptableModel
+ class Representation
+ def initialize params = {}
+ @attributes = params
+ end
+
+ def to_xml params = {}
+ xml = Builder::XmlMarkup.new :indent => 2
+ xml.instruct!
+ if params[:root]
+ xml.__send__ params[:root] do |root|
+ walk_node root, @attributes
+ end
+ else
+ walk_node xml, @attributes
+ end
+ xml.target!
+ end
+
+ def to_hash
+ @attributes
+ end
+
+ def walk_node node, attributes
+ attributes.each do |k,v|
+ if k.to_s == 'links'
+ node.links do |node|
+ attributes[k].collect do |link|
+ node.link(link)
+ end
+ end
+ elsif v.class == Hash
+ node.__send__ k do |children|
+ walk_node children, v
+ end
+ elsif v.class == Array
+ node.__send__ k do |children|
+ v.collect do |attr|
+ node.__send__ k.to_s.singularize do |child|
+ walk_node child, attr
+ end
+ end
+ end
+ else
+ eval "node.#{ k } v"
+ end
+ end
+ end
+ end
+end
View
45 spec/acceptable_model/enumerable_spec.rb
@@ -16,28 +16,29 @@
describe "#for" do
let(:relationships) {
- [
- {
- :id => 'busta-rhymes',
- :name => 'Busta Rhymes',
- :links => [
- {
- :href => '/artists/busta-rhymes',
- :rel => '/self'
- }
- ]
- },
- {
- :id => 'jay-z',
- :name => 'Jay-Z',
- :links => [
- {
- :href => '/artists/jay-z',
- :rel => '/self'
- }
- ]
- },
- ]
+ { :artists => [
+ {
+ :id => 'busta-rhymes',
+ :name => 'Busta Rhymes',
+ :links => [
+ {
+ :href => '/artists/busta-rhymes',
+ :rel => '/self'
+ }
+ ]
+ },
+ {
+ :id => 'jay-z',
+ :name => 'Jay-Z',
+ :links => [
+ {
+ :href => '/artists/jay-z',
+ :rel => '/self'
+ }
+ ]
+ },
+ ]
+ }
}
before :each do
View
75 spec/acceptable_model/hateos_spec.rb
@@ -15,6 +15,10 @@
end
describe "#relationship" do
+ let(:model) { AcceptableModel::Artist.new :name => 'Busta Rhymes', :aliases => ['Busta Bus'], :groups => ['Flipmode Squad', 'Leaders of The New School'] }
+ let(:group1) { Group.new :name => 'Flipmode Squad', :id => 'flipmode-squad' }
+ let(:group2) { Group.new :name => 'Leaders of The New School', :id => 'leaders-of-the-new-school' }
+
before do
AcceptableModel.define 'artist'
class AcceptableModel::Artist
@@ -26,7 +30,11 @@ class AcceptableModel::Artist
end
relationship :groups
+ def part_of
+ groups.all
+ end
end
+ model.groups.stub(:all).and_return [group1, group2]
end
after do
@@ -42,40 +50,55 @@ class AcceptableModel::Artist
end
it "should allow use to define a relationship" do
- expected = {
- 'id' => 'busta-rhymes',
- 'name' => 'Busta Rhymes',
- 'groups' => [
+ expected =
+ { 'artist' =>
{
- 'id' => 'flipmode-squad',
- 'name' => 'Flipmode Squad',
- 'links' => [
+ 'id' => 'busta-rhymes',
+ 'name' => 'Busta Rhymes',
+ 'groups' => [
{
- 'href' => '/groups/flipmode-squad',
- 'rel' => '/children'
+ 'id' => 'flipmode-squad',
+ 'name' => 'Flipmode Squad',
+ 'links' => [
+ {
+ 'href' => '/groups/flipmode-squad',
+ 'rel' => '/children'
+ }
+ ]
+ },
+ {
+ 'id' => 'leaders-of-the-new-school',
+ 'name' => 'Leaders of The New School',
+ 'links' => [
+ {
+ 'href' => '/groups/leaders-of-the-new-school',
+ 'rel' => '/children'
+ }
+ ]
}
- ]
- },
- {
- 'id' => 'leaders-of-the-new-school',
- 'name' => 'Leaders of The New School',
+ ],
'links' => [
{
- 'href' => '/groups/leaders-of-the-new-school',
- 'rel' => '/children'
+ 'href' => '/artists/busta-rhymes',
+ 'rel' => '/self'
+ },
+ {
+ 'href' => '/groups/flipmode-squad',
+ 'rel' => '/partOf'
+ },
+ {
+ 'href' => '/groups/leaders-of-the-new-school',
+ 'rel' => '/partOf'
}
]
}
- ],
- 'links' => [
- {
- 'href' => '/artists/busta-rhymes',
- 'rel' => '/self'
- }
- ]
- }.to_json
- model = AcceptableModel::Artist.new :name => 'Busta Rhymes', :groups => ['Flipmode Squad', 'Leaders of The New School']
- model.for('vnd.acme.artist-v1+json').should eql expected
+ }
+ model.for('vnd.acme.artist-v1+json').should eql expected.to_json
+ end
+
+ it "outputs links with the href and rel as an attribute" do
+ expected = File.open('spec/fixtures/artist_with_groups.xml').read
+ model.for('vnd.acme.artist-v1+xml').should eql expected
end
end
end
View
10 spec/acceptable_model/relationships_mapper_spec.rb
@@ -77,7 +77,7 @@ def part_of
describe "#representation" do
it "includes the models attributes" do
expected = {:id=>"busta-rhymes", :name=>"Busta Rhymes"}
- mapper.representation.should include expected
+ mapper.representation.to_hash.should include expected
end
it "includes the models associations" do
@@ -88,7 +88,7 @@ def part_of
{:href=>"/groups/leaders-of-the-new-school", :rel=>"/partOf"}
]
}
- mapper.representation.should include links
+ mapper.representation.to_hash.should include links
end
it "includes the models relationships" do
@@ -117,11 +117,11 @@ def part_of
]
}
- mapper.representation.should include expected
+ mapper.representation.to_hash.should include expected
end
it "should not include attributes we don't care about" do
- mapper.representation.should_not include :groups => ["Flipmode Squad", "Leaders of The New School"]
+ mapper.representation.to_hash.should_not include :groups => ["Flipmode Squad", "Leaders of The New School"]
end
it "can represent a one to one relationship" do
@@ -145,7 +145,7 @@ def alias
end
expected = { :alias => {:id=>"busta-bus", :name=>"Busta Bus", :links=>[{:href=>"/aliases/busta-bus", :rel=>"/children"}]} }
AcceptableModel::RelationshipsMapper.new :model => artist, :response_block => structure, :attributes_block => attributes, :associations => associations
- mapper.representation.should include expected
+ mapper.representation.to_hash.should include expected
end
end
end
View
68 spec/acceptable_model/representation_spec.rb
@@ -0,0 +1,68 @@
+require "spec_helper"
+
+describe AcceptableModel::Representation do
+ let( :params ) {
+ {
+ :links => [
+ :href => '/foo-bar',
+ :rel => '/children'
+ ]
+ }
+ }
+ it "takes a hash of parameters" do
+ AcceptableModel::Representation.new params
+ end
+
+ it "returns the links node in the expected format" do
+ representation = AcceptableModel::Representation.new params
+ representation.to_xml.should eql "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<links>\n <link href=\"/foo-bar\" rel=\"/children\"/>\n</links>\n"
+ end
+
+ it "can turn convert properties into XML elements" do
+ params = { 'artist' =>
+ {
+ 'id' => 'busta-rhymes',
+ 'name' => 'Busta Rhymes',
+ 'groups' => [
+ {
+ 'id' => 'flipmode-squad',
+ 'name' => 'Flipmode Squad',
+ 'links' => [
+ {
+ 'href' => '/groups/flipmode-squad',
+ 'rel' => '/children'
+ }
+ ]
+ },
+ {
+ 'id' => 'leaders-of-the-new-school',
+ 'name' => 'Leaders of The New School',
+ 'links' => [
+ {
+ 'href' => '/groups/leaders-of-the-new-school',
+ 'rel' => '/children'
+ }
+ ]
+ }
+ ],
+ 'links' => [
+ {
+ 'href' => '/artists/busta-rhymes',
+ 'rel' => '/self'
+ },
+ {
+ 'href' => '/groups/flipmode-squad',
+ 'rel' => '/partOf'
+ },
+ {
+ 'href' => '/groups/leaders-of-the-new-school',
+ 'rel' => '/partOf'
+ }
+ ]
+ }
+ }
+ expected = File.open('spec/fixtures/artist_with_groups.xml').read
+ representation = AcceptableModel::Representation.new params
+ representation.to_xml.should eql expected
+ end
+end
View
97 spec/acceptable_model_spec.rb
@@ -53,63 +53,66 @@ class AcceptableModel::Artist
describe "#for" do
it "returns at HATEOS like format" do
- expected = {
- :id => 'busta-rhymes',
- :name => 'Busta Rhymes',
- :links => [
- {
- :href => '/artists/busta-rhymes',
- :rel => '/self'
- }
- ]
- }.to_json
- model = AcceptableModel::Artist.new :name => 'Busta Rhymes'
- model.for('vnd.acme.artist-v1+json').should eql expected
- end
-
- context "extended relationships" do
- let(:relationships) {
- {
+ expected = { :artist => {
:id => 'busta-rhymes',
:name => 'Busta Rhymes',
- :groups => [
- {
- 'id' => 'flipmode-squad',
- 'name' => 'Flipmode Squad',
- 'links' => [
- {
- 'href' => '/groups/flipmode-squad',
- 'rel' => '/children'
- }
- ]
- },
- {
- 'id' => 'leaders-of-the-new-school',
- 'name' => 'Leaders of The New School',
- 'links' => [
- {
- 'href' => '/groups/leaders-of-the-new-school',
- 'rel' => '/children'
- }
- ]
- }
- ],
:links => [
{
:href => '/artists/busta-rhymes',
:rel => '/self'
- },
- {
- :href => '/groups/flipmode-squad',
- :rel => '/partOf'
- },
- {
- :href => '/groups/leaders-of-the-new-school',
- :rel => '/partOf'
}
]
}
}
+ model = AcceptableModel::Artist.new :name => 'Busta Rhymes'
+ model.for('vnd.acme.artist-v1+json').should eql expected.to_json
+ end
+
+ context "extended relationships" do
+ let(:relationships) {
+ { :artist =>
+ {
+ :id => 'busta-rhymes',
+ :name => 'Busta Rhymes',
+ :groups => [
+ {
+ 'id' => 'flipmode-squad',
+ 'name' => 'Flipmode Squad',
+ 'links' => [
+ {
+ 'href' => '/groups/flipmode-squad',
+ 'rel' => '/children'
+ }
+ ]
+ },
+ {
+ 'id' => 'leaders-of-the-new-school',
+ 'name' => 'Leaders of The New School',
+ 'links' => [
+ {
+ 'href' => '/groups/leaders-of-the-new-school',
+ 'rel' => '/children'
+ }
+ ]
+ }
+ ],
+ :links => [
+ {
+ :href => '/artists/busta-rhymes',
+ :rel => '/self'
+ },
+ {
+ :href => '/groups/flipmode-squad',
+ :rel => '/partOf'
+ },
+ {
+ :href => '/groups/leaders-of-the-new-school',
+ :rel => '/partOf'
+ }
+ ]
+ }
+ }
+ }
let(:expected_xml) { File.read('spec/fixtures/artist_with_groups.xml') }
let( :model ) {
View
25 spec/fixtures/artist_with_groups.xml
@@ -7,35 +7,20 @@
<id>flipmode-squad</id>
<name>Flipmode Squad</name>
<links>
- <link>
- <href>/groups/flipmode-squad</href>
- <rel>/children</rel>
- </link>
+ <link href="/groups/flipmode-squad" rel="/children"/>
</links>
</group>
<group>
<id>leaders-of-the-new-school</id>
<name>Leaders of The New School</name>
<links>
- <link>
- <href>/groups/leaders-of-the-new-school</href>
- <rel>/children</rel>
- </link>
+ <link href="/groups/leaders-of-the-new-school" rel="/children"/>
</links>
</group>
</groups>
<links>
- <link>
- <href>/artists/busta-rhymes</href>
- <rel>/self</rel>
- </link>
- <link>
- <href>/groups/flipmode-squad</href>
- <rel>/partOf</rel>
- </link>
- <link>
- <href>/groups/leaders-of-the-new-school</href>
- <rel>/partOf</rel>
- </link>
+ <link href="/artists/busta-rhymes" rel="/self"/>
+ <link href="/groups/flipmode-squad" rel="/partOf"/>
+ <link href="/groups/leaders-of-the-new-school" rel="/partOf"/>
</links>
</artist>
View
10 spec/fixtures/artists.xml
@@ -4,20 +4,14 @@
<id>busta-rhymes</id>
<name>Busta Rhymes</name>
<links>
- <link>
- <href>/artists/busta-rhymes</href>
- <rel>/self</rel>
- </link>
+ <link href="/artists/busta-rhymes" rel="/self"/>
</links>
</artist>
<artist>
<id>jay-z</id>
<name>Jay-Z</name>
<links>
- <link>
- <href>/artists/jay-z</href>
- <rel>/self</rel>
- </link>
+ <link href="/artists/jay-z" rel="/self"/>
</links>
</artist>
</artists>
View
1 spec/spec_helper.rb
@@ -3,6 +3,7 @@
require 'rspec'
require 'acceptable_model'
require 'acceptable_model/relationships_mapper'
+require 'acceptable_model/representation'
# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.

0 comments on commit 95efff1

Please sign in to comment.