uploadcare-ruby 1.0.1.rc1
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/.DS_Store +0 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +42 -0
- data/LICENSE +22 -0
- data/README.md +385 -0
- data/Rakefile +2 -0
- data/lib/uploadcare.rb +43 -0
- data/lib/uploadcare/api.rb +20 -0
- data/lib/uploadcare/api/connections.rb +32 -0
- data/lib/uploadcare/api/file_api.rb +9 -0
- data/lib/uploadcare/api/file_list_api.rb +8 -0
- data/lib/uploadcare/api/group_api.rb +38 -0
- data/lib/uploadcare/api/group_list_api.rb +8 -0
- data/lib/uploadcare/api/parser.rb +42 -0
- data/lib/uploadcare/api/project_api.rb +9 -0
- data/lib/uploadcare/api/raw_api.rb +73 -0
- data/lib/uploadcare/api/uploading_api.rb +122 -0
- data/lib/uploadcare/resources/file.rb +136 -0
- data/lib/uploadcare/resources/file_list.rb +41 -0
- data/lib/uploadcare/resources/group.rb +116 -0
- data/lib/uploadcare/resources/group_list.rb +31 -0
- data/lib/uploadcare/resources/project.rb +21 -0
- data/lib/uploadcare/version.rb +3 -0
- data/spec/file_list_spec.rb +65 -0
- data/spec/file_spec.rb +105 -0
- data/spec/group_list_spec.rb +31 -0
- data/spec/group_spec.rb +90 -0
- data/spec/operations_spec.rb +60 -0
- data/spec/parser_spec.rb +87 -0
- data/spec/project_spec.rb +21 -0
- data/spec/raw_api_spec.rb +25 -0
- data/spec/spec_helper.rb +16 -0
- data/spec/uploading_multiple_spec.rb +45 -0
- data/spec/uploading_spec.rb +40 -0
- data/spec/view.png +0 -0
- data/spec/view2.jpg +0 -0
- data/uploadcare-ruby.gemspec +35 -0
- metadata +184 -0
data/Rakefile
ADDED
data/lib/uploadcare.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
require 'uploadcare/api'
|
6
|
+
require 'uploadcare/version'
|
7
|
+
|
8
|
+
module Uploadcare
|
9
|
+
DEFAULT_SETTINGS = {
|
10
|
+
public_key: 'demopublickey',
|
11
|
+
private_key: 'demoprivatekey',
|
12
|
+
upload_url_base: 'https://upload.uploadcare.com',
|
13
|
+
api_url_base: 'https://api.uploadcare.com',
|
14
|
+
static_url_base: 'http://www.ucarecdn.com',
|
15
|
+
api_version: '0.3',
|
16
|
+
cache_files: true,
|
17
|
+
}
|
18
|
+
|
19
|
+
USER_AGENT = "uploadcare-ruby/#{Uploadcare::VERSION}"
|
20
|
+
|
21
|
+
|
22
|
+
def self.default_settings
|
23
|
+
DEFAULT_SETTINGS
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.user_agent
|
27
|
+
USER_AGENT
|
28
|
+
end
|
29
|
+
|
30
|
+
UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
31
|
+
|
32
|
+
GROUP_UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}~(?<count>\d+)$/
|
33
|
+
|
34
|
+
CDN_URL_FILE_REGEX = /
|
35
|
+
(?<uuid>[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})
|
36
|
+
(?:\/-\/(?<operations>.*?))?\/?$
|
37
|
+
/ix
|
38
|
+
|
39
|
+
CDN_URL_GROUP_REGEX = /
|
40
|
+
(?<uuid>[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}~(?<count>\d+))
|
41
|
+
(?:\/-\/(?<operations>.*?))?\/?$
|
42
|
+
/ix
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
Dir[File.dirname(__FILE__) + '/api/*.rb'].each {|file| require file }
|
5
|
+
Dir[File.dirname(__FILE__) + '/resources/*.rb'].each {|file| require file }
|
6
|
+
|
7
|
+
|
8
|
+
module Uploadcare
|
9
|
+
class Api
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
include Uploadcare::RawApi
|
13
|
+
include Uploadcare::UploadingApi
|
14
|
+
include Uploadcare::FileApi
|
15
|
+
include Uploadcare::ProjectApi
|
16
|
+
include Uploadcare::FileListApi
|
17
|
+
include Uploadcare::GroupApi
|
18
|
+
include Uploadcare::GroupListApi
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
|
4
|
+
module Uploadcare
|
5
|
+
class Connections
|
6
|
+
def self.api_connection options
|
7
|
+
connection = Faraday.new url: options[:api_url_base] do |frd|
|
8
|
+
frd.request :url_encoded
|
9
|
+
frd.use FaradayMiddleware::FollowRedirects, limit: 3
|
10
|
+
frd.adapter :net_http # actually, default adapter, just to be clear
|
11
|
+
frd.headers['Authorization'] = "Uploadcare.Simple #{options[:public_key]}:#{options[:private_key]}"
|
12
|
+
frd.headers['Accept'] = "application/vnd.uploadcare-v#{options[:api_version]}+json"
|
13
|
+
frd.headers['User-Agent'] = Uploadcare::user_agent
|
14
|
+
end
|
15
|
+
|
16
|
+
connection
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.upload_connection options
|
20
|
+
ca_path = '/etc/ssl/certs' if File.exists?('/etc/ssl/certs')
|
21
|
+
|
22
|
+
connection = Faraday.new ssl: { ca_path: ca_path }, url: options[:upload_url_base] do |frd|
|
23
|
+
frd.request :multipart
|
24
|
+
frd.request :url_encoded
|
25
|
+
frd.adapter Faraday.default_adapter
|
26
|
+
frd.headers['User-Agent'] = Uploadcare::user_agent
|
27
|
+
end
|
28
|
+
|
29
|
+
connection
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Uploadcare
|
2
|
+
module GroupApi
|
3
|
+
|
4
|
+
def group uuid_or_cdn_url
|
5
|
+
group = Uploadcare::Api::Group.new self, uuid_or_cdn_url
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def create_group ary
|
10
|
+
unless ary.kind_of?(Array)
|
11
|
+
raise ArgumentError.new "You should send and array of files or valid UUIDs"
|
12
|
+
else
|
13
|
+
if ary.select {|f| !!f.kind_of?(Uploadcare::Api::File) }.any?
|
14
|
+
files = Hash.new
|
15
|
+
ary.each_with_index do |file, i|
|
16
|
+
files["files[#{i}]"] = file.uuid
|
17
|
+
end
|
18
|
+
elsif ary.select {|f| !!f.kind_of?(String) }.any?
|
19
|
+
files = Hash.new
|
20
|
+
ary.each_with_index do |uuid, i|
|
21
|
+
files["files[#{i}]"] = uuid
|
22
|
+
end
|
23
|
+
else
|
24
|
+
raise ArgumentError.new "You should send and array of files or valid UUIDs"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
data = {
|
30
|
+
pub_key: @options[:public_key],
|
31
|
+
}
|
32
|
+
|
33
|
+
data.merge! files
|
34
|
+
post = parse(upload_request :post, "/group/", data)
|
35
|
+
group = Uploadcare::Api::Group.new self, post["id"], post
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Uploadcare
|
4
|
+
module Parser
|
5
|
+
|
6
|
+
META_URL = /
|
7
|
+
(?<uuid>[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} # base uuid
|
8
|
+
~?(?<count>\d+)?) # optional count
|
9
|
+
(?:\/-\/(?<operations>.*?))?\/?$ # optional operations
|
10
|
+
/ix
|
11
|
+
|
12
|
+
def self.parse string
|
13
|
+
matched = META_URL.match(string)
|
14
|
+
|
15
|
+
# just a simple hash - easy to pass next
|
16
|
+
captured = Hash[ matched.names.zip( matched.captures ) ]
|
17
|
+
|
18
|
+
# raise an error if no uuid was given in the sting
|
19
|
+
raise "Invalid UUID or url was given" if captured["uuid"].nil?
|
20
|
+
|
21
|
+
# operations sring to array of operations
|
22
|
+
if captured["operations"]
|
23
|
+
captured["operations"] = captured["operations"].split("/-/")
|
24
|
+
else
|
25
|
+
captured["operations"] = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# if count was given - it is a group
|
29
|
+
if captured["count"]
|
30
|
+
obj = Group.new captured
|
31
|
+
else
|
32
|
+
obj = File.new captured
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class File < OpenStruct
|
37
|
+
end
|
38
|
+
|
39
|
+
class Group < OpenStruct
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'uploadcare/api/connections'
|
4
|
+
|
5
|
+
module Uploadcare
|
6
|
+
module RawApi
|
7
|
+
|
8
|
+
def initialize options={}
|
9
|
+
@options = Uploadcare::default_settings.merge(options)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
# basic request method
|
14
|
+
def request method = :get, path = "/files/", params = {}
|
15
|
+
response = send_request(method, path, params)
|
16
|
+
parse(response)
|
17
|
+
end
|
18
|
+
alias_method :api_request, :request
|
19
|
+
|
20
|
+
|
21
|
+
# request with GET verb
|
22
|
+
def get path= "/files/", params={}
|
23
|
+
request :get, path, params
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# request with POST verb
|
28
|
+
def post path= "/files/", params={}
|
29
|
+
request :post, path, params
|
30
|
+
end
|
31
|
+
|
32
|
+
# request with PUT verb
|
33
|
+
def put path= "/files/", params={}
|
34
|
+
request :put, path, params
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# request with DELETE verb
|
39
|
+
def delete path= "/files/", params={}
|
40
|
+
request :delete, path, params
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
protected
|
45
|
+
def send_request method, path, params={}
|
46
|
+
connection = Uploadcare::Connections.api_connection(@options)
|
47
|
+
response = connection.send method, path, params
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def parse response
|
52
|
+
begin
|
53
|
+
object = JSON.parse(response.body)
|
54
|
+
rescue JSON::ParserError
|
55
|
+
object = false
|
56
|
+
end
|
57
|
+
|
58
|
+
# and returning the object (file actually) or raise new error
|
59
|
+
if response.status < 300
|
60
|
+
object
|
61
|
+
else
|
62
|
+
message = "HTTP code #{response.status}"
|
63
|
+
if object # add active_support god damn it
|
64
|
+
message += ": #{object["detail"]}"
|
65
|
+
else
|
66
|
+
message += ": unknown error occured."
|
67
|
+
end
|
68
|
+
|
69
|
+
raise ArgumentError.new(message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require "uri"
|
2
|
+
require 'mime/types'
|
3
|
+
|
4
|
+
module Uploadcare
|
5
|
+
module UploadingApi
|
6
|
+
# intelegent guess for file or url uploading
|
7
|
+
def upload object
|
8
|
+
# if object is file - uploading it as file
|
9
|
+
if object.kind_of?(File)
|
10
|
+
upload_file(object)
|
11
|
+
|
12
|
+
# if a string - try to upload as url
|
13
|
+
elsif object.kind_of?(String)
|
14
|
+
upload_url(object)
|
15
|
+
|
16
|
+
# array of files
|
17
|
+
elsif object.kind_of?(Array)
|
18
|
+
upload_files(object)
|
19
|
+
|
20
|
+
else
|
21
|
+
raise ArgumentError.new "you should give File object, array of files or valid url string"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def upload_files files
|
27
|
+
if files.select {|f| !f.kind_of?(File)}.any?
|
28
|
+
raise ArgumentError.new "one or more of given files is not actually files"
|
29
|
+
else
|
30
|
+
data = {
|
31
|
+
UPLOADCARE_PUB_KEY: @options[:public_key],
|
32
|
+
}
|
33
|
+
|
34
|
+
files.each_with_index do |f, i|
|
35
|
+
data["file[#{i}]"] = Faraday::UploadIO.new(f.path, extract_mime_type(f))
|
36
|
+
end
|
37
|
+
|
38
|
+
response = upload_request :post, '/base/', data
|
39
|
+
uuids = upload_parse(response)
|
40
|
+
|
41
|
+
files = uuids.values.map! {|f| Uploadcare::Api::File.new self, f }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# upload file to servise
|
47
|
+
def upload_file file
|
48
|
+
if file.kind_of?(File)
|
49
|
+
mime_type = extract_mime_type(file)
|
50
|
+
|
51
|
+
response = upload_request :post, '/base/', {
|
52
|
+
UPLOADCARE_PUB_KEY: @options[:public_key],
|
53
|
+
file: Faraday::UploadIO.new(file.path, mime_type)
|
54
|
+
}
|
55
|
+
uuid = upload_parse(response)["file"]
|
56
|
+
Uploadcare::Api::File.new self, uuid
|
57
|
+
else
|
58
|
+
raise ArgumentError.new 'expecting File object'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# create file is the same as uplaod file
|
63
|
+
alias_method :create_file, :upload_file
|
64
|
+
|
65
|
+
|
66
|
+
#upload from url
|
67
|
+
def upload_url url
|
68
|
+
uri = URI.parse(url)
|
69
|
+
|
70
|
+
if uri.kind_of?(URI::HTTP) # works both for HTTP and HTTPS as HTTPS inherits from HTTP
|
71
|
+
token = get_token(url)
|
72
|
+
|
73
|
+
while (response = get_status_response(token))['status'] == 'unknown'
|
74
|
+
sleep 0.5
|
75
|
+
end
|
76
|
+
|
77
|
+
raise ArgumentError.new(response['error']) if response['status'] == 'error'
|
78
|
+
uuid = response['file_id']
|
79
|
+
Uploadcare::Api::File.new self, uuid
|
80
|
+
else
|
81
|
+
raise ArgumentError.new 'invalid url was given'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
alias_method :upload_from_url, :upload_url
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
protected
|
90
|
+
def upload_request method, path, params = {}
|
91
|
+
connection = Uploadcare::Connections.upload_connection(@options)
|
92
|
+
response = connection.send method, path, params
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def upload_parse response
|
97
|
+
raise ArgumentError.new(response.body) if response.status > 200
|
98
|
+
begin
|
99
|
+
JSON.parse(response.body)
|
100
|
+
rescue JSON::ParserError
|
101
|
+
response.body
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
private
|
107
|
+
def get_status_response token
|
108
|
+
upload_parse(upload_request(:post, '/from_url/status/', {token: token}))
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def get_token url
|
113
|
+
response = upload_request :post, '/from_url/', { source_url: url, pub_key: @options[:public_key] }
|
114
|
+
token = upload_parse(response)["token"]
|
115
|
+
end
|
116
|
+
|
117
|
+
def extract_mime_type file
|
118
|
+
types = MIME::Types.of(file.path)
|
119
|
+
types[0].content_type
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Uploadcare
|
4
|
+
class Api
|
5
|
+
class File < OpenStruct
|
6
|
+
def initialize api, uuid_or_cdn_url, data=nil
|
7
|
+
result = Uploadcare::Parser.parse(uuid_or_cdn_url)
|
8
|
+
|
9
|
+
unless result.is_a?(Uploadcare::Parser::File)
|
10
|
+
msg = "invalid CDN URL or UUID was given for file: #{uuid_or_cdn_url}."
|
11
|
+
if result.is_a?(Uploadcare::Parser::Group)
|
12
|
+
msg = msg + "\n Group UUID was given. Try call @api.group if it is what you intended."
|
13
|
+
end
|
14
|
+
raise msg
|
15
|
+
end
|
16
|
+
|
17
|
+
file = {uuid: result["uuid"], operations: result["operations"]}
|
18
|
+
|
19
|
+
@api = api
|
20
|
+
|
21
|
+
super file
|
22
|
+
|
23
|
+
set_data(data) if data
|
24
|
+
end
|
25
|
+
|
26
|
+
def cdn_url add_operations=false
|
27
|
+
if add_operations
|
28
|
+
cdn_url_with_operations
|
29
|
+
else
|
30
|
+
cdn_url_without_operations
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :public_url, :cdn_url
|
34
|
+
|
35
|
+
|
36
|
+
def cdn_url_without_operations
|
37
|
+
@api.options[:static_url_base] + "/#{@table[:uuid]}/"
|
38
|
+
end
|
39
|
+
alias_method :public_url_without_operations, :cdn_url_without_operations
|
40
|
+
|
41
|
+
|
42
|
+
def cdn_url_with_operations
|
43
|
+
url = cdn_url_without_operations
|
44
|
+
unless operations.empty?
|
45
|
+
ops = operations.join("/-/")
|
46
|
+
url = url + "-/#{ops}/"
|
47
|
+
end
|
48
|
+
url
|
49
|
+
end
|
50
|
+
alias_method :public_url_with_operations, :cdn_url_with_operations
|
51
|
+
|
52
|
+
|
53
|
+
def load_data
|
54
|
+
load_data! unless is_loaded?
|
55
|
+
self
|
56
|
+
end
|
57
|
+
alias_method :load, :load_data
|
58
|
+
|
59
|
+
def load_data!
|
60
|
+
data = @api.get "/files/#{uuid}/"
|
61
|
+
set_data(data)
|
62
|
+
|
63
|
+
self
|
64
|
+
end
|
65
|
+
alias_method :load!, :load_data!
|
66
|
+
|
67
|
+
def is_loaded?
|
68
|
+
!send(:datetime_uploaded).nil?
|
69
|
+
end
|
70
|
+
alias_method :loaded?, :is_loaded?
|
71
|
+
|
72
|
+
|
73
|
+
def store
|
74
|
+
data = @api.put "/files/#{uuid}/storage/"
|
75
|
+
set_data data
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# nil is returning if there is no way to say for sure
|
80
|
+
def is_stored?
|
81
|
+
return nil unless is_loaded?
|
82
|
+
!send(:datetime_stored).nil?
|
83
|
+
end
|
84
|
+
alias_method :stored?, :is_stored?
|
85
|
+
|
86
|
+
|
87
|
+
def delete
|
88
|
+
data = @api.delete "/files/#{uuid}/storage/"
|
89
|
+
set_data data
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
# nil is returning if there is no way to say for sure
|
94
|
+
def is_deleted?
|
95
|
+
return nil unless is_loaded?
|
96
|
+
!send(:datetime_removed).nil?
|
97
|
+
end
|
98
|
+
alias_method :deleted?, :is_deleted?
|
99
|
+
alias_method :removed?, :is_deleted?
|
100
|
+
alias_method :is_removed?, :is_deleted?
|
101
|
+
|
102
|
+
|
103
|
+
# Datetime methods
|
104
|
+
# practicly try and parse the string to date objects
|
105
|
+
["original", "uploaded", "stored", "removed"].each do |dt|
|
106
|
+
define_method "datetime_#{dt}" do
|
107
|
+
date = @table["datetime_#{dt}".to_sym]
|
108
|
+
if date.is_a?(String)
|
109
|
+
begin
|
110
|
+
parsed = DateTime.parse(date)
|
111
|
+
self.send("datetime_#{dt}=", parsed)
|
112
|
+
parsed
|
113
|
+
rescue Exception => e
|
114
|
+
date
|
115
|
+
end
|
116
|
+
else
|
117
|
+
date
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
alias_method :datetime_deleted, :datetime_removed
|
122
|
+
|
123
|
+
|
124
|
+
private
|
125
|
+
def set_data data
|
126
|
+
if data.respond_to? :each
|
127
|
+
data.each do |key, value|
|
128
|
+
self.send "#{key}=", value
|
129
|
+
end
|
130
|
+
else
|
131
|
+
self.data = data
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|