api-resource 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9705d238f913f0554c790cfe2958570fac35ba1
4
- data.tar.gz: f6038bdafafd1544a5a49bd633ba87e5ff01b9c0
3
+ metadata.gz: 2f52282f574b5922a5f27ee524075ea79307b3cf
4
+ data.tar.gz: e713e470bb8e7a4f267c25ca94caf113adcf3ffb
5
5
  SHA512:
6
- metadata.gz: ec9cd10d4e3e5bc87931e9b9b69bf15f5ca266a706cf21128e5f2e9678ee119c8d82cee0d284d711a0ec457af0a3f2ab041897a584a61c9b3306e6e0c9d53a36
7
- data.tar.gz: 4641956e83fcb957bb54319afb7229792eaa863aad170e6b53e23e5969f965fc782c9677cd7509b13a05d093537cc2a1ac26bcd3bf8a9101e9a69e3117c75804
6
+ metadata.gz: 6a23bdd034f812966a75a8c36ef5fbd5b6496c52c7f3e95971c3a9b01e16742b77acf2d8054ed262f04d3b1b8b8f3db90d12279f80538714c3f3e88e5fe9d8da
7
+ data.tar.gz: 22ca88e67fb933867c2fc09076b0ef31cf24e2ecab4f1595b6169518952f34b3519ff8908d6912e10445b2629ba7c9acdbaebeec883dc138885a39e3c46cb265
@@ -7,7 +7,6 @@ require 'active_support/core_ext/object/to_param'
7
7
 
8
8
  module ApiResource
9
9
  class Resource
10
-
11
10
  include ActiveModel::Model
12
11
  include ActiveModel::Serializers::JSON
13
12
 
@@ -34,19 +33,21 @@ module ApiResource
34
33
 
35
34
  def self.find(id)
36
35
  result = client(:get, {}, resource_name, id)
37
- self.new.from_json(result)
36
+ json = JSON.parse(result)
37
+ self.new(json['data'])
38
38
  end
39
39
 
40
40
  def self.all
41
41
  result = client(:get, {}, resource_name)
42
- array = JSON.parse(result)
43
- array.map { |h| self.new(h) }
42
+ json = JSON.parse(result)
43
+ ResourceCollection.new(json['data'], json['meta'], self)
44
44
  end
45
45
 
46
+
46
47
  def self.where(options, verb=:get)
47
48
  result = client(verb, *(verb==:get ? [{}, "#{resource_name}?#{options.to_param}"] : [options, resource_name]))
48
- array = JSON.parse(result)
49
- array.map { |h| self.new(h) }
49
+ json = JSON.parse(result)
50
+ ResourceCollection.new(json['data'], json['meta'], self)
50
51
  end
51
52
 
52
53
  def attributes
@@ -60,15 +61,17 @@ module ApiResource
60
61
  if respond_to? "#{key}="
61
62
  send("#{key}=", value)
62
63
  else
63
- child_class = self.class.load_class(key)
64
- if child_class && child_class < Resource
65
- if value.is_a?(Hash)
66
- child_value = child_class.new(value)
67
- define_singleton_method(key) { child_value }
68
- elsif value.is_a?(Array)
69
- child_values = value.map { |h| child_class.new(h) }
70
- define_singleton_method(key) { child_values }
71
- end
64
+ # check for embedded resource
65
+ next unless value.is_a?(Hash) && value['data']
66
+ child_class = self.class.load_class((value['meta'] && value['meta']['type']) || key)
67
+ next unless child_class
68
+ data = value['data']
69
+ if data.is_a?(Hash)
70
+ child_value = child_class.new(data)
71
+ define_singleton_method(key) { child_value }
72
+ elsif data.is_a?(Array)
73
+ child_values = ResourceCollection.new(data, value['meta'], child_class)
74
+ define_singleton_method(key) { child_values }
72
75
  end
73
76
  end
74
77
  end
@@ -84,14 +87,15 @@ module ApiResource
84
87
  end
85
88
  end
86
89
 
87
- def self.load_class(plural_resource_name)
88
- plural_resource_name.to_s.singularize.classify.constantize rescue nil
89
- end
90
-
91
90
  def self.with_parent_resource(parent_resource_id, parent_resource_name)
92
91
  result = client(:get, {}, parent_resource_name.pluralize, parent_resource_id, self.resource_name)
93
- hashes = JSON.parse(result)
94
- hashes.map { |h| self.new(h) }
92
+ json = JSON.parse(result)
93
+ ResourceCollection.new(json['data'], json['meta'], self)
94
+ end
95
+
96
+ def self.load_class(tableized_class)
97
+ klass = tableized_class && tableized_class.to_s.singularize.classify.constantize rescue nil
98
+ klass if klass < ApiResource::Resource
95
99
  end
96
100
 
97
101
  def self.client(verb, params, *paths)
@@ -111,5 +115,18 @@ module ApiResource
111
115
  end
112
116
  req.execute
113
117
  end
118
+
119
+ class ResourceCollection
120
+ include Enumerable
121
+
122
+ def initialize(data, meta, klass)
123
+ meta.each { |k, v| self.class.class_eval { define_method(k) { v } } } if meta
124
+ @resources = data.map { |e| klass.new(e) }
125
+ end
126
+
127
+ def each(&block)
128
+ @resources.each(&block)
129
+ end
130
+ end
114
131
  end
115
132
  end
@@ -1,3 +1,3 @@
1
1
  module ApiResource
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -16,94 +16,133 @@ RSpec.describe ApiResource::Resource do
16
16
  attr_accessor :id, :name
17
17
  end
18
18
 
19
+ def check_returned_array(type, expected, actual)
20
+ expect(actual).to be_a Enumerable
21
+ actual.each { |e| expect(e).to be_a type }
22
+ expect(actual.map { |e| e.attributes }).to match (expected[:data])
23
+ end
24
+
25
+ def check_returned_object(type, expected, actual, *except)
26
+ expect(actual).to be_a(type)
27
+ expect(actual.attributes).to match(expected[:data].except(*except))
28
+ end
29
+
30
+ before do
31
+ @expected_resources = { data: [{ id: 1257, title: 'Hello' },
32
+ { id: 33, title: 'World' },
33
+ { id: 38, title: 'Bonjour!' }] }
34
+ end
19
35
  context '#find' do
36
+
20
37
  it 'creates the resource when found' do
21
- resource_values = { 'id' => 1257, 'title' => 'Hello' }
22
- resource_id = 3
38
+ expected_value = { data: { id: 1257, title: 'Hello' } }
39
+ resource_id = 3
23
40
  stub_request(:get, "#{Blog.base_url}/blogs/#{resource_id}").
24
41
  with(headers: { 'Accept' => 'application/json' }).
25
- to_return(status: 200, body: resource_values.to_json)
26
-
42
+ to_return(status: 200, body: expected_value.to_json)
27
43
  result = Blog.find(resource_id)
28
- expect(result).to be_a Blog
29
- expect(result.attributes).to match (resource_values)
44
+ check_returned_object(Blog, expected_value, result)
30
45
  end
31
46
  end
32
47
 
33
48
  context '#all' do
34
- it 'fetches all resourced' do
35
- resource_values = [{ id: 1257, title: 'Hello' },
36
- { id: 33, title: 'World' },
37
- { id: 38, title: 'Bonjour!' }]
38
49
 
50
+ it 'fetches all resources' do
39
51
  stub_request(:get, "#{Blog.base_url}/blogs").
40
52
  with(headers: { 'Accept' => 'application/json' }).
41
- to_return(status: 200, body: resource_values.to_json)
53
+ to_return(status: 200, body: @expected_resources.to_json)
42
54
 
43
- blogs = Blog.all
44
-
45
- expect(blogs).to be_a Array
46
- blogs.each { |e| expect(e).to be_a Blog }
47
- expect(blogs.map { |e| e.attributes }).to match (resource_values)
55
+ result = Blog.all
56
+ check_returned_array(Blog, @expected_resources, result)
48
57
  end
49
58
  end
50
59
 
51
60
  context '#where' do
52
- before do
53
- @resource_values = [{ id: 1257, title: 'Hello' },
54
- { id: 33, title: 'World' },
55
- { id: 38, title: 'Bonjour!' }]
56
- end
61
+
57
62
  it 'fetches resourced filtered by query string with GET' do
58
63
  stub_request(:get, "#{Blog.base_url}/blogs?title=hello+world&tags%5B%5D=hello&tags%5B%5D=first&tags%5B%5D=greeting").
59
64
  with(headers: { 'Accept' => 'application/json' }).
60
- to_return(status: 200, body: @resource_values.to_json)
61
-
62
- blogs = Blog.where(title: 'hello world', tags: ['hello', 'first', 'greeting'])
65
+ to_return(status: 200, body: @expected_resources.to_json)
63
66
 
64
- expect(blogs).to be_a Array
65
- blogs.each { |e| expect(e).to be_a Blog }
66
- expect(blogs.map { |e| e.attributes }).to match (@resource_values)
67
+ result = Blog.where(title: 'hello world', tags: ['hello', 'first', 'greeting'])
68
+ check_returned_array(Blog, @expected_resources, result)
67
69
  end
68
70
 
69
- it 'fetches resourced filtered by query string with POST' do
71
+ it 'fetches resources filtered by query string with POST' do
70
72
  stub_request(:post, "#{Blog.base_url}/blogs").
71
73
  with(headers: { 'Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded' },
72
74
  body: 'title=hello%20world&tags[]=hello&tags[]=first&tags[]=greeting').
73
- to_return(status: 200, body: @resource_values.to_json)
74
-
75
- blogs = Blog.where({ title: 'hello world', tags: ['hello', 'first', 'greeting'] }, :post)
75
+ to_return(status: 200, body: @expected_resources.to_json)
76
76
 
77
- expect(blogs).to be_a Array
78
- blogs.each { |e| expect(e).to be_a Blog }
79
- expect(blogs.map { |e| e.attributes }).to match (@resource_values)
77
+ result = Blog.where({ title: 'hello world', tags: ['hello', 'first', 'greeting'] }, :post)
78
+ check_returned_array(Blog, @expected_resources, result)
80
79
  end
81
80
  end
82
81
 
83
82
  context '#by_parent_resource' do
83
+
84
84
  it 'fetches resource nested with parent resource' do
85
- resource_values = [{ id: 1257, title: 'Hello' },
86
- { id: 33, title: 'World' }]
87
- resource_id = 3
85
+ resource_id = 3
88
86
 
89
87
  stub_request(:get, "#{Blog.base_url}/tags/#{resource_id}/blogs").
90
88
  with(headers: { 'Accept' => 'application/json' }).
91
- to_return(status: 200, body: resource_values.to_json)
92
-
93
- blogs = Blog.by_tag(resource_id)
89
+ to_return(status: 200, body: @expected_resources.to_json)
94
90
 
95
- expect(blogs).to be_a Array
96
- blogs.each { |e| expect(e).to be_a Blog }
97
- expect(blogs.map { |e| e.attributes }).to match (resource_values)
91
+ result = Blog.by_tag(resource_id)
92
+ check_returned_array(Blog, @expected_resources, result)
98
93
  end
99
94
  end
100
95
 
101
96
  context '#child_ressources' do
102
- it 'has_many association with inlined json' do
103
- resource_values = {
104
- id: 1257, title: 'Tarte a la creme',
105
- tags: [{ id: 33, name: 'food' }, { id: 39, name: 'cuisine' }]
106
- }
97
+ context 'has_many' do
98
+ it 'defines a method and parses resources from inlined json array' do
99
+ resource_values = { data: {
100
+ id: 1257, title: 'Tarte a la creme',
101
+ tags: { data: [{ id: 33, name: 'food' }, { id: 39, name: 'cuisine' }] }
102
+ } }
103
+ resource_id = 3
104
+
105
+ stub_request(:get, "#{Blog.base_url}/blogs/#{resource_id}").
106
+ with(headers: { 'Accept' => 'application/json' }).
107
+ to_return(status: 200, body: resource_values.to_json)
108
+
109
+ blog = Blog.find(resource_id)
110
+ check_returned_object(Blog, resource_values, blog, :tags)
111
+ check_returned_array(Tag, resource_values[:data][:tags], blog.tags)
112
+ end
113
+
114
+ it 'override type and retrieve meta info' do
115
+ resource_values = {
116
+ data: {
117
+ id: 1257,
118
+ title: 'Tarte a la creme',
119
+ my_sweet_tags: {
120
+ data: [{ id: 33, name: 'food' }, { id: 39, name: 'cuisine' }],
121
+ meta: { type: :tags, page: 3, per_page: 50, total: 102 }
122
+ }
123
+ }
124
+ }
125
+ resource_id = 3
126
+
127
+ stub_request(:get, "#{Blog.base_url}/blogs/#{resource_id}").
128
+ with(headers: { 'Accept' => 'application/json' }).
129
+ to_return(status: 200, body: resource_values.to_json)
130
+
131
+ blog = Blog.find(resource_id)
132
+ check_returned_object(Blog, resource_values, blog, :my_sweet_tags)
133
+ check_returned_array(Tag, resource_values[:data][:my_sweet_tags], blog.my_sweet_tags)
134
+
135
+ expect(blog.my_sweet_tags.page).to eq(3)
136
+ expect(blog.my_sweet_tags.per_page).to eq(50)
137
+ expect(blog.my_sweet_tags.total).to eq(102)
138
+ end
139
+ end
140
+
141
+ it 'has_one association with inlined json' do
142
+ resource_values = { data: {
143
+ id: 1257, title: 'Tarte a la creme',
144
+ category: { data: { id: 33, name: 'Hobbies' } }
145
+ } }
107
146
  resource_id = 3
108
147
 
109
148
  stub_request(:get, "#{Blog.base_url}/blogs/#{resource_id}").
@@ -111,18 +150,20 @@ RSpec.describe ApiResource::Resource do
111
150
  to_return(status: 200, body: resource_values.to_json)
112
151
 
113
152
  blog = Blog.find(resource_id)
114
- expect(blog).to be_a Blog
115
- expect(blog.attributes).to match(resource_values.except :tags)
116
-
117
- tags = blog.tags
118
- expect(tags).to be_a Array
119
- tags.each { |e| expect(e).to be_a Tag }
120
- expect(tags.map { |e| e.attributes }).to match (resource_values[:tags])
153
+ check_returned_object(Blog, resource_values, blog, :category)
154
+ check_returned_object(Category, resource_values[:data][:category], blog.category)
121
155
  end
122
- it 'has_one association with inlined json' do
156
+
157
+ it 'override type from meta' do
123
158
  resource_values = {
124
- id: 1257, title: 'Tarte a la creme',
125
- category: { id: 33, name: 'Hobbies' }
159
+ data: {
160
+ id: 1257,
161
+ title: 'Tarte a la creme',
162
+ main_category: {
163
+ data: { id: 33, name: 'Hobbies' },
164
+ meta: { type: :category }
165
+ }
166
+ }
126
167
  }
127
168
  resource_id = 3
128
169
 
@@ -131,12 +172,8 @@ RSpec.describe ApiResource::Resource do
131
172
  to_return(status: 200, body: resource_values.to_json)
132
173
 
133
174
  blog = Blog.find(resource_id)
134
- expect(blog).to be_a Blog
135
- expect(blog.attributes).to match(resource_values.except :category)
136
-
137
- category = blog.category
138
- expect(category).to be_a Category
139
- expect(category.attributes).to match(resource_values[:category])
175
+ check_returned_object(Blog, resource_values, blog, :main_category)
176
+ check_returned_object(Category, resource_values[:data][:main_category], blog.main_category)
140
177
  end
141
178
  end
142
179
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chaker Nakhli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-09 00:00:00.000000000 Z
11
+ date: 2015-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: simple-hmac