Skip to content

Commit

Permalink
[Representor] Replace links with transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
kylef committed May 19, 2015
1 parent 385837a commit c3d9fae
Show file tree
Hide file tree
Showing 10 changed files with 47 additions and 77 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ Representor.framework as a target dependency.
import Representor

let representor = Representor<HTTPTransition> { builder in
builder.addLink("self", uri:"/notes/2/")
builder.addLink("previous", uri:"/notes/1/")
builder.addLink("next", uri:"/notes/3/")
builder.addTransition("self", uri:"/notes/2/")
builder.addTransition("previous", uri:"/notes/1/")
builder.addTransition("next", uri:"/notes/3/")

builder.addMetaData("title", "Customer Details")

builder.addTransition("create", uri:"/notes/") { transitionBuilder in
transitionBuilder.method = "POST"
transitionBuilder.addAttribute("title")
transitionBuilder.addAttribute("note")
}
Expand All @@ -44,12 +45,12 @@ if let create = representor.transitions["create"] {
println("You can create with the URI: \(create.uri).")
}

if let uri = representor.links["next"] {
println("The next representor can be found at: \(uri).")
if let next = representor.transitions["next"] {
println("The next representor can be found at: \(next).")
}

if let uri = representor.links["previous"] {
println("The previous representor can be found at: \(uri).")
if let prev = representor.transitions["previous"] {
println("The previous representor can be found at: \(prev).")
}
```

Expand Down
10 changes: 0 additions & 10 deletions Representor/Builder/RepresentorBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,6 @@ public class RepresentorBuilder<Transition : TransitionType> {
transitions[name] = transition
}

// MARK: Links

/// Adds a link
///
/// :param: name The name (or relation) for the link
/// :param: uri The URI of the link
public func addLink(name:String, uri:String) {
links[name] = uri
}

// MARK: Metadata

/// Adds an piece of metadata
Expand Down
28 changes: 14 additions & 14 deletions Representor/HTTP/Adapters/HTTPHALAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@

import Foundation

func parseHALLinks(halLinks:[String:[String:AnyObject]]) -> [String:String] {
var links = [String:String]()
func parseHALLinks(halLinks:[String:[String:AnyObject]]) -> [String:HTTPTransition] {
var links = [String:HTTPTransition]()

for (link, options) in halLinks {
if let href = options["href"] as? String {

This comment has been minimized.

Copy link
@paulofaria

paulofaria Sep 6, 2015

This part only takes in consideration a relation with a link pointing to a single key. What about when a relation has multiple links sharing the same key like in "upsell" below:

{
  "_links": {
    "self": { "href": "/product/987" },
    "upsell": [
      { "href": "/product/452", "title": "Flower pot" },
      { "href": "/product/832", "title": "Hover donkey" }
    ]
  },
  "name": "A product",
  "weight": 400,
}

I know that is not possible to represent this with the current structure of Representor since the transitions property is a dictionary of Transition.

let transitions: [String: Transition]

If the property is changed to:

let transitions: [String: [Transition]]

This scenario could be accommodated. What do you guys think about this?

This comment has been minimized.

Copy link
@kylef

kylef Sep 6, 2015

Author Member

That's a very good point and I agree, filed #22.

links[link] = href
links[link] = HTTPTransition(uri: href)
}
}

return links
}


func parseEmbeddedHALs<Transition : TransitionType>(embeddedHALs:[String:AnyObject]) -> [String:[Representor<Transition>]] {
var representors = [String:[Representor<Transition>]]()
func parseEmbeddedHALs(embeddedHALs:[String:AnyObject]) -> [String:[Representor<HTTPTransition>]] {
var representors = [String:[Representor<HTTPTransition>]]()

func parseEmbedded(embedded:[String:AnyObject]) -> Representor<Transition> {
func parseEmbedded(embedded:[String:AnyObject]) -> Representor<HTTPTransition> {
return deserializeHAL(embedded)
}

Expand All @@ -40,31 +40,31 @@ func parseEmbeddedHALs<Transition : TransitionType>(embeddedHALs:[String:AnyObje
}

/// A function to deserialize a HAL structure into a HTTP Transition.
public func deserializeHAL<Transition : TransitionType>(hal:[String:AnyObject]) -> Representor<Transition> {
public func deserializeHAL(hal:[String:AnyObject]) -> Representor<HTTPTransition> {
var hal = hal

var links = [String:String]()
var links = [String:HTTPTransition]()
if let halLinks = hal.removeValueForKey("_links") as? [String:[String:AnyObject]] {
links = parseHALLinks(halLinks)
}

var representors = [String:[Representor<Transition>]]()
var representors = [String:[Representor<HTTPTransition>]]()
if let embedded = hal.removeValueForKey("_embedded") as? [String:AnyObject] {
representors = parseEmbeddedHALs(embedded)
}

return Representor(transitions: nil, representors: representors, attributes: hal, links: links)
return Representor(transitions: links, representors: representors, attributes: hal)
}

/// A function to serialize a HTTP Representor into a Siren structure
public func serializeHAL<Transition : TransitionType>(representor:Representor<Transition>) -> [String:AnyObject] {
public func serializeHAL(representor:Representor<HTTPTransition>) -> [String:AnyObject] {
var representation = representor.attributes

if representor.links.count > 0 {
if representor.transitions.count > 0 {
var links = [String:[String:String]]()

for (relation, uri) in representor.links {
links[relation] = ["href": uri]
for (relation, transition) in representor.transitions {
links[relation] = ["href": transition.uri]
}

representation["_links"] = links
Expand Down
28 changes: 13 additions & 15 deletions Representor/HTTP/Adapters/HTTPSirenAdapter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ private func transitionToSirenAction(relation:String, transition:HTTPTransition)

/// A function to deserialize a Siren structure into a HTTP Transition.
public func deserializeSiren(siren:[String:AnyObject]) -> Representor<HTTPTransition> {
var links = [String:String]()
var representors = [String:[Representor<HTTPTransition>]]()
var transitions = [String:HTTPTransition]()
var attributes = [String:AnyObject]()
Expand All @@ -82,7 +81,7 @@ public func deserializeSiren(siren:[String:AnyObject]) -> Representor<HTTPTransi
if let href = link["href"] as? String {
if let relations = link["rel"] as? [String] {
for relation in relations {
links[relation] = href
transitions[relation] = HTTPTransition(uri: href)
}
}
}
Expand Down Expand Up @@ -118,23 +117,13 @@ public func deserializeSiren(siren:[String:AnyObject]) -> Representor<HTTPTransi
attributes = properties
}

return Representor<HTTPTransition>(transitions: transitions, representors: representors, attributes: attributes, links: links, metadata: [:])
return Representor<HTTPTransition>(transitions: transitions, representors: representors, attributes: attributes, metadata: [:])
}

/// A function to serialize a HTTP Representor into a Siren structure
public func serializeSiren(representor:Representor<HTTPTransition>) -> [String:AnyObject] {
var representation = [String:AnyObject]()

if representor.links.count > 0 {
var links = [[String:AnyObject]]()

for (name, uri) in representor.links {
links.append(["rel": [name], "href": uri])
}

representation["links"] = links
}

if representor.representors.count > 0 {
var entities = [[String:AnyObject]]()

Expand All @@ -153,8 +142,17 @@ public func serializeSiren(representor:Representor<HTTPTransition>) -> [String:A
representation["properties"] = representor.attributes
}

if representor.transitions.count > 0 {
representation["actions"] = map(representor.transitions, transitionToSirenAction)
let links = filter(representor.transitions) { $1.method == "GET" }
let actions = filter(representor.transitions) { $1.method != "GET" }

if links.count > 0 {
representation["links"] = map(links) { relation, transition in
return ["rel": [relation], "href": transition.uri]
}
}

if actions.count > 0 {
representation["actions"] = map(actions, transitionToSirenAction)
}

return representation
Expand Down
2 changes: 1 addition & 1 deletion Representor/HTTP/HTTPTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public struct HTTPTransition : TransitionType {
self.uri = uri
self.attributes = attributes ?? [:]
self.parameters = parameters ?? [:]
self.method = "POST"
self.method = "GET"
self.suggestedContentTypes = [String]()
}

Expand Down
9 changes: 2 additions & 7 deletions Representor/Representor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,20 @@ public struct Representor<Transition : TransitionType> : Equatable, Hashable {
/// The separate representors embedded in the current representor.
public let representors:[String:[Representor]]

public let links:[String:String]

public let metadata:[String:String]

/// The attributes of the representor
public let attributes:[String:AnyObject]

public init(transitions:[String:Transition]? = nil, representors:[String:[Representor]]? = nil, attributes:[String:AnyObject]? = nil, links:[String:String]? = nil, metadata:[String:String]? = nil) {
public init(transitions:[String:Transition]? = nil, representors:[String:[Representor]]? = nil, attributes:[String:AnyObject]? = nil, metadata:[String:String]? = nil) {
self.transitions = transitions ?? [:]
self.representors = representors ?? [:]
self.attributes = attributes ?? [:]
self.links = links ?? [:]
self.metadata = metadata ?? [:]
}

public var hashValue:Int {
return transitions.count + representors.count + links.count + metadata.count + attributes.count
return transitions.count + representors.count + metadata.count + attributes.count
}

/// An extension to Representor to provide a builder interface for creating a Representor.
Expand All @@ -50,7 +47,6 @@ public struct Representor<Transition : TransitionType> : Equatable, Hashable {
self.transitions = builder.transitions
self.representors = builder.representors
self.attributes = builder.attributes
self.links = builder.links
self.metadata = builder.metadata
}
}
Expand Down Expand Up @@ -88,7 +84,6 @@ public func ==<Transition : TransitionType>(lhs:Representor<Transition>, rhs:Rep
return (
lhs.transitions == rhs.transitions &&
lhs.representors == rhs.representors &&
lhs.links == rhs.links &&
lhs.metadata == rhs.metadata &&
(lhs.attributes as NSObject) == (rhs.attributes as NSObject)
)
Expand Down
12 changes: 1 addition & 11 deletions RepresentorTests/Builder/RepresentorBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class RepresentorBuilderTests: XCTestCase {

func testAddRepresentor() {
let representor = Representor<HTTPTransition> { builder in
builder.addRepresentor("parent", representor:Representor(transitions:[:], representors:[:], attributes:[:], links:[:], metadata:[:]))
builder.addRepresentor("parent", representor: Representor())
}

XCTAssertTrue(representor.representors["parent"] != nil)
Expand Down Expand Up @@ -84,16 +84,6 @@ class RepresentorBuilderTests: XCTestCase {
XCTAssertTrue(representor.transitions["self"] != nil)
}

// MARK: Links

func testAddLink() {
let representor = Representor<HTTPTransition> { builder in
builder.addLink("next", uri:"/next/")
}

XCTAssertEqual(representor.links, ["next": "/next/"])
}

// MARK: Metadata

func testAddMetaData() {
Expand Down
2 changes: 1 addition & 1 deletion RepresentorTests/HTTPTransitionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class HTTPTransitionTests : XCTestCase {
}

func testHasMethod() {
XCTAssertEqual(transition.method, "POST")
XCTAssertEqual(transition.method, "GET")
}

func testHasContentType() {
Expand Down
8 changes: 2 additions & 6 deletions RepresentorTests/RepresentorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class RepresentorTests: XCTestCase {
super.setUp()
transition = HTTPTransition(uri:"/self/")
embeddedRepresentor = Representor()
representor = Representor(transitions:["self": transition], representors:["embedded": [embeddedRepresentor]], attributes:["name":"Kyle"], links:["next": "/next/"], metadata:["key": "value"])
representor = Representor(transitions:["self": transition], representors:["embedded": [embeddedRepresentor]], attributes:["name":"Kyle"], metadata:["key": "value"])
}

func testHasTransitions() {
Expand All @@ -34,16 +34,12 @@ class RepresentorTests: XCTestCase {
XCTAssertEqual(representor.attributes["name"] as! String, "Kyle")
}

func testHasLinks() {
XCTAssertEqual(representor.links, ["next": "/next/"])
}

func testHasMetaData() {
XCTAssertEqual(representor.metadata, ["key": "value"])
}

func testEquality() {
XCTAssertEqual(representor, Representor(transitions:["self": transition], representors:["embedded": [embeddedRepresentor]], attributes:["name":"Kyle"], links:["next": "/next/"], metadata:["key": "value"]))
XCTAssertEqual(representor, Representor(transitions:["self": transition], representors:["embedded": [embeddedRepresentor]], attributes:["name":"Kyle"], metadata:["key": "value"]))
XCTAssertNotEqual(representor, Representor())
}

Expand Down
10 changes: 5 additions & 5 deletions RepresentorTests/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ func PollFixtureAttributes(forObject:AnyObject) -> [String:AnyObject] {

func PollFixture(forObject:AnyObject) -> Representor<HTTPTransition> {
return Representor { builder in
builder.addLink("self", uri:"/polls/1/")
builder.addLink("next", uri:"/polls/2/")
builder.addTransition("self", uri:"/polls/1/")
builder.addTransition("next", uri:"/polls/2/")

builder.addAttribute("question", value:"Favourite programming language?")
builder.addAttribute("published_at", value:"2014-11-11T08:40:51.620Z")
Expand All @@ -52,9 +52,9 @@ func PollFixture(forObject:AnyObject) -> Representor<HTTPTransition> {
])

builder.addRepresentor("next") { builder in
builder.addLink("self", uri:"/polls/2/")
builder.addLink("next", uri:"/polls/3/")
builder.addLink("previous", uri:"/polls/1/")
builder.addTransition("self", uri:"/polls/2/")
builder.addTransition("next", uri:"/polls/3/")
builder.addTransition("previous", uri:"/polls/1/")
}
}
}

0 comments on commit c3d9fae

Please sign in to comment.