copy-ruby 0.0.1
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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +10 -0
- data/LICENSE +23 -0
- data/README.md +232 -0
- data/Rakefile +8 -0
- data/copy.gemspec +24 -0
- data/lib/copy.rb +85 -0
- data/lib/copy/base.rb +43 -0
- data/lib/copy/client.rb +70 -0
- data/lib/copy/file.rb +134 -0
- data/lib/copy/link.rb +52 -0
- data/lib/copy/operations/activity.rb +27 -0
- data/lib/copy/operations/all.rb +31 -0
- data/lib/copy/operations/base.rb +47 -0
- data/lib/copy/operations/create.rb +20 -0
- data/lib/copy/operations/delete.rb +25 -0
- data/lib/copy/operations/find.rb +21 -0
- data/lib/copy/operations/meta.rb +20 -0
- data/lib/copy/operations/show.rb +20 -0
- data/lib/copy/operations/update.rb +21 -0
- data/lib/copy/request/base.rb +41 -0
- data/lib/copy/request/connection.rb +120 -0
- data/lib/copy/request/helpers.rb +36 -0
- data/lib/copy/request/info.rb +41 -0
- data/lib/copy/request/validator.rb +53 -0
- data/lib/copy/revision.rb +25 -0
- data/lib/copy/session.rb +56 -0
- data/lib/copy/user.rb +24 -0
- data/lib/copy/version.rb +3 -0
- data/spec/copy/base_spec.rb +12 -0
- data/spec/copy/client_spec.rb +69 -0
- data/spec/copy/file_spec.rb +306 -0
- data/spec/copy/link_spec.rb +238 -0
- data/spec/copy/request/base_spec.rb +53 -0
- data/spec/copy/request/connection_spec.rb +73 -0
- data/spec/copy/request/info_spec.rb +27 -0
- data/spec/copy/request/validator_spec.rb +13 -0
- data/spec/copy/revision_spec.rb +42 -0
- data/spec/copy/user_spec.rb +119 -0
- data/spec/copy_spec.rb +52 -0
- data/spec/fixtures/hola.txt +1 -0
- data/spec/spec_helper.rb +12 -0
- metadata +170 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module Copy
|
2
|
+
module Operations
|
3
|
+
module Show
|
4
|
+
module ClassMethods
|
5
|
+
# Shows a given object
|
6
|
+
#
|
7
|
+
# @param [Integer] id The id of the object that should be shown
|
8
|
+
# @return [Copy::Base] The found object
|
9
|
+
def show(attributes={})
|
10
|
+
response = Copy.request(:get, nil, api_member_url(attributes[:id], :show), {}, options_for_request(attributes))
|
11
|
+
self.new(response)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Copy
|
2
|
+
module Operations
|
3
|
+
module Update
|
4
|
+
module ClassMethods
|
5
|
+
# Updates a object
|
6
|
+
# @param [Integer] id The id of the object that should be updated
|
7
|
+
# @param [Hash] attributes The attributes that should be updated
|
8
|
+
def update(attributes={})
|
9
|
+
id = attributes.delete(:id)
|
10
|
+
session = attributes.delete(:session)
|
11
|
+
response = Copy.request(:put, nil, api_member_url(id, :updated), attributes, options_for_request(session: session))
|
12
|
+
self.new(response)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.extend(ClassMethods)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Copy
|
2
|
+
module Request
|
3
|
+
class Base
|
4
|
+
attr_reader :info
|
5
|
+
attr_accessor :response
|
6
|
+
|
7
|
+
def initialize(info)
|
8
|
+
@info = info
|
9
|
+
end
|
10
|
+
|
11
|
+
def perform
|
12
|
+
raise Copy::AuthenticationError unless valid?
|
13
|
+
connection.set_request_data
|
14
|
+
send_request
|
15
|
+
|
16
|
+
validator.validated_data_for(response)
|
17
|
+
end
|
18
|
+
|
19
|
+
def valid?
|
20
|
+
return false unless info
|
21
|
+
return false unless info.session
|
22
|
+
return false unless info.session.valid?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def send_request
|
29
|
+
self.response = connection.request
|
30
|
+
end
|
31
|
+
|
32
|
+
def connection
|
33
|
+
@connection ||= Connection.new(info)
|
34
|
+
end
|
35
|
+
|
36
|
+
def validator
|
37
|
+
@validator ||= Validator.new(info)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'net/http/post/multipart'
|
2
|
+
|
3
|
+
module Copy
|
4
|
+
module Request
|
5
|
+
class Connection
|
6
|
+
include Helpers
|
7
|
+
attr_reader :access_token, :request_data
|
8
|
+
|
9
|
+
def initialize(request_info)
|
10
|
+
@info = request_info
|
11
|
+
@session = @info.session if @info
|
12
|
+
@access_token = @info.session.access_token if @session
|
13
|
+
end
|
14
|
+
|
15
|
+
def request
|
16
|
+
return unless access_token
|
17
|
+
if use_body_file?
|
18
|
+
::File.open(body_file_attrs[:local_path]) do |file|
|
19
|
+
req = Net::HTTP::Post::Multipart.new(
|
20
|
+
api_url,
|
21
|
+
'file' => UploadIO.new(
|
22
|
+
file,
|
23
|
+
'application/octet-stream',
|
24
|
+
body_file_attrs[:name]
|
25
|
+
)
|
26
|
+
)
|
27
|
+
access_token.sign! req
|
28
|
+
req['X-Api-Version'] = '1'
|
29
|
+
# Todo finish this to work over https
|
30
|
+
http = Net::HTTP.new("api.copy.com", Net::HTTP.https_default_port)
|
31
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
32
|
+
http.use_ssl = true
|
33
|
+
http.start do |http|
|
34
|
+
http.request(req)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
else
|
38
|
+
access_token.send(*request_data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_request_data
|
43
|
+
@request_data = []
|
44
|
+
@request_data << @info.http_method if @info
|
45
|
+
@request_data << api_url
|
46
|
+
unless use_url_params?
|
47
|
+
@request_data << body_json
|
48
|
+
end
|
49
|
+
@request_data << headers
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
# Body params hash
|
55
|
+
#
|
56
|
+
# @return [Hash]
|
57
|
+
def body_hash
|
58
|
+
return {} unless @info
|
59
|
+
@info.data
|
60
|
+
end
|
61
|
+
|
62
|
+
# Json encoded body
|
63
|
+
#
|
64
|
+
# @return [String]
|
65
|
+
def body_json
|
66
|
+
return '' unless body_hash
|
67
|
+
JSON.generate(body_hash)
|
68
|
+
end
|
69
|
+
|
70
|
+
def body_file_attrs
|
71
|
+
body_hash[:file_attrs] || {}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Body params url encoded
|
75
|
+
#
|
76
|
+
# @return [String]
|
77
|
+
def encoded_www_body
|
78
|
+
return '' unless body_hash
|
79
|
+
URI.encode_www_form(body_hash)
|
80
|
+
end
|
81
|
+
|
82
|
+
def use_body_file?
|
83
|
+
return false if use_url_params?
|
84
|
+
body_hash.has_key?(:file)
|
85
|
+
end
|
86
|
+
|
87
|
+
def use_url_params?
|
88
|
+
return false unless @info
|
89
|
+
case @info.http_method
|
90
|
+
when :post, :put
|
91
|
+
false
|
92
|
+
else
|
93
|
+
true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def headers
|
98
|
+
{ "X-Api-Version" => API_VERSION }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the api url foir this request or default
|
102
|
+
def api_url
|
103
|
+
url = 'https://' + domain + '.' + API_BASE
|
104
|
+
if @info
|
105
|
+
url += @info.url
|
106
|
+
if use_url_params? && !body_hash.empty?
|
107
|
+
url += '?' + encoded_www_body
|
108
|
+
end
|
109
|
+
end
|
110
|
+
url
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the domain for the current request or the default one
|
114
|
+
def domain
|
115
|
+
return @info.subdomain if @info
|
116
|
+
DOMAIN_BASE
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Copy
|
2
|
+
module Request
|
3
|
+
module Helpers
|
4
|
+
def flatten_hash_keys(old_hash, new_hash={}, keys=nil)
|
5
|
+
old_hash.each do |key, value|
|
6
|
+
key = key.to_s
|
7
|
+
if value.is_a?(Hash)
|
8
|
+
all_keys_formatted = keys + "[#{key}]"
|
9
|
+
flatten_hash_keys(value, new_hash, all_keys_formatted)
|
10
|
+
else
|
11
|
+
new_hash[key] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
new_hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def normalize_params(params, key=nil)
|
18
|
+
params = flatten_hash_keys(params) if params.is_a?(Hash)
|
19
|
+
result = {}
|
20
|
+
params.each do |key, value|
|
21
|
+
case value
|
22
|
+
when Hash
|
23
|
+
result[key.to_s] = normalize_params(value)
|
24
|
+
when Array
|
25
|
+
value.each_with_index do |item_value, index|
|
26
|
+
result["#{key.to_s}[#{index}]"] = item_value.to_s
|
27
|
+
end
|
28
|
+
else
|
29
|
+
result[key.to_s] = value.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Copy
|
2
|
+
module Request
|
3
|
+
class Info
|
4
|
+
attr_accessor :http_method, :api_url, :data, :subdomain, :session, :base_path
|
5
|
+
|
6
|
+
def initialize(http_method, subdomain, api_url, data, options={})
|
7
|
+
@http_method = http_method
|
8
|
+
@subdomain = subdomain || DOMAIN_BASE
|
9
|
+
@api_url = api_url
|
10
|
+
@data = data
|
11
|
+
@base_path = API_BASE_PATH
|
12
|
+
@session = options[:session]
|
13
|
+
end
|
14
|
+
|
15
|
+
def url
|
16
|
+
url = "/#{base_path}/#{api_url}"
|
17
|
+
if has_id?
|
18
|
+
url += "/#{data[:id]}"
|
19
|
+
data.delete(:id)
|
20
|
+
end
|
21
|
+
|
22
|
+
url
|
23
|
+
end
|
24
|
+
|
25
|
+
def path_with_params(path, params)
|
26
|
+
unless params.empty?
|
27
|
+
encoded_params = URI.encode_www_form(params)
|
28
|
+
[path, encoded_params].join("?")
|
29
|
+
else
|
30
|
+
path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def has_id?
|
37
|
+
!data[:id].nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Copy
|
2
|
+
module Request
|
3
|
+
class Validator
|
4
|
+
attr_reader :info
|
5
|
+
attr_accessor :response
|
6
|
+
|
7
|
+
def initialize(info)
|
8
|
+
@info = info
|
9
|
+
end
|
10
|
+
|
11
|
+
def validated_data_for(incoming_response)
|
12
|
+
self.response = incoming_response
|
13
|
+
verify_response_code
|
14
|
+
info.data = JSON.parse(response.body) if response.code.to_i != 204
|
15
|
+
info.data ||= {}
|
16
|
+
validate_response_data
|
17
|
+
info.data
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def verify_response_code
|
23
|
+
raise AuthenticationError if response.code.to_i == 401
|
24
|
+
raise APIError if response.code.to_i >= 500
|
25
|
+
raise NotFound if response.code.to_i >= 404
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_response_data
|
29
|
+
body ||= info.data
|
30
|
+
if body.is_a?(Hash)
|
31
|
+
if body['error']
|
32
|
+
handle_api_error(body['error'], body['message'])
|
33
|
+
elsif body['errors']
|
34
|
+
body['errors'].each do |error|
|
35
|
+
handle_api_error(error['code'], error['message'])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def handle_api_error(code, message)
|
42
|
+
error = case code
|
43
|
+
when 1021, 1024 then ObjectNotFound.new(message)
|
44
|
+
when 1300, 1303 then BadRequest.new(message)
|
45
|
+
else
|
46
|
+
binding.pry
|
47
|
+
APIError.new(message)
|
48
|
+
end
|
49
|
+
fail error
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Copy
|
2
|
+
class Revision < Base
|
3
|
+
|
4
|
+
attr_accessor :id, :revision_id, :modified_time, :size, :latest, :conflict,
|
5
|
+
:type, :creator
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
super(attributes)
|
9
|
+
parse_creator
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def parse_creator
|
15
|
+
return unless creator
|
16
|
+
@creator = Copy::User.new(creator)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Parses UNIX timestamps and creates Time objects.
|
20
|
+
def parse_timestamps
|
21
|
+
super
|
22
|
+
@modified_time = Time.at(modified_time) if modified_time
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/copy/session.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'oauth'
|
2
|
+
|
3
|
+
module Copy
|
4
|
+
class Session
|
5
|
+
|
6
|
+
attr_accessor :token, :secret, :access_token
|
7
|
+
attr_accessor :consumer_key, :consumer_secret
|
8
|
+
attr_accessor :oauth_verifier, :oauth_token
|
9
|
+
|
10
|
+
OAUTH_URLS = {
|
11
|
+
:site => 'https://api.copy.com',
|
12
|
+
:authorize_url => 'https://www.copy.com/applications/authorize',
|
13
|
+
:request_token_url => 'https://api.copy.com/oauth/request',
|
14
|
+
:access_token_url => 'https://api.copy.com/oauth/access',
|
15
|
+
:http_method => :get
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(opts={})
|
19
|
+
@secret = opts[:secret]
|
20
|
+
@token = opts[:token]
|
21
|
+
@consumer_secret = opts[:secret]
|
22
|
+
@consumer_key = opts[:consumer_key] || Copy.configuration[:consumer_key]
|
23
|
+
@consumer_secret = opts[:consumer_secret] || Copy.configuration[:consumer_secret]
|
24
|
+
@oauth_verifier = opts[:oauth_verifier]
|
25
|
+
@oauth_token = opts[:oauth_token]
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
return false unless token
|
30
|
+
return false unless secret
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def consumer
|
35
|
+
@consumer ||= OAuth::Consumer.new(consumer_key, consumer_secret,
|
36
|
+
:site => OAUTH_URLS[:site]
|
37
|
+
# :authorize_url => OAUTH_URLS[:authorize_url],
|
38
|
+
# :request_token_url => OAUTH_URLS[:request_token_url],
|
39
|
+
# :access_token_url => get_access_token_url
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_access_token_url
|
44
|
+
url = OAUTH_URLS[:request_token_url]
|
45
|
+
url += "?oauth_verifier=#{oauth_verifier}" if oauth_verifier
|
46
|
+
url += "&oauth_token=#{oauth_token}" if oauth_token
|
47
|
+
end
|
48
|
+
|
49
|
+
def access_token
|
50
|
+
@access_token ||= OAuth::AccessToken.new(consumer, token, secret)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
data/lib/copy/user.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Copy
|
2
|
+
class User < Base
|
3
|
+
include Copy::Operations::Show
|
4
|
+
include Copy::Operations::Update
|
5
|
+
|
6
|
+
attr_accessor :id, :email, :first_name, :last_name, :developer, :created_time,
|
7
|
+
:emails, :storage, :confirmed, :user_id
|
8
|
+
|
9
|
+
def id
|
10
|
+
@id || @user_id
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# TODO: should be users
|
16
|
+
#
|
17
|
+
def api_resource_name(method=nil)
|
18
|
+
'user'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|