Skip to content

Commit

Permalink
Add rendering to responses + Add collection requesting on GET
Browse files Browse the repository at this point in the history
In order to get a collection of objects, this commit adds the rendering
of responses trough templates and Hypa::Application.collection adds a
new route to be able to request the collection on GET.
  • Loading branch information
nmerouze committed May 20, 2013
1 parent f1d3a74 commit c8e48b9
Show file tree
Hide file tree
Showing 14 changed files with 156 additions and 21 deletions.
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ PATH
hypa (0.1.0)
extlib
json
sequel
sinatra
virtus

Expand Down Expand Up @@ -67,6 +68,7 @@ GEM
rspec-expectations (2.13.0)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.13.1)
sequel (3.47.0)
simplecov (0.7.1)
multi_json (~> 1.0)
simplecov-html (~> 0.7.1)
Expand All @@ -76,6 +78,7 @@ GEM
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
slop (3.4.4)
sqlite3 (1.3.7)
thor (0.18.1)
tilt (1.4.0)
virtus (0.5.4)
Expand All @@ -93,3 +96,4 @@ DEPENDENCIES
rake
rspec
simplecov
sqlite3
3 changes: 2 additions & 1 deletion examples/sinatra/Gemfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
source 'https://rubygems.org'

gem 'puma'
gem 'hypa', path: '../../'
gem 'hypa', path: '../../'
gem 'sqlite3'
4 changes: 4 additions & 0 deletions examples/sinatra/Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ PATH
hypa (0.1.0)
extlib
json
sequel
sinatra
virtus

Expand All @@ -19,10 +20,12 @@ GEM
rack (1.5.2)
rack-protection (1.5.0)
rack
sequel (3.47.0)
sinatra (1.4.2)
rack (~> 1.5, >= 1.5.2)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
sqlite3 (1.3.7)
tilt (1.4.1)
virtus (0.5.4)
backports (~> 2.6.1)
Expand All @@ -34,3 +37,4 @@ PLATFORMS
DEPENDENCIES
hypa!
puma
sqlite3
14 changes: 14 additions & 0 deletions examples/sinatra/config.ru
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
require 'bundler/setup'
require 'hypa'
require 'sequel'

DB = Sequel.sqlite

DB.create_table :posts do
primary_key :id
String :title
end

DB.from(:posts).insert(title: 'HypaMedia')

class Post < Sequel::Model
end

Hypa::Application.resource :post, '/posts/:id' do |r|
r.properties :id, :title
r.model Post

r.get :self do |a|
a.params :id
Expand Down
2 changes: 2 additions & 0 deletions hypa.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Gem::Specification.new do |s|
s.add_dependency 'extlib'
s.add_dependency 'sinatra'
s.add_dependency 'json'
s.add_dependency 'sequel'

s.add_development_dependency 'sqlite3'
s.add_development_dependency 'rspec'
s.add_development_dependency 'rack-test'
s.add_development_dependency 'rake'
Expand Down
11 changes: 9 additions & 2 deletions lib/hypa/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@ def self.resource(name, path, &block)
end

def self.collection(name, path, &block)
self.collections[name] = Hypa::Collection.new(name: name, href: path, &block)
collection = self.collections[name] = Hypa::Collection.new(name: name, href: path, &block)

route 'OPTIONS', path, {} do
JSON.dump(self.collections[name].to_hash)
JSON.dump(collection.to_hash)
end

collection.actions.each do |action_name, action|
route action.method, path, {} do
data = collection.resource.model.all # TODO: Just for GET routes, with filter depending on params
JSON.dump(name => action.responses[200].render(data.map(&:values)))
end
end

self.collections[name]
Expand Down
7 changes: 6 additions & 1 deletion lib/hypa/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ def resource(resource = nil)
end

def to_hash
{ name: self.name, href: self.href, actions: actions.map { |a| a.to_hash } }.merge(self.resource ? self.resource.to_hash.only(:resources) : {})
{ name: self.name, href: self.href, actions: actions.map { |v,a| a.to_hash } }.merge(self.resource ? self.resource.to_hash.only(:resources) : {})
end

# def render(action)
# data = self.resource.model.all
# self.actions[action].responses[200].render(data.map(&:values))
# end
end
4 changes: 4 additions & 0 deletions lib/hypa/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ class Hypa::Response
def to_hash
{ status: self.status, template: (self.template ? self.template.to_hash : nil) }
end

def render(data)
self.template.render(data)
end
end
4 changes: 4 additions & 0 deletions lib/hypa/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ def initialize(collection)
def to_hash
{ type: 'array', items: { properties: @collection.resource.properties } }
end

def render(data)
data.map { |item| item.only(*@collection.resource.properties) }
end
end
47 changes: 43 additions & 4 deletions spec/integration/application_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,35 @@
require 'spec_helper'
require 'rack/test'

require 'sequel'

DB = Sequel.sqlite

DB.create_table :posts do
primary_key :id
String :title
end

DB.from(:posts).insert(title: 'HypaMedia')

class Post < Sequel::Model
end

class IntegrationApp < Hypa::Application
end

IntegrationApp.collection :posts, '/posts'
IntegrationApp.resource :post, '/posts/:id'
IntegrationApp.resource :post, '/posts/:id' do
properties :id, :title
model Post
end

IntegrationApp.collection :posts, '/posts' do |c|
c.resource IntegrationApp.resources[:post]

c.get :self do |a|
a.response 200, Hypa::CollectionTemplate.new(c)
end
end

describe Hypa::Application do
include Rack::Test::Methods
Expand All @@ -25,7 +49,22 @@ def app

it 'returns the collection serialization' do
body = JSON.load(last_response.body)
expect(body).to eq({"name"=>"posts", "href"=>"/posts", "actions"=>[]})
expect(body).to eq({"name"=>"posts", "href"=>"/posts", "actions" => [{"name"=>"self", "method"=>"GET", "params"=>[], "responses"=>[{"status"=>200, "template"=>{"type"=>"array", "items"=>{"properties"=>["id", "title"]}}}]}], "resources" => {"post"=>{"properties"=>["id", "title"]}} })
end
end

context 'GET /posts' do
before do
get '/posts'
end

it 'returns JSON' do
expect(last_response.headers['Content-Type']).to eq('application/json;charset=utf-8')
end

it 'returns the collection of posts' do
body = JSON.load(last_response.body)
expect(body).to eq({ "posts" => [{ "id" => 1, "title" => "HypaMedia" }]})
end
end

Expand All @@ -40,7 +79,7 @@ def app

it 'returns the resource serialization' do
body = JSON.load(last_response.body)
expect(body).to eq({"name"=>"post", "href"=>"/posts/:id", "resources"=>{"post"=>{"properties"=>[]}}, "actions"=>[]})
expect(body).to eq({"name"=>"post", "href"=>"/posts/:id", "resources"=>{"post"=>{"properties"=>["id","title"]}}, "actions"=>[]})
end
end
end
39 changes: 26 additions & 13 deletions spec/integration/collection_spec.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
require 'spec_helper'
require 'sequel'

PostResource = Hypa::Resource.new do
properties :id, :title
DB = Sequel.sqlite

DB.create_table :posts do
primary_key :id
String :title
end

DB.from(:posts).insert(title: 'HypaMedia')

class Post < Sequel::Model
end

PostResource = Hypa::Resource.new do |r|
r.properties :id, :title
r.model Post
end

describe 'A collection' do
let(:resource) { PostResource }

let :collection do
Hypa::Collection.new do
resource PostResource
Hypa::Collection.new do |c|
c.resource PostResource

get :self do
params :title_in
response 200, Hypa::Template.new
c.get :self do |a|
a.params :title_in
a.response 200, Hypa::CollectionTemplate.new(c)
end
end
end

let(:template) { Hypa::Template.new }
let(:action) { collection.actions[:self] }
let(:response) { action.responses[200] }

before do
Hypa::Template.stub(:new).and_return(template)
end

it 'defines an action' do
expect(collection.actions.size).to eq(1)

Expand All @@ -35,10 +44,14 @@
expect(action.responses.size).to eq(1)

expect(response.status).to eq(200)
expect(response.template).to eq(template)
end

it 'defines a resource' do
expect(collection.resource).to eq(resource)
end

it 'renders an action\'s response' do
result = collection.actions[:self].responses[200].render(collection.resource.model.all.map(&:values))
expect(result).to eq([{ id: 1, title: 'HypaMedia' }])
end
end
16 changes: 16 additions & 0 deletions spec/unit/hypa/collection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,20 @@
expect(collection.to_hash).to eq({ name: :posts, href: '/posts', actions: [] })
end
end

# describe '#render' do
# before do
# post = double('Post', all: [{ id: 1, title: 'Foobar' }])
# resource = double('Hypa::Resource', properties: [:id, :title], model: post, to_hash: {})
# collection.resource(resource)
# collection.get :self do |a|
# a.response 200, Hypa::CollectionTemplate.new(collection)
# end
# end

# it 'renders an action\'s response' do
# result = collection.render(:self)
# expect(result).to eq([{ id: 1, title: 'Foobar' }])
# end
# end
end
15 changes: 15 additions & 0 deletions spec/unit/hypa/response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,19 @@
expect(response.to_hash).to eq({ status: 200, template: nil })
end
end

describe '#render' do
context 'with a collection template' do
before do
resource = double('Hypa::Resource', properties: [:id, :title])
collection = double('Hypa::Collection', resource: resource)
response.template = Hypa::CollectionTemplate.new(collection)
end

it 'renders the template' do
result = response.render([{ id: 1, title: 'Foobar', not_in_resource: 'Test' }])
expect(result).to eq([{ id: 1, title: 'Foobar' }])
end
end
end
end
7 changes: 7 additions & 0 deletions spec/unit/hypa/template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@
items: { properties: [:id, :title] }
})
end

describe '#render' do
it 'renders the template' do
result = @template.render([{ id: 1, title: 'Foobar', not_in_resource: 'Test' }])
expect(result).to eq([{ id: 1, title: 'Foobar' }])
end
end
end

0 comments on commit c8e48b9

Please sign in to comment.