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/spec/support.rb ADDED
@@ -0,0 +1,7 @@
1
+ require File.expand_path("../../initialize", __FILE__)
2
+ require "webmock/rspec"
3
+ require "support/helpers"
4
+
5
+ RSpec.configure do |config|
6
+ #
7
+ end
@@ -0,0 +1,7 @@
1
+ require "extensions/boolean"
2
+
3
+ describe Boolean do
4
+ it "exists" do
5
+ expect { Boolean }.to_not raise_error
6
+ end
7
+ end
@@ -0,0 +1,128 @@
1
+ module Render
2
+ describe Attribute do
3
+ describe "#initialize" do
4
+ describe "#name" do
5
+ it "is set from options key" do
6
+ options = { id: { type: UUID } }
7
+ Attribute.new(options).name.should == :id
8
+ end
9
+ end
10
+
11
+ describe "#type" do
12
+ it "is set from options" do
13
+ type = Integer
14
+ attribute = Attribute.new({ type: type })
15
+ attribute.type.should == type
16
+ end
17
+
18
+ it "is set from name hash" do
19
+ type = String
20
+ attribute = Attribute.new({ id: { type: UUID } })
21
+ attribute.type.should == UUID
22
+ end
23
+
24
+ it "determines type from string" do
25
+ Attribute.new({ type: "string" }).type.should == String
26
+ end
27
+ end
28
+
29
+ describe "#schema" do
30
+ it "is set to nil if its a regular attribute" do
31
+ Attribute.new({ id: { type: UUID } }).schema.should == nil
32
+ end
33
+
34
+ it "is initiazed from options" do
35
+ options = {
36
+ film: {
37
+ type: Object,
38
+ attributes: {
39
+ year: { type: Integer }
40
+ }
41
+ }
42
+ }
43
+
44
+ schema = Attribute.new(options).schema
45
+ schema.title.should == :film
46
+ schema.type.should == Object
47
+ attributes = schema.attributes
48
+ attributes.size.should == 1
49
+ attribute = attributes.first
50
+ attribute.name.should == :year
51
+ attribute.type.should == Integer
52
+ end
53
+ end
54
+
55
+ context "enums" do
56
+ it "sets enum values" do
57
+ enum_values = ["foo", "bar", "baz"]
58
+ attribute = Attribute.new({ type: String, enum: enum_values })
59
+ attribute.enums.should == enum_values
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#to_hash" do
65
+ it "converts attributes to hashes" do
66
+ attributes = { foo: { type: String } }
67
+ attribute = Attribute.new(attributes)
68
+ attribute.to_hash.should == { foo: nil }
69
+ end
70
+
71
+ it "converts attributes to hashes with values" do
72
+ attributes = { foo: { type: String } }
73
+ attribute = Attribute.new(attributes)
74
+ attribute.to_hash("bar").should == { foo: "bar" }
75
+ end
76
+
77
+ it "converts schema values to hashes" do
78
+ schema_name = "foo"
79
+ attributes = {
80
+ schema_name => {
81
+ type: Object,
82
+ attributes: {
83
+ attribute: { type: String }
84
+ }
85
+ }
86
+ }
87
+
88
+ value = "baz"
89
+ data = { attribute: value }
90
+
91
+ attribute = Attribute.new(attributes)
92
+ attribute.to_hash(data).should == { foo: { :attribute => value } }
93
+ end
94
+ end
95
+
96
+ describe "#value" do
97
+ context "offline mode" do
98
+ before(:all) do
99
+ @original_live = Render.live
100
+ Render.live = false
101
+ end
102
+
103
+ after(:all) do
104
+ Render.live = @original_live
105
+ end
106
+
107
+ it "generate value based on type" do
108
+ supported_classes = [
109
+ String,
110
+ Integer
111
+ ]
112
+
113
+ supported_classes.each do |klass|
114
+ Attribute.new({ type: klass }).default_value.should be_a(klass)
115
+ end
116
+ UUID.validate(Attribute.new({ type: UUID }).default_value).should be_true
117
+ end
118
+
119
+ it "generates value from enum" do
120
+ enums = ["horror", "comedy", "drama"]
121
+ attribute = Attribute.new({ genre: { enum: enums, type: String } })
122
+ enums.should include(attribute.default_value)
123
+ end
124
+ end
125
+ end
126
+
127
+ end
128
+ end
@@ -0,0 +1,39 @@
1
+ require "render/generator"
2
+
3
+ module Render
4
+ describe Generator do
5
+ it "exists" do
6
+ expect { Generator }.to_not raise_error
7
+ end
8
+
9
+ describe "attributes" do
10
+ before(:each) do
11
+ @mandatory_options = { algorithm: proc {} }
12
+ end
13
+
14
+ it "is a type-specific generator for flexibility" do
15
+ Generator.new(@mandatory_options.merge({ type: String })).type.should == String
16
+ end
17
+
18
+ it "has a matcher to only be used on specific attributes" do
19
+ matcher = %r{.*name.*}
20
+ Generator.new(@mandatory_options.merge({ matcher: matcher })).matcher.should == matcher
21
+ end
22
+
23
+ describe "#algorith" do
24
+ it "has an algorithm that generates a value to be used" do
25
+ algorithm = lambda { "The Darjeeling limited" }
26
+ Generator.new({ algorithm: algorithm }).algorithm.should == algorithm
27
+ end
28
+
29
+ it "raises an error if algorithm does not respond to call" do
30
+ expect {
31
+ Generator.new({ algorithm: "want this to be the fake value" })
32
+ }.to raise_error(Errors::Generator::MalformedAlgorithm)
33
+ end
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,205 @@
1
+ # encoding: utf-8
2
+ require "render/graph"
3
+ require "uuid"
4
+
5
+ module Render
6
+ describe Graph do
7
+ before(:each) do
8
+ @schema = double(:schema)
9
+ end
10
+
11
+ describe ".initialize" do
12
+ it "has defaults" do
13
+ graph = Graph.new(@schema)
14
+ graph.raw_endpoint.should == ""
15
+ graph.relationships.should == {}
16
+ graph.graphs.should == []
17
+ graph.parental_params.should == {}
18
+ graph.config.should == {}
19
+ end
20
+
21
+ describe "schema" do
22
+ it "sets argument" do
23
+ graph = Graph.new(@schema)
24
+ graph.schema.should == @schema
25
+ end
26
+
27
+ it "creates new schema from symbol (for loaded schema lookup)" do
28
+ Schema.should_receive(:new).with(:film).and_return(:new_schema)
29
+ Graph.new(:film).schema.should == :new_schema
30
+ end
31
+ end
32
+
33
+ it "sets attributes" do
34
+ relationships = { director_id: :id }
35
+ graphs = [double(:graph)]
36
+ graph = Graph.new(@schema, {
37
+ relationships: relationships,
38
+ graphs: graphs,
39
+ })
40
+
41
+ graph.relationships.should == relationships
42
+ graph.graphs.should == graphs
43
+ end
44
+
45
+ it "treats non-used attributes as config" do
46
+ relationships = { some: :relationship }
47
+ graphs = [double(:some_graph)]
48
+ client_id = UUID.generate
49
+ graph = Graph.new(@schema, {
50
+ relationships: relationships,
51
+ graphs: graphs,
52
+ client_id: client_id
53
+ })
54
+
55
+ graph.config.should == { client_id: client_id }
56
+ end
57
+
58
+ describe "#raw_endpoint" do
59
+ it "is set with endpoint" do
60
+ endpoint = "http://endpoint.local"
61
+ graph = Graph.new(@schema, { endpoint: endpoint })
62
+ graph.raw_endpoint.should == endpoint
63
+ end
64
+ end
65
+
66
+ it "initializes params from endpoint" do
67
+ endpoint = "http://endpoint.local/:id?client_id=:client_id"
68
+ graph = Graph.new(@schema, { endpoint: endpoint })
69
+ graph.params.should == { id: nil, client_id: nil }
70
+ end
71
+ end
72
+
73
+ describe ".endpoint" do
74
+ it "returns #raw_endpoint" do
75
+ simple_endpoint = "http://endpoint.local"
76
+ graph = Graph.new(@schema, { endpoint: simple_endpoint })
77
+ graph.endpoint.should == simple_endpoint
78
+ end
79
+
80
+ it "interpolates parental_params" do
81
+ director_id = UUID.generate
82
+ endpoint = "http://endpoint.local/directors/:id"
83
+ interpolated_endpoint = "http://endpoint.local/directors/#{director_id}"
84
+
85
+ relationships = { director_id: :id }
86
+ graph = Graph.new(@schema, { endpoint: endpoint, relationships: relationships })
87
+ graph.parental_params[:id] = director_id
88
+
89
+ graph.endpoint.should == interpolated_endpoint
90
+ end
91
+
92
+ it "interpolates config attributes" do
93
+ client_id = UUID.generate
94
+ endpoint = "http://endpoint.local/?:client_id"
95
+ interpolated_endpoint = "http://endpoint.local/?client_id=#{client_id}"
96
+
97
+ graph = Graph.new(@schema, { endpoint: endpoint, client_id: client_id })
98
+ graph.endpoint.should == interpolated_endpoint
99
+ end
100
+
101
+ it "interpolates multiple path and query values" do
102
+ the_shinning = UUID.generate
103
+ kubrick = UUID.generate
104
+ client_id = UUID.generate
105
+ client_secret = UUID.generate
106
+
107
+ endpoint = "http://endpoint.local/directors/:id/films/:film_id?:client_id&:client_secret"
108
+ interpolated_endpoint = "http://endpoint.local/directors/#{kubrick}/films/#{the_shinning}?client_id=#{client_id}&client_secret=#{client_secret}"
109
+
110
+ graph = Graph.new(@schema, { endpoint: endpoint, client_id: client_id, client_secret: client_secret })
111
+ graph.parental_params = { id: kubrick, film_id: the_shinning }
112
+
113
+ graph.endpoint.should == interpolated_endpoint
114
+ end
115
+
116
+ it "raises an error if no value can be found" do
117
+ endpoint = "http://endpoint.com/?:undefined_key"
118
+ graph = Graph.new(@schema, { endpoint: endpoint })
119
+
120
+ expect {
121
+ graph.endpoint
122
+ }.to raise_error(Errors::Graph::EndpointKeyNotFound)
123
+ end
124
+ end
125
+
126
+ describe ".pull" do
127
+ it "returns its schema's data" do
128
+ pull = { film: { id: UUID.generate } }
129
+ @schema.stub({ pull: pull })
130
+
131
+ graph = Graph.new(@schema)
132
+ graph.pull.should == pull
133
+ end
134
+
135
+ it "sends interpolated endpoint to its schema" do
136
+ endpoint = "http://endpoint.local/?:client_id"
137
+ client_id = UUID.generate
138
+ graph = Graph.new(@schema, { endpoint: endpoint, client_id: client_id })
139
+
140
+ @schema.should_receive(:pull).with({ endpoint: graph.endpoint }).and_return(@pull)
141
+ graph.pull.should == @pull
142
+ end
143
+
144
+ context "with nested graphs" do
145
+ before(:each) do
146
+ Render.stub({ live: false })
147
+
148
+ director_schema = {
149
+ title: "director",
150
+ type: Object,
151
+ attributes: { id: { type: UUID } }
152
+ }
153
+ @director_schema = Schema.new(director_schema)
154
+
155
+ film_schema = {
156
+ title: "film",
157
+ type: Object,
158
+ attributes: { director_id: { type: UUID } }
159
+ }
160
+ @film_schema = Schema.new(film_schema)
161
+ end
162
+
163
+ it "merges nested graphs" do
164
+ pulled_data = { a: "attribute" }
165
+ @director_schema.stub({ pull: pulled_data })
166
+
167
+ director = Graph.new(@director_schema)
168
+ film = Graph.new(@film_schema, { graphs: [director]})
169
+
170
+ film_graph = film.pull[@film_schema.title.to_sym]
171
+ film_graph.should include(pulled_data)
172
+ end
173
+
174
+ it "uses parent data to calculate endpoint" do
175
+ director_id = UUID.generate
176
+ film = Graph.new(@film_schema)
177
+ film.schema.stub({ pull: { film: { director_id: director_id } } })
178
+
179
+ endpoint = "http://endpoint.local/directors/:id"
180
+ interpolated_endpoint = "http://endpoint.local/directors/#{director_id}"
181
+ relationships = { director_id: :id }
182
+ director = Graph.new(@director_schema, { endpoint: endpoint, relationships: relationships })
183
+
184
+ film.graphs << director
185
+ director.schema.should_receive(:pull).with do |args|
186
+ args[:endpoint].should == interpolated_endpoint
187
+ end.and_return({})
188
+
189
+ film.pull
190
+ end
191
+
192
+ context "offline" do
193
+ it "uses parent data for childrens attributes" do
194
+ relationships = { director_id: :id }
195
+ director = Graph.new(@director_schema, { relationships: relationships })
196
+ film = Graph.new(@film_schema, { graphs: [director]})
197
+
198
+ film_graph = film.pull[@film_schema.title.to_sym]
199
+ film_graph[@director_schema.title.to_sym][:id].should == film_graph[:director_id]
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,250 @@
1
+ require "render"
2
+
3
+ module Render
4
+ describe Schema do
5
+ before(:each) do
6
+ @film_schema = {
7
+ title: "film",
8
+ type: Object,
9
+ attributes: {
10
+ name: { type: String },
11
+ genre: { type: String }
12
+ }
13
+ }
14
+
15
+ @films_schema = {
16
+ title: "films",
17
+ type: Array,
18
+ elements: {
19
+ title: :film,
20
+ attributes: {
21
+ name: { type: String },
22
+ genre: { type: String }
23
+ }
24
+ }
25
+ }
26
+
27
+ @director_schema = {
28
+ title: "director",
29
+ type: Object,
30
+ attributes: {
31
+ films: {
32
+ type: Array,
33
+ elements: {
34
+ type: Object,
35
+ attributes: {
36
+ name: { type: String },
37
+ year: { type: Integer }
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ end
44
+
45
+ describe "#initialize" do
46
+ describe "#schema" do
47
+ before(:each) do
48
+ @schema = { attributes: {} }
49
+ end
50
+
51
+ it "is set to hash argument" do
52
+ Schema.new(@schema).schema.should == @schema
53
+ end
54
+
55
+ it "is set to preloaded schema" do
56
+ Render.stub({ schemas: { film: @schema } })
57
+ Schema.new(:film).schema.should == @schema
58
+ end
59
+
60
+ it "raises an error if preloaded schema cannot be found" do
61
+ expect {
62
+ Schema.new(:unloaded_schema)
63
+ }.to raise_error(Render::Errors::Schema::NotFound)
64
+ end
65
+ end
66
+
67
+ it "sets title from schema" do
68
+ title = "films"
69
+ schema = { title: title, attributes: {} }
70
+ Schema.new(schema).title.should == title
71
+ end
72
+
73
+ describe "#type" do
74
+ it "is set from schema" do
75
+ type = [Array, Object].sample
76
+ schema = { type: type, attributes: {} }
77
+ Schema.new(schema).type.should == type
78
+ end
79
+
80
+ it "is parsed from string" do
81
+ schema = { type: "string", attributes: {} }
82
+ Schema.new(schema).type.should == String
83
+ end
84
+ end
85
+
86
+ describe "#attributes" do
87
+ it "is set with simple Attributes" do
88
+ simple_schema = {
89
+ attributes: {
90
+ name: { type: String },
91
+ genre: { type: String }
92
+ }
93
+ }
94
+
95
+ schema = Schema.new(simple_schema)
96
+ schema.attributes.size.should == 2
97
+ schema.attributes.any? { |a| a.name == :name && a.type == String }.should == true
98
+ schema.attributes.any? { |a| a.name == :genre && a.type == String }.should == true
99
+ end
100
+
101
+ it "is set with array archetypes" do
102
+ archetype_schema = {
103
+ elements: {
104
+ type: String
105
+ }
106
+ }
107
+
108
+ attributes = Schema.new(archetype_schema).attributes
109
+ attributes.size.should == 1
110
+ attributes.first.type.should == String
111
+ end
112
+
113
+ it "is set with schema-Attributes" do
114
+ nested_schema = {
115
+ attributes: {
116
+ film: {
117
+ type: Object,
118
+ attributes: {
119
+ name: { type: String }
120
+ }
121
+ }
122
+ }
123
+ }
124
+
125
+ schema = Schema.new(nested_schema)
126
+ schema.attributes.size.should == 1
127
+ schema.attributes.first.schema.should be
128
+ end
129
+
130
+ it "is set with array-Attributes" do
131
+ array_schema = {
132
+ elements: {
133
+ film: {
134
+ type: Object,
135
+ attributes: {
136
+ name: { type: String }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ schema = Schema.new(array_schema)
142
+ schema.attributes.size.should == 1
143
+ schema.attributes.first.schema.should be
144
+ end
145
+ end
146
+ end
147
+
148
+ describe "#pull" do
149
+ context "live" do
150
+ it "returns schema with values from endpoint" do
151
+ endpoint = "http://endpoint.local"
152
+ name = "The Shining"
153
+ genre = "Horror"
154
+ response_body = { film: { name: name, genre: genre } }
155
+ response = { status: 200, body: response_body.to_json }
156
+ stub_request(:get, endpoint).to_return(response)
157
+
158
+ film = Schema.new(@film_schema)
159
+ film.pull({ endpoint: endpoint }).should == response_body
160
+ end
161
+
162
+ it "raises error if response is not 2xx" do
163
+ endpoint = "http://endpoint.local"
164
+ response = { status: 403, body: "OMGWTFBBQ" }
165
+ stub_request(:get, endpoint).to_return(response)
166
+
167
+ expect {
168
+ film = Schema.new(@film_schema)
169
+ film.pull({ endpoint: endpoint })
170
+ }.to raise_error(Errors::Schema::RequestError)
171
+ end
172
+
173
+ it "returns meaningful error when response contains invalid JSON" do
174
+ endpoint = "http://enpoint.local"
175
+ stub_request(:get, endpoint).to_return({ body: "Server Error: 500" })
176
+
177
+ expect {
178
+ Schema.new(@film_schema).pull({ endpoint: endpoint })
179
+ }.to raise_error(Errors::Schema::InvalidResponse)
180
+ end
181
+ end
182
+
183
+ context "faked" do
184
+ before(:each) do
185
+ Render.stub({ live: false })
186
+ end
187
+
188
+ it "returns schema with fake values" do
189
+ film = Schema.new(@film_schema)
190
+ film = film.pull[:film]
191
+ film[:name].should be_a(String)
192
+ film[:genre].should be_a(String)
193
+ end
194
+ end
195
+
196
+ it "handles responses that do not use schema title as root key" do
197
+ endpoint = "http://endpoint.local"
198
+ response_body = { name: "the name", genre: "the genre" }
199
+ response = { status: 200, body: response_body.to_json }
200
+ stub_request(:get, endpoint).to_return(response)
201
+
202
+ film = Schema.new(@film_schema)
203
+ film.pull({ endpoint: endpoint }).should == { film: response_body }
204
+ end
205
+
206
+ it "handles Array responses" do
207
+ endpoint = "http://endpoint.local"
208
+ first_name = "The Shining"
209
+ second_name = "Eyes Wide Shut"
210
+ genre = "Horror"
211
+
212
+ response_body = [
213
+ { name: first_name, genre: genre },
214
+ { name: second_name, genre: genre }
215
+ ]
216
+ response = { status: 200, body: response_body.to_json }
217
+ stub_request(:get, endpoint).to_return(response)
218
+
219
+ film = Schema.new(@films_schema)
220
+ film.pull({ endpoint: endpoint }).should == { films: response_body }
221
+ end
222
+ end
223
+
224
+ describe "#serialize" do
225
+ it "returns parsed array elements" do
226
+ Render.stub({ live: false })
227
+ director = Schema.new(@director_schema)
228
+
229
+ kubrick = "Stanley Kubrick"
230
+ first_film = "Flying Padre: An RKO-Pathe Screenliner"
231
+ year = 1951
232
+ data = {
233
+ films: [{
234
+ name: first_film,
235
+ notInSchema: "experimental",
236
+ year: year
237
+ }]
238
+ }
239
+
240
+ director.serialize(data).should == {
241
+ films: [{
242
+ name: first_film,
243
+ year: year
244
+ }]
245
+ }
246
+ end
247
+
248
+ end
249
+ end
250
+ end