render 0.0.1
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/.gitignore +20 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +49 -0
- data/LICENSE.txt +22 -0
- data/extensions/boolean.rb +3 -0
- data/extensions/dottable_hash.rb +113 -0
- data/extensions/enumerable.rb +36 -0
- data/extensions/hash.rb +52 -0
- data/initialize.rb +7 -0
- data/lib/render/attribute.rb +99 -0
- data/lib/render/errors.rb +64 -0
- data/lib/render/generator.rb +14 -0
- data/lib/render/graph.rb +106 -0
- data/lib/render/schema.rb +114 -0
- data/lib/render/version.rb +3 -0
- data/lib/render.rb +42 -0
- data/rakefile.rb +11 -0
- data/readme.md +90 -0
- data/render.gemspec +30 -0
- data/spec/functional/representation/attribute_spec.rb +29 -0
- data/spec/functional/representation/nested_schemas_spec.rb +103 -0
- data/spec/functional/representation/schema_spec.rb +86 -0
- data/spec/integration/nested_graph_spec.rb +77 -0
- data/spec/integration/single_graph_spec.rb +75 -0
- data/spec/schemas/film.json +8 -0
- data/spec/schemas/films.json +13 -0
- data/spec/support/helpers.rb +3 -0
- data/spec/support.rb +7 -0
- data/spec/unit/extensions/boolean_spec.rb +7 -0
- data/spec/unit/render/attribute_spec.rb +128 -0
- data/spec/unit/render/generator_spec.rb +39 -0
- data/spec/unit/render/graph_spec.rb +205 -0
- data/spec/unit/render/schema_spec.rb +250 -0
- data/spec/unit/render_spec.rb +81 -0
- metadata +217 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
# The Schema defines a collection of attributes.
|
2
|
+
# It is responsible for returning its attributes' values back to its Graph.
|
3
|
+
|
4
|
+
require "net/http"
|
5
|
+
require "json"
|
6
|
+
require "render"
|
7
|
+
require "render/attribute"
|
8
|
+
|
9
|
+
module Render
|
10
|
+
class Schema
|
11
|
+
attr_accessor :title,
|
12
|
+
:type,
|
13
|
+
:attributes,
|
14
|
+
:schema
|
15
|
+
|
16
|
+
# The schema need to know where its getting a value from
|
17
|
+
# an Attribute, e.g. { foo: "bar" } => { foo: { type: String } }
|
18
|
+
# an Archetype, e.g. [1,2,3] => { type: Integer } # could this be a pass-through?
|
19
|
+
# an Attribute-Schema, e.g. { foo: { bar: "baz" } } => { foo: { type: Object, attributes: { bar: { type: String } } }
|
20
|
+
# an Attribute-Array, e.g. [{ foo: "bar" }] => { type: Array, elements: { type: Object, attributes: { foo: { type: String } } } }
|
21
|
+
# and we need to identify when given { ids: [1,2] }, parental_mapping { ids: id } means to make 2 calls
|
22
|
+
def initialize(schema_or_title)
|
23
|
+
self.schema = schema_or_title.is_a?(Hash) ? schema_or_title : find_schema(schema_or_title)
|
24
|
+
self.title = schema[:title]
|
25
|
+
self.type = Render.parse_type(schema[:type])
|
26
|
+
|
27
|
+
if array_of_schemas?(schema[:elements])
|
28
|
+
self.attributes = [Attribute.new({ elements: schema[:elements] })]
|
29
|
+
else
|
30
|
+
definitions = schema[:attributes] || schema[:elements]
|
31
|
+
self.attributes = definitions.collect do |key, value|
|
32
|
+
Attribute.new({ key => value })
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def array_of_schemas?(definition = {})
|
38
|
+
return false unless definition
|
39
|
+
definition.keys.include?(:attributes)
|
40
|
+
end
|
41
|
+
|
42
|
+
def pull(options = {})
|
43
|
+
endpoint = options.delete(:endpoint)
|
44
|
+
data = Render.live ? request(endpoint) : options
|
45
|
+
{ title.to_sym => serialize(data) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def serialize(data)
|
49
|
+
# data.is_a?(Array) ? to_array(data) : to_hash(data)
|
50
|
+
(type == Array) ? to_array(data) : to_hash(data)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def find_schema(title)
|
56
|
+
loaded_schema = Render.schemas[title.to_sym]
|
57
|
+
raise Errors::Schema::NotFound.new(title) if !loaded_schema
|
58
|
+
loaded_schema
|
59
|
+
end
|
60
|
+
|
61
|
+
def request(endpoint)
|
62
|
+
response = Net::HTTP.get_response(URI(endpoint))
|
63
|
+
if response.kind_of?(Net::HTTPSuccess)
|
64
|
+
response = JSON.parse(response.body).recursive_symbolize_keys!
|
65
|
+
if (response.is_a?(Array) || (response[title.to_sym] == nil))
|
66
|
+
response
|
67
|
+
else
|
68
|
+
response[title.to_sym]
|
69
|
+
end
|
70
|
+
else
|
71
|
+
raise Errors::Schema::RequestError.new(endpoint, response)
|
72
|
+
end
|
73
|
+
rescue JSON::ParserError => error
|
74
|
+
raise Errors::Schema::InvalidResponse.new(endpoint, response.body)
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_array(elements)
|
78
|
+
# elements.first.is_a?(Hash) ? to_array_of_schemas(elements) : to_array_of_elements(elements)
|
79
|
+
attributes.first.schema_value? ? to_array_of_schemas(elements) : to_array_of_elements(elements)
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_array_of_elements(elements)
|
83
|
+
(elements = stubbed_array) if !Render.live && (!elements || elements.empty?)
|
84
|
+
archetype = attributes.first # there should only be one in the event that it's an array schema
|
85
|
+
elements.collect do |element|
|
86
|
+
archetype.serialize(element)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_array_of_schemas(elements)
|
91
|
+
(elements = stubbed_array) if !Render.live && (!elements || elements.empty?)
|
92
|
+
elements.collect do |element|
|
93
|
+
attributes.inject({}) do |attributes, attribute|
|
94
|
+
attributes.merge(attribute.to_hash(element)).values.first
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_hash(explicit_values = {})
|
100
|
+
explicit_values ||= {} # !Render.live check
|
101
|
+
attributes.inject({}) do |accum, attribute|
|
102
|
+
explicit_value = explicit_values[attribute.name]
|
103
|
+
hash = attribute.to_hash(explicit_value)
|
104
|
+
accum.merge(hash)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def stubbed_array
|
109
|
+
elements = []
|
110
|
+
rand(1..3).times { elements << nil }
|
111
|
+
elements
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/render.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Render allows one to define object Graphs with Schema/endpoint information.
|
2
|
+
# Once defined and constructed, a Graph can be built at once that will:
|
3
|
+
# - Query its endpoint to construct a hash for its Schema
|
4
|
+
# - Add nested Graphs by interpreting/sending data they need
|
5
|
+
|
6
|
+
require "render/version"
|
7
|
+
require "render/graph"
|
8
|
+
require "render/generator"
|
9
|
+
|
10
|
+
module Render
|
11
|
+
@live = true
|
12
|
+
@schemas = {}
|
13
|
+
@generators = []
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :live, :schemas, :generators
|
17
|
+
|
18
|
+
def load_schemas!(directory)
|
19
|
+
Dir.glob("#{directory}/**/*.json").each do |schema_file|
|
20
|
+
parsed_schema = parse_schema(File.read(schema_file))
|
21
|
+
schema_title = parsed_schema[:title].to_sym
|
22
|
+
# TODO Throw an error in the event of conflicts?
|
23
|
+
self.schemas[schema_title] = parsed_schema
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_schema(json)
|
28
|
+
JSON.parse(json).recursive_symbolize_keys!
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_type(type)
|
32
|
+
if type.is_a?(String)
|
33
|
+
return UUID if type == "uuid"
|
34
|
+
return Boolean if type == "boolean"
|
35
|
+
Object.const_get(type.capitalize) # TODO better type parsing
|
36
|
+
else
|
37
|
+
type
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/rakefile.rb
ADDED
data/readme.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Render
|
2
|
+
|
3
|
+
Create and test API requests simply with schemas.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Representation.load_schemas!("spec/schemas") # JSON schema directory
|
7
|
+
Representation::Graph.new(:film, { endpoint: "http://films.local/films" }).pull
|
8
|
+
# or stub out schema-specific data
|
9
|
+
Representation.live = false
|
10
|
+
Representation::Graph.new(:film).pull
|
11
|
+
```
|
12
|
+
|
13
|
+
*Use with caution* (Representation is under initial development) by updating your Gemfile:
|
14
|
+
|
15
|
+
gem "representation"
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Try out examples with `Representation.live = false`.
|
20
|
+
|
21
|
+
*Simple*
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
schema = Representation::Schema.new({
|
25
|
+
title: :film,
|
26
|
+
type: Object,
|
27
|
+
attributes: {
|
28
|
+
id: { type: UUID },
|
29
|
+
title: { type: String }
|
30
|
+
}
|
31
|
+
})
|
32
|
+
|
33
|
+
options = {
|
34
|
+
endpoint: "http://films.local/films/:id"
|
35
|
+
}
|
36
|
+
|
37
|
+
Representation::Graph.new(schema, options).pull({ id: "4cb6b490-d706-0130-2a93-7c6d628f9b06" })
|
38
|
+
```
|
39
|
+
|
40
|
+
*Nested*
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
film_schema = Representation::Schema.new({
|
44
|
+
title: :film,
|
45
|
+
type: Object,
|
46
|
+
attributes: {
|
47
|
+
id: { type: UUID },
|
48
|
+
title: { type: String }
|
49
|
+
}
|
50
|
+
})
|
51
|
+
|
52
|
+
films_schema = Representation::Schema.new({
|
53
|
+
title: :films,
|
54
|
+
type: Array,
|
55
|
+
elements: {
|
56
|
+
title: :film,
|
57
|
+
type: Object,
|
58
|
+
attributes: {
|
59
|
+
id: { type: UUID }
|
60
|
+
}
|
61
|
+
}
|
62
|
+
})
|
63
|
+
|
64
|
+
films_graph = Representation::Graph.new(films_schema, { endpoint: "http://films.local/films" })
|
65
|
+
film_graph = Representation::Graph.new(film_schema, { endpoint: "http://films.local/films/:id", relationships: { id: :id } })
|
66
|
+
films_graph.graphs << film_graph
|
67
|
+
films_graph.pull
|
68
|
+
```
|
69
|
+
*Autoload schemas*
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
Representation.load_schemas!("path/to/json/schemas")
|
73
|
+
Representation::Graph.new(:schema_title, { endpoint: "http://films.local/films" }).pull
|
74
|
+
```
|
75
|
+
|
76
|
+
*Variable interpolation*
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
options = { endpoint: "http://films.local/films/:id?:client_token", client_token: "token" }
|
80
|
+
graph = Representation::Graph.new(:schema_title, options)
|
81
|
+
graph.pull({ id: "an-id" })
|
82
|
+
```
|
83
|
+
|
84
|
+
## Contributing
|
85
|
+
|
86
|
+
1. Fork it
|
87
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
5. Create new Pull Request
|
data/render.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'render/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "render"
|
8
|
+
spec.version = Render::VERSION
|
9
|
+
spec.authors = ["Steve Weber"]
|
10
|
+
spec.email = ["steve@copyright1984.com"]
|
11
|
+
spec.description = %q{Simple management of API calls.}
|
12
|
+
spec.summary = %q{With a JSON schema, Render will manage requests, dependent request and build meaningful, extensible sample data for testing.}
|
13
|
+
spec.homepage = "https://github.com/stevenweber/render"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "uuid", "2.3.7"
|
25
|
+
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "debugger"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
spec.add_development_dependency "webmock"
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Render
|
2
|
+
describe Attribute do
|
3
|
+
context "generators" do
|
4
|
+
before(:each) do
|
5
|
+
@original_generators = Render.generators
|
6
|
+
@original_live = Render.live
|
7
|
+
Render.live = false
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
Render.generators = @original_generators
|
12
|
+
Render.live = @original_live
|
13
|
+
end
|
14
|
+
|
15
|
+
it "uses matching generator for #faux_value" do
|
16
|
+
name = "Canada Dry"
|
17
|
+
generator = Generator.new({ type: String, matcher: %r{.*name.*}, algorithm: proc { name } })
|
18
|
+
Render.generators << generator
|
19
|
+
|
20
|
+
Attribute.new({ name: { type: String } }).default_value.should == name
|
21
|
+
end
|
22
|
+
|
23
|
+
it "uses really bare-boned type if no generator is found" do
|
24
|
+
bare_boned_string = "A String"
|
25
|
+
Attribute.new({ foo: { type: String } }).default_value.should == bare_boned_string
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require "render/schema"
|
2
|
+
|
3
|
+
module Render
|
4
|
+
describe Schema do
|
5
|
+
before(:each) do
|
6
|
+
Render.stub({ live: false })
|
7
|
+
end
|
8
|
+
|
9
|
+
it "parses nested schemas" do
|
10
|
+
schema = {
|
11
|
+
title: "person",
|
12
|
+
type: Object,
|
13
|
+
attributes: {
|
14
|
+
contact: {
|
15
|
+
type: Object,
|
16
|
+
attributes: {
|
17
|
+
name: { type: String },
|
18
|
+
phone: { type: String }
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
contact_name = "Home"
|
25
|
+
contact_phone = "9675309"
|
26
|
+
data = {
|
27
|
+
extraneous_name: "Tommy Tutone",
|
28
|
+
contact: {
|
29
|
+
name: contact_name,
|
30
|
+
phone: contact_phone,
|
31
|
+
extraneous_details: "aka 'Jenny'"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
Schema.new(schema).pull(data).should == {
|
36
|
+
person: {
|
37
|
+
contact: {
|
38
|
+
name: contact_name,
|
39
|
+
phone: contact_phone
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
it "parses nested arrays" do
|
46
|
+
schema = {
|
47
|
+
title: "people",
|
48
|
+
type: Array,
|
49
|
+
elements: {
|
50
|
+
title: :person,
|
51
|
+
type: Object,
|
52
|
+
attributes: {
|
53
|
+
name: { type: String },
|
54
|
+
nicknames: {
|
55
|
+
title: "nicknames",
|
56
|
+
type: Array,
|
57
|
+
elements: {
|
58
|
+
title: :formalized_name,
|
59
|
+
type: Object,
|
60
|
+
attributes: {
|
61
|
+
name: { type: String },
|
62
|
+
age: { type: Integer }
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
zissou = {
|
71
|
+
name: "Steve Zissou",
|
72
|
+
nicknames: [
|
73
|
+
{ name: "Stevezies", age: 2 },
|
74
|
+
{ name: "Papa Steve", age: 1 }
|
75
|
+
]
|
76
|
+
}
|
77
|
+
ned = {
|
78
|
+
name: "Ned Plimpton",
|
79
|
+
nicknames: [
|
80
|
+
{ name: "Kinsley", age: 4 }
|
81
|
+
]
|
82
|
+
}
|
83
|
+
people = [zissou, ned]
|
84
|
+
|
85
|
+
Schema.new(schema).pull(people).should == {
|
86
|
+
people: [{
|
87
|
+
name: "Steve Zissou",
|
88
|
+
nicknames: [
|
89
|
+
{ name: "Stevezies", age: 2 },
|
90
|
+
{ name: "Papa Steve", age: 1 }
|
91
|
+
]
|
92
|
+
},
|
93
|
+
{
|
94
|
+
name: "Ned Plimpton",
|
95
|
+
nicknames: [
|
96
|
+
{ name: "Kinsley", age: 4 }
|
97
|
+
]
|
98
|
+
}
|
99
|
+
]
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "render/schema"
|
2
|
+
|
3
|
+
module Render
|
4
|
+
describe Schema do
|
5
|
+
before(:each) do
|
6
|
+
Render.stub({ live: false })
|
7
|
+
end
|
8
|
+
|
9
|
+
it "parses hash data" do
|
10
|
+
schema = Schema.new({
|
11
|
+
title: "television",
|
12
|
+
type: Object,
|
13
|
+
attributes: {
|
14
|
+
brand: { type: String }
|
15
|
+
}
|
16
|
+
})
|
17
|
+
|
18
|
+
brand_name = "Sony"
|
19
|
+
response = { brand: brand_name }
|
20
|
+
|
21
|
+
schema.pull(response).should == {
|
22
|
+
television: { brand: brand_name }
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
it "parses simple arrays" do
|
27
|
+
schema = Schema.new({
|
28
|
+
title: "televisions",
|
29
|
+
type: Array,
|
30
|
+
elements: {
|
31
|
+
type: UUID
|
32
|
+
}
|
33
|
+
})
|
34
|
+
|
35
|
+
television_ids = rand(10).times.collect { UUID.generate }
|
36
|
+
|
37
|
+
schema.pull(television_ids).should == {
|
38
|
+
televisions: television_ids
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
it "parses arrays of objects" do
|
43
|
+
schema = Schema.new({
|
44
|
+
title: :televisions,
|
45
|
+
type: Array,
|
46
|
+
elements: {
|
47
|
+
title: :television,
|
48
|
+
type: Object,
|
49
|
+
attributes: {
|
50
|
+
brand: { type: String }
|
51
|
+
}
|
52
|
+
}
|
53
|
+
})
|
54
|
+
|
55
|
+
brand_1, brand_2 = *%w(Sony Samsung)
|
56
|
+
response = [{ brand: brand_1 }, { brand: brand_2 }]
|
57
|
+
|
58
|
+
schema.pull(response).should == {
|
59
|
+
televisions: [{ brand: brand_1 }, { brand: brand_2 }]
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
it "parses nested object data" do
|
64
|
+
schema = Schema.new({
|
65
|
+
title: :television,
|
66
|
+
type: Object,
|
67
|
+
attributes: {
|
68
|
+
brand: {
|
69
|
+
title: :brand,
|
70
|
+
type: Object,
|
71
|
+
attributes: {
|
72
|
+
name: { type: String }
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
})
|
77
|
+
|
78
|
+
brand_name = "Sony"
|
79
|
+
response = { brand: { name: brand_name } }
|
80
|
+
|
81
|
+
schema.pull(response).should == {
|
82
|
+
television: { brand: { name: brand_name } }
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "render"
|
2
|
+
|
3
|
+
describe Render do
|
4
|
+
before(:all) do
|
5
|
+
Render.load_schemas!(Helpers::SCHEMA_DIRECTORY)
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:all) do
|
9
|
+
Render.schemas = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "request" do
|
13
|
+
before(:each) do
|
14
|
+
@films_endpoint = "http://films.local/films"
|
15
|
+
@film_endpoint = "http://films.local/films/:id"
|
16
|
+
@aquatic_id = UUID.generate
|
17
|
+
@darjeeling_id = UUID.generate
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns structured data for nested queries" do
|
21
|
+
stub_request(:get, @films_endpoint).to_return({ body: [{ id: @aquatic_id }, { id: @darjeeling_id }].to_json })
|
22
|
+
|
23
|
+
aquatic_name = "The Life Aquatic with Steve Zissou"
|
24
|
+
aquatic_uri = @film_endpoint.gsub(":id", @aquatic_id)
|
25
|
+
stub_request(:get, aquatic_uri).to_return({ body: { name: aquatic_name }.to_json })
|
26
|
+
|
27
|
+
darjeeling_name = "The Darjeeling Limited"
|
28
|
+
darjeeling_uri = @film_endpoint.gsub(":id", @darjeeling_id)
|
29
|
+
stub_request(:get, darjeeling_uri).to_return({ body: { name: darjeeling_name }.to_json })
|
30
|
+
|
31
|
+
options = {
|
32
|
+
graphs: [Render::Graph.new(:film, { endpoint: @film_endpoint, relationships: { id: :id }})],
|
33
|
+
endpoint: @films_endpoint
|
34
|
+
}
|
35
|
+
graph = Render::Graph.new(:films, options)
|
36
|
+
graph.pull.should == {
|
37
|
+
films: [
|
38
|
+
{ name: aquatic_name, year: nil },
|
39
|
+
{ name: darjeeling_name, year: nil }
|
40
|
+
]
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
it "makes subsequent calls from archetype array data" do
|
45
|
+
pending "Simple arrays need to be able to make multiple calls"
|
46
|
+
|
47
|
+
stub_request(:get, @films_endpoint).to_return({ body: [@aquatic_id, @darjeeling_id].to_json })
|
48
|
+
|
49
|
+
aquatic = @film_endpoint.gsub("id", @aquatic_id)
|
50
|
+
stub_request(:get, aquatic).to_return({ body: { name: @aquatic_name }.to_json })
|
51
|
+
|
52
|
+
darjeeling = @film_endpoint.gsub("id", @darjeeling_id)
|
53
|
+
stub_request(:get, darjeeling).to_return({ body: { name: @darjeeling_name }.to_json })
|
54
|
+
|
55
|
+
films = Representation::Schema.new({
|
56
|
+
title: :films,
|
57
|
+
type: Array,
|
58
|
+
elements: {
|
59
|
+
type: UUID
|
60
|
+
}
|
61
|
+
})
|
62
|
+
|
63
|
+
film = Representation::Schema.new({
|
64
|
+
title: :film,
|
65
|
+
type: Object,
|
66
|
+
attributes: {
|
67
|
+
title: { type: String }
|
68
|
+
}
|
69
|
+
})
|
70
|
+
|
71
|
+
films = Representation::Graph.new(films, { endpoint: @films_endpoint })
|
72
|
+
films.graphs << Representation::Graph.new(film, { endpoint: @film_endpoint, relationships: { films: :id } })
|
73
|
+
films.pull.should == {}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "render"
|
2
|
+
|
3
|
+
describe Render do
|
4
|
+
before(:all) do
|
5
|
+
Render.load_schemas!(Helpers::SCHEMA_DIRECTORY)
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:all) do
|
9
|
+
Render.schemas = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
@film_id = UUID.generate
|
14
|
+
@film_name = "The Life Aqautic with Steve Zissou"
|
15
|
+
|
16
|
+
# Typically in environmental config
|
17
|
+
@secret_code = "3892n-2-n2iu1bf1cSdas0dDSAF"
|
18
|
+
@films_endpoint = "http://films.local/films?:secret_code"
|
19
|
+
@film_endpoint = "http://films.local/films/:id?:secret_code"
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "requests" do
|
23
|
+
it "returns structured data" do
|
24
|
+
aquatic_uri = @films_endpoint.gsub(":secret_code", "secret_code=#{@secret_code}")
|
25
|
+
stub_request(:get, aquatic_uri).to_return({ body: [{ id: @film_id }].to_json })
|
26
|
+
|
27
|
+
graph = Render::Graph.new(:films, { endpoint: @films_endpoint, secret_code: @secret_code })
|
28
|
+
graph.pull.should == { films: [{ id: @film_id }] }
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns structured data for specific resources" do
|
32
|
+
id = UUID.generate
|
33
|
+
aquatic_uri = @film_endpoint.gsub(":id", id).gsub(":secret_code", "secret_code=#{@secret_code}")
|
34
|
+
stub_request(:get, aquatic_uri).to_return({ body: { name: @film_name }.to_json })
|
35
|
+
|
36
|
+
graph = Render::Graph.new(:film, { id: id, endpoint: @film_endpoint, secret_code: @secret_code })
|
37
|
+
graph.pull.should == { film: { name: @film_name, year: nil } }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "stubbed responses" do
|
42
|
+
before(:each) do
|
43
|
+
Render.stub({ live: false })
|
44
|
+
end
|
45
|
+
|
46
|
+
it "use meaningful values" do
|
47
|
+
response = Render::Graph.new(:film).pull({ name: @film_name })
|
48
|
+
|
49
|
+
stub_request(:post, "http://films.local/create").to_return({ body: response.to_json })
|
50
|
+
response = post_film(:anything)["film"]
|
51
|
+
|
52
|
+
response["name"].should be_a(String)
|
53
|
+
response["year"].should be_a(Integer)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "allows users to specify specific values" do
|
57
|
+
response = Render::Graph.new(:film).pull({ name: @film_name })
|
58
|
+
|
59
|
+
data = { name: @film_name }.to_json
|
60
|
+
stub_request(:post, "http://films.local/create").with({ body: data }).to_return({ body: response.to_json })
|
61
|
+
response = post_film(data)["film"]
|
62
|
+
|
63
|
+
response["name"].should == @film_name
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def post_film(data)
|
68
|
+
response = Net::HTTP.start("films.local", 80) do |http|
|
69
|
+
request = Net::HTTP::Post.new("/create")
|
70
|
+
request.body = data
|
71
|
+
http.request(request)
|
72
|
+
end
|
73
|
+
JSON.parse(response.body)
|
74
|
+
end
|
75
|
+
end
|