render 0.0.4 → 0.0.5

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.
Files changed (50) hide show
  1. checksums.yaml +8 -8
  2. data/Gemfile +1 -1
  3. data/lib/render.rb +12 -50
  4. data/lib/render/{array_attribute.rb → attributes/array_attribute.rb} +3 -4
  5. data/lib/render/attributes/attribute.rb +48 -0
  6. data/lib/render/{hash_attribute.rb → attributes/hash_attribute.rb} +2 -4
  7. data/lib/render/definition.rb +31 -0
  8. data/lib/render/extensions/dottable_hash.rb +82 -0
  9. data/lib/render/extensions/symbolizable_array.rb +26 -0
  10. data/lib/render/extensions/symbolizable_hash.rb +28 -0
  11. data/lib/render/generator.rb +58 -4
  12. data/lib/render/graph.rb +29 -34
  13. data/lib/render/schema.rb +12 -12
  14. data/lib/render/type.rb +63 -0
  15. data/lib/render/version.rb +1 -1
  16. data/rakefile.rb +3 -3
  17. data/readme.md +17 -38
  18. data/render.gemspec +5 -7
  19. data/spec/functional/{representation → render}/attribute_spec.rb +3 -4
  20. data/spec/functional/{representation → render}/graph_spec.rb +6 -6
  21. data/spec/functional/{representation → render}/nested_schemas_spec.rb +0 -0
  22. data/spec/functional/{representation → render}/schema_spec.rb +0 -0
  23. data/spec/integration/render/graph_spec.rb +119 -0
  24. data/spec/integration/render/nested_graph_spec.rb +67 -0
  25. data/spec/integration/render/schema_spec.rb +90 -0
  26. data/spec/support/helpers.rb +2 -1
  27. data/spec/{schemas → support/schemas}/film.json +1 -0
  28. data/spec/{schemas → support/schemas}/films.json +1 -0
  29. data/spec/unit/gemspec_spec.rb +8 -0
  30. data/spec/unit/{array_attribute_spec.rb → render/attributes/array_attribute_spec.rb} +1 -1
  31. data/spec/unit/render/{attribute_spec.rb → attributes/attribute_spec.rb} +0 -0
  32. data/spec/unit/render/{hash_attribute_spec.rb → attributes/hash_attribute_spec.rb} +3 -1
  33. data/spec/unit/render/definition_spec.rb +85 -0
  34. data/spec/unit/render/extensions/dottable_hash_spec.rb +148 -0
  35. data/spec/unit/render/extensions/symbolizable_array_spec.rb +20 -0
  36. data/spec/unit/render/generator_spec.rb +44 -22
  37. data/spec/unit/render/graph_spec.rb +18 -18
  38. data/spec/unit/render/schema_spec.rb +11 -16
  39. data/spec/unit/render/type_spec.rb +83 -0
  40. data/spec/unit/render_spec.rb +0 -139
  41. metadata +70 -60
  42. data/lib/extensions/boolean.rb +0 -2
  43. data/lib/extensions/enumerable.rb +0 -16
  44. data/lib/extensions/hash.rb +0 -39
  45. data/lib/render/attribute.rb +0 -59
  46. data/lib/render/dottable_hash.rb +0 -113
  47. data/spec/integration/nested_graph_spec.rb +0 -85
  48. data/spec/integration/single_graph_spec.rb +0 -76
  49. data/spec/unit/extensions/boolean_spec.rb +0 -7
  50. data/spec/unit/render/dottable_hash_spec.rb +0 -231
@@ -0,0 +1,20 @@
1
+ require "render/extensions/symbolizable_array"
2
+
3
+ module Render
4
+ module Extensions
5
+ describe SymbolizableArray do
6
+ describe "#initialize" do
7
+ it "recursively casts Hashes as DottableHashes" do
8
+ result = SymbolizableArray.new([{ "a" => :b }])
9
+ result.first.should be_a(DottableHash)
10
+ end
11
+
12
+ it "recursively casts Arrays as SymbolizableArrays" do
13
+ result = SymbolizableArray.new([[1]])
14
+ result.first.should be_a(SymbolizableArray)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -2,38 +2,60 @@ require "render/generator"
2
2
 
3
3
  module Render
4
4
  describe Generator do
5
- it "exists" do
6
- expect { Generator }.to_not raise_error
5
+ before(:each) do
6
+ @original_generators = Generator.instances.dup
7
7
  end
8
8
 
9
- describe "properties" do
10
- before(:each) do
11
- @mandatory_options = { algorithm: proc {} }
9
+ after(:each) do
10
+ Generator.instances = @original_generators
11
+ end
12
+
13
+ describe ".create!" do
14
+ it "adds generator to Render" do
15
+ expect {
16
+ Generator.create!(UUID, /id/i, lambda { UUID.generate })
17
+ }.to change { Generator.instances.size }.by(1)
18
+ end
19
+
20
+ it "preferences newer generators" do
21
+ Generator.instances.clear
22
+
23
+ first_generator = Generator.create!(String, /.*/, proc { "first" })
24
+ second_generator = Generator.create!(String, /.*/, proc { "second" })
25
+ Generator.find(String, :anything).trigger.should == second_generator.trigger
12
26
  end
27
+ end
13
28
 
14
- it "is a type-specific generator for flexibility" do
15
- Generator.new(@mandatory_options.merge({ type: String })).type.should == String
29
+ describe "#initialize" do
30
+ it "sets the type of data it can be used to generate data for" do
31
+ type = [UUID, Float].sample
32
+ Generator.new(type, nil, proc {}).type.should == type
16
33
  end
17
34
 
18
- it "has a matcher to only be used on specific properties" do
19
- matcher = %r{.*name.*}
20
- Generator.new(@mandatory_options.merge({ matcher: matcher })).matcher.should == matcher
35
+ it "sets a matcher to classify what attribute-name(s) it should be used for" do
36
+ matcher = %r{film_title.*}i
37
+ Generator.new(String, matcher, proc {}).matcher.should == matcher
21
38
  end
22
39
 
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
40
+ it "sets an algorithm to be used to generate values" do
41
+ algorithm = lambda { UUID.generate }
42
+ Generator.new(UUID, //, algorithm).algorithm.should == algorithm
43
+ end
44
+
45
+ it "guarantees algorithm responds to #call for real-time value generation" do
46
+ algorithm = UUID.generate
47
+ expect {
48
+ Generator.new(UUID, //, algorithm)
49
+ }.to raise_error(Errors::Generator::MalformedAlgorithm)
34
50
  end
35
51
  end
36
52
 
53
+ describe "#trigger" do
54
+ it "calls algorithm" do
55
+ x = "foo"
56
+ algorithm = proc { |y| "algorithm called with #{y}" }
57
+ Generator.new(UUID, //, algorithm).trigger(x).should == algorithm.call(x)
58
+ end
59
+ end
37
60
  end
38
-
39
61
  end
@@ -6,8 +6,8 @@ module Render
6
6
  describe Graph do
7
7
  before(:each) do
8
8
  Render.stub({ live: false })
9
- @definition = double(:definition)
10
- @schema = double(:schema, { type: Hash })
9
+ @definition = double(:definition, { :[] => nil })
10
+ @schema = double(:schema, { type: Hash, definition: @definition })
11
11
  Schema.stub(:new).with(@definition).and_return(@schema)
12
12
  end
13
13
 
@@ -20,8 +20,8 @@ module Render
20
20
  end
21
21
 
22
22
  it "is set to new Schema from definition" do
23
- Schema.should_receive(:new).with(:title_or_definition).and_return(:schema)
24
- Graph.new(:title_or_definition).schema.should == :schema
23
+ Schema.should_receive(:new).with(:title_or_definition).and_return(@schema)
24
+ Graph.new(:title_or_definition).schema.should == @schema
25
25
  end
26
26
  end
27
27
 
@@ -49,11 +49,11 @@ module Render
49
49
  end
50
50
  end
51
51
 
52
- describe ".endpoint" do
52
+ describe ".send(:endpoint)" do
53
53
  it "returns #raw_endpoint" do
54
54
  simple_endpoint = "http://endpoint.local"
55
55
  graph = Graph.new(@definition, { endpoint: simple_endpoint })
56
- graph.endpoint.should == simple_endpoint
56
+ graph.send(:endpoint).should == simple_endpoint
57
57
  end
58
58
 
59
59
  it "interpolates inherited parameters" do
@@ -64,7 +64,7 @@ module Render
64
64
  graph = Graph.new(@definition, { endpoint: endpoint, relationships: relationships })
65
65
  graph.inherited_data = { director_id: director_id }
66
66
 
67
- graph.endpoint.should == "http://endpoint.local/directors/#{director_id}"
67
+ graph.send(:endpoint).should == "http://endpoint.local/directors/#{director_id}"
68
68
  end
69
69
 
70
70
  it "interpolates config options" do
@@ -72,7 +72,7 @@ module Render
72
72
  endpoint = "http://endpoint.local/?:client_id"
73
73
 
74
74
  graph = Graph.new(@definition, { endpoint: endpoint, client_id: client_id })
75
- graph.endpoint.should == "http://endpoint.local/?client_id=#{client_id}"
75
+ graph.send(:endpoint).should == "http://endpoint.local/?client_id=#{client_id}"
76
76
  end
77
77
 
78
78
  it "raises an error if no value can be found" do
@@ -80,7 +80,7 @@ module Render
80
80
  graph = Graph.new(@definition, { endpoint: endpoint })
81
81
 
82
82
  expect {
83
- graph.endpoint
83
+ graph.send(:endpoint)
84
84
  }.to raise_error(Errors::Graph::EndpointKeyNotFound)
85
85
  end
86
86
  end
@@ -92,7 +92,7 @@ module Render
92
92
  @schema.stub({ render!: pull, serialized_data: serialized_data })
93
93
 
94
94
  graph = Graph.new(@definition)
95
- graph.render.should == pull
95
+ graph.render!.should == pull
96
96
  end
97
97
 
98
98
  it "returns a dottable hash" do
@@ -100,7 +100,7 @@ module Render
100
100
  @schema.stub({ render!: pull })
101
101
 
102
102
  graph = Graph.new(@definition)
103
- graph.render.should be_a(DottableHash)
103
+ graph.render!.should be_a(Extensions::DottableHash)
104
104
  end
105
105
 
106
106
  it "sends interpolated endpoint to its schema" do
@@ -108,8 +108,8 @@ module Render
108
108
  client_id = UUID.generate
109
109
  graph = Graph.new(@definition, { endpoint: endpoint, client_id: client_id })
110
110
 
111
- @schema.should_receive(:render!).with(anything, graph.endpoint).and_return({})
112
- graph.render
111
+ @schema.should_receive(:render!).with(anything, graph.send(:endpoint)).and_return({})
112
+ graph.render!
113
113
  end
114
114
 
115
115
  context "with nested graphs" do
@@ -133,7 +133,7 @@ module Render
133
133
 
134
134
  it "includes nested graphs" do
135
135
  film = Graph.new(@film_schema, { graphs: [Graph.new(@director_schema)] })
136
- film = film.render
136
+ film = film.render!
137
137
  film.keys.should =~ [:film, :director]
138
138
  end
139
139
 
@@ -148,7 +148,7 @@ module Render
148
148
 
149
149
  endpoint = "http://endpoint.local/directors/#{@director_id}"
150
150
  @director_schema.should_receive(:render!).with(anything, endpoint).and_return({})
151
- film.render
151
+ film.render!
152
152
  end
153
153
 
154
154
  it "uses parent data to make multiple queries" do
@@ -170,7 +170,7 @@ module Render
170
170
  films_response = [{ id: first_film_id }, { id: second_film_id }]
171
171
  films_schema.should_receive(:render!).and_yield(films_response).and_return({ films: films_response })
172
172
 
173
- response = films.render
173
+ response = films.render!
174
174
  response.film.should be_a(Array)
175
175
  response.film.should =~ [{ director_id: first_film_id }, { director_id: second_film_id }]
176
176
  end
@@ -180,7 +180,7 @@ module Render
180
180
  film = Graph.new(@film_schema, { graphs: [director] })
181
181
  @film_schema.should_receive(:render!).and_yield({ director_id: @director_id }).and_return({})
182
182
 
183
- film.render.director.id.should == @director_id
183
+ film.render!.director.id.should == @director_id
184
184
  end
185
185
 
186
186
  it "uses archetype parental data" do
@@ -199,7 +199,7 @@ module Render
199
199
  films_response = [film_id]
200
200
  films_schema.should_receive(:render!).and_yield(films_response).and_return({ films: films_response })
201
201
 
202
- response = films.render
202
+ response = films.render!
203
203
  response.film.should be_a(Array)
204
204
  response.film.should =~ [{ director_id: film_id }]
205
205
  end
@@ -2,16 +2,16 @@ require "render"
2
2
 
3
3
  module Render
4
4
  describe Schema do
5
- describe "#initialize" do
6
- describe "#definition" do
7
- before(:all) do
8
- @original_defs = Render.definitions
9
- end
5
+ before(:each) do
6
+ @original_defs = Definition.instances.dup
7
+ end
10
8
 
11
- after(:all) do
12
- Render.definitions = @original_defs
13
- end
9
+ after(:each) do
10
+ Definition.instances = @original_defs
11
+ end
14
12
 
13
+ describe "#initialize" do
14
+ describe "#definition" do
15
15
  it "is set from argument" do
16
16
  schema_definition = { properties: {} }
17
17
  Schema.new(schema_definition).definition.should == schema_definition
@@ -20,7 +20,7 @@ module Render
20
20
  it "is set to preloaded definition" do
21
21
  definition_title = :preloaded_schema
22
22
  definition = { title: definition_title, properties: { title: { type: String } } }
23
- Render.load_definition!(definition)
23
+ Definition.load!(definition)
24
24
  Schema.new(definition_title).definition.should == definition
25
25
  end
26
26
 
@@ -96,9 +96,8 @@ module Render
96
96
  end
97
97
 
98
98
  describe "#render!" do
99
- before(:all) do
100
- @original_defs = Render.definitions
101
- Render.load_definition!({
99
+ before(:each) do
100
+ Definition.load!({
102
101
  title: :film,
103
102
  properties: {
104
103
  genre: { type: String }
@@ -106,10 +105,6 @@ module Render
106
105
  })
107
106
  end
108
107
 
109
- after(:all) do
110
- Render.definitions = @original_defs
111
- end
112
-
113
108
  describe "#raw_data" do
114
109
  it "is set from endpoint response" do
115
110
  schema = Schema.new(:film)
@@ -0,0 +1,83 @@
1
+ require "render/type"
2
+
3
+ module Render
4
+ describe Type do
5
+ before(:each) do
6
+ @original_types = Type.instances.dup
7
+ end
8
+
9
+ after(:each) do
10
+ Type.instances = @original_types
11
+ end
12
+
13
+ describe ".parse" do
14
+ it "returns constant for string" do
15
+ Type.parse("integer").should == Integer
16
+ end
17
+
18
+ it "returns argument when not a string" do
19
+ class ::Foo; end
20
+ Type.parse(Foo).should == Foo
21
+ Object.__send__(:remove_const, :Foo)
22
+ end
23
+
24
+ it "raises meaningful error for unmatched types" do
25
+ expect {
26
+ Type.parse!("NotAClass")
27
+ }.to raise_error(Render::Errors::InvalidType)
28
+ end
29
+
30
+ describe "non-standard formats" do
31
+ it "maps regardless of capitalization" do
32
+ string_representations = %w(uuid UUID)
33
+ string_representations.each do |name|
34
+ Type.parse(name).should == UUID
35
+ end
36
+ end
37
+
38
+ it "returns UUID for uuid" do
39
+ Type.parse("uUId").should == UUID
40
+ end
41
+
42
+ it "returns Boolean for boolean" do
43
+ Type.parse("boolean").should == Render::Type::Boolean
44
+ end
45
+
46
+ it "returns Float for number" do
47
+ Type.parse("FloAt").should == Float
48
+ end
49
+
50
+ it "returns Time for date-time" do
51
+ Type.parse("date-time").should == Time
52
+ end
53
+ end
54
+ end
55
+
56
+ describe ".find" do
57
+ it "returns Render's custom types" do
58
+ Type.find(:boolean).should == Render::Type::Boolean
59
+ end
60
+
61
+ it "returns falsie when no type is found" do
62
+ Type.find(:foo).should_not be
63
+ end
64
+
65
+ describe "user type" do
66
+ before(:each) do
67
+ @original_types = Type.instances.dup
68
+ module ::Foo; module Boolean; end; end
69
+ Type.add!(:boolean, Foo::Boolean)
70
+ end
71
+
72
+ after(:each) do
73
+ Type.instances = @original_types
74
+ Object.__send__(:remove_const, :Foo)
75
+ end
76
+
77
+ it "is returned instead of Render's" do
78
+ Type.find(:boolean).should == Foo::Boolean
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -32,144 +32,5 @@ describe Render do
32
32
  end
33
33
  end
34
34
 
35
- describe ".generators" do
36
- before(:each) do
37
- @original_value = Render.generators
38
- Render.generators.clear
39
- end
40
-
41
- after(:each) do
42
- Render.generators = @original_value
43
- end
44
-
45
- it "defaults to an empty array" do
46
- Render.generators.should == []
47
- end
48
- end
49
-
50
- context "schema definitions" do
51
- before(:each) do
52
- @original_defs = Render.definitions
53
- end
54
-
55
- after(:each) do
56
- Render.definitions = @original_defs
57
- end
58
-
59
- describe ".load_defintion!" do
60
- it "preferences #universal_title over title" do
61
- universal_title = "a_service_films_show"
62
- definition = {
63
- universal_title: universal_title,
64
- title: "film",
65
- type: Object,
66
- properties: {
67
- name: { type: String },
68
- year: { type: Integer }
69
- }
70
- }
71
-
72
- Render.load_definition!(definition)
73
- Render.definitions.keys.should include(universal_title.to_sym)
74
- end
75
- end
76
-
77
- describe ".load_schemas!" do
78
- before(:each) do
79
- Render.definitions.clear
80
- @schema_title = "film"
81
- @json_schema = <<-JSON
82
- {
83
- "title": "#{@schema_title}",
84
- "type": "object",
85
- "properties": {
86
- "name": { "type": "string" },
87
- "year": { "type": "integer" }
88
- }
89
- }
90
- JSON
91
-
92
- @directory = "/a"
93
- @schema_file = "/a/schema.json"
94
- Dir.stub(:glob).with(%r{#{@directory}}).and_return([@schema_file])
95
- IO.stub(:read).with(@schema_file).and_return(@json_schema)
96
- end
97
-
98
- after(:each) do
99
- Render.definitions = {}
100
- end
101
-
102
- it "stores JSON files" do
103
- expect {
104
- Render.load_definitions!(@directory)
105
- }.to change { Render.definitions.keys.size }.by(1)
106
- end
107
-
108
- it "accesses parsed schemas with symbols" do
109
- Render.load_definitions!(@directory)
110
- parsed_json = JSON.parse(@json_schema).recursive_symbolize_keys!
111
- Render.definitions[@schema_title.to_sym].should == parsed_json
112
- end
113
- end
114
- end
115
-
116
- describe ".definition" do
117
- it "returns definition by its title" do
118
- def_title = :the_name
119
- definition = { title: def_title, properties: {} }
120
- Render.load_definition!(definition)
121
-
122
- Render.definition(def_title).should == definition
123
- end
124
-
125
- it "raises meaningful error if definition is not found" do
126
- expect {
127
- Render.definition(:definition_with_this_title_has_not_been_loaded)
128
- }.to raise_error(Render::Errors::DefinitionNotFound)
129
- end
130
- end
131
-
132
- describe ".parse_type" do
133
- it "returns constant for string" do
134
- Render.parse_type("integer").should == Integer
135
- end
136
-
137
- it "returns argument when not a string" do
138
- class Foo; end
139
- Render.parse_type(Foo).should == Foo
140
- Object.__send__(:remove_const, :Foo)
141
- end
142
-
143
- it "raises meaningful error for unmatched types" do
144
- expect {
145
- Render.parse_type("NotAClass")
146
- }.to raise_error(Render::Errors::InvalidType)
147
- end
148
-
149
- describe "non-standard formats" do
150
- it "maps regardless of capitalization" do
151
- string_representations = %w(uuid UUID)
152
- string_representations.each do |name|
153
- Render.parse_type(name).should == UUID
154
- end
155
- end
156
-
157
- it "returns UUID for uuid" do
158
- Render.parse_type("uUId").should == UUID
159
- end
160
-
161
- it "returns Boolean for boolean" do
162
- Render.parse_type("boolean").should == Boolean
163
- end
164
-
165
- it "returns Float for number" do
166
- Render.parse_type("FloAt").should == Float
167
- end
168
-
169
- it "returns Time for date-time" do
170
- Render.parse_type("date-time").should == Time
171
- end
172
- end
173
- end
174
35
  end
175
36
  end