motion_ocean 0.1.1 → 0.2.1

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: cb14f92dec01163395d43525ad7e66664ff2e796
4
- data.tar.gz: 3ca20c500add45aacc4bb2c358276bca28461d0e
3
+ metadata.gz: 8e8d553b93122e2453a621d617a90831ec6e225d
4
+ data.tar.gz: ca435978b2a0e40383976aa343297413c49d80d4
5
5
  SHA512:
6
- metadata.gz: 0deb674b9e246ba6b9a5584cbf65ff19ceea0577892fb197390837534f27b697a9a9d7396e784447dbc1d04b13cf6fbc906c6360d701e034b7ef41e53d0cd2a6
7
- data.tar.gz: 7be9fba616e5fe10e218ba2330e6e64d75b60f4cd35538a2ffcaa50827b9b16c18cc4e3eb898f71218674d1b40b2814c06788b3bbb68814dc792d88c6d91a1fb
6
+ metadata.gz: aabec4b730b1aa08865c0e844eda3521f0d72781814479ffebde1c0ce0334568bb2e722998489d5a32d63c488d10a6da6783d7d144d0b41d62e73702b8945123
7
+ data.tar.gz: de1257b71353f6f6a278d04a0b9ca68d779857166e133801fd81360878fa49c713d32a8a12baaaff77b695825fc1bbb5bddd68504cc36f437ae44ba90854d384
data/README.md CHANGED
@@ -42,11 +42,13 @@ end
42
42
 
43
43
  ### Usage
44
44
 
45
- Since MotionOcean is based on [AFMotion][afmotion], requests are being made asynchronous. Every MotionOcean method call therefore requires a block, which yields the data (or nil if the request failed).
45
+ The API client is based on NSURLSession and every request is executed asynchronously. Every MotionOcean method call therefore requires a block, which yields a `MotionOcean::API::Response`.
46
46
 
47
47
  MotionOcean pretty much implements the DO API 1:1, so please check [their documentation](https://developers.digitalocean.com/v2/) for the available functions and options.
48
48
 
49
- ### success?
49
+ ### The response object
50
+
51
+ #### success?
50
52
 
51
53
  You can use `success?` to check if a successful HTTP status code was returned:
52
54
 
@@ -56,25 +58,41 @@ client.droplet.create(options) do |result|
56
58
  end
57
59
  ```
58
60
 
59
- ### response
61
+ #### data
62
+
63
+ The response data is assigned to the `data`-property of the response object. This is a `Hash` which
64
+ corresponds with the returned JSON by DigitalOcean.
60
65
 
61
- MotionOcean uses [AFMotion][afmotion]. You can use `response` to get to the response
66
+ ``` ruby
67
+ client.droplet.create(options) do |result|
68
+ result.data # => Hash with the JSON response
69
+ end
70
+ ```
71
+
72
+
73
+ #### response
74
+
75
+ MotionOcean uses NSURLSession. You can use `response` to get to the original response
62
76
  object:
63
77
 
64
78
  ``` ruby
65
79
  client.droplet.create(options) do |result|
66
- result.response # => AFMotion::HTTPResult
80
+ result.response # => NSHTTPURLResponse
67
81
  end
68
82
  ```
69
- [afmotion]: https://github.com/usepropellor/afmotion
83
+ #### error
84
+
85
+ When there is an error you can check this for the specific error. If you think it is a bug in MotionOcean, please open an issue or even better: submit a Pull Request with the fix!
70
86
 
71
87
  ## Aknowledgements
72
88
 
73
89
  This library is an adaptation for RubyMotion of [barge](https://github.com/boats/barge) by [Ørjan Blom](https://github.com/blom).
74
90
 
91
+ The brilliant [MotionInMotion](https://motioninmotion.tv) screencasts helped me with implementing the API client using NSURLSession (specifically [episode #33](https://motioninmotion.tv/screencasts/33)) and iOS development using RubyMotion in general.
92
+
75
93
  ## Contributing
76
94
 
77
- 1. Fork it ( https://github.com/[my-github-username]/motion_ocean/fork )
95
+ 1. Fork it ( https://github.com/datarift/motion_ocean/fork )
78
96
  2. Create your feature branch (`git checkout -b my-new-feature`)
79
97
  3. Commit your changes (`git commit -am 'Add some feature'`)
80
98
  4. Push to the branch (`git push origin my-new-feature`)
data/lib/motion_ocean.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'dbt'
2
- require 'afmotion'
3
2
 
4
3
  unless defined?(Motion::Project::Config)
5
4
  raise "This file must be required within a RubyMotion project Rakefile."
@@ -0,0 +1,5 @@
1
+ module MotionOcean
2
+ module API
3
+
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ module MotionOcean
2
+ module API
3
+ class Authorization
4
+ class << self
5
+ def build(options = {})
6
+ username, password = options.values_at :username, :password
7
+ token = options.fetch(:token, nil)
8
+ token_name = options.fetch(:token_name, "Token")
9
+
10
+ if username && password
11
+ http_basic_header(username, password)
12
+ elsif token
13
+ token_header(token, token_name)
14
+ else
15
+ nil
16
+ end
17
+ end
18
+
19
+ def http_basic_header(username, password)
20
+ token_data = "#{username}:#{password}".dataUsingEncoding(NSUTF8StringEncoding)
21
+ token_header(token_data.base64Encoding, "Basic")
22
+ end
23
+
24
+ def token_header(token, token_name)
25
+ "#{token_name} #{token}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,115 @@
1
+ module MotionOcean
2
+ module API
3
+ class Client
4
+ attr_reader :base_url
5
+
6
+ def initialize(base_url, &block)
7
+ base_url += '/' unless base_url.end_with? '/'
8
+ @base_url = NSURL.URLWithString(base_url)
9
+ @headers = {}
10
+
11
+ # yield(self) if block_given?
12
+ if block_given?
13
+ case block.arity
14
+ when 0
15
+ instance_eval(&block)
16
+ when 1
17
+ block.call(self)
18
+ end
19
+ end
20
+ end
21
+
22
+ [:delete, :get, :head, :post, :put].each do |verb|
23
+ define_method verb do |path, options = {}, &block|
24
+ request = create_request(path, options, verb)
25
+ create_task(request, &block).resume
26
+ end
27
+ end
28
+
29
+ def header(name, value)
30
+ @headers[name] = value
31
+ end
32
+
33
+ def authorization(options = {})
34
+ header "Authorization", MotionOcean::API::Authorization.build(options)
35
+ end
36
+
37
+ private
38
+ def config
39
+ NSURLSessionConfiguration.defaultSessionConfiguration
40
+ end
41
+
42
+ def session
43
+ NSURLSession.sessionWithConfiguration(config)
44
+ end
45
+
46
+ def create_request(path, options, method)
47
+ headers = options.fetch(:headers, {})
48
+ body = options.fetch(:body, nil)
49
+ query = options.fetch(:query, nil)
50
+
51
+ url = create_url(path, query)
52
+
53
+ request = NSMutableURLRequest.requestWithURL(url)
54
+
55
+ set_headers(request, @headers.merge(headers))
56
+ set_body(request, body) if body
57
+
58
+ request.setHTTPMethod(method.to_s.upcase)
59
+
60
+ request
61
+ end
62
+
63
+ def create_url(path, query)
64
+ query_string = query.map { |key, value| "#{key}=#{value}" }.join '&' unless query.nil?
65
+
66
+ path.sub!('/', '') if path.start_with? '/'
67
+
68
+ components = NSURLComponents.new
69
+ components.path = path
70
+ components.query = query_string
71
+ components.URLRelativeToURL @base_url
72
+ end
73
+
74
+ def set_headers(request, headers)
75
+ headers.each do |name, value|
76
+ request.addValue value.to_s, forHTTPHeaderField: name.to_s
77
+ end
78
+ end
79
+
80
+ def set_body(request, params)
81
+ data = json_params(params)
82
+ request.setHTTPBody(data)
83
+ end
84
+
85
+ def json_params(params)
86
+ NSJSONSerialization.dataWithJSONObject(params, options: 0, error: nil)
87
+ end
88
+
89
+ def create_task(request, &block)
90
+ if block_given?
91
+ session.dataTaskWithRequest(request, completionHandler: -> (data, response, error) {
92
+ block.call(APIResponse.new(data, response, error))
93
+ })
94
+ else
95
+ session.dataTaskWithRequest(request)
96
+ end
97
+ end
98
+
99
+ class APIResponse
100
+ attr_reader :success, :data, :error, :response
101
+
102
+ def initialize(data, response, error)
103
+ @success = (200...300).include?(response.statusCode) if response
104
+ @data = NSJSONSerialization.JSONObjectWithData(data, options: 0, error: nil)
105
+ @response = response
106
+ @error = error
107
+ end
108
+
109
+ def success?
110
+ @success
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -25,20 +25,18 @@ module MotionOcean
25
25
  @action = Resource::Action.new(api_client)
26
26
  @domain = Resource::Domain.new(api_client)
27
27
  @droplet = Resource::Droplet.new(api_client)
28
+ @image = Resource::Image.new(api_client)
29
+ @key = Resource::Key.new(api_client)
28
30
  @region = Resource::Region.new(api_client)
29
31
  @size = Resource::Size.new(api_client)
30
32
  end
31
33
 
32
34
  def api_client
33
- @api_client ||= AFMotion::SessionClient.build(DIGITAL_OCEAN_URL) do |client|
34
- client.session_configuration :default
35
-
35
+ @api_client ||= MotionOcean::API::Client.new(DIGITAL_OCEAN_URL) do |client|
36
36
  client.header 'Accept', 'application/json'
37
37
  client.header 'Content-Type', 'application/json'
38
- client.header 'Authorization', "Bearer #{access_token}"
39
38
 
40
- client.request_serializer :json
41
- client.response_serializer :json
39
+ client.authorization token: access_token, token_name: 'Bearer'
42
40
  end
43
41
  end
44
42
  end
@@ -21,13 +21,7 @@ module MotionOcean
21
21
  opts = { per_page: PER_PAGE } if verb == :get
22
22
  options = opts.merge(options)
23
23
 
24
- api_client.public_send(verb, url, options) do |response|
25
- data = response.object.tap do |r|
26
- r.define_singleton_method(:response) { response }
27
- r.define_singleton_method(:success?) { response.success? }
28
- end
29
- block.call(data)
30
- end
24
+ api_client.public_send(verb, url, options, &block)
31
25
  end
32
26
  end
33
27
  end
@@ -1,3 +1,3 @@
1
1
  module MotionOcean
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -0,0 +1,27 @@
1
+ describe "MotionOcean::API::Authorization" do
2
+ before do
3
+ @token = 'some_token'
4
+ @token_name = 'FooBar'
5
+ @username = 'foo'
6
+ @password = 'bar'
7
+ @auth_data = 'Zm9vOmJhcg=='
8
+ end
9
+
10
+ it "creates a token-based auth header" do
11
+ header_value = MotionOcean::API::Authorization.build(token: @token)
12
+
13
+ header_value.should == "Token #{@token}"
14
+ end
15
+
16
+ it "uses the specified token_name" do
17
+ header_value = MotionOcean::API::Authorization.build(token: @token, token_name: @token_name)
18
+
19
+ header_value.should == "#{@token_name} #{@token}"
20
+ end
21
+
22
+ it "accepts HTTP Basic parameters" do
23
+ header_value = MotionOcean::API::Authorization.build(username: @username, password: @password)
24
+
25
+ header_value.should == "Basic #{@auth_data}"
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ describe "MotionOcean::API::Client" do
2
+ before do
3
+ @base_url = 'http://httpbin.org/'
4
+ @base_url_no_slash = 'http://httpbin.org'
5
+ @token = 'some_test_token'
6
+ end
7
+
8
+ it "accepts a base URL and a block" do
9
+ client = MotionOcean::API::Client.new(@base_url) do
10
+
11
+ end
12
+
13
+ client.base_url.should != nil
14
+ client.base_url.absoluteString.should == @base_url
15
+ end
16
+
17
+ it "adds a slash to the base_url if it is not present" do
18
+ client = MotionOcean::API::Client.new(@base_url_no_slash) do
19
+
20
+ end
21
+
22
+ client.base_url.should != nil
23
+ client.base_url.absoluteString.should == @base_url
24
+ end
25
+
26
+ it "accepts headers" do
27
+ client = MotionOcean::API::Client.new(@base_url_no_slash) do
28
+ header 'Accept', 'application/json'
29
+ end
30
+
31
+ headers = client.instance_variable_get(:@headers)
32
+
33
+ headers.should != nil
34
+ headers.empty?.should == false
35
+ headers.should.be.kind_of Hash
36
+ headers.include?('Accept').should == true
37
+ headers['Accept'].should == 'application/json'
38
+ end
39
+
40
+ it "sets authorization headers" do
41
+ client = MotionOcean::API::Client.new(@base_url_no_slash) do
42
+ authorization token: 'some_test_token'
43
+ end
44
+
45
+ headers = client.instance_variable_get(:@headers)
46
+
47
+ headers.should != nil
48
+ headers.empty?.should == false
49
+
50
+ headers.include?('Authorization').should == true
51
+ headers['Authorization'].should == "Token #{@token}"
52
+ end
53
+ end
data/spec/client_spec.rb CHANGED
@@ -1,9 +1,16 @@
1
1
  describe "MotionOcean::Client" do
2
- describe "#access_token" do
3
- before do
4
- @token ||= "some_token"
5
- end
2
+ before do
3
+ @token ||= "some_token"
4
+ end
6
5
 
6
+ describe "class" do
7
+ it "is the right class" do
8
+ client = MotionOcean::Client.new(access_token: @token)
9
+ client.should.be.kind_of(MotionOcean::Client)
10
+ end
11
+ end
12
+
13
+ describe "#access_token" do
7
14
  it "can be set via a hash" do
8
15
  client = MotionOcean::Client.new(access_token: @token)
9
16
  expect(client.access_token).to eq(@token)
@@ -24,4 +31,22 @@ describe "MotionOcean::Client" do
24
31
  end
25
32
 
26
33
  end
34
+
35
+ describe "operation" do
36
+ it "can call the API" do
37
+ client = MotionOcean::Client.new(access_token: ENV['DO_API_TOKEN'])
38
+ client.should != nil
39
+
40
+ client.domain.all do |response|
41
+ expect(response).not_to be_nil
42
+ expect(response.success?).to be_true
43
+ expect(response.error).to be_nil
44
+ response.data.should.be.kind_of(Hash)
45
+
46
+ resume
47
+ end
48
+
49
+ wait_max(10) {}
50
+ end
51
+ end
27
52
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion_ocean
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erwin Boskma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-16 00:00:00.000000000 Z
11
+ date: 2014-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dbt
@@ -30,26 +30,6 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.1.0
33
- - !ruby/object:Gem::Dependency
34
- name: afmotion
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '2.3'
40
- - - ">="
41
- - !ruby/object:Gem::Version
42
- version: 2.3.0
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '2.3'
50
- - - ">="
51
- - !ruby/object:Gem::Version
52
- version: 2.3.0
53
33
  - !ruby/object:Gem::Dependency
54
34
  name: rake
55
35
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +100,9 @@ extra_rdoc_files: []
120
100
  files:
121
101
  - README.md
122
102
  - lib/motion_ocean.rb
103
+ - lib/motion_ocean/api.rb
104
+ - lib/motion_ocean/api/authorization.rb
105
+ - lib/motion_ocean/api/client.rb
123
106
  - lib/motion_ocean/client.rb
124
107
  - lib/motion_ocean/resource/action.rb
125
108
  - lib/motion_ocean/resource/base.rb
@@ -130,6 +113,8 @@ files:
130
113
  - lib/motion_ocean/resource/region.rb
131
114
  - lib/motion_ocean/resource/size.rb
132
115
  - lib/motion_ocean/version.rb
116
+ - spec/api/authorization_spec.rb
117
+ - spec/api/client_spec.rb
133
118
  - spec/client_spec.rb
134
119
  homepage: https://github.com/datarift/motion_ocean
135
120
  licenses:
@@ -156,4 +141,6 @@ signing_key:
156
141
  specification_version: 4
157
142
  summary: Digital Ocean API implementation for RubyMotion
158
143
  test_files:
144
+ - spec/api/authorization_spec.rb
145
+ - spec/api/client_spec.rb
159
146
  - spec/client_spec.rb