restful_resource 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5e007325204f8aa0dfb465f3370a0d2e84f54a94
4
+ data.tar.gz: c23007a5a0a3e8320fda92f43ed0a2290c0c3b65
5
+ SHA512:
6
+ metadata.gz: 6f6abda88019ec8739f80b92aaab7c4a3b014689caf050b92ddc58ab9ef61b5da5c7eca24012917e39986d57cd1e667675c6598c85fe6b7730e12dcff7ee76cd
7
+ data.tar.gz: 41eee9a852ea62125c0c44d6264a4d3817ac35304850bb9c1cb66cc66e8db85b234bbe9d800909003c54fd2b1593bbe20b44ebf1a61f2c28ff63b9eb4ca52cf3
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 David Santoro
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # RestfulResource
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'restful_resource'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install restful_resource
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ desc "Upload to rubygems"
9
+ task :upload => :build do
10
+ # Check if tag with v#{ResearchSiteApiClient::VERSION} version exists, if so, return with error
11
+
12
+ if tag_exists?(current_tag_name)
13
+ puts "Tag exists, did you run rake increase_revision_number after merging with master?"
14
+ exit 1
15
+ end
16
+
17
+ create_tag(current_tag_name)
18
+ Rake::Task[:release].invoke
19
+ end
20
+
21
+ desc "Increase the revision number"
22
+ task :increase_revision_number do
23
+ version_file = "lib/restful_resource/version.rb"
24
+ file_content = File.read(version_file)
25
+ rule = /(\d+\.\d+\.)(\d+)/
26
+ new_revision_number = rule.match(file_content)[2].to_i + 1
27
+ new_file_content = file_content.sub(rule, '\1' + new_revision_number.to_s)
28
+
29
+ File.open(version_file, 'w') { |file| file.write(new_file_content) }
30
+ end
31
+
32
+ def tag_exists?(tag_name)
33
+ result = `git tag | grep #{tag_name}`.strip
34
+ result == tag_name
35
+ end
36
+
37
+ def create_tag(tag_name)
38
+ sh "git tag -a #{tag_name} -m \"Released version #{tag_name}\""
39
+ sh "git push origin #{tag_name}"
40
+ end
41
+
42
+ def current_tag_name
43
+ "v#{RestfulResource::VERSION}"
44
+ end
@@ -0,0 +1,137 @@
1
+ module RestfulResource
2
+ class Base
3
+ def self.url=(url)
4
+ @url = url
5
+ end
6
+
7
+ def self.url(params={})
8
+ url = @url
9
+ params.keys.each do |key|
10
+ url = url.gsub(':'+key.to_s, params[key].to_s)
11
+ end
12
+
13
+ url_params = url.scan(/:([^\/]+)/)
14
+ if url_params.any?
15
+ raise ParameterMissingError.new(url_params)
16
+ end
17
+
18
+ url
19
+ end
20
+
21
+ def self.has_one(nested_resource_type)
22
+ klass = nested_resource_type.to_s.camelize.safe_constantize
23
+ klass = OpenStruct if (klass.nil? || klass.superclass != RestResource)
24
+
25
+ self.send(:define_method, nested_resource_type) do
26
+ nested_resource = @inner_object.send(nested_resource_type)
27
+ return nil if nested_resource.nil?
28
+ klass.new(@inner_object.send(nested_resource_type))
29
+ end
30
+ end
31
+
32
+ def self.has_many(nested_resource_type)
33
+ klass = nested_resource_type.to_s.singularize.camelize.safe_constantize
34
+ klass = OpenStruct if (klass.nil? || (klass.superclass != RestResource))
35
+
36
+ self.send(:define_method, nested_resource_type) do
37
+ @inner_object.send(nested_resource_type).map { |obj| klass.new(obj) }
38
+ end
39
+ end
40
+
41
+ def initialize(attributes = {}, hack_for_activeresource = false)
42
+ @inner_object = OpenStruct.new(attributes)
43
+ end
44
+
45
+ def method_missing(method, *args, &block)
46
+ @inner_object.send(method, *args, &block)
47
+ end
48
+
49
+ def valid?
50
+ errors.nil? || errors.count == g
51
+ end
52
+
53
+ def self.find(id, url_params={})
54
+ response = RestClient.get("#{url(url_params)}/#{id}", params: {})
55
+ self.new(ActiveSupport::JSON.decode(response))
56
+ end
57
+
58
+ def self.update_attributes(id, attributes)
59
+ begin
60
+ result = parse(RestClient.put("#{url}/#{id}", attributes))
61
+ rescue RestClient::UnprocessableEntity => e
62
+ errors = parse(e.response)
63
+ result = attributes.merge(errors: errors)
64
+ end
65
+ self.new(result)
66
+ end
67
+
68
+ def self.create(attributes)
69
+ begin
70
+ result = parse(RestClient.post("#{url}", attributes))
71
+ rescue RestClient::UnprocessableEntity => e
72
+ errors = parse(e.response)
73
+ result = attributes.merge(errors: errors)
74
+ end
75
+ self.new(result)
76
+ end
77
+
78
+ def self.search(params = {})
79
+ response = RestClient.get("#{url}/search", params: params)
80
+ paginate_response(response)
81
+ end
82
+
83
+ def self.all(params = {})
84
+ response = RestClient.get("#{url}", params: params)
85
+ paginate_response(response)
86
+ end
87
+
88
+ def self.get(postfix_url = "", params = {})
89
+ response = RestClient.get("#{url}#{postfix_url}", params: params)
90
+ paginate_response(response)
91
+ end
92
+
93
+ def self.put(postfix_url = "", params = {})
94
+ response = RestClient.put("#{url}#{postfix_url}", params)
95
+ end
96
+
97
+ def self.post(postfix_url = "", params = {})
98
+ response = RestClient.post("#{url}#{postfix_url}", params)
99
+ end
100
+
101
+ def self.all_not_paginated
102
+ page = 1
103
+ results = []
104
+ while page
105
+ page_data = self.all(page: page);
106
+ results += page_data
107
+ page = page_data.next_page
108
+ end
109
+
110
+ results
111
+ end
112
+
113
+ def self.parse(json)
114
+ ActiveSupport::JSON.decode(json)
115
+ end
116
+
117
+ def to_json(*args)
118
+ @inner_object.send(:table).to_json(*args)
119
+ end
120
+
121
+ def as_json(*)
122
+ @inner_object.send(:table).as_json
123
+ end
124
+
125
+ private
126
+ def self.paginate_response(response)
127
+ links_header = response.headers[:links]
128
+ links = LinkHeader.parse(links_header)
129
+
130
+ prev_url = links.find_link(['rel', 'prev']).try(:href)
131
+ next_url = links.find_link(['rel', 'next']).try(:href)
132
+
133
+ array = ActiveSupport::JSON.decode(response).map { |attributes| self.new(attributes) }
134
+ PaginatedArray.new(array, previous_page_url: prev_url, next_page_url: next_url)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,25 @@
1
+ module RestfulResource
2
+ class PaginatedArray < Array
3
+ def initialize(original_array, previous_page_url: previous_page_url, next_page_url: next_page_url)
4
+ super(original_array)
5
+
6
+ @previous_page_url = previous_page_url
7
+ @next_page_url = next_page_url
8
+ end
9
+
10
+ def previous_page
11
+ get_page_from_url(@previous_page_url)
12
+ end
13
+
14
+ def next_page
15
+ get_page_from_url(@next_page_url)
16
+ end
17
+
18
+ private
19
+ def get_page_from_url(url)
20
+ return nil unless url
21
+ params = Rack::Utils.parse_query URI(url).query
22
+ params['page'].to_i
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module RestfulResource
2
+ class ParameterMissingError < StandardError
3
+ def initialize(missing_parameters)
4
+ @missing_parameters = missing_parameters
5
+ end
6
+
7
+ def message
8
+ "You must pass values for the following parameters: [#{@missing_parameters.join(', ')}]"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module RestfulResource
2
+ VERSION = "0.0.5"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'rack'
2
+ require 'link_header'
3
+ require 'restclient'
4
+ require 'active_support'
5
+ 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"
10
+
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'restful_resource/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "restful_resource"
8
+ spec.version = RestfulResource::VERSION
9
+ spec.authors = ["David Santoro"]
10
+ spec.email = ["developers@carwow.co.uk"]
11
+ spec.description = %q{A simple activerecord inspired rest resource base class implemented using rest-client}
12
+ spec.summary = %q{A simple activerecord inspired rest resource base class implemented using rest-client}
13
+ spec.homepage = "http://www.github.com/carwow/restful_resource"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "rest-client"
26
+ spec.add_dependency "link_header"
27
+ spec.add_dependency "activesupport"
28
+ spec.add_dependency "rack"
29
+ end
@@ -0,0 +1,89 @@
1
+ require_relative '../spec_helper'
2
+
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
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
+ end
47
+
48
+ context "#all" do
49
+ it "should provide a paginated result if response contains rest pagination headers" do
50
+ response = [{ id: 1, name: 'Arsenal'}, { id: 2, name: 'Chelsea' }].to_json
51
+ allow(response).to receive(:headers) {
52
+ {:links => '<http://api.carwow.co.uk/teams.json?page=6>;rel="last",<http://api.carwow.co.uk/teams.json?page=2>;rel="next"'}
53
+ }
54
+ stub_get('http://api.carwow.co.uk/teams', response)
55
+
56
+ teams = Team.all
57
+
58
+ expect(teams.previous_page).to be_nil
59
+ expect(teams.next_page).to eq 2
60
+ expect(teams.first.name).to eq 'Arsenal'
61
+ expect(teams.last.name).to eq 'Chelsea'
62
+ end
63
+ end
64
+
65
+ it "should act as an openstruct" do
66
+ example = Player.new(name: 'David', surname: 'Santoro')
67
+
68
+ expect(example.name).to eq 'David'
69
+ expect(example.surname).to eq 'Santoro'
70
+ end
71
+
72
+ private
73
+ def stub_get(url, fake_response, params = {})
74
+ expect(RestClient).to receive(:get).
75
+ with(url, params: params).
76
+ and_return(fake_response)
77
+ end
78
+ end
79
+
80
+ class Team < RestfulResource::Base
81
+ self.url = "http://api.carwow.co.uk/teams"
82
+ end
83
+
84
+ class Player < RestfulResource::Base
85
+ self.url = "http://api.carwow.co.uk/teams/:team_id/players"
86
+ end
87
+
88
+
89
+
@@ -0,0 +1,7 @@
1
+ require 'rspec'
2
+ require_relative '../lib/restful_resource'
3
+
4
+ RSpec.configure do |config|
5
+ config.color = true
6
+ config.formatter = :progress
7
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restful_resource
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - David Santoro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rest-client
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: link_header
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: A simple activerecord inspired rest resource base class implemented using
112
+ rest-client
113
+ email:
114
+ - developers@carwow.co.uk
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - .gitignore
120
+ - Gemfile
121
+ - LICENSE.txt
122
+ - README.md
123
+ - Rakefile
124
+ - lib/restful_resource.rb
125
+ - lib/restful_resource/base.rb
126
+ - lib/restful_resource/paginated_array.rb
127
+ - lib/restful_resource/parameter_missing_error.rb
128
+ - lib/restful_resource/version.rb
129
+ - restful_resource.gemspec
130
+ - spec/restful_resource/base_spec.rb
131
+ - spec/spec_helper.rb
132
+ homepage: http://www.github.com/carwow/restful_resource
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.0.14
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: A simple activerecord inspired rest resource base class implemented using
156
+ rest-client
157
+ test_files:
158
+ - spec/restful_resource/base_spec.rb
159
+ - spec/spec_helper.rb