render 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/.ruby-version +1 -1
  2. data/lib/json/draft-04/hyper-schema.json +168 -0
  3. data/lib/json/draft-04/schema.json +150 -0
  4. data/lib/render.rb +2 -0
  5. data/lib/render/attributes/array_attribute.rb +20 -14
  6. data/lib/render/attributes/attribute.rb +23 -7
  7. data/lib/render/attributes/hash_attribute.rb +6 -2
  8. data/lib/render/definition.rb +13 -7
  9. data/lib/render/errors.rb +33 -6
  10. data/lib/render/generator.rb +67 -8
  11. data/lib/render/graph.rb +39 -64
  12. data/lib/render/json_schema.rb +12 -0
  13. data/lib/render/schema.rb +92 -31
  14. data/lib/render/type.rb +51 -9
  15. data/lib/render/version.rb +1 -1
  16. data/readme.md +66 -9
  17. data/render.gemspec +4 -3
  18. data/spec/functional/render/attribute_spec.rb +66 -8
  19. data/spec/functional/render/nested_schemas_spec.rb +18 -26
  20. data/spec/functional/render/schema_spec.rb +28 -0
  21. data/spec/integration/render/graph_spec.rb +3 -3
  22. data/spec/integration/render/nested_graph_spec.rb +12 -14
  23. data/spec/integration/render/schema_spec.rb +4 -4
  24. data/spec/support/schemas/film.json +3 -3
  25. data/spec/support/schemas/films.json +3 -3
  26. data/spec/unit/render/attributes/array_attribute_spec.rb +34 -9
  27. data/spec/unit/render/attributes/attribute_spec.rb +13 -0
  28. data/spec/unit/render/attributes/hash_attribute_spec.rb +17 -7
  29. data/spec/unit/render/definition_spec.rb +7 -25
  30. data/spec/unit/render/generator_spec.rb +102 -2
  31. data/spec/unit/render/graph_spec.rb +18 -19
  32. data/spec/unit/render/schema_spec.rb +185 -54
  33. data/spec/unit/render/type_spec.rb +88 -13
  34. metadata +66 -29
  35. checksums.yaml +0 -15
@@ -18,16 +18,39 @@ module Render
18
18
  end
19
19
 
20
20
  it "is set to preloaded definition" do
21
- definition_title = :preloaded_schema
22
- definition = { title: definition_title, properties: { title: { type: String } } }
21
+ definition_id = :preloaded_schema
22
+ definition = { id: definition_id, properties: { title: { type: String } } }
23
23
  Definition.load!(definition)
24
- Schema.new(definition_title).definition.should == definition
24
+
25
+ Schema.new(definition_id).definition.should == definition
25
26
  end
26
27
 
27
28
  it "raises an error if definition is not found and argument is not a schema" do
28
29
  expect {
29
30
  Schema.new(:does_not_exists)
30
- }.to raise_error(Errors::DefinitionNotFound)
31
+ }.to raise_error(Errors::Definition::NotFound)
32
+ end
33
+
34
+ # This is probably not the best form
35
+ describe "non-container definitions" do
36
+ it "creates Object container for subschemas" do
37
+ non_container_definition = {
38
+ students: { type: Array, items: { type: String } },
39
+ teacher: { type: Object, properties: { name: { type: String } } }
40
+ }
41
+
42
+ converted_definition = {
43
+ type: Object,
44
+ properties: {
45
+ students: { type: Array, items: { type: String } },
46
+ teacher: { type: Object, properties: { name: { type: String } } }
47
+ }
48
+ }
49
+
50
+ non_container_schema = Schema.new(non_container_definition)
51
+ converted_schema = Schema.new(converted_definition)
52
+ non_container_schema.definition.should == converted_schema.definition
53
+ end
31
54
  end
32
55
  end
33
56
 
@@ -44,16 +67,16 @@ module Render
44
67
 
45
68
  describe "#array_attribute" do
46
69
  it "is set for array schemas" do
47
- archetype_schema = {
70
+ simple_schema = {
48
71
  type: Array,
49
72
  items: {
50
73
  type: String
51
74
  }
52
75
  }
53
76
 
54
- attribute = Schema.new(archetype_schema).array_attribute
55
- attribute.type.should == String
56
- attribute.archetype.should == true
77
+ attribute = Schema.new(simple_schema).array_attribute
78
+ attribute.types.should == [String]
79
+ attribute.simple.should == true
57
80
  end
58
81
  end
59
82
 
@@ -69,8 +92,158 @@ module Render
69
92
 
70
93
  schema = Schema.new(simple_schema)
71
94
  schema.hash_attributes.size.should == 2
72
- schema.hash_attributes.any? { |a| a.name == :name && a.type == String }.should == true
73
- schema.hash_attributes.any? { |a| a.name == :genre && a.type == String }.should == true
95
+ schema.hash_attributes.any? { |a| a.name == :name && a.types == [String] }.should == true
96
+ schema.hash_attributes.any? { |a| a.name == :genre && a.types == [String] }.should == true
97
+ end
98
+ end
99
+
100
+ describe "$ref" do
101
+ before(:each) do
102
+ @original_instances = Definition.instances.dup
103
+ end
104
+
105
+ after(:each) do
106
+ Definition.instances = @original_instances
107
+ end
108
+
109
+ it "creates subschemas for absolute references" do
110
+ topping_definition = {
111
+ id: "http://pizzas.local/schema#topping",
112
+ type: Object, properties: { name: { type: String } }
113
+ }
114
+ Definition.load!(topping_definition)
115
+
116
+ pizza_definition = {
117
+ type: Object,
118
+ properties: {
119
+ toppings: { type: Array, items: { :$ref => "http://pizzas.local/schema#topping" } }
120
+ }
121
+ }
122
+ pizza_schema = Schema.new(pizza_definition)
123
+
124
+ pizza_schema.definition.should == {
125
+ type: Object,
126
+ properties: {
127
+ toppings: {
128
+ type: Array,
129
+ items: {
130
+ id: "http://pizzas.local/schema#topping",
131
+ type: Object,
132
+ properties: { name: { type: String } }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ end
138
+
139
+ it "creates subschemas for relative references from root" do
140
+ definition = {
141
+ definitions: {
142
+ address: {
143
+ type: Object,
144
+ properties: { number: { type: Integer } }
145
+ }
146
+ },
147
+ type: Object,
148
+ properties: {
149
+ address: { :$ref => "#/definitions/address" }
150
+ }
151
+ }
152
+ schema = Schema.new(definition)
153
+
154
+ schema.definition[:properties].should == {
155
+ address: {
156
+ type: Object,
157
+ properties: { number: { type: Integer } }
158
+ }
159
+ }
160
+ end
161
+
162
+ it "creates nested subschemas for relative references from root" do
163
+ definition = {
164
+ definitions: {
165
+ address: {
166
+ type: Object,
167
+ properties: { number: { type: Integer } }
168
+ }
169
+ },
170
+ type: Object,
171
+ properties: {
172
+ primary_location: {
173
+ type: Object,
174
+ properties: {
175
+ address: { :$ref => "#/definitions/address" }
176
+ }
177
+ }
178
+ }
179
+ }
180
+ schema = Schema.new(definition)
181
+
182
+ schema.definition[:properties].should == {
183
+ primary_location: {
184
+ type: Object,
185
+ properties: {
186
+ address: {
187
+ type: Object,
188
+ properties: {
189
+ number: { type: Integer }
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ end
196
+
197
+ it "interpolates closest relative definition" do
198
+ definition = {
199
+ definitions: { year: { type: :number } },
200
+ type: Object,
201
+ properties: {
202
+ book: {
203
+ definitions: { year: { type: Integer } },
204
+ type: Object,
205
+ properties: {
206
+ year: { :$ref => "definitions/year" }
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ parsed_definition = Schema.new(definition).definition
213
+ year_schema = parsed_definition.fetch(:properties).fetch(:book).fetch(:properties).fetch(:year)
214
+ year_schema.fetch(:type).should == Integer
215
+ end
216
+
217
+ it "interpolates root-relative definition when specified" do
218
+ definition = {
219
+ definitions: { year: { type: :number } },
220
+ type: Object,
221
+ properties: {
222
+ book: {
223
+ definitions: { year: { type: Integer } },
224
+ type: Object,
225
+ properties: {
226
+ year: { :$ref => "#/definitions/year" }
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ parsed_definition = Schema.new(definition).definition
233
+ parsed_definition.fetch(:properties).fetch(:book).fetch(:properties).fetch(:year).fetch(:type).should == :number
234
+ end
235
+
236
+ it "expands to empty schema if no ref is found" do
237
+ definition = {
238
+ type: Object,
239
+ properties: {
240
+ year_relative: { :$ref => "definitions/year" },
241
+ year_root: { :$ref => "#/definitions/year" }
242
+ }
243
+ }
244
+
245
+ Schema.new(definition).definition.fetch(:properties).fetch(:year_relative).should == {}
246
+ Schema.new(definition).definition.fetch(:properties).fetch(:year_root).should == {}
74
247
  end
75
248
  end
76
249
  end
@@ -104,7 +277,7 @@ module Render
104
277
  describe "#render!" do
105
278
  before(:each) do
106
279
  Definition.load!({
107
- title: :film,
280
+ id: :film,
108
281
  type: Object,
109
282
  properties: {
110
283
  genre: { type: String }
@@ -112,35 +285,6 @@ module Render
112
285
  })
113
286
  end
114
287
 
115
- describe "#raw_data" do
116
- it "is set from endpoint response" do
117
- schema = Schema.new(:film)
118
- schema.stub({ request: { response: :body } })
119
- schema.render!
120
- schema.raw_data.should == { response: :body }
121
- end
122
-
123
- it "is set to explicit_data when offline" do
124
- Render.stub({ live: false })
125
- schema = Schema.new(:film)
126
- schema.render!({ explicit: :value })
127
- schema.raw_data.should == { explicit: :value }
128
- end
129
-
130
- it "is yielded" do
131
- Render.stub({ live: false })
132
- schema = Schema.new(:film)
133
-
134
- data = { explicit: :value }
135
- schema.should_receive(:serialize!).with(data)
136
- schema.serialized_data = :serialized_data
137
-
138
- expect { |a_block|
139
- schema.render!(data, &a_block)
140
- }.to yield_with_args(:serialized_data)
141
- end
142
- end
143
-
144
288
  context "request" do
145
289
  it "raises error if endpoint does not return a 2xx" do
146
290
  endpoint = "http://endpoint.local"
@@ -175,20 +319,7 @@ module Render
175
319
  schema = Schema.new(:film)
176
320
  schema.hash_attributes.first.should_receive(:serialize).with(genre, anything).and_return({ genre: genre })
177
321
 
178
- schema.render!(nil, endpoint).should == { film: data }
179
- end
180
-
181
- it "is serialized value nested under #universal_title" do
182
- Render.stub({ live: false })
183
- definition = {
184
- title: :film,
185
- universal_title: :imdb_films_show,
186
- type: Object,
187
- properties: {
188
- genre: { type: String }
189
- }
190
- }
191
- Schema.new(definition).render!.should have_key(:imdb_films_show)
322
+ schema.render!(nil, endpoint).should == data
192
323
  end
193
324
  end
194
325
 
@@ -15,12 +15,35 @@ module Render
15
15
  Type.parse("integer").should == Integer
16
16
  end
17
17
 
18
+ it "returns constant for symbols" do
19
+ Type.parse(:integer).should == Integer
20
+ end
21
+
18
22
  it "returns nil if no type is found" do
19
23
  Type.parse("not-a-type").should == nil
20
24
  end
21
25
  end
22
26
 
23
27
  describe ".parse!" do
28
+ it "returns ruby classes for standard json types" do
29
+ Type.parse!("string").should == String
30
+ Type.parse!("number").should == Float
31
+ Type.parse!("integer").should == Integer
32
+ Type.parse!("object").should == Object
33
+ Type.parse!("array").should == Array
34
+ Type.parse!("boolean").should == Type::Boolean
35
+ Type.parse!("null").should == NilClass
36
+ end
37
+
38
+ it "returns ruby classes for standard json formats" do
39
+ Type.parse!("uri").should == URI
40
+ Type.parse!("date-time").should == DateTime
41
+ Type.parse!("ipv4").should == Type::IPv4
42
+ Type.parse!("ipv6").should == Type::IPv6
43
+ Type.parse!("email").should == Type::Email
44
+ Type.parse!("hostname").should == Type::Hostname
45
+ end
46
+
24
47
  it "returns constant for string" do
25
48
  Type.parse("integer").should == Integer
26
49
  end
@@ -48,18 +71,6 @@ module Render
48
71
  it "returns UUID for uuid" do
49
72
  Type.parse("uUId").should == UUID
50
73
  end
51
-
52
- it "returns Boolean for boolean" do
53
- Type.parse("boolean").should == Render::Type::Boolean
54
- end
55
-
56
- it "returns Float for number" do
57
- Type.parse("FloAt").should == Float
58
- end
59
-
60
- it "returns Time for date-time" do
61
- Type.parse("date-time").should == Time
62
- end
63
74
  end
64
75
  end
65
76
 
@@ -72,7 +83,7 @@ module Render
72
83
  Type.find(:foo).should_not be
73
84
  end
74
85
 
75
- describe "user type" do
86
+ describe "user types" do
76
87
  before(:each) do
77
88
  @original_types = Type.instances.dup
78
89
  module ::Foo; module Boolean; end; end
@@ -89,5 +100,69 @@ module Render
89
100
  end
90
101
  end
91
102
  end
103
+
104
+ describe ".to" do
105
+ it "maintains nil values" do
106
+ Type.to([Float], nil).should == nil
107
+ end
108
+
109
+ it "returns nil for undefined classes" do
110
+ Type.to([nil], {}).should == nil
111
+ end
112
+
113
+ it "converts to floats" do
114
+ Type.to([Float], "2").should == 2
115
+ end
116
+
117
+ it "converts to Integers" do
118
+ Type.to([Integer], "1.2").should == 1
119
+ end
120
+
121
+ it "converts to Strings" do
122
+ Type.to([String], 2).should == "2"
123
+ end
124
+
125
+ describe "enum" do
126
+ it "returns valid enum" do
127
+ enums = [:foo, :bar]
128
+ Type.to([Type::Enum], :foo, enums).should == :foo
129
+ end
130
+
131
+ it "return nil for invalid enums" do
132
+ enums = [:foo]
133
+ Type.to([Type::Enum], :bar, enums).should == nil
134
+ end
135
+ end
136
+
137
+ describe "boolean" do
138
+ it "converts strings" do
139
+ Type.to([Type::Boolean], "true").should eq(true)
140
+ Type.to([Type::Boolean], "false").should eq(false)
141
+ end
142
+
143
+ it "returns nil for invalid booleans" do
144
+ Type.to([Type::Boolean], "foo").should == nil
145
+ end
146
+ end
147
+
148
+ it "returns value for unknown types" do
149
+ class Foo; end
150
+
151
+ Type.to([Foo], :bar).should == :bar
152
+
153
+ Render.send(:remove_const, :Foo)
154
+ end
155
+
156
+ context "multiple types" do
157
+ it "returns value in original type if valid" do
158
+ Type.to([Integer, Float], 2.0).should == 2.0
159
+ end
160
+
161
+ it "returns value of first class if original type is not valid" do
162
+ Type.to([Integer, String], 2.0).should == 2
163
+ end
164
+ end
165
+
166
+ end
92
167
  end
93
168
  end
metadata CHANGED
@@ -1,88 +1,116 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: render
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Steve Weber
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-02-16 00:00:00.000000000 Z
12
+ date: 2014-03-18 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: bundler
15
+ name: uuid
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - ~>
19
+ - - '='
18
20
  - !ruby/object:Gem::Version
19
- version: '1.3'
20
- type: :development
21
+ version: 2.3.7
22
+ type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - ~>
27
+ - - '='
25
28
  - !ruby/object:Gem::Version
26
- version: '1.3'
29
+ version: 2.3.7
27
30
  - !ruby/object:Gem::Dependency
28
- name: rake
31
+ name: addressable
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - ~>
35
+ - - '='
32
36
  - !ruby/object:Gem::Version
33
- version: '10.1'
34
- type: :development
37
+ version: 2.3.5
38
+ type: :runtime
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - ~>
43
+ - - '='
39
44
  - !ruby/object:Gem::Version
40
- version: '10.1'
45
+ version: 2.3.5
41
46
  - !ruby/object:Gem::Dependency
42
- name: uuid
47
+ name: macaddr
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
51
  - - '='
46
52
  - !ruby/object:Gem::Version
47
- version: 2.3.7
53
+ version: 1.6.1
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - '='
53
60
  - !ruby/object:Gem::Version
54
- version: 2.3.7
61
+ version: 1.6.1
55
62
  - !ruby/object:Gem::Dependency
56
- name: macaddr
63
+ name: bundler
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
- - - '='
67
+ - - ~>
60
68
  - !ruby/object:Gem::Version
61
- version: 1.6.1
62
- type: :runtime
69
+ version: '1.3'
70
+ type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
- - - '='
75
+ - - ~>
67
76
  - !ruby/object:Gem::Version
68
- version: 1.6.1
77
+ version: '1.3'
69
78
  - !ruby/object:Gem::Dependency
70
- name: debugger
79
+ name: rake
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
83
  - - ~>
74
84
  - !ruby/object:Gem::Version
75
- version: '1.6'
85
+ version: '10.1'
76
86
  type: :development
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
91
  - - ~>
81
92
  - !ruby/object:Gem::Version
82
- version: '1.6'
93
+ version: '10.1'
94
+ - !ruby/object:Gem::Dependency
95
+ name: debugger
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 1.6.6
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.6.6
83
110
  - !ruby/object:Gem::Dependency
84
111
  name: rspec
85
112
  requirement: !ruby/object:Gem::Requirement
113
+ none: false
86
114
  requirements:
87
115
  - - ~>
88
116
  - !ruby/object:Gem::Version
@@ -90,6 +118,7 @@ dependencies:
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
93
122
  requirements:
94
123
  - - ~>
95
124
  - !ruby/object:Gem::Version
@@ -97,6 +126,7 @@ dependencies:
97
126
  - !ruby/object:Gem::Dependency
98
127
  name: webmock
99
128
  requirement: !ruby/object:Gem::Requirement
129
+ none: false
100
130
  requirements:
101
131
  - - ~>
102
132
  - !ruby/object:Gem::Version
@@ -104,6 +134,7 @@ dependencies:
104
134
  type: :development
105
135
  prerelease: false
106
136
  version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
107
138
  requirements:
108
139
  - - ~>
109
140
  - !ruby/object:Gem::Version
@@ -111,6 +142,7 @@ dependencies:
111
142
  - !ruby/object:Gem::Dependency
112
143
  name: yard
113
144
  requirement: !ruby/object:Gem::Requirement
145
+ none: false
114
146
  requirements:
115
147
  - - ~>
116
148
  - !ruby/object:Gem::Version
@@ -118,6 +150,7 @@ dependencies:
118
150
  type: :development
119
151
  prerelease: false
120
152
  version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
121
154
  requirements:
122
155
  - - ~>
123
156
  - !ruby/object:Gem::Version
@@ -137,6 +170,8 @@ files:
137
170
  - Gemfile.lock
138
171
  - LICENSE.txt
139
172
  - initialize.rb
173
+ - lib/json/draft-04/hyper-schema.json
174
+ - lib/json/draft-04/schema.json
140
175
  - lib/render.rb
141
176
  - lib/render/attributes/array_attribute.rb
142
177
  - lib/render/attributes/attribute.rb
@@ -148,6 +183,7 @@ files:
148
183
  - lib/render/extensions/symbolizable_hash.rb
149
184
  - lib/render/generator.rb
150
185
  - lib/render/graph.rb
186
+ - lib/render/json_schema.rb
151
187
  - lib/render/schema.rb
152
188
  - lib/render/type.rb
153
189
  - lib/render/version.rb
@@ -180,26 +216,27 @@ files:
180
216
  homepage: https://github.com/stevenweber/render
181
217
  licenses:
182
218
  - MIT
183
- metadata: {}
184
219
  post_install_message:
185
220
  rdoc_options: []
186
221
  require_paths:
187
222
  - lib
188
223
  required_ruby_version: !ruby/object:Gem::Requirement
224
+ none: false
189
225
  requirements:
190
226
  - - ! '>='
191
227
  - !ruby/object:Gem::Version
192
228
  version: '0'
193
229
  required_rubygems_version: !ruby/object:Gem::Requirement
230
+ none: false
194
231
  requirements:
195
232
  - - ! '>='
196
233
  - !ruby/object:Gem::Version
197
234
  version: '0'
198
235
  requirements: []
199
236
  rubyforge_project:
200
- rubygems_version: 2.2.1
237
+ rubygems_version: 1.8.23
201
238
  signing_key:
202
- specification_version: 4
239
+ specification_version: 3
203
240
  summary: With a JSON schema, Render will manage requests, dependent request and build
204
241
  meaningful, extensible sample data for testing.
205
242
  test_files: