render 0.0.1 → 0.0.2

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/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