render 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/initialize.rb CHANGED
@@ -2,6 +2,4 @@
2
2
  $: << path
3
3
  end
4
4
 
5
- Dir.glob("extensions/*").sort.each { |file| require file }
6
-
7
5
  require "render"
@@ -1,3 +1,2 @@
1
1
  class Boolean
2
2
  end
3
-
@@ -0,0 +1,16 @@
1
+ ::Enumerable.module_eval do
2
+ # This is named the same as its Hash counterpart for a reason. I'm not
3
+ # going to tell you why, consider it my riddle for you.
4
+ def recursive_symbolize_keys!
5
+ each do |item|
6
+ item.recursive_symbolize_keys! if item.respond_to?(:recursive_symbolize_keys!)
7
+ end
8
+ self
9
+ end
10
+
11
+ def recursive_stringify_keys!
12
+ each do |item|
13
+ item.recursive_stringify_keys! if item.respond_to?(:recursive_stringify_keys!)
14
+ end
15
+ end
16
+ end
@@ -36,17 +36,4 @@ class ::Hash
36
36
  def symbolize_keys
37
37
  dup.symbolize_keys!
38
38
  end
39
-
40
- def hardcode(other_hash)
41
- dup.hardcode!(other_hash)
42
- end
43
-
44
- def hardcode!(other_hash)
45
- other_hash.each_pair do |k,v|
46
- tv = self[k]
47
- self[k] = tv.respond_to?(:hardcode) && v.respond_to?(:hardcode) ? tv.hardcode(v) : v
48
- end
49
- self
50
- end
51
-
52
39
  end
data/lib/render.rb CHANGED
@@ -3,6 +3,10 @@
3
3
  # - Query its endpoint to construct a hash for its Schema
4
4
  # - Add nested Graphs by interpreting/sending data they need
5
5
 
6
+ require "extensions/enumerable"
7
+ require "extensions/boolean"
8
+ require "extensions/hash"
9
+
6
10
  require "render/version"
7
11
  require "render/graph"
8
12
  require "render/generator"
@@ -0,0 +1,114 @@
1
+ module Render
2
+ class DottableHash < Hash
3
+ class << self
4
+ def new(element_to_hash = {})
5
+ hash = super().merge!(element_to_hash.symbolize_keys)
6
+ hash.each do |key, value|
7
+ hash[key] = initialize_element(value)
8
+ end
9
+ hash
10
+ end
11
+
12
+ def initialize_element(value)
13
+ case value
14
+ when Hash
15
+ new(value)
16
+ when Array
17
+ value.collect { |v| initialize_element(v) }
18
+ else
19
+ value
20
+ end
21
+ end
22
+ end
23
+
24
+ def []=(key, value)
25
+ key = key.to_sym
26
+ value = self.class.initialize_element(value)
27
+ super
28
+ end
29
+
30
+ def [](key)
31
+ key = key.to_sym
32
+ super
33
+ end
34
+
35
+ def delete(key)
36
+ key = key.to_sym
37
+ super
38
+ end
39
+
40
+ def has_key?(key)
41
+ super(key.to_sym)
42
+ end
43
+
44
+ def merge!(other_hash)
45
+ super(other_hash.symbolize_keys)
46
+ end
47
+
48
+ def merge(other_hash)
49
+ super(other_hash.symbolize_keys)
50
+ end
51
+
52
+ def method_missing(method, *arguments)
53
+ if method.match(/\=$/)
54
+ self[method.to_s.chop] = arguments.first
55
+ elsif has_key?(method)
56
+ self[method]
57
+ else
58
+ super
59
+ end
60
+ end
61
+
62
+ def fetch(key)
63
+ key = key.to_sym
64
+ raise KeyError.new("Invalid key: #{key}") unless has_key?(key)
65
+ super
66
+ end
67
+
68
+ def fetch_path(full_path)
69
+ begin
70
+ fetch_path!(full_path)
71
+ rescue KeyError
72
+ nil
73
+ end
74
+ end
75
+
76
+ def fetch_path!(full_path)
77
+ full_path.split(".").inject(self) do |hash, path|
78
+ raise(KeyError) unless hash.is_a?(Hash)
79
+
80
+ hash.fetch(path)
81
+ end
82
+ end
83
+
84
+ def set_path(full_path, value)
85
+ self.dup.set_path!(full_path, value)
86
+ end
87
+
88
+ def set_path!(full_path, value)
89
+ built_hash_for_value = full_path.split(".").reverse.inject({}) do |cumulative, path_to_source|
90
+ if cumulative.empty?
91
+ { path_to_source.to_sym => value }
92
+ else
93
+ { path_to_source.to_sym => cumulative }
94
+ end
95
+ end
96
+
97
+ deep_merge!(built_hash_for_value)
98
+ end
99
+
100
+ protected
101
+
102
+ def deep_merge(other_hash)
103
+ merge(other_hash) do |key, oldval, newval|
104
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
105
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
106
+ oldval.is_a?(Hash) && newval.is_a?(Hash) ? self.class.new(oldval).deep_merge(newval) : self.class.new(newval)
107
+ end
108
+ end
109
+
110
+ def deep_merge!(other_hash)
111
+ replace(deep_merge(other_hash))
112
+ end
113
+ end
114
+ end
data/lib/render/graph.rb CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  require "render/schema"
8
8
  require "render/errors"
9
+ require "render/dottable_hash"
9
10
 
10
11
  module Render
11
12
  class Graph
@@ -54,11 +55,11 @@ module Render
54
55
  uri.to_s
55
56
  end
56
57
 
57
- def pull(inherited_attributes = {})
58
+ def render(inherited_attributes = {})
58
59
  calculate_parental_params!(inherited_attributes)
59
- graph_attributes = schema.pull(inherited_attributes.merge(parental_params.merge({ endpoint: endpoint })))
60
+ graph_attributes = schema.render(inherited_attributes.merge(parental_params.merge({ endpoint: endpoint })))
60
61
 
61
- graphs.inject(graph_attributes) do |attributes, nested_graph|
62
+ graph = graphs.inject(graph_attributes) do |attributes, nested_graph|
62
63
  threads = []
63
64
  # TODO threading should be configured so people may also think about Thread.abort_on_transaction!
64
65
  threads << Thread.new do
@@ -66,18 +67,19 @@ module Render
66
67
  parent_data = attributes[title]
67
68
  nested_graph_data = if parent_data.is_a?(Array)
68
69
  data = parent_data.collect do |element|
69
- nested_graph.pull(element)
70
+ nested_graph.render(element)
70
71
  end
71
72
  key = data.first.keys.first
72
73
  attributes[title] = data.collect { |d| d[key] }
73
74
  else
74
- data = nested_graph.pull(parent_data)
75
+ data = nested_graph.render(parent_data)
75
76
  parent_data.merge!(data)
76
77
  end
77
78
  end
78
79
  threads.collect(&:join)
79
- DottableHash.new(attributes)
80
+ attributes
80
81
  end
82
+ DottableHash.new(graph)
81
83
  end
82
84
 
83
85
  private
data/lib/render/schema.rb CHANGED
@@ -39,7 +39,7 @@ module Render
39
39
  definition.keys.include?(:attributes)
40
40
  end
41
41
 
42
- def pull(options = {})
42
+ def render(options = {})
43
43
  endpoint = options.delete(:endpoint)
44
44
  data = Render.live ? request(endpoint) : options
45
45
  { title.to_sym => serialize(data) }
@@ -1,3 +1,3 @@
1
1
  module Render
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/readme.md CHANGED
@@ -3,84 +3,43 @@
3
3
  Create and test API requests simply with schemas.
4
4
 
5
5
  ```ruby
6
- Representation.load_schemas!("spec/schemas") # JSON schema directory
7
- Representation::Graph.new(:film, { endpoint: "http://films.local/films" }).pull
6
+ Render.load_schemas!("spec/schemas") # JSON schema directory
7
+ Render::Graph.new(:film, { endpoint: "http://films.local/films" }).render
8
8
  # or stub out schema-specific data
9
- Representation.live = false
10
- Representation::Graph.new(:film).pull
9
+ Render.live = false
10
+ Render::Graph.new(:film).render
11
11
  ```
12
12
 
13
- *Use with caution* (Representation is under initial development) by updating your Gemfile:
13
+ *Use with caution* (Render is under initial development) by updating your Gemfile:
14
14
 
15
- gem "representation"
15
+ gem "render"
16
16
 
17
- ## Usage
18
-
19
- Try out examples with `Representation.live = false`.
20
-
21
- *Simple*
17
+ ## Caveats
22
18
 
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
- ```
19
+ - Render will modify ::Hash and ::Enumerable to provide symbolize/stringify keys methods.
39
20
 
40
- *Nested*
21
+ ## Usage
41
22
 
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
23
  *Autoload schemas*
70
24
 
71
25
  ```ruby
72
- Representation.load_schemas!("path/to/json/schemas")
73
- Representation::Graph.new(:schema_title, { endpoint: "http://films.local/films" }).pull
26
+ Render.load_schemas!("path/to/json/schemas")
27
+ Render::Graph.new(:schema_title, { endpoint: "http://films.local/films" }).render
74
28
  ```
75
29
 
76
30
  *Variable interpolation*
77
31
 
78
32
  ```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" })
33
+ api_endpoint = "http://films.local/films/:id?:client_token"
34
+ env_specific_client_token = "token"
35
+
36
+ graph = Render::Graph.new(:schema_title, { endpoint: api_endpoint, client_token: env_specific_client_token })
37
+ graph.render({ id: "an-id" }) # makes request to "http://films.local/films/an-id?client_token=token"
82
38
  ```
83
39
 
40
+ Check out the examples in [integration tests](spec/integration/).
41
+
42
+
84
43
  ## Contributing
85
44
 
86
45
  1. Fork it
@@ -32,7 +32,7 @@ module Render
32
32
  }
33
33
  }
34
34
 
35
- Schema.new(schema).pull(data).should == {
35
+ Schema.new(schema).render(data).should == {
36
36
  person: {
37
37
  contact: {
38
38
  name: contact_name,
@@ -82,7 +82,7 @@ module Render
82
82
  }
83
83
  people = [zissou, ned]
84
84
 
85
- Schema.new(schema).pull(people).should == {
85
+ Schema.new(schema).render(people).should == {
86
86
  people: [{
87
87
  name: "Steve Zissou",
88
88
  nicknames: [
@@ -18,7 +18,7 @@ module Render
18
18
  brand_name = "Sony"
19
19
  response = { brand: brand_name }
20
20
 
21
- schema.pull(response).should == {
21
+ schema.render(response).should == {
22
22
  television: { brand: brand_name }
23
23
  }
24
24
  end
@@ -34,7 +34,7 @@ module Render
34
34
 
35
35
  television_ids = rand(10).times.collect { UUID.generate }
36
36
 
37
- schema.pull(television_ids).should == {
37
+ schema.render(television_ids).should == {
38
38
  televisions: television_ids
39
39
  }
40
40
  end
@@ -55,7 +55,7 @@ module Render
55
55
  brand_1, brand_2 = *%w(Sony Samsung)
56
56
  response = [{ brand: brand_1 }, { brand: brand_2 }]
57
57
 
58
- schema.pull(response).should == {
58
+ schema.render(response).should == {
59
59
  televisions: [{ brand: brand_1 }, { brand: brand_2 }]
60
60
  }
61
61
  end
@@ -78,7 +78,7 @@ module Render
78
78
  brand_name = "Sony"
79
79
  response = { brand: { name: brand_name } }
80
80
 
81
- schema.pull(response).should == {
81
+ schema.render(response).should == {
82
82
  television: { brand: { name: brand_name } }
83
83
  }
84
84
  end
@@ -33,7 +33,7 @@ describe Render do
33
33
  endpoint: @films_endpoint
34
34
  }
35
35
  graph = Render::Graph.new(:films, options)
36
- graph.pull.should == {
36
+ graph.render.should == {
37
37
  films: [
38
38
  { name: aquatic_name, year: nil },
39
39
  { name: darjeeling_name, year: nil }
@@ -70,7 +70,7 @@ describe Render do
70
70
 
71
71
  films = Representation::Graph.new(films, { endpoint: @films_endpoint })
72
72
  films.graphs << Representation::Graph.new(film, { endpoint: @film_endpoint, relationships: { films: :id } })
73
- films.pull.should == {}
73
+ films.render.should == {}
74
74
  end
75
75
 
76
76
  end
@@ -25,7 +25,7 @@ describe Render do
25
25
  stub_request(:get, aquatic_uri).to_return({ body: [{ id: @film_id }].to_json })
26
26
 
27
27
  graph = Render::Graph.new(:films, { endpoint: @films_endpoint, secret_code: @secret_code })
28
- graph.pull.should == { films: [{ id: @film_id }] }
28
+ graph.render.should == { films: [{ id: @film_id }] }
29
29
  end
30
30
 
31
31
  it "returns structured data for specific resources" do
@@ -34,7 +34,7 @@ describe Render do
34
34
  stub_request(:get, aquatic_uri).to_return({ body: { name: @film_name }.to_json })
35
35
 
36
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 } }
37
+ graph.render.should == { film: { name: @film_name, year: nil } }
38
38
  end
39
39
  end
40
40
 
@@ -44,7 +44,7 @@ describe Render do
44
44
  end
45
45
 
46
46
  it "use meaningful values" do
47
- response = Render::Graph.new(:film).pull({ name: @film_name })
47
+ response = Render::Graph.new(:film).render({ name: @film_name })
48
48
 
49
49
  stub_request(:post, "http://films.local/create").to_return({ body: response.to_json })
50
50
  response = post_film(:anything)["film"]
@@ -54,7 +54,7 @@ describe Render do
54
54
  end
55
55
 
56
56
  it "allows users to specify specific values" do
57
- response = Render::Graph.new(:film).pull({ name: @film_name })
57
+ response = Render::Graph.new(:film).render({ name: @film_name })
58
58
 
59
59
  data = { name: @film_name }.to_json
60
60
  stub_request(:post, "http://films.local/create").with({ body: data }).to_return({ body: response.to_json })
@@ -0,0 +1,234 @@
1
+ require "render/dottable_hash"
2
+
3
+ module Render
4
+ describe DottableHash do
5
+ before(:each) do
6
+ @dottable_hash = DottableHash.new
7
+ end
8
+
9
+ describe "new" do
10
+ it "creates from a hash" do
11
+ dottable_hash = DottableHash.new({ "foo" => "bar" })
12
+ dottable_hash.should == { :foo => "bar" }
13
+ end
14
+
15
+ it "initializes new hashes as dottable_hashes" do
16
+ dottable_hash = DottableHash.new({ :foo => { :bar => "baz" } })
17
+ dottable_hash[:foo].class.should == DottableHash
18
+ end
19
+
20
+ it "converts all keys to symbols" do
21
+ dottable_hash = DottableHash.new({ "foo" => { "bar" => "baz" } })
22
+ dottable_hash.keys.include?(:foo).should be_true
23
+ end
24
+ end
25
+
26
+ describe "#[]" do
27
+ it "converts keys to strings" do
28
+ @dottable_hash[:foo] = "bar"
29
+ @dottable_hash.keys.include?(:foo).should be_true
30
+ end
31
+
32
+ it "converts hash values to dottable_hashs" do
33
+ @dottable_hash[:foo] = { bar: { baz: "baz" } }
34
+ @dottable_hash.foo.bar.class.should == DottableHash
35
+ end
36
+
37
+ it "retrieves values by stringified keys" do
38
+ @dottable_hash["foo"] = "bar"
39
+ @dottable_hash[:foo].should == "bar"
40
+ end
41
+
42
+ it "converts hashes in arrays to dottable hashes" do
43
+ pallet = DottableHash.new
44
+ pallet.foo = [{ bar: "baz" }]
45
+ pallet.foo.first.class.should == DottableHash
46
+ end
47
+ end
48
+
49
+ describe "#delete" do
50
+ it "symbolizes keys" do
51
+ @dottable_hash["foo"] = { "bar" => "bar", "baz" => "baz" }
52
+ @dottable_hash.foo.delete(:bar)
53
+ @dottable_hash.should == { :foo => { :baz => "baz" } }
54
+ end
55
+ end
56
+
57
+ describe ".has_key?" do
58
+ it "converts symbols to strings" do
59
+ DottableHash.new({ foo: "bar" }).has_key?(:foo).should == true
60
+ end
61
+ end
62
+
63
+ describe "#method_missing" do
64
+ it "returns value for key when it exists" do
65
+ @dottable_hash[:foo] = "bar"
66
+ @dottable_hash.foo.should == "bar"
67
+ end
68
+
69
+ it "raises an error when no key exists" do
70
+ lambda {
71
+ @dottable_hash.foo
72
+ }.should raise_error(NoMethodError)
73
+ end
74
+
75
+ it "returns the same object as in the hash" do
76
+ @dottable_hash[:foo] = { bar: "baz" }
77
+ dottable_hash_object_id = @dottable_hash.foo.object_id
78
+ @dottable_hash.foo.object_id.should == dottable_hash_object_id
79
+ end
80
+
81
+ it "sets values" do
82
+ @dottable_hash.foo = "bar"
83
+ @dottable_hash.foo.should == "bar"
84
+ end
85
+ end
86
+
87
+ describe "dot access" do
88
+ it "provides acess to keys as methods" do
89
+ dottable_hash = DottableHash.new({ "foo" => "bar" })
90
+ dottable_hash.foo.should == "bar"
91
+ end
92
+
93
+ it "provides acess to nested keys as methods" do
94
+ dottable_hash = DottableHash.new({ "foo" => {"bar" => {"baz" => "bat"}}})
95
+ dottable_hash.foo.bar.baz.should == "bat"
96
+ end
97
+
98
+ it "provides indifferent accesss" do
99
+ dottable_hash = DottableHash.new({ :foo => {:bar => {"baz" => "bat"}}})
100
+ dottable_hash.foo.bar.baz.should == "bat"
101
+ end
102
+
103
+ it "provides acess to keys with nil values" do
104
+ dottable_hash = DottableHash.new({ "foo" => {"bar" => nil} })
105
+ dottable_hash.foo.bar.should == nil
106
+ end
107
+
108
+ it "raises key error when it doesn't exist" do
109
+ dottable_hash = DottableHash.new({ "foo" => "bar" })
110
+ expect { dottable_hash.fu }.to raise_error(NoMethodError)
111
+ end
112
+
113
+ it "provides the dot access to a hash inside of an array" do
114
+ dottable_hash = DottableHash.new({ "foo" => [{"bar" => "baz"}]})
115
+ dottable_hash.foo.first.bar.should == "baz"
116
+ end
117
+
118
+ it "provides the dot access to to a list of strings inside an array" do
119
+ dottable_hash = DottableHash.new({ "foo" => ["bar", "baz"]})
120
+ dottable_hash.foo.should == ["bar", "baz"]
121
+ end
122
+
123
+ it "initializes hashes in nested arrays as dottable_hashs" do
124
+ dottable_hash = DottableHash.new({ foo: [{ bar: [{ baz: "one" }] }] })
125
+ dottable_hash.foo.first.bar.first.class.should == DottableHash
126
+ end
127
+ end
128
+
129
+ describe "#fetch" do
130
+ it "raises KeyErrors" do
131
+ lambda {
132
+ DottableHash.new.fetch("non_existent_key")
133
+ }.should raise_error
134
+ end
135
+
136
+ it "returns dottable_hashs in lieu of hashes" do
137
+ @dottable_hash["nested_hash"] = { "foo" => "bar" }
138
+ @dottable_hash.fetch("nested_hash").class.should == DottableHash
139
+ end
140
+
141
+ it "returns value of corresponding key object" do
142
+ @dottable_hash["foo"] = "bar"
143
+ @dottable_hash.fetch("foo").should == "bar"
144
+ end
145
+
146
+ end
147
+
148
+ describe "#fetch_path[!]" do
149
+ it "returns value of corresponding key object" do
150
+ @dottable_hash["foo"] = "bar"
151
+ @dottable_hash.fetch_path("foo").should == "bar"
152
+ end
153
+
154
+ it "returns value of expanded key object" do
155
+ @dottable_hash["foo"] = { "bar" => "baz" }
156
+ @dottable_hash.fetch_path("foo.bar").should == "baz"
157
+ end
158
+
159
+ it "raises key errors for nonexistent hashes" do
160
+ expect {
161
+ @dottable_hash.fetch_path!("foo")
162
+ }.to raise_error(KeyError)
163
+ end
164
+
165
+ it "raises key errors when searching into a string" do
166
+ @dottable_hash["foo"] = "bar"
167
+ expect {
168
+ @dottable_hash.fetch_path!("foo.bar")
169
+ }.to raise_error(KeyError)
170
+ end
171
+
172
+ it "does not raise errors for dottable_hashs with suppressed key errors" do
173
+ expect {
174
+ @dottable_hash.fetch_path("foo")
175
+ }.not_to raise_error
176
+
177
+ @dottable_hash["foo"] = "bar"
178
+ expect {
179
+ @dottable_hash.fetch_path("foo.bar")
180
+ }.not_to raise_error
181
+ end
182
+ end
183
+
184
+ describe "#set_path!" do
185
+ it "sets key's corresponding value" do
186
+ @dottable_hash.set_path!("foo", "bar")
187
+ @dottable_hash[:foo].should == "bar"
188
+ end
189
+
190
+ it "sets values for nested paths" do
191
+ @dottable_hash.set_path!("foo.bar.baz", "i'm really in here!")
192
+ {
193
+ :foo => {
194
+ :bar => {
195
+ :baz => "i'm really in here!"
196
+ }
197
+ }
198
+ }.should == @dottable_hash
199
+ end
200
+
201
+ it "does not overwrite the root key" do
202
+ @dottable_hash.set_path!("foo.bar", "bar")
203
+ @dottable_hash.set_path!("foo.baz.one", "baz1")
204
+ @dottable_hash.set_path!("foo.baz.two", "baz2")
205
+ {
206
+ :foo => {
207
+ :bar => "bar",
208
+ :baz => {
209
+ :one => "baz1",
210
+ :two => "baz2"
211
+ }
212
+ }
213
+ }.should == @dottable_hash
214
+ end
215
+ end
216
+
217
+ describe "#merge!" do
218
+ it "works with merged keys as symbols" do
219
+ dottable_hash = DottableHash.new({ stuff: {} })
220
+ dottable_hash.stuff.merge!({ things: "widgets" })
221
+ dottable_hash.stuff.things.should == "widgets"
222
+ end
223
+ end
224
+
225
+ describe "#merge" do
226
+ it "works with merged keys as symbols" do
227
+ dottable_hash = DottableHash.new({ stuff: {} })
228
+ stuff_dottable_hash = dottable_hash.stuff.merge({ things: "widgets" })
229
+ stuff_dottable_hash.things.should == "widgets"
230
+ end
231
+ end
232
+
233
+ end
234
+ end
@@ -123,13 +123,21 @@ module Render
123
123
  end
124
124
  end
125
125
 
126
- describe ".pull" do
126
+ describe "#render" do
127
127
  it "returns its schema's data" do
128
128
  pull = { film: { id: UUID.generate } }
129
- @schema.stub({ pull: pull })
129
+ @schema.stub({ render: pull })
130
130
 
131
131
  graph = Graph.new(@schema)
132
- graph.pull.should == pull
132
+ graph.render.should == pull
133
+ end
134
+
135
+ it "returns a dottable hash" do
136
+ pull = { film: { id: UUID.generate } }
137
+ @schema.stub({ render: pull })
138
+
139
+ graph = Graph.new(@schema)
140
+ graph.render.should be_a(DottableHash)
133
141
  end
134
142
 
135
143
  it "sends interpolated endpoint to its schema" do
@@ -137,8 +145,9 @@ module Render
137
145
  client_id = UUID.generate
138
146
  graph = Graph.new(@schema, { endpoint: endpoint, client_id: client_id })
139
147
 
140
- @schema.should_receive(:pull).with({ endpoint: graph.endpoint }).and_return(@pull)
141
- graph.pull.should == @pull
148
+ pull = { foo: "bar" }
149
+ @schema.should_receive(:render).with({ endpoint: graph.endpoint }).and_return(pull)
150
+ graph.render.should == pull
142
151
  end
143
152
 
144
153
  context "with nested graphs" do
@@ -162,19 +171,19 @@ module Render
162
171
 
163
172
  it "merges nested graphs" do
164
173
  pulled_data = { a: "attribute" }
165
- @director_schema.stub({ pull: pulled_data })
174
+ @director_schema.stub({ render: pulled_data })
166
175
 
167
176
  director = Graph.new(@director_schema)
168
177
  film = Graph.new(@film_schema, { graphs: [director]})
169
178
 
170
- film_graph = film.pull[@film_schema.title.to_sym]
179
+ film_graph = film.render[@film_schema.title.to_sym]
171
180
  film_graph.should include(pulled_data)
172
181
  end
173
182
 
174
183
  it "uses parent data to calculate endpoint" do
175
184
  director_id = UUID.generate
176
185
  film = Graph.new(@film_schema)
177
- film.schema.stub({ pull: { film: { director_id: director_id } } })
186
+ film.schema.stub({ render: { film: { director_id: director_id } } })
178
187
 
179
188
  endpoint = "http://endpoint.local/directors/:id"
180
189
  interpolated_endpoint = "http://endpoint.local/directors/#{director_id}"
@@ -182,11 +191,11 @@ module Render
182
191
  director = Graph.new(@director_schema, { endpoint: endpoint, relationships: relationships })
183
192
 
184
193
  film.graphs << director
185
- director.schema.should_receive(:pull).with do |args|
194
+ director.schema.should_receive(:render).with do |args|
186
195
  args[:endpoint].should == interpolated_endpoint
187
196
  end.and_return({})
188
197
 
189
- film.pull
198
+ film.render
190
199
  end
191
200
 
192
201
  context "offline" do
@@ -195,7 +204,7 @@ module Render
195
204
  director = Graph.new(@director_schema, { relationships: relationships })
196
205
  film = Graph.new(@film_schema, { graphs: [director]})
197
206
 
198
- film_graph = film.pull[@film_schema.title.to_sym]
207
+ film_graph = film.render[@film_schema.title.to_sym]
199
208
  film_graph[@director_schema.title.to_sym][:id].should == film_graph[:director_id]
200
209
  end
201
210
  end
@@ -156,7 +156,7 @@ module Render
156
156
  stub_request(:get, endpoint).to_return(response)
157
157
 
158
158
  film = Schema.new(@film_schema)
159
- film.pull({ endpoint: endpoint }).should == response_body
159
+ film.render({ endpoint: endpoint }).should == response_body
160
160
  end
161
161
 
162
162
  it "raises error if response is not 2xx" do
@@ -166,7 +166,7 @@ module Render
166
166
 
167
167
  expect {
168
168
  film = Schema.new(@film_schema)
169
- film.pull({ endpoint: endpoint })
169
+ film.render({ endpoint: endpoint })
170
170
  }.to raise_error(Errors::Schema::RequestError)
171
171
  end
172
172
 
@@ -175,7 +175,7 @@ module Render
175
175
  stub_request(:get, endpoint).to_return({ body: "Server Error: 500" })
176
176
 
177
177
  expect {
178
- Schema.new(@film_schema).pull({ endpoint: endpoint })
178
+ Schema.new(@film_schema).render({ endpoint: endpoint })
179
179
  }.to raise_error(Errors::Schema::InvalidResponse)
180
180
  end
181
181
  end
@@ -187,7 +187,7 @@ module Render
187
187
 
188
188
  it "returns schema with fake values" do
189
189
  film = Schema.new(@film_schema)
190
- film = film.pull[:film]
190
+ film = film.render[:film]
191
191
  film[:name].should be_a(String)
192
192
  film[:genre].should be_a(String)
193
193
  end
@@ -200,7 +200,7 @@ module Render
200
200
  stub_request(:get, endpoint).to_return(response)
201
201
 
202
202
  film = Schema.new(@film_schema)
203
- film.pull({ endpoint: endpoint }).should == { film: response_body }
203
+ film.render({ endpoint: endpoint }).should == { film: response_body }
204
204
  end
205
205
 
206
206
  it "handles Array responses" do
@@ -217,7 +217,7 @@ module Render
217
217
  stub_request(:get, endpoint).to_return(response)
218
218
 
219
219
  film = Schema.new(@films_schema)
220
- film.pull({ endpoint: endpoint }).should == { films: response_body }
220
+ film.render({ endpoint: endpoint }).should == { films: response_body }
221
221
  end
222
222
  end
223
223
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: render
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -137,13 +137,13 @@ files:
137
137
  - Gemfile
138
138
  - Gemfile.lock
139
139
  - LICENSE.txt
140
- - extensions/boolean.rb
141
- - extensions/dottable_hash.rb
142
- - extensions/enumerable.rb
143
- - extensions/hash.rb
144
140
  - initialize.rb
141
+ - lib/extensions/boolean.rb
142
+ - lib/extensions/enumerable.rb
143
+ - lib/extensions/hash.rb
145
144
  - lib/render.rb
146
145
  - lib/render/attribute.rb
146
+ - lib/render/dottable_hash.rb
147
147
  - lib/render/errors.rb
148
148
  - lib/render/generator.rb
149
149
  - lib/render/graph.rb
@@ -163,6 +163,7 @@ files:
163
163
  - spec/support/helpers.rb
164
164
  - spec/unit/extensions/boolean_spec.rb
165
165
  - spec/unit/render/attribute_spec.rb
166
+ - spec/unit/render/dottable_hash_spec.rb
166
167
  - spec/unit/render/generator_spec.rb
167
168
  - spec/unit/render/graph_spec.rb
168
169
  - spec/unit/render/schema_spec.rb
@@ -182,7 +183,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
183
  version: '0'
183
184
  segments:
184
185
  - 0
185
- hash: 4037297994786414252
186
+ hash: 1123791212905751399
186
187
  required_rubygems_version: !ruby/object:Gem::Requirement
187
188
  none: false
188
189
  requirements:
@@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
191
192
  version: '0'
192
193
  segments:
193
194
  - 0
194
- hash: 4037297994786414252
195
+ hash: 1123791212905751399
195
196
  requirements: []
196
197
  rubyforge_project:
197
198
  rubygems_version: 1.8.25
@@ -211,6 +212,7 @@ test_files:
211
212
  - spec/support/helpers.rb
212
213
  - spec/unit/extensions/boolean_spec.rb
213
214
  - spec/unit/render/attribute_spec.rb
215
+ - spec/unit/render/dottable_hash_spec.rb
214
216
  - spec/unit/render/generator_spec.rb
215
217
  - spec/unit/render/graph_spec.rb
216
218
  - spec/unit/render/schema_spec.rb
@@ -1,113 +0,0 @@
1
- class DottableHash < Hash
2
- class << self
3
- def new(element_to_hash = {})
4
- hash = super().merge!(element_to_hash.symbolize_keys)
5
- hash.each do |key, value|
6
- hash[key] = initialize_element(value)
7
- end
8
- hash
9
- end
10
-
11
- def initialize_element(value)
12
- case value
13
- when Hash
14
- new(value)
15
- when Array
16
- value.collect { |v| initialize_element(v) }
17
- else
18
- value
19
- end
20
- end
21
- end
22
-
23
- def []=(key, value)
24
- key = key.to_sym
25
- value = self.class.initialize_element(value)
26
- super
27
- end
28
-
29
- def [](key)
30
- key = key.to_sym
31
- super
32
- end
33
-
34
- def delete(key)
35
- key = key.to_sym
36
- super
37
- end
38
-
39
- def has_key?(key)
40
- super(key.to_sym)
41
- end
42
-
43
- def merge!(other_hash)
44
- super(other_hash.symbolize_keys)
45
- end
46
-
47
- def merge(other_hash)
48
- super(other_hash.symbolize_keys)
49
- self
50
- end
51
-
52
- def method_missing(method, *arguments)
53
- if method.match(/\=$/)
54
- self[method.to_sym.chop] = arguments.first
55
- elsif has_key?(method)
56
- self[method]
57
- else
58
- super
59
- end
60
- end
61
-
62
- def fetch(key)
63
- key = key.to_sym
64
- raise KeyError.new("Invalid key: #{key}") unless has_key?(key)
65
- super
66
- end
67
-
68
- def fetch_path(full_path)
69
- begin
70
- fetch_path!(full_path)
71
- rescue KeyError
72
- nil
73
- end
74
- end
75
-
76
- def fetch_path!(full_path)
77
- full_path.split(".").inject(self) do |hash, path|
78
- raise(KeyError) unless hash.is_a?(Hash)
79
-
80
- hash.fetch(path)
81
- end
82
- end
83
-
84
- def set_path(full_path, value)
85
- self.dup.set_path!(full_path, value)
86
- end
87
-
88
- def set_path!(full_path, value)
89
- built_hash_for_value = full_path.split(".").reverse.inject({}) do |cumulative, path_to_symource|
90
- if cumulative.empty?
91
- { path_to_symource.to_sym => value }
92
- else
93
- { path_to_symource.to_sym => cumulative }
94
- end
95
- end
96
-
97
- deep_merge!(built_hash_for_value)
98
- end
99
-
100
- protected
101
-
102
- def deep_merge(other_hash)
103
- merge(other_hash) do |key, oldval, newval|
104
- oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
105
- newval = newval.to_hash if newval.respond_to?(:to_hash)
106
- oldval.is_a?(Hash) && newval.is_a?(Hash) ? self.class.new(oldval).deep_merge(newval) : self.class.new(newval)
107
- end
108
- end
109
-
110
- def deep_merge!(other_hash)
111
- replace(deep_merge(other_hash))
112
- end
113
- end
@@ -1,36 +0,0 @@
1
- ::Enumerable.module_eval do
2
- # This is named the same as its Hash counterpart for a reason. I'm not
3
- # going to tell you why, consider it my riddle for you.
4
- def recursive_symbolize_keys!
5
- each do |item|
6
- item.recursive_symbolize_keys! if item.respond_to?(:recursive_symbolize_keys!)
7
- end
8
- self
9
- end
10
-
11
- def recursive_stringify_keys!
12
- each do |item|
13
- item.recursive_stringify_keys! if item.respond_to?(:recursive_stringify_keys!)
14
- end
15
- end
16
-
17
- def hardcode(enumerable)
18
- dup.hardcode!(enumerable)
19
- end
20
-
21
- def hardcode!(enumerable)
22
- if self.empty? && enumerable.any?
23
- enumerable
24
- else
25
- each_with_index do |a, index|
26
- if a.is_a?(Hash)
27
- self[index] = a.hardcode(enumerable[index]) if enumerable[index]
28
- elsif a.is_a?(Enumerable)
29
- self[index] = a.collect { |v| a.hardcode(enumerable[index]) }
30
- else
31
- self[index] = enumerable[index] if enumerable[index]
32
- end
33
- end
34
- end
35
- end
36
- end