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.
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