Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: baphled/acceptable_model
base: 0a7b5d80c6
...
head fork: baphled/acceptable_model
compare: 3d63121b74
  • 5 commits
  • 5 files changed
  • 0 commit comments
  • 1 contributor
1  Gemfile
View
@@ -1,6 +1,7 @@
source "http://rubygems.org"
gem 'active_support'
+gem 'builder'
# Add dependencies required to use your gem here.
# Example:
# gem "activesupport", ">= 2.3.5"
2  Gemfile.lock
View
@@ -4,6 +4,7 @@ GEM
active_support (3.0.0)
activesupport (= 3.0.0)
activesupport (3.0.0)
+ builder (3.0.0)
diff-lcs (1.1.3)
git (1.2.5)
jeweler (1.8.3)
@@ -29,6 +30,7 @@ PLATFORMS
DEPENDENCIES
active_support
+ builder
bundler (~> 1.0.0)
jeweler (~> 1.8.3)
rdoc (~> 3.12)
17 README.md
View
@@ -91,12 +91,15 @@ own Re-open the defined class and simple create your own relationship.
end
Defining these methods exposes the objects relationships, visiting the resource
-`curl -H 'Accept: application/json' -i http://localhost:9292/artists/busta-rhymes`
+
+ artist = AcceptableModel::Artist.first
+ Artist.to_json
+
exposes the following response.
{
'name': 'Busta Rhymes',
- 'debut': '1990'
+ 'debut': '1990',
'albums': [
'name': 'The Coming',
'links': [
@@ -108,7 +111,7 @@ exposes the following response.
],
'songs': [
{'title': 'Gimme Some more', 'duration': '4:05'}
- ]
+ ],
'links': [
{
'href': '/artists/cilla_black',
@@ -127,6 +130,8 @@ exposes the following response.
All this from a few lines of code :D
+### Adding rel attributes
+
AcceptableModel define a range of rel values but we should also be able to
create our own rel types, we could do this via the config method as follows:
@@ -134,9 +139,7 @@ create our own rel types, we could do this via the config method as follows:
config.relationships = %w{self contains part_of parent child}
end
-This will prefix all of our rel attribuetes with the string above
-
-## TODO
+### Displaying model associations
In true DRY fashion there is not need define a links href as they will be
looked up via our controllers.
@@ -166,6 +169,8 @@ When calling `model.all` the output will now be as following:
]
}
+## TODO
+
We should also be able to easily change the rel attributes so that we can fully
customised the way they are displayed. It would be nice if we could do
something like this:
115 lib/acceptable_model.rb
View
@@ -1,4 +1,5 @@
require "active_support/inflector"
+require "builder"
require "delegate"
require "json"
@@ -17,6 +18,44 @@ def self.define_class model_object
class #{model_object} < SimpleDelegator
include HATEOS
+ class << self
+ attr_accessor :associations, :version_mapper
+
+ #
+ # Maps API version and MIME type
+ #
+ def version versions, &block
+ @version_mapper = [] if @version_mapper.nil?
+ versions.collect { |version| @version_mapper << {:version => version, :attributes => block } }
+ end
+
+ #
+ # Map associations
+ #
+ # This macro is used to allow users to map associations to a model
+ # allowing for a HATEOS compliant format
+ #
+ def relationship association
+ @associations = [] if @associations.nil?
+ @associations << association.to_s unless @associations.include? association.to_s
+ end
+
+ #
+ # FIXME Shouldn't need this ideally, need to find a way to remove it or make cleaner
+ #
+ def find params
+ object = super
+ new object.to_hash
+ end
+
+ #
+ # Delegate class methods to the correct object
+ #
+ def method_missing method, *args
+ ::#{model_object}.send(method,args)
+ end
+ end
+
def initialize params
@delegate_model = ::#{model_object}.new params
super @delegate_model
@@ -29,6 +68,7 @@ def to_model
def class
__getobj__.class
end
+
end
"""
end
@@ -64,24 +104,97 @@ def relationship_types
module InstanceMethods
attr_accessor :base_relationship
+ attr_accessor :associations
attr_accessor :relationships
private :relationships=
#
+ # returns the correct response type and API version
+ #
+ def for mime_type
+ map = version_lookup mime_type
+ mime = mime_type_lookup mime_type
+ attributes = map[:attributes].call self
+ convert_to = "to_#{mime}".to_sym
+ send convert_to
+ end
+
+ def mime_type_lookup mime_type
+ respond_with = version_lookup mime_type
+ mime_type.split('+').last unless respond_with.nil?
+ end
+
+ def version_lookup mime_type
+ mappers = eval( "AcceptableModel::#{ self.class }" ).version_mapper
+ mappers.detect { |mapper| mime_type == mapper[:version] }
+ end
+
+ def rel_links
+ associations = eval( "AcceptableModel::#{ self.class }" ).associations
+ return [] if associations.nil?
+ associations.collect { |association|
+ return [] if send(association.pluralize.to_sym).nil?
+ build_association association
+ }
+ end
+
+ #
# Overide the models to_json method so that we can can display our
# serialised data
#
def to_json options = {}
+ rel_links.each{|association| attributes.merge! association }
opts = {:links => relationships}.merge! options
attributes.merge! opts
super attributes
end
+ def to_xml options = {}
+ rel_links.each{|association| attributes.merge! association }
+ opts = {:links => relationships}.merge! options
+ attributes.merge! opts
+ xml = Builder::XmlMarkup.new :output => STDOUT
+ build_xml( xml, attributes ).target!
+ end
+
protected
+ def build_xml xml, attributes
+ attributes.each do |k,v|
+ if v.class == Array
+ v.each_with_index {|attr, index| build_xml xml,attr }
+ else
+ eval"xml.__send__(k.to_sym, v)"
+ end
+ end
+ xml
+ end
+
+ def build_association association
+ {
+ association.pluralize.to_sym =>
+ send(association.pluralize.to_sym).collect { |model|
+ model.attributes.merge! build_relationship model, association
+ }
+ }
+ end
+ #
+ # Dynamically builds associative relationships
+ #
+ def build_relationship model, association
+ {
+ :links => [
+ {
+ :href => "/#{association.pluralize}/#{model.id}",
+ :rel => "/children"
+ }
+ ]
+ }
+ end
+
#
- # A list of the models relationships
+ # A list of the model relationships
#
def relationships
relationships = extended_relationships.collect! { |relationship| relationship_mapper relationship }
109 spec/acceptable_model_spec.rb
View
@@ -2,10 +2,11 @@
class Artist
- attr_accessor :name, :id, :groups
+ attr_accessor :name, :aliases, :id, :groups
def initialize params = {}
self.name = params[:name]
+ self.aliases = params[:aliases]
self.groups = params[:groups]
self.id = self.name.downcase.gsub(' ', '-')
end
@@ -17,10 +18,12 @@ def to_json params = {}
def attributes
@attributes ||= {:id => id, :name => name}
end
+ alias :to_hash :attributes
+
protected
def groups= groups
- @group = groups.each {|group| Group.new :name => group} unless groups.nil?
+ @groups = groups.collect {|group| Group.new :name => group} unless groups.nil?
end
end
@@ -71,6 +74,59 @@ def to_json params = {}
end
end
+ context "define associative relationships" do
+ before do
+ class AcceptableModel::Artist
+ relationship :group
+ end
+ end
+
+ it "lists the objects relationships" do
+ AcceptableModel::Artist.associations.should include 'group'
+ end
+
+ it "should have a list of associations" do
+ AcceptableModel::Artist.associations.should be_a Array
+ end
+
+ it "should allow use to define a relationship" do
+ expected = {
+ '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'
+ }
+ ]
+ }.to_json
+ model = AcceptableModel::Artist.new :name => 'Busta Rhymes', :groups => ['Flipmode Squad', 'Leaders of The New School']
+ model.to_json.should eql expected
+ end
+ end
+
describe "#to_json" do
it "returns at HATEOS like format" do
expected = {
@@ -92,6 +148,28 @@ def to_json params = {}
{
: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',
@@ -109,9 +187,15 @@ def to_json params = {}
}
}
- before :all do
- AcceptableModel.define 'group'
+ before do
class AcceptableModel::Artist
+ version ['vnd.acme.sandwich-v1+json', 'vnd.acme.sandwich-v1+xml'] do |artist|
+ {
+ :id => artist.id,
+ :name => artist.name
+ }
+ end
+
def part_of
groups.all
end
@@ -125,6 +209,23 @@ def part_of
model.groups.stub(:all).and_return [group1, group2]
model.to_json.should eql relationships.to_json
end
+
+ it "allows for the output format to be passed" do
+ model = AcceptableModel::Artist.new :name => 'Busta Rhymes', :aliases => ['Busta Bus'], :groups => ['Flipmode Squad', 'Leaders of The New School']
+ group1 = Group.new :name => 'Flipmode Squad', :id => 'flipmode-squad'
+ group2 = Group.new :name => 'Leaders of The New School', :id => 'leaders-of-the-new-school'
+ model.groups.stub(:all).and_return [group1, group2]
+ model.for('vnd.acme.sandwich-v1+json').should eql relationships.to_json
+ end
+
+ it "can deal with XML formats the same as JSON formats" do
+ model = AcceptableModel::Artist.new :name => 'Busta Rhymes', :aliases => ['Busta Bus'], :groups => ['Flipmode Squad', 'Leaders of The New School']
+ group1 = Group.new :name => 'Flipmode Squad', :id => 'flipmode-squad'
+ group2 = Group.new :name => 'Leaders of The New School', :id => 'leaders-of-the-new-school'
+ model.groups.stub(:all).and_return [group1, group2]
+ model.for('vnd.acme.sandwich-v1+xml').should eql "<id>busta-rhymes</id><name>Busta Rhymes</name><id>flipmode-squad</id><name>Flipmode Squad</name><href>/groups/flipmode-squad</href><rel>/children</rel><id>leaders-of-the-new-school</id><name>Leaders of The New School</name><href>/groups/leaders-of-the-new-school</href><rel>/children</rel><href>/artists/busta-rhymes</href><rel>/self</rel><href>/groups/flipmode-squad</href><rel>/partOf</rel><href>/groups/leaders-of-the-new-school</href><rel>/partOf</rel>"
+ end
+ it "mime type not found"
end
end
end

No commit comments for this range

Something went wrong with that request. Please try again.