apitizer 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.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +6 -0
- data/.yardopts +6 -0
- data/CHANGELOG.md +10 -0
- data/Guardfile +11 -4
- data/README.md +74 -7
- data/apitizer.gemspec +1 -2
- data/lib/apitizer.rb +0 -1
- data/lib/apitizer/base.rb +16 -27
- data/lib/apitizer/connection.rb +3 -1
- data/lib/apitizer/connection/adaptor.rb +1 -1
- data/lib/apitizer/connection/adaptor/standard.rb +24 -7
- data/lib/apitizer/connection/dispatcher.rb +5 -12
- data/lib/apitizer/connection/format.rb +14 -0
- data/lib/apitizer/{processing/parser → connection/format}/json.rb +6 -2
- data/lib/apitizer/{processing/parser → connection/format}/yaml.rb +6 -2
- data/lib/apitizer/connection/request.rb +3 -3
- data/lib/apitizer/connection/response.rb +3 -3
- data/lib/apitizer/core.rb +4 -4
- data/lib/apitizer/helper.rb +38 -14
- data/lib/apitizer/result.rb +2 -2
- data/lib/apitizer/routing.rb +1 -1
- data/lib/apitizer/routing/{mapper.rb → map.rb} +3 -10
- data/lib/apitizer/routing/node.rb +0 -1
- data/lib/apitizer/routing/node/base.rb +15 -17
- data/lib/apitizer/routing/node/collection.rb +17 -16
- data/lib/apitizer/routing/node/operation.rb +14 -15
- data/lib/apitizer/routing/node/root.rb +8 -2
- data/lib/apitizer/routing/path.rb +16 -8
- data/lib/apitizer/version.rb +1 -1
- data/spec/apitizer/base_spec.rb +36 -28
- data/spec/apitizer/connection/adaptor_spec.rb +87 -11
- data/spec/apitizer/connection/dispatcher_spec.rb +21 -23
- data/spec/apitizer/connection/format_spec.rb +15 -0
- data/spec/apitizer/helper_spec.rb +53 -24
- data/spec/apitizer/result_spec.rb +5 -7
- data/spec/apitizer/routing/map_spec.rb +71 -0
- data/spec/apitizer/routing/node_spec.rb +108 -36
- data/spec/apitizer/routing/path_spec.rb +12 -92
- data/spec/spec_helper.rb +4 -6
- data/spec/support/factory_helper.rb +25 -5
- data/spec/support/resource_helper.rb +8 -0
- metadata +14 -15
- data/lib/apitizer/processing.rb +0 -8
- data/lib/apitizer/processing/parser.rb +0 -14
- data/lib/apitizer/processing/translator.rb +0 -13
- data/lib/apitizer/routing/node/scope.rb +0 -19
- data/spec/apitizer/processing/parser_spec.rb +0 -23
- data/spec/apitizer/routing/mapper_spec.rb +0 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40fc294404896c8bf5da7bcc98d9a1fb4888d559
|
4
|
+
data.tar.gz: 0f20b6555f17c71ed78843794b7b37c9b3e1abb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a38a7ce97d22a84accc45630b5810a36875e300dcfb097c90ceb05ca809ce4b80122bdd54350d35972c2ec04789cd0f260c3355f003a598a7fc43bfea2000ec
|
7
|
+
data.tar.gz: 20a7a1afa6a79245ac342635422fcfae978f22f70b75651d9287a813b5bfb0f4057da601dea5d60b62f19c0253b9341de200b2f177088e77811931143f03e174
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1 +1,11 @@
|
|
1
|
+
## Apitizer 0.0.2 (July 20, 2014)
|
2
|
+
|
3
|
+
* A new strategy for searching request endpoints.
|
4
|
+
* Support for `on: :collection` in addition to `on: :member`.
|
5
|
+
* Support for `except: [ ... ]` in addition to `only: [ ... ]`.
|
6
|
+
* Setting the Accept header according to the data format.
|
7
|
+
* Sending the parameter of a POST/PUT/PATCH/DELETE request in its body.
|
8
|
+
* Enforcing UTF-8 when sending HTTP requests.
|
9
|
+
* Improved parameter encoding.
|
10
|
+
|
1
11
|
## Apitizer 0.0.1 (June 1, 2014)
|
data/Guardfile
CHANGED
@@ -1,7 +1,14 @@
|
|
1
1
|
guard :rspec do
|
2
2
|
watch(%r{^spec/.+_spec\.rb$})
|
3
|
-
watch(
|
4
|
-
watch(%r{^
|
5
|
-
|
6
|
-
|
3
|
+
watch(%r{^spec/.+_helper\.rb$}) { 'spec' }
|
4
|
+
watch(%r{^lib/(.+)\.rb$}) do |match|
|
5
|
+
result = "spec/#{ match[1] }_spec.rb"
|
6
|
+
loop do
|
7
|
+
break if File.exist?(result)
|
8
|
+
result = File.dirname(result)
|
9
|
+
break if result.empty?
|
10
|
+
end
|
11
|
+
result
|
12
|
+
end
|
7
13
|
end
|
14
|
+
# vim: ft=ruby
|
data/README.md
CHANGED
@@ -1,28 +1,91 @@
|
|
1
|
-
# Apitizer
|
1
|
+
# Apitizer [](http://badge.fury.io/rb/apitizer) [](https://gemnasium.com/IvanUkhov/apitizer) [](https://travis-ci.org/IvanUkhov/apitizer)
|
2
|
+
|
2
3
|
The main ingredient of a RESTful API client.
|
3
4
|
|
4
5
|
## Installation
|
6
|
+
|
5
7
|
Add the following line to your `Gemfile`:
|
8
|
+
|
6
9
|
```ruby
|
7
10
|
gem 'apitizer'
|
8
11
|
```
|
9
12
|
|
10
13
|
Then execute:
|
14
|
+
|
11
15
|
```bash
|
12
16
|
$ bundle
|
13
17
|
```
|
14
18
|
|
15
19
|
Alternatively, you can install the gem manually:
|
20
|
+
|
16
21
|
```bash
|
17
22
|
$ gem install apitizer
|
18
23
|
```
|
19
24
|
|
25
|
+
Note that the minimal supported version of Ruby is `2.1`.
|
26
|
+
|
20
27
|
## Usage
|
28
|
+
|
29
|
+
Create an apitizer describing the API of the Web service you would like
|
30
|
+
to interact with:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
apitizer = Apitizer::Base.new do
|
34
|
+
address 'https://service.com/api'
|
35
|
+
|
36
|
+
resources :posts do
|
37
|
+
resources :comments
|
38
|
+
end
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
The apitizer can now be used to manipulate the resources provided by the
|
43
|
+
Web service. To this end, there are five methods: `index`, `show`, `create`,
|
44
|
+
`update`, and `delete`, which can be used as shown below.
|
45
|
+
|
46
|
+
To list the members of a collection:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
apitizer.index(:posts)
|
50
|
+
apitizer.index(:posts, post_id, :comments)
|
51
|
+
```
|
52
|
+
|
53
|
+
To read a member of a collection:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
apitizer.show(:posts, post_id)
|
57
|
+
apitizer.show(:posts, post_id, :comments, comment_id)
|
58
|
+
```
|
59
|
+
|
60
|
+
To create a new member in a collection:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
apitizer.create(:posts, title: 'To be or not to be')
|
64
|
+
apitizer.create(:posts, post_id, :comments, content: 'That is the question.')
|
65
|
+
```
|
66
|
+
|
67
|
+
To update a member of a collection:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
apitizer.update(:posts, post_id, title: 'What is the meaning of life?')
|
71
|
+
apitizer.update(:posts, post_id, :comments, comment_id, content: '42.')
|
72
|
+
```
|
73
|
+
|
74
|
+
To delete a member of a collection:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
apitizer.delete(:posts, post_id)
|
78
|
+
apitizer.delete(:posts, post_id, :comments, comment_id)
|
79
|
+
```
|
80
|
+
|
81
|
+
## Example
|
82
|
+
|
21
83
|
Here is an example for the [Typekit API](https://typekit.com/docs/api).
|
22
84
|
Check out [Typekit Client](https://github.com/IvanUkhov/typekit-client)
|
23
85
|
as well.
|
24
86
|
|
25
87
|
Code:
|
88
|
+
|
26
89
|
```ruby
|
27
90
|
require 'apitizer'
|
28
91
|
|
@@ -39,7 +102,7 @@ apitizer = Apitizer::Base.new(options) do
|
|
39
102
|
address 'https://typekit.com/api/v1/json'
|
40
103
|
|
41
104
|
resources :families, only: :show do
|
42
|
-
show ':
|
105
|
+
show ':variation', on: :member
|
43
106
|
end
|
44
107
|
|
45
108
|
resources :kits do
|
@@ -55,6 +118,7 @@ puts JSON.pretty_generate(apitizer.index(:kits))
|
|
55
118
|
```
|
56
119
|
|
57
120
|
Output:
|
121
|
+
|
58
122
|
```json
|
59
123
|
{
|
60
124
|
"kits": [
|
@@ -79,13 +143,16 @@ Output:
|
|
79
143
|
```
|
80
144
|
|
81
145
|
## History
|
146
|
+
|
82
147
|
Apitizer was a part of
|
83
148
|
[Typekit Client](https://github.com/IvanUkhov/typekit-client).
|
84
149
|
|
85
150
|
## Contributing
|
86
151
|
|
87
|
-
1. Fork
|
88
|
-
2. Create your feature
|
89
|
-
3.
|
90
|
-
4.
|
91
|
-
5.
|
152
|
+
1. [Fork](https://help.github.com/articles/fork-a-repo) the project.
|
153
|
+
2. Create a branch for your feature (`git checkout -b awesome-feature`).
|
154
|
+
3. Implement your feature (`vim`).
|
155
|
+
4. Commit your changes (`git commit -am 'Implemented an awesome feature'`).
|
156
|
+
5. Push to the branch (`git push origin awesome-feature`).
|
157
|
+
6. [Create](https://help.github.com/articles/creating-a-pull-request)
|
158
|
+
a new Pull Request.
|
data/apitizer.gemspec
CHANGED
@@ -15,7 +15,6 @@ Gem::Specification.new do |spec|
|
|
15
15
|
spec.license = 'MIT'
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0")
|
18
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
18
|
spec.test_files = spec.files.grep(%r{^spec/})
|
20
19
|
spec.require_paths = [ 'lib' ]
|
21
20
|
|
@@ -26,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
26
25
|
|
27
26
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
28
27
|
spec.add_development_dependency 'rake'
|
29
|
-
spec.add_development_dependency 'rspec', '~>
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
30
29
|
spec.add_development_dependency 'guard-rspec', '~> 4.2'
|
31
30
|
spec.add_development_dependency 'webmock', '~> 1.18'
|
32
31
|
end
|
data/lib/apitizer.rb
CHANGED
data/lib/apitizer/base.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
module Apitizer
|
2
2
|
class Base
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegator :map, :define
|
6
|
+
|
3
7
|
def initialize(**options, &block)
|
4
8
|
@options = Helper.deep_merge(Apitizer.defaults, options)
|
5
9
|
@block = block
|
@@ -8,8 +12,7 @@ module Apitizer
|
|
8
12
|
def process(*arguments)
|
9
13
|
request = build_request(*arguments)
|
10
14
|
response = dispatcher.process(request)
|
11
|
-
|
12
|
-
Result.new(request: request, response: response, content: content)
|
15
|
+
Result.new(request: request, response: response)
|
13
16
|
end
|
14
17
|
|
15
18
|
Apitizer.actions.each do |action|
|
@@ -20,42 +23,28 @@ module Apitizer
|
|
20
23
|
|
21
24
|
private
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
def #{ component }
|
26
|
-
@#{ component } ||= build_#{ component }
|
27
|
-
end
|
28
|
-
METHOD
|
29
|
-
end
|
30
|
-
|
31
|
-
def build_mapper
|
32
|
-
Routing::Mapper.new(&@block)
|
26
|
+
def map
|
27
|
+
@map ||= Routing::Map.new(&@block)
|
33
28
|
end
|
34
29
|
|
35
|
-
def
|
36
|
-
Connection::Dispatcher.new(
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
def build_translator
|
41
|
-
Processing::Translator.new(format: self.format)
|
30
|
+
def dispatcher
|
31
|
+
@dispatcher ||= Connection::Dispatcher.new(format: @options[:format],
|
32
|
+
adaptor: @options[:adaptor], headers: @options[:headers])
|
42
33
|
end
|
43
34
|
|
44
35
|
def build_request(*arguments)
|
45
|
-
action, steps, parameters = prepare(*arguments)
|
46
|
-
path
|
47
|
-
Connection::Request.new(action: action, path: path,
|
36
|
+
action, method, steps, parameters = prepare(*arguments)
|
37
|
+
Connection::Request.new(method: method, path: map.trace(action, steps),
|
48
38
|
parameters: parameters)
|
49
39
|
end
|
50
40
|
|
51
41
|
def prepare(action, *path)
|
42
|
+
action = action.to_sym
|
43
|
+
method = @options[:dictionary][action] or raise Error, 'Unknown action'
|
52
44
|
parameters = path.last.is_a?(Hash) ? path.pop : {}
|
53
|
-
|
54
|
-
end
|
45
|
+
steps = path.flatten.map(&:to_sym)
|
55
46
|
|
56
|
-
|
57
|
-
return @options[name] if @options.key?(name)
|
58
|
-
super
|
47
|
+
[ action, method, steps, parameters ]
|
59
48
|
end
|
60
49
|
end
|
61
50
|
end
|
data/lib/apitizer/connection.rb
CHANGED
@@ -6,13 +6,14 @@ module Apitizer
|
|
6
6
|
module Adaptor
|
7
7
|
class Standard
|
8
8
|
def process(method, address, parameters = {}, headers = {})
|
9
|
-
|
10
|
-
request = klass.new(build_uri(address, parameters))
|
9
|
+
request = build_request(method, address, parameters)
|
11
10
|
headers.each { |k, v| request[k] = v }
|
11
|
+
|
12
12
|
http = Net::HTTP.new(request.uri.host, request.uri.port)
|
13
13
|
http.use_ssl = true if address =~ /^https:/
|
14
|
+
|
14
15
|
response = http.request(request)
|
15
|
-
[ response.code, response.to_hash, response.body ]
|
16
|
+
[ response.code.to_i, response.to_hash, Array(response.body) ]
|
16
17
|
rescue NoMethodError
|
17
18
|
raise
|
18
19
|
rescue NameError
|
@@ -23,10 +24,26 @@ module Apitizer
|
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
URI(
|
27
|
+
def build_request(method, address, parameters)
|
28
|
+
klass = Net::HTTP.const_get(method.to_s.capitalize)
|
29
|
+
|
30
|
+
return klass.new(URI(address)) if parameters.empty?
|
31
|
+
|
32
|
+
parameters = Helper.build_query(parameters)
|
33
|
+
|
34
|
+
if klass == Net::HTTP::Get
|
35
|
+
address = [ address, parameters ].join('?')
|
36
|
+
request = klass.new(URI(address))
|
37
|
+
else
|
38
|
+
request = klass.new(URI(address))
|
39
|
+
request.body = parameters
|
40
|
+
request['Content-Type'] = [
|
41
|
+
'application/x-www-form-urlencoded',
|
42
|
+
"charset=#{ parameters.encoding.to_s }"
|
43
|
+
].join('; ')
|
44
|
+
end
|
45
|
+
|
46
|
+
request
|
30
47
|
end
|
31
48
|
end
|
32
49
|
end
|
@@ -1,23 +1,16 @@
|
|
1
1
|
module Apitizer
|
2
2
|
module Connection
|
3
3
|
class Dispatcher
|
4
|
-
def initialize(adaptor: :standard,
|
4
|
+
def initialize(format:, adaptor: :standard, headers: {})
|
5
|
+
@format = Format.build(format)
|
5
6
|
@adaptor = Adaptor.build(adaptor)
|
6
|
-
@
|
7
|
-
@headers = headers
|
7
|
+
@headers = headers.merge('Accept' => @format.mime_type)
|
8
8
|
end
|
9
9
|
|
10
10
|
def process(request)
|
11
|
-
|
12
|
-
code, _, body = @adaptor.process(method, request.address,
|
11
|
+
code, _, body = @adaptor.process(request.method, request.address,
|
13
12
|
request.parameters, @headers)
|
14
|
-
Response.new(code: code
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def translate(action)
|
20
|
-
@dictionary[action] or raise Error, 'Unknown action'
|
13
|
+
Response.new(code: code, content: @format.process(body.join))
|
21
14
|
end
|
22
15
|
end
|
23
16
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'format/json'
|
2
|
+
require_relative 'format/yaml'
|
3
|
+
|
4
|
+
module Apitizer
|
5
|
+
module Connection
|
6
|
+
module Format
|
7
|
+
def self.build(name)
|
8
|
+
self.const_get(name.to_s.upcase).new
|
9
|
+
rescue NameError
|
10
|
+
raise Error, 'Unknown format'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|