copy-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE +23 -0
  7. data/README.md +232 -0
  8. data/Rakefile +8 -0
  9. data/copy.gemspec +24 -0
  10. data/lib/copy.rb +85 -0
  11. data/lib/copy/base.rb +43 -0
  12. data/lib/copy/client.rb +70 -0
  13. data/lib/copy/file.rb +134 -0
  14. data/lib/copy/link.rb +52 -0
  15. data/lib/copy/operations/activity.rb +27 -0
  16. data/lib/copy/operations/all.rb +31 -0
  17. data/lib/copy/operations/base.rb +47 -0
  18. data/lib/copy/operations/create.rb +20 -0
  19. data/lib/copy/operations/delete.rb +25 -0
  20. data/lib/copy/operations/find.rb +21 -0
  21. data/lib/copy/operations/meta.rb +20 -0
  22. data/lib/copy/operations/show.rb +20 -0
  23. data/lib/copy/operations/update.rb +21 -0
  24. data/lib/copy/request/base.rb +41 -0
  25. data/lib/copy/request/connection.rb +120 -0
  26. data/lib/copy/request/helpers.rb +36 -0
  27. data/lib/copy/request/info.rb +41 -0
  28. data/lib/copy/request/validator.rb +53 -0
  29. data/lib/copy/revision.rb +25 -0
  30. data/lib/copy/session.rb +56 -0
  31. data/lib/copy/user.rb +24 -0
  32. data/lib/copy/version.rb +3 -0
  33. data/spec/copy/base_spec.rb +12 -0
  34. data/spec/copy/client_spec.rb +69 -0
  35. data/spec/copy/file_spec.rb +306 -0
  36. data/spec/copy/link_spec.rb +238 -0
  37. data/spec/copy/request/base_spec.rb +53 -0
  38. data/spec/copy/request/connection_spec.rb +73 -0
  39. data/spec/copy/request/info_spec.rb +27 -0
  40. data/spec/copy/request/validator_spec.rb +13 -0
  41. data/spec/copy/revision_spec.rb +42 -0
  42. data/spec/copy/user_spec.rb +119 -0
  43. data/spec/copy_spec.rb +52 -0
  44. data/spec/fixtures/hola.txt +1 -0
  45. data/spec/spec_helper.rb +12 -0
  46. 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
@@ -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