api-resource 0.2.0 → 0.3.0

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