restful_resource 0.0.11 → 0.8.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: eb0baa3d62df8c2cf76abb026cf3f3d66f9cdd01
4
- data.tar.gz: 0e623b12bc7f5ea6add379f80ece6eb88f91c2a0
3
+ metadata.gz: 5e4f4e6af3b1bee81026d51b88de88305d854791
4
+ data.tar.gz: ccc94c88defe5f2867214778ba49b27af23ea42f
5
5
  SHA512:
6
- metadata.gz: e98a1b3a5b2d105c6cd520bd3c456b7cdeeef58c0993be275bfa6d90d9bad41ed701d17099268f0eb2a0787948bb1d32ff8bcf23658bd925ca5ffc91171546f8
7
- data.tar.gz: cce5a8ea9f6a743e4e0c2f65954426d744ab03584fb26274dcb4c814124a0f518aa8c9f5005222769191683f35f60b669d8d2fb6273f6b384ce2e379fb39cbcc
6
+ metadata.gz: b276550299aa86e9574211dd018dd3e86a79c357d79ed05a6bdd1d9830b47e340e904a51ff80caa4c9dd8d32256217200146b183b367cc428e4edd5796f6ef88
7
+ data.tar.gz: 9c4eb7a4326eac86a7cc23e0c25fe1881ad9e6a968cd4da65f693d80efbc216abbb4c1284a0edccf6e1369f42672f6730fae25a7719ad770754d4b31d0c785d6
data/README.md CHANGED
@@ -27,3 +27,14 @@ TODO: Write usage instructions here
27
27
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
28
  4. Push to the branch (`git push origin my-new-feature`)
29
29
  5. Create new Pull Request
30
+
31
+ ## Planned Features
32
+
33
+ ### Core
34
+ - Test that has_many and has_one pick correct class for children (if defined)
35
+ - Make base_url resilient when missing trailing slash
36
+ - Implement http authentication
37
+
38
+ ### Active record style validation
39
+
40
+ ### Constraints(mandatory fields)
@@ -0,0 +1,23 @@
1
+ module RestfulResource
2
+ module Associations
3
+ def has_many(nested_resource_type)
4
+ klass = nested_resource_type.to_s.singularize.camelize.safe_constantize
5
+ klass = OpenStruct if (klass.nil? || (klass.superclass != RestfulResource))
6
+
7
+ self.send(:define_method, nested_resource_type) do
8
+ @inner_object.send(nested_resource_type).map { |obj| klass.new(obj) }
9
+ end
10
+ end
11
+
12
+ def has_one(nested_resource_type)
13
+ klass = nested_resource_type.to_s.camelize.safe_constantize
14
+ klass = OpenStruct if (klass.nil? || klass.superclass != RestfulResource)
15
+
16
+ self.send(:define_method, nested_resource_type) do
17
+ nested_resource = @inner_object.send(nested_resource_type)
18
+ return nil if nested_resource.nil?
19
+ klass.new(@inner_object.send(nested_resource_type))
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,158 +1,107 @@
1
1
  module RestfulResource
2
- class Base
3
- def self.url=(url)
4
- @url = url
5
- end
6
-
7
- def self.processed_url_and_params(params={})
8
- uri = URI(@url)
9
- path = uri.path
10
- other_params = params.clone
11
- missing_params = []
2
+ class Base < OpenObject
3
+ extend RestfulResource::Associations
12
4
 
13
- path_params = path.scan(/:([A-Za-z][^\/]*)/).flatten
14
- path_params.each do |key|
15
- value = other_params.delete(key.to_sym)
16
- if value.nil?
17
- missing_params << key
18
- else
19
- path = path.gsub(':'+key.to_s, value.to_s)
20
- end
21
- end
22
-
23
- if missing_params.any?
24
- raise ParameterMissingError.new(missing_params)
25
- end
26
-
27
- uri.path = path
28
- [uri.to_s, other_params]
29
- end
30
-
31
- def self.url(params={})
32
- processed_url_and_params(params).first
33
- end
34
-
35
-
36
- def self.has_one(nested_resource_type)
37
- klass = nested_resource_type.to_s.camelize.safe_constantize
38
- klass = OpenStruct if (klass.nil? || klass.superclass != RestfulResource)
39
-
40
- self.send(:define_method, nested_resource_type) do
41
- nested_resource = @inner_object.send(nested_resource_type)
42
- return nil if nested_resource.nil?
43
- klass.new(@inner_object.send(nested_resource_type))
44
- end
5
+ def self.http=(http)
6
+ @@http = http
45
7
  end
46
8
 
47
- def self.has_many(nested_resource_type)
48
- klass = nested_resource_type.to_s.singularize.camelize.safe_constantize
49
- klass = OpenStruct if (klass.nil? || (klass.superclass != RestfulResource))
50
-
51
- self.send(:define_method, nested_resource_type) do
52
- @inner_object.send(nested_resource_type).map { |obj| klass.new(obj) }
53
- end
9
+ def self.http
10
+ @@http ||= RestfulResource::HttpClient.new()
54
11
  end
55
12
 
56
- def initialize(attributes = {}, hack_for_activeresource = false)
57
- @inner_object = OpenStruct.new(attributes)
13
+ def self.base_url=(url)
14
+ @base_url = URI.parse(url)
58
15
  end
59
16
 
60
- def method_missing(method)
61
- if @inner_object.respond_to?(method)
62
- @inner_object.send(method)
63
- else
64
- super(method)
65
- end
17
+ def self.base_url
18
+ @base_url
66
19
  end
67
20
 
68
- def respond_to?(method, include_private = false)
69
- super || @inner_object.respond_to?(method, include_private)
21
+ def self.resource_path(url)
22
+ @resource_path = url
70
23
  end
71
24
 
72
- def valid?
73
- errors.nil? || errors.count == g
25
+ def self.find(id, params={})
26
+ response = http.get(member_url(id, params))
27
+ self.new(parse_json(response.body))
74
28
  end
75
29
 
76
- def self.find(id, url_params={})
77
- response = RestClient.get("#{url(url_params)}/#{id}", params: {})
78
- self.new(ActiveSupport::JSON.decode(response))
30
+ def self.where(params={})
31
+ response = http.get(collection_url(params))
32
+ self.paginate_response(response)
79
33
  end
80
34
 
81
- def self.get_one(url_params={})
82
- resource = create_new_resource(url_params)
83
- response = resource.get
84
- self.new(ActiveSupport::JSON.decode(response))
35
+ def self.get(params={})
36
+ response = http.get(collection_url(params))
37
+ RestfulResource::OpenObject.new(parse_json(response.body))
85
38
  end
86
39
 
87
- def self.update_attributes(id, attributes)
88
- begin
89
- result = parse(RestClient.put("#{url}/#{id}", attributes))
90
- rescue RestClient::UnprocessableEntity => e
91
- errors = parse(e.response)
92
- result = attributes.merge(errors: errors)
93
- end
94
- self.new(result)
40
+ def self.all
41
+ self.where
95
42
  end
96
43
 
97
- def self.create(attributes)
98
- begin
99
- result = parse(RestClient.post("#{url}", attributes))
100
- rescue RestClient::UnprocessableEntity => e
101
- errors = parse(e.response)
102
- result = attributes.merge(errors: errors)
103
- end
104
- self.new(result)
44
+ def self.action(action_name)
45
+ clone = self.clone
46
+ clone.action_prefix = action_name
47
+ clone
105
48
  end
106
49
 
107
- def self.search(params = {})
108
- response = RestClient.get("#{url}/search", params: params)
109
- paginate_response(response)
50
+ def self.action_prefix=(action_prefix)
51
+ @action_prefix = action_prefix.to_s
110
52
  end
111
53
 
112
- def self.all(params = {})
113
- resource = create_new_resource(params)
114
- response = resource.get
115
- paginate_response(response)
54
+ def as_json(options=nil)
55
+ @inner_object.send(:table).as_json(options)
116
56
  end
117
57
 
118
- def self.get(postfix_url = "", params = {})
119
- response = RestClient.get("#{url}#{postfix_url}", params: params)
120
- paginate_response(response)
58
+ private
59
+ def self.merge_url_paths(uri, *paths)
60
+ uri.merge(paths.compact.join('/')).to_s
121
61
  end
122
62
 
123
- def self.put(postfix_url = "", params = {})
124
- response = RestClient.put("#{url}#{postfix_url}", params)
63
+ def self.member_url(id, params)
64
+ url = merge_url_paths(superclass.base_url, @resource_path, id, @action_prefix)
65
+ replace_parameters(url, params)
125
66
  end
126
67
 
127
- def self.post(postfix_url = "", params = {})
128
- response = RestClient.post("#{url}#{postfix_url}", params)
68
+ def self.collection_url(params)
69
+ url = merge_url_paths(superclass.base_url, @resource_path, @action_prefix)
70
+ replace_parameters(url, params)
129
71
  end
130
72
 
131
- def self.all_not_paginated
132
- page = 1
133
- results = []
134
- while page
135
- page_data = self.all(page: page);
136
- results += page_data
137
- page = page_data.next_page
73
+ def self.new_collection(json)
74
+ json.map do |element|
75
+ self.new(element)
138
76
  end
139
-
140
- results
141
77
  end
142
78
 
143
- def self.parse(json)
79
+ def self.parse_json(json)
144
80
  ActiveSupport::JSON.decode(json)
145
81
  end
146
82
 
147
- def to_json(*args)
148
- @inner_object.send(:table).to_json(*args)
149
- end
83
+ def self.replace_parameters(url, params)
84
+ missing_params = []
85
+ params = params.with_indifferent_access
150
86
 
151
- def as_json(*)
152
- @inner_object.send(:table).as_json
87
+ url_params = url.scan(/:([A-Za-z][^\/]*)/).flatten
88
+ url_params.each do |key|
89
+ value = params.delete(key)
90
+ if value.nil?
91
+ missing_params << key
92
+ else
93
+ url = url.gsub(':'+key, value.to_s)
94
+ end
95
+ end
96
+
97
+ if missing_params.any?
98
+ raise ParameterMissingError.new(missing_params)
99
+ end
100
+
101
+ url = url + "?#{params.to_query}" unless params.empty?
102
+ url
153
103
  end
154
104
 
155
- private
156
105
  def self.paginate_response(response)
157
106
  links_header = response.headers[:links]
158
107
  links = LinkHeader.parse(links_header)
@@ -160,15 +109,8 @@ module RestfulResource
160
109
  prev_url = links.find_link(['rel', 'prev']).try(:href)
161
110
  next_url = links.find_link(['rel', 'next']).try(:href)
162
111
 
163
- array = ActiveSupport::JSON.decode(response).map { |attributes| self.new(attributes) }
112
+ array = parse_json(response.body).map { |attributes| self.new(attributes) }
164
113
  PaginatedArray.new(array, previous_page_url: prev_url, next_page_url: next_url)
165
114
  end
166
-
167
- def self.create_new_resource(params={})
168
- url, other_params = processed_url_and_params(params)
169
- url += "?#{other_params.to_query}" if not other_params.empty?
170
- resource = RestClient::Resource.new("#{url}")
171
- end
172
-
173
115
  end
174
116
  end
@@ -0,0 +1,8 @@
1
+ module RestfulResource
2
+ class HttpClient
3
+ def get(url)
4
+ response = RestClient.get(url, :accept => :json)
5
+ Response.new(body: response.body, headers: response.headers)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,172 @@
1
+ module RestfulResource
2
+ class OldBase
3
+ def self.url=(url)
4
+ @url = url
5
+ end
6
+
7
+ def self.processed_url_and_params(params={})
8
+ url = @url
9
+ other_params = params.clone
10
+ missing_params = []
11
+
12
+ url_params = url.scan(/:([A-Za-z][^\/]*)/).flatten
13
+ url_params.each do |key|
14
+ value = other_params.delete(key.to_sym)
15
+ if value.nil?
16
+ missing_params << key
17
+ else
18
+ url = url.gsub(':'+key.to_s, value.to_s)
19
+ end
20
+ end
21
+
22
+ if missing_params.any?
23
+ raise ParameterMissingError.new(missing_params)
24
+ end
25
+
26
+ [url, other_params]
27
+ end
28
+
29
+ def self.url(params={})
30
+ processed_url_and_params(params).first
31
+ end
32
+
33
+
34
+ def self.has_one(nested_resource_type)
35
+ klass = nested_resource_type.to_s.camelize.safe_constantize
36
+ klass = OpenStruct if (klass.nil? || klass.superclass != RestfulResource)
37
+
38
+ self.send(:define_method, nested_resource_type) do
39
+ nested_resource = @inner_object.send(nested_resource_type)
40
+ return nil if nested_resource.nil?
41
+ klass.new(@inner_object.send(nested_resource_type))
42
+ end
43
+ end
44
+
45
+ def self.has_many(nested_resource_type)
46
+ klass = nested_resource_type.to_s.singularize.camelize.safe_constantize
47
+ klass = OpenStruct if (klass.nil? || (klass.superclass != RestfulResource))
48
+
49
+ self.send(:define_method, nested_resource_type) do
50
+ @inner_object.send(nested_resource_type).map { |obj| klass.new(obj) }
51
+ end
52
+ end
53
+
54
+ def initialize(attributes = {}, hack_for_activeresource = false)
55
+ @inner_object = OpenStruct.new(attributes)
56
+ end
57
+
58
+ def method_missing(method)
59
+ if @inner_object.respond_to?(method)
60
+ @inner_object.send(method)
61
+ else
62
+ super(method)
63
+ end
64
+ end
65
+
66
+ def respond_to?(method, include_private = false)
67
+ super || @inner_object.respond_to?(method, include_private)
68
+ end
69
+
70
+ def valid?
71
+ errors.nil? || errors.count == g
72
+ end
73
+
74
+ def self.find(id, url_params={})
75
+ response = RestClient.get("#{url(url_params)}/#{id}", params: {})
76
+ self.new(ActiveSupport::JSON.decode(response))
77
+ end
78
+
79
+ def self.get_one(url_params={})
80
+ resource = create_new_resource(url_params)
81
+ response = resource.get
82
+ self.new(ActiveSupport::JSON.decode(response))
83
+ end
84
+
85
+ def self.update_attributes(id, attributes)
86
+ begin
87
+ result = parse(RestClient.put("#{url}/#{id}", attributes))
88
+ rescue RestClient::UnprocessableEntity => e
89
+ errors = parse(e.response)
90
+ result = attributes.merge(errors: errors)
91
+ end
92
+ self.new(result)
93
+ end
94
+
95
+ def self.create(attributes)
96
+ begin
97
+ result = parse(RestClient.post("#{url}", attributes))
98
+ rescue RestClient::UnprocessableEntity => e
99
+ errors = parse(e.response)
100
+ result = attributes.merge(errors: errors)
101
+ end
102
+ self.new(result)
103
+ end
104
+
105
+ def self.search(params = {})
106
+ response = RestClient.get("#{url}/search", params: params)
107
+ paginate_response(response)
108
+ end
109
+
110
+ def self.all(params = {})
111
+ resource = create_new_resource(params)
112
+ response = resource.get
113
+ paginate_response(response)
114
+ end
115
+
116
+ def self.get(postfix_url = "", params = {})
117
+ response = RestClient.get("#{url}#{postfix_url}", params: params)
118
+ paginate_response(response)
119
+ end
120
+
121
+ def self.put(postfix_url = "", params = {})
122
+ response = RestClient.put("#{url}#{postfix_url}", params)
123
+ end
124
+
125
+ def self.post(postfix_url = "", params = {})
126
+ response = RestClient.post("#{url}#{postfix_url}", params)
127
+ end
128
+
129
+ def self.all_not_paginated
130
+ page = 1
131
+ results = []
132
+ while page
133
+ page_data = self.all(page: page);
134
+ results += page_data
135
+ page = page_data.next_page
136
+ end
137
+
138
+ results
139
+ end
140
+
141
+ def self.parse(json)
142
+ ActiveSupport::JSON.decode(json)
143
+ end
144
+
145
+ def to_json(*args)
146
+ @inner_object.send(:table).to_json(*args)
147
+ end
148
+
149
+ def as_json(*)
150
+ @inner_object.send(:table).as_json
151
+ end
152
+
153
+ private
154
+ def self.paginate_response(response)
155
+ links_header = response.headers[:links]
156
+ links = LinkHeader.parse(links_header)
157
+
158
+ prev_url = links.find_link(['rel', 'prev']).try(:href)
159
+ next_url = links.find_link(['rel', 'next']).try(:href)
160
+
161
+ array = ActiveSupport::JSON.decode(response).map { |attributes| self.new(attributes) }
162
+ PaginatedArray.new(array, previous_page_url: prev_url, next_page_url: next_url)
163
+ end
164
+
165
+ def self.create_new_resource(params={})
166
+ url, other_params = processed_url_and_params(params)
167
+ url += "?#{other_params.to_query}" if not other_params.empty?
168
+ resource = RestClient::Resource.new("#{url}")
169
+ end
170
+
171
+ end
172
+ end
@@ -0,0 +1,19 @@
1
+ module RestfulResource
2
+ class OpenObject
3
+ def initialize(attributes = {}, hack_for_activeresource = false)
4
+ @inner_object = OpenStruct.new(attributes)
5
+ end
6
+
7
+ def method_missing(method)
8
+ if @inner_object.respond_to?(method)
9
+ @inner_object.send(method)
10
+ else
11
+ super(method)
12
+ end
13
+ end
14
+
15
+ def respond_to?(method, include_private = false)
16
+ super || @inner_object.respond_to?(method, include_private)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ module RestfulResource
2
+ class Response
3
+ attr_reader :body, :headers
4
+
5
+ def initialize(body: "{}", headers: {})
6
+ @body, @headers = body, headers
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module RestfulResource
2
- VERSION = "0.0.11"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -1,10 +1,16 @@
1
1
  require 'rack'
2
+ require 'uri'
2
3
  require 'link_header'
3
4
  require 'restclient'
4
5
  require 'active_support'
5
6
  require 'active_support/all'
6
- require "restful_resource/version"
7
- require "restful_resource/paginated_array"
8
- require "restful_resource/parameter_missing_error"
9
- require "restful_resource/base"
7
+ require_relative "restful_resource/version"
8
+ require_relative "restful_resource/paginated_array"
9
+ require_relative "restful_resource/parameter_missing_error"
10
+ require_relative "restful_resource/open_object"
11
+ require_relative 'restful_resource/response'
12
+ require_relative 'restful_resource/http_client'
13
+ require_relative "restful_resource/associations"
14
+ require_relative "restful_resource/base"
15
+ require_relative "restful_resource/old_base"
10
16
 
data/spec/fixtures.rb ADDED
@@ -0,0 +1,23 @@
1
+ class Make < RestfulResource::Base
2
+ resource_path "makes"
3
+ has_many :models
4
+ end
5
+
6
+ class Model < RestfulResource::Base
7
+ has_one :make
8
+ resource_path "groups/:group_id/makes/:make_slug/models"
9
+ end
10
+
11
+ class BaseA < RestfulResource::Base
12
+ end
13
+
14
+ class BaseB < RestfulResource::Base
15
+ end
16
+
17
+ class TestA < BaseA
18
+ self.resource_path "testa"
19
+ end
20
+
21
+ class TestB < BaseB
22
+ self.resource_path "testb"
23
+ end
@@ -0,0 +1,32 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestfulResource::Associations do
4
+ describe "#has_many" do
5
+ it "should add a method to access nested resource" do
6
+ make = Make.new({
7
+ name: 'Volkswagen',
8
+ models:
9
+ [
10
+ {name: 'Golf', rrp: 1000},
11
+ {name: 'Passat', rrp: 3000}
12
+ ]
13
+ })
14
+
15
+ expect(make.models.first.name).to eq 'Golf'
16
+ expect(make.models.last.name).to eq 'Passat'
17
+ expect(make.models.first.rrp).to eq 1000
18
+ expect(make.models.last.rrp).to eq 3000
19
+ end
20
+ end
21
+
22
+ describe "#has_one" do
23
+ it "should add a method to access nested resource" do
24
+ model = Model.new({
25
+ name: 'Golf',
26
+ make: {name: 'Volkswagen'}
27
+ })
28
+
29
+ expect(model.make.name).to eq 'Volkswagen'
30
+ end
31
+ end
32
+ end
@@ -1,125 +1,148 @@
1
1
  require_relative '../spec_helper'
2
2
 
3
3
  describe RestfulResource::Base do
4
- context "#find" do
5
- it "should retrieve a resource with a simple url" do
6
- response = { id: 15, name: 'Arsenal' }.to_json
7
- stub_get('http://api.carwow.co.uk/teams/15', response)
8
-
9
- team = Team.find(15)
10
-
11
- expect(team.id).to eq 15
12
- expect(team.name).to eq 'Arsenal'
13
- end
4
+ before :each do
5
+ @mock_http = double("mock_http")
6
+ RestfulResource::Base.http = @mock_http
7
+ RestfulResource::Base.base_url = "http://api.carwow.co.uk/"
8
+ end
14
9
 
15
- it "should retrieve a nested resource" do
16
- response = { id: 1, team_id: 15, name: 'David', santoro: 'Santoro' }.to_json
17
- stub_get('http://api.carwow.co.uk/teams/15/players/1', response)
10
+ it "should act as an openobject" do
11
+ object = RestfulResource::Base.new(name: 'David', surname: 'Santoro')
18
12
 
19
- player = Player.find(1, team_id: 15)
20
-
21
- expect(player.id).to eq 1
22
- expect(player.team_id).to eq 15
23
- expect(player.name).to eq 'David'
24
- end
13
+ expect(object.name).to eq 'David'
14
+ expect(object.surname).to eq 'Santoro'
15
+ expect { object.age }.to raise_error(NoMethodError)
25
16
  end
26
17
 
27
- context "#url" do
28
- it "should return the url set if no extra parameters are necessary" do
29
- Player.url = 'http://api.carwow.co.uk/players'
18
+ describe "#find" do
19
+ it "should return an object instance for the correct id" do
20
+ expected_response = RestfulResource::Response.new(body: {id: 12}.to_json)
21
+ expect_get("http://api.carwow.co.uk/makes/12", expected_response)
22
+
23
+ object = Make.find(12)
30
24
 
31
- expect(Player.url).to eq 'http://api.carwow.co.uk/players'
25
+ expect(object).not_to be_nil
26
+ expect(object.id).to be 12
32
27
  end
33
28
 
34
- it "should return the url with the right parameters replaced" do
35
- Player.url = 'http://api.carwow.co.uk/teams/:team_id/players'
29
+ it "should return an object instance for nested routes" do
30
+ expected_response = RestfulResource::Response.new(body: {name: 'Golf', price: 15000}.to_json)
31
+ expect_get("http://api.carwow.co.uk/groups/15/makes/Volkswagen/models/Golf", expected_response)
36
32
 
37
- expect(Player.url(team_id: 13)).to eq 'http://api.carwow.co.uk/teams/13/players'
33
+ object = Model.find('Golf', make_slug: 'Volkswagen', group_id: 15)
34
+
35
+ expect(object).not_to be_nil
36
+ expect(object.name).to eq 'Golf'
37
+ expect(object.price).to eq 15000
38
38
  end
39
+ end
39
40
 
40
- it "should raise a parameter required exception if parameter needed and not provided" do
41
- Player.url = 'http://api.carwow.co.uk/countries/:country_slug/teams/:team_id/players'
41
+ describe "#where" do
42
+ it "should return an array of objects" do
43
+ expected_response = RestfulResource::Response.new(body: [{name: 'Golf', price: 15000}, {name: 'Polo', price: 11000}].to_json)
44
+ expect_get("http://api.carwow.co.uk/groups/15/makes/Volkswagen/models?on_sale=true", expected_response)
45
+ object = Model.where(make_slug: 'Volkswagen', on_sale: true, group_id: 15)
42
46
 
43
- expected_error_message = 'You must pass values for the following parameters: [country_slug, team_id]'
44
- expect { Player.url }.to raise_error(RestfulResource::ParameterMissingError, expected_error_message)
47
+ expect(object).not_to be_nil
48
+ expect(object.length).to eq 2
49
+ expect(object.first.name).to eq 'Golf'
50
+ expect(object.first.price).to eq 15000
45
51
  end
46
52
 
47
- it "should not confuse port number as a parameter" do
48
- Player.url = 'http://api.carwow.co.uk:7000/teams/:team_id/players'
53
+ it "should provide a paginated result if response contains rest pagination headers" do
54
+ expected_response = response_with_page_information()
55
+ expect_get("http://api.carwow.co.uk/groups/15/makes/Volkswagen/models", expected_response)
56
+
57
+ models = Model.where(group_id: 15, make_slug: 'Volkswagen')
49
58
 
50
- expect { Player.url(team_id: 13) }.not_to raise_error
59
+ expect(models.first.name).to eq 'Golf'
60
+ expect(models.last.name).to eq 'Polo'
61
+ expect(models.previous_page).to be_nil
62
+ expect(models.next_page).to eq 2
51
63
  end
64
+ end
52
65
 
53
- it "should not substitue parameters in the http auth part of the url" do
54
- Player.url = 'http://admin:secret@api.carwow.co.uk:7000/teams/:team_id/players'
66
+ describe "#all" do
67
+ it "should return all items" do
68
+ expected_response = RestfulResource::Response.new(body: [{name: 'Volkswagen'}, {name: 'Audi'}].to_json)
69
+ expect_get("http://api.carwow.co.uk/makes", expected_response)
70
+ makes = Make.all
55
71
 
56
- expect { Player.url(team_id: 13) }.not_to raise_error
72
+ expect(makes).not_to be_nil
73
+ expect(makes.length).to eq 2
74
+ expect(makes.first.name).to eq 'Volkswagen'
57
75
  end
58
76
  end
59
77
 
60
- context "#all" do
61
- it "should provide a paginated result if response contains rest pagination headers" do
62
- response = response_with_page_information()
63
- stub_new_resource('http://api.carwow.co.uk/teams', response)
78
+ describe "#base_url" do
79
+ it "should be different for each subclass of Base" do
80
+ BaseA.base_url = "http://a.carwow.co.uk"
81
+ BaseB.base_url = "http://b.carwow.co.uk"
64
82
 
65
- teams = Team.all
83
+ expect_get('http://a.carwow.co.uk/testa/1', RestfulResource::Response.new())
84
+ expect_get('http://b.carwow.co.uk/testb/2', RestfulResource::Response.new())
66
85
 
67
- expect(teams.previous_page).to be_nil
68
- expect(teams.next_page).to eq 2
69
- expect(teams.first.name).to eq 'Arsenal'
70
- expect(teams.last.name).to eq 'Chelsea'
86
+ TestA.find(1)
87
+ TestB.find(2)
71
88
  end
72
89
  end
73
90
 
74
- it "should act as an openstruct" do
75
- example = Player.new(name: 'David', surname: 'Santoro')
91
+ describe "#action" do
92
+ it "should retrieve a resource using a custom action" do
93
+ expect_get('http://api.carwow.co.uk/makes/15/lazy', RestfulResource::Response.new(body: {name: 'Volk.'}.to_json))
76
94
 
77
- expect(example.name).to eq 'David'
78
- expect(example.surname).to eq 'Santoro'
79
- end
95
+ make = Make.action(:lazy).find(15)
80
96
 
81
- it "should use some params for the url and other for the query string" do
82
- stub_new_resource('http://api.carwow.co.uk/teams/15/players?name_like=Ars', response_with_page_information)
97
+ expect(make.name).to eq 'Volk.'
98
+ end
83
99
 
84
- players = Player.all(team_id: 15, name_like: 'Ars')
85
- end
100
+ it 'should retrieve many resources using a custom action' do
101
+ expect_get('http://api.carwow.co.uk/makes/available', RestfulResource::Response.new(body: [{name: 'Audi'}, {name: 'Fiat'}].to_json))
86
102
 
87
- it "should raise an error when accessing a field that doesn't exist" do
88
- player = Player.new({name: 'David', surname: 'Santoro'})
89
-
90
- expect { player.age }.to raise_error(NoMethodError)
91
- end
103
+ make = Make.action(:available).all
92
104
 
93
- private
94
- def stub_new_resource(url, fake_response)
95
- resource = instance_double('RestClient::Resource', get: fake_response)
96
- allow(RestClient::Resource).to receive(:new).with(url).and_return resource
105
+ expect(make.first.name).to eq 'Audi'
106
+ expect(make.last.name).to eq 'Fiat'
107
+ end
97
108
  end
98
109
 
99
- def stub_get(url, fake_response, params = {})
100
- expect(RestClient).to receive(:get).
101
- with(url, params: params).
102
- and_return(fake_response)
103
- end
110
+ describe "#get" do
111
+ it "should return an open_object" do
112
+ expected_response = RestfulResource::Response.new(body: {average_score: 4.3}.to_json)
113
+ expect_get('http://api.carwow.co.uk/makes/average_score?make_slug%5B%5D=Volkswagen&make_slug%5B%5D=Audi', expected_response)
104
114
 
105
- def response_with_page_information
106
- response = [{ id: 1, name: 'Arsenal'}, { id: 2, name: 'Chelsea' }].to_json
107
- allow(response).to receive(:headers) {
108
- {:links => '<http://api.carwow.co.uk/teams.json?page=6>;rel="last",<http://api.carwow.co.uk/teams.json?page=2>;rel="next"'}
109
- }
110
- response
115
+ object = Make.action(:average_score).get(make_slug: ['Volkswagen', 'Audi'])
116
+
117
+ expect(object.average_score).to eq 4.3
118
+ expect(object.class).to eq RestfulResource::OpenObject
119
+ end
111
120
  end
112
- end
113
121
 
114
- class Team < RestfulResource::Base
115
- self.url = "http://api.carwow.co.uk/teams"
116
- end
122
+ describe ".as_json" do
123
+ before :each do
124
+ expected_response = RestfulResource::Response.new(body: [{name: 'Audi', slug: 'Audi-Slug'}, {name: 'Fiat', slug: 'Fiat-Slug'}].to_json)
125
+ expect_get('http://api.carwow.co.uk/makes', expected_response)
117
126
 
118
- class Player < RestfulResource::Base
119
- has_one :team
127
+ @makes = Make.all
128
+ end
120
129
 
121
- self.url = "http://api.carwow.co.uk/teams/:team_id/players"
122
- end
130
+ it 'should not return inner object table' do
131
+ expect(@makes.first.as_json).to eq ({'name' => 'Audi', 'slug' => 'Audi-Slug'})
132
+ end
123
133
 
134
+ it 'should return inner object table on selected fields' do
135
+ expect(@makes.last.as_json(only: [:name])).to eq ({'name' => 'Fiat'})
136
+ end
137
+ end
138
+
139
+ def expect_get(url, response)
140
+ expect(@mock_http).to receive(:get).with(url).and_return(response)
141
+ end
124
142
 
143
+ def response_with_page_information
144
+ RestfulResource::Response.new(body: [{ id: 1, name: 'Golf'}, { id: 2, name: 'Polo' }].to_json,
145
+ headers: { links: '<http://api.carwow.co.uk/makes/Volkswagen/models.json?page=6>;rel="last",<http://api.carwow.co.uk/makes/Volkswagen/models.json?page=2>;rel="next"'})
146
+ end
147
+ end
125
148
 
@@ -0,0 +1,119 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestfulResource::OldBase do
4
+ context "#find" do
5
+ it "should retrieve a resource with a simple url" do
6
+ response = { id: 15, name: 'Arsenal' }.to_json
7
+ stub_get('http://api.carwow.co.uk/teams/15', response)
8
+
9
+ team = Team.find(15)
10
+
11
+ expect(team.id).to eq 15
12
+ expect(team.name).to eq 'Arsenal'
13
+ end
14
+
15
+ it "should retrieve a nested resource" do
16
+ response = { id: 1, team_id: 15, name: 'David', santoro: 'Santoro' }.to_json
17
+ stub_get('http://api.carwow.co.uk/teams/15/players/1', response)
18
+
19
+ player = Player.find(1, team_id: 15)
20
+
21
+ expect(player.id).to eq 1
22
+ expect(player.team_id).to eq 15
23
+ expect(player.name).to eq 'David'
24
+ end
25
+ end
26
+
27
+ context "#url" do
28
+ it "should return the url set if no extra parameters are necessary" do
29
+ Player.url = 'http://api.carwow.co.uk/players'
30
+
31
+ expect(Player.url).to eq 'http://api.carwow.co.uk/players'
32
+ end
33
+
34
+ it "should return the url with the right parameters replaced" do
35
+ Player.url = 'http://api.carwow.co.uk/teams/:team_id/players'
36
+
37
+ expect(Player.url(team_id: 13)).to eq 'http://api.carwow.co.uk/teams/13/players'
38
+ end
39
+
40
+ it "should raise a parameter required exception if parameter needed and not provided" do
41
+ Player.url = 'http://api.carwow.co.uk/countries/:country_slug/teams/:team_id/players'
42
+
43
+ expected_error_message = 'You must pass values for the following parameters: [country_slug, team_id]'
44
+ expect { Player.url }.to raise_error(RestfulResource::ParameterMissingError, expected_error_message)
45
+ end
46
+
47
+ it "should not confuse port number as a parameter" do
48
+ Player.url = 'http://api.carwow.co.uk:7000/teams/:team_id/players'
49
+
50
+ expect { Player.url(team_id: 13) }.not_to raise_error
51
+ end
52
+ end
53
+
54
+ context "#all" do
55
+ it "should provide a paginated result if response contains rest pagination headers" do
56
+ response = response_with_page_information()
57
+ stub_new_resource('http://api.carwow.co.uk/teams', response)
58
+
59
+ teams = Team.all
60
+
61
+ expect(teams.previous_page).to be_nil
62
+ expect(teams.next_page).to eq 2
63
+ expect(teams.first.name).to eq 'Arsenal'
64
+ expect(teams.last.name).to eq 'Chelsea'
65
+ end
66
+ end
67
+
68
+ it "should act as an openstruct" do
69
+ example = Player.new(name: 'David', surname: 'Santoro')
70
+
71
+ expect(example.name).to eq 'David'
72
+ expect(example.surname).to eq 'Santoro'
73
+ end
74
+
75
+ it "should use some params for the url and other for the query string" do
76
+ stub_new_resource('http://api.carwow.co.uk/teams/15/players?name_like=Ars', response_with_page_information)
77
+
78
+ players = Player.all(team_id: 15, name_like: 'Ars')
79
+ end
80
+
81
+ it "should raise an error when accessing a field that doesn't exist" do
82
+ player = Player.new({name: 'David', surname: 'Santoro'})
83
+
84
+ expect { player.age }.to raise_error(NoMethodError)
85
+ end
86
+
87
+ private
88
+ def stub_new_resource(url, fake_response)
89
+ resource = instance_double('RestClient::Resource', get: fake_response)
90
+ allow(RestClient::Resource).to receive(:new).with(url).and_return resource
91
+ end
92
+
93
+ def stub_get(url, fake_response, params = {})
94
+ expect(RestClient).to receive(:get).
95
+ with(url, params: params).
96
+ and_return(fake_response)
97
+ end
98
+
99
+ def response_with_page_information
100
+ response = [{ id: 1, name: 'Arsenal'}, { id: 2, name: 'Chelsea' }].to_json
101
+ allow(response).to receive(:headers) {
102
+ {:links => '<http://api.carwow.co.uk/teams.json?page=6>;rel="last",<http://api.carwow.co.uk/teams.json?page=2>;rel="next"'}
103
+ }
104
+ response
105
+ end
106
+ end
107
+
108
+ class Team < RestfulResource::OldBase
109
+ self.url = "http://api.carwow.co.uk/teams"
110
+ end
111
+
112
+ class Player < RestfulResource::OldBase
113
+ has_one :team
114
+
115
+ self.url = "http://api.carwow.co.uk/teams/:team_id/players"
116
+ end
117
+
118
+
119
+
@@ -0,0 +1,16 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestfulResource::OpenObject do
4
+ it "should act as an openstruct" do
5
+ object = RestfulResource::OpenObject.new(name: 'David', surname: 'Santoro')
6
+
7
+ expect(object.name).to eq 'David'
8
+ expect(object.surname).to eq 'Santoro'
9
+ end
10
+
11
+ it "should raise an error when accessing a field that doesn't exist" do
12
+ object = RestfulResource::OpenObject.new({name: 'David', surname: 'Santoro'})
13
+
14
+ expect { object.age }.to raise_error(NoMethodError)
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe RestfulResource::Base do
4
+ context "#get" do
5
+ it "should throw an exception with wrong url" do
6
+ expect{RestClient.get("http://www.example.com/djksafjadl", params: {})}.to raise_error
7
+ end
8
+
9
+ it "should throw an exception with wrong url" do
10
+ r = RestClient::Resource.new("http://www.example.com/djksafjadl")
11
+ expect{r.get}.to raise_error
12
+ end
13
+ end
14
+
15
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rspec'
2
2
  require_relative '../lib/restful_resource'
3
+ require_relative 'fixtures'
3
4
 
4
5
  RSpec.configure do |config|
5
6
  config.color = true
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restful_resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Santoro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-16 00:00:00.000000000 Z
11
+ date: 2014-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,12 +122,22 @@ files:
122
122
  - README.md
123
123
  - Rakefile
124
124
  - lib/restful_resource.rb
125
+ - lib/restful_resource/associations.rb
125
126
  - lib/restful_resource/base.rb
127
+ - lib/restful_resource/http_client.rb
128
+ - lib/restful_resource/old_base.rb
129
+ - lib/restful_resource/open_object.rb
126
130
  - lib/restful_resource/paginated_array.rb
127
131
  - lib/restful_resource/parameter_missing_error.rb
132
+ - lib/restful_resource/response.rb
128
133
  - lib/restful_resource/version.rb
129
134
  - restful_resource.gemspec
135
+ - spec/fixtures.rb
136
+ - spec/restful_resource/associations_spec.rb
130
137
  - spec/restful_resource/base_spec.rb
138
+ - spec/restful_resource/old_base_spec.rb
139
+ - spec/restful_resource/open_object_spec.rb
140
+ - spec/restful_resource/rest_client_spec.rb
131
141
  - spec/spec_helper.rb
132
142
  homepage: http://www.github.com/carwow/restful_resource
133
143
  licenses:
@@ -155,5 +165,10 @@ specification_version: 4
155
165
  summary: A simple activerecord inspired rest resource base class implemented using
156
166
  rest-client
157
167
  test_files:
168
+ - spec/fixtures.rb
169
+ - spec/restful_resource/associations_spec.rb
158
170
  - spec/restful_resource/base_spec.rb
171
+ - spec/restful_resource/old_base_spec.rb
172
+ - spec/restful_resource/open_object_spec.rb
173
+ - spec/restful_resource/rest_client_spec.rb
159
174
  - spec/spec_helper.rb