koala 1.0.0 → 1.2.0beta1

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 (52) hide show
  1. data/.autotest +12 -0
  2. data/.gitignore +3 -1
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +52 -2
  5. data/Gemfile +8 -0
  6. data/Rakefile +0 -1
  7. data/autotest/discover.rb +1 -0
  8. data/koala.gemspec +14 -14
  9. data/lib/koala/batch_operation.rb +74 -0
  10. data/lib/koala/graph_api.rb +157 -133
  11. data/lib/koala/graph_batch_api.rb +87 -0
  12. data/lib/koala/graph_collection.rb +54 -0
  13. data/lib/koala/http_service.rb +177 -0
  14. data/lib/koala/oauth.rb +181 -0
  15. data/lib/koala/realtime_updates.rb +23 -29
  16. data/lib/koala/rest_api.rb +13 -8
  17. data/lib/koala/test_users.rb +33 -16
  18. data/lib/koala/uploadable_io.rb +152 -87
  19. data/lib/koala/utils.rb +11 -0
  20. data/lib/koala.rb +54 -217
  21. data/readme.md +71 -52
  22. data/spec/cases/api_base_spec.rb +6 -6
  23. data/spec/cases/error_spec.rb +32 -0
  24. data/spec/cases/graph_and_rest_api_spec.rb +20 -3
  25. data/spec/cases/graph_api_batch_spec.rb +600 -0
  26. data/spec/cases/graph_api_spec.rb +21 -4
  27. data/spec/cases/http_service_spec.rb +446 -0
  28. data/spec/cases/koala_spec.rb +50 -0
  29. data/spec/cases/oauth_spec.rb +220 -201
  30. data/spec/cases/realtime_updates_spec.rb +45 -31
  31. data/spec/cases/rest_api_spec.rb +23 -7
  32. data/spec/cases/test_users_spec.rb +112 -52
  33. data/spec/cases/uploadable_io_spec.rb +92 -37
  34. data/spec/cases/utils_spec.rb +10 -0
  35. data/spec/fixtures/cat.m4v +0 -0
  36. data/spec/fixtures/facebook_data.yml +23 -22
  37. data/spec/fixtures/mock_facebook_responses.yml +201 -76
  38. data/spec/spec_helper.rb +30 -5
  39. data/spec/support/graph_api_shared_examples.rb +134 -56
  40. data/spec/support/json_testing_fix.rb +42 -0
  41. data/spec/support/koala_test.rb +163 -0
  42. data/spec/support/mock_http_service.rb +60 -57
  43. data/spec/support/ordered_hash.rb +205 -0
  44. data/spec/support/rest_api_shared_examples.rb +139 -15
  45. data/spec/support/uploadable_io_shared_examples.rb +2 -8
  46. metadata +98 -112
  47. data/lib/koala/http_services.rb +0 -146
  48. data/spec/cases/http_services/http_service_spec.rb +0 -54
  49. data/spec/cases/http_services/net_http_service_spec.rb +0 -350
  50. data/spec/cases/http_services/typhoeus_service_spec.rb +0 -144
  51. data/spec/support/live_testing_data_helper.rb +0 -40
  52. data/spec/support/setup_mocks_or_live.rb +0 -52
@@ -3,7 +3,14 @@ require 'koala'
3
3
  module Koala
4
4
  module Facebook
5
5
  module TestUserMethods
6
-
6
+
7
+ def self.included(base)
8
+ base.class_eval do
9
+ # make the Graph API accessible in case someone wants to make other calls to interact with their users
10
+ attr_reader :api, :app_id, :app_access_token, :secret
11
+ end
12
+ end
13
+
7
14
  def initialize(options = {})
8
15
  @app_id = options[:app_id]
9
16
  @app_access_token = options[:app_access_token]
@@ -17,29 +24,34 @@ module Koala
17
24
  oauth = Koala::Facebook::OAuth.new(@app_id, @secret)
18
25
  @app_access_token = oauth.get_app_access_token
19
26
  end
20
- @graph_api = GraphAPI.new(@app_access_token)
27
+ @api = API.new(@app_access_token)
21
28
  end
22
29
 
23
30
  def create(installed, permissions = nil, args = {}, options = {})
24
31
  # Creates and returns a test user
25
32
  args['installed'] = installed
26
33
  args['permissions'] = (permissions.is_a?(Array) ? permissions.join(",") : permissions) if installed
27
- result = @graph_api.graph_call(accounts_path, args, "post", options)
34
+ result = @api.graph_call(accounts_path, args, "post", options)
28
35
  end
29
-
36
+
30
37
  def list
31
- @graph_api.graph_call(accounts_path)["data"]
38
+ @api.graph_call(accounts_path)["data"]
32
39
  end
33
-
40
+
34
41
  def delete(test_user)
35
42
  test_user = test_user["id"] if test_user.is_a?(Hash)
36
- @graph_api.delete_object(test_user)
43
+ @api.delete_object(test_user)
37
44
  end
38
-
45
+
39
46
  def delete_all
40
- list.each {|u| delete u }
47
+ list.each {|u| delete u}
48
+ end
49
+
50
+ def update(test_user, args = {}, http_options = {})
51
+ test_user = test_user["id"] if test_user.is_a?(Hash)
52
+ @api.graph_call(test_user, args, "post", http_options)
41
53
  end
42
-
54
+
43
55
  def befriend(user1_hash, user2_hash)
44
56
  user1_id = user1_hash["id"] || user1_hash[:id]
45
57
  user2_id = user2_hash["id"] || user2_hash[:id]
@@ -51,11 +63,11 @@ module Koala
51
63
  # but the Facebook call would fail
52
64
  raise ArgumentError, "TestUsers#befriend requires hash arguments for both users with id and access_token"
53
65
  end
54
-
55
- u1_graph_api = GraphAPI.new(user1_token)
56
- u2_graph_api = GraphAPI.new(user2_token)
57
66
 
58
- u1_graph_api.graph_call("#{user1_id}/friends/#{user2_id}", {}, "post") &&
67
+ u1_graph_api = API.new(user1_token)
68
+ u2_graph_api = API.new(user2_token)
69
+
70
+ u1_graph_api.graph_call("#{user1_id}/friends/#{user2_id}", {}, "post") &&
59
71
  u2_graph_api.graph_call("#{user2_id}/friends/#{user1_id}", {}, "post")
60
72
  end
61
73
 
@@ -73,9 +85,14 @@ module Koala
73
85
  end
74
86
  return users
75
87
  end
76
-
88
+
89
+ def graph_api
90
+ Koala::Utils.deprecate("the TestUsers.graph_api accessor is deprecated and will be removed in a future version; please use .api instead.")
91
+ @api
92
+ end
93
+
77
94
  protected
78
-
95
+
79
96
  def accounts_path
80
97
  @accounts_path ||= "/#{@app_id}/accounts/test-users"
81
98
  end
@@ -1,115 +1,180 @@
1
- require 'koala'
1
+ require "net/http/post/multipart"
2
2
 
3
3
  module Koala
4
4
  class UploadableIO
5
- attr_reader :io_or_path, :content_type
5
+ attr_reader :io_or_path, :content_type, :filename
6
6
 
7
- def initialize(io_or_path_or_mixed, content_type = nil)
7
+ def initialize(io_or_path_or_mixed, content_type = nil, filename = nil)
8
8
  # see if we got the right inputs
9
- if content_type.nil?
10
- parse_init_mixed_param io_or_path_or_mixed
11
- elsif !content_type.nil? && (io_or_path_or_mixed.respond_to?(:read) or io_or_path_or_mixed.kind_of?(String))
12
- @io_or_path = io_or_path_or_mixed
13
- @content_type = content_type
14
- end
15
-
9
+ parse_init_mixed_param io_or_path_or_mixed, content_type
10
+
11
+ # filename is used in the Ads API
12
+ # if it's provided, take precedence over the detected filename
13
+ # otherwise, fall back to a dummy name
14
+ @filename = filename || @filename || "koala-io-file.dum"
15
+
16
16
  raise KoalaError.new("Invalid arguments to initialize an UploadableIO") unless @io_or_path
17
- raise KoalaError.new("Unable to determine MIME type for UploadableIO") if !@content_type && Koala.multipart_requires_content_type?
17
+ raise KoalaError.new("Unable to determine MIME type for UploadableIO") if !@content_type
18
18
  end
19
-
19
+
20
20
  def to_upload_io
21
- UploadIO.new(@io_or_path, @content_type, "koala-io-file.dum")
21
+ UploadIO.new(@io_or_path, @content_type, @filename)
22
22
  end
23
-
23
+
24
24
  def to_file
25
25
  @io_or_path.is_a?(String) ? File.open(@io_or_path) : @io_or_path
26
26
  end
27
27
 
28
+ def self.binary_content?(content)
29
+ content.is_a?(UploadableIO) || DETECTION_STRATEGIES.detect {|method| send(method, content)}
30
+ end
31
+
28
32
  private
29
- PARSE_STRATEGIES = [
30
- :parse_rails_3_param,
31
- :parse_sinatra_param,
32
- :parse_file_object,
33
- :parse_string_path
34
- ]
35
-
36
- def parse_init_mixed_param(mixed)
37
- PARSE_STRATEGIES.each do |method|
38
- send(method, mixed)
39
- return if @io_or_path && @content_type
40
- end
33
+ DETECTION_STRATEGIES = [
34
+ :sinatra_param?,
35
+ :rails_3_param?,
36
+ :file_param?
37
+ ]
38
+
39
+ PARSE_STRATEGIES = [
40
+ :parse_rails_3_param,
41
+ :parse_sinatra_param,
42
+ :parse_file_object,
43
+ :parse_string_path
44
+ ]
45
+
46
+ def parse_init_mixed_param(mixed, content_type = nil)
47
+ PARSE_STRATEGIES.each do |method|
48
+ send(method, mixed, content_type)
49
+ return if @io_or_path && @content_type
41
50
  end
42
-
43
- # Expects a parameter of type ActionDispatch::Http::UploadedFile
44
- def parse_rails_3_param(uploaded_file)
45
- if uploaded_file.respond_to?(:content_type) and uploaded_file.respond_to?(:tempfile) and uploaded_file.tempfile.respond_to?(:path)
46
- @io_or_path = uploaded_file.tempfile.path
47
- @content_type = uploaded_file.content_type
48
- end
51
+ end
52
+
53
+ # Expects a parameter of type ActionDispatch::Http::UploadedFile
54
+ def self.rails_3_param?(uploaded_file)
55
+ uploaded_file.respond_to?(:content_type) and uploaded_file.respond_to?(:tempfile) and uploaded_file.tempfile.respond_to?(:path)
56
+ end
57
+
58
+ def parse_rails_3_param(uploaded_file, content_type = nil)
59
+ if UploadableIO.rails_3_param?(uploaded_file)
60
+ @io_or_path = uploaded_file.tempfile.path
61
+ @content_type = content_type || uploaded_file.content_type
62
+ @filename = uploaded_file.original_filename
49
63
  end
50
-
51
- # Expects a Sinatra hash of file info
52
- def parse_sinatra_param(file_hash)
53
- if file_hash.kind_of?(Hash) and file_hash.has_key?(:type) and file_hash.has_key?(:tempfile)
54
- @io_or_path = file_hash[:tempfile]
55
- @content_type = file_hash[:type] || detect_mime_type(tempfile)
56
- end
64
+ end
65
+
66
+ # Expects a Sinatra hash of file info
67
+ def self.sinatra_param?(file_hash)
68
+ file_hash.kind_of?(Hash) and file_hash.has_key?(:type) and file_hash.has_key?(:tempfile)
69
+ end
70
+
71
+ def parse_sinatra_param(file_hash, content_type = nil)
72
+ if UploadableIO.sinatra_param?(file_hash)
73
+ @io_or_path = file_hash[:tempfile]
74
+ @content_type = content_type || file_hash[:type] || detect_mime_type(tempfile)
75
+ @filename = file_hash[:filename]
57
76
  end
58
-
59
- # takes a file object
60
- def parse_file_object(file)
61
- if file.kind_of?(File)
62
- @io_or_path = file
63
- @content_type = detect_mime_type(file.path)
64
- end
77
+ end
78
+
79
+ # takes a file object
80
+ def self.file_param?(file)
81
+ file.kind_of?(File)
82
+ end
83
+
84
+ def parse_file_object(file, content_type = nil)
85
+ if UploadableIO.file_param?(file)
86
+ @io_or_path = file
87
+ @content_type = content_type || detect_mime_type(file.path)
88
+ @filename = File.basename(file.path)
65
89
  end
66
-
67
- def parse_string_path(path)
68
- if path.kind_of?(String)
69
- @io_or_path = path
70
- @content_type = detect_mime_type(path)
71
- end
90
+ end
91
+
92
+ def parse_string_path(path, content_type = nil)
93
+ if path.kind_of?(String)
94
+ @io_or_path = path
95
+ @content_type = content_type || detect_mime_type(path)
96
+ @filename = File.basename(path)
72
97
  end
73
-
74
- MIME_TYPE_STRATEGIES = [
75
- :use_mime_module,
76
- :use_simple_detection
77
- ]
78
-
79
- def detect_mime_type(filename)
80
- if filename
81
- MIME_TYPE_STRATEGIES.each do |method|
82
- result = send(method, filename)
83
- return result if result
84
- end
85
- end
86
- nil # if we can't find anything
98
+ end
99
+
100
+ def parse_io(io, content_type = nil)
101
+ if io.respond_to?(:read)
102
+ @io_or_path = io
103
+ @content_type = content_type
87
104
  end
88
-
89
- def use_mime_module(filename)
90
- # if the user has installed mime/types, we can use that
91
- # if not, rescue and return nil
92
- begin
93
- type = MIME::Types.type_for(filename).first
94
- type ? type.to_s : nil
95
- rescue
96
- nil
105
+ end
106
+
107
+ MIME_TYPE_STRATEGIES = [
108
+ :use_mime_module,
109
+ :use_simple_detection
110
+ ]
111
+
112
+ def detect_mime_type(filename)
113
+ if filename
114
+ MIME_TYPE_STRATEGIES.each do |method|
115
+ result = send(method, filename)
116
+ return result if result
97
117
  end
98
118
  end
99
-
100
- def use_simple_detection(filename)
101
- # very rudimentary extension analysis for images
102
- # first, get the downcased extension, or an empty string if it doesn't exist
103
- extension = ((filename.match(/\.([a-zA-Z0-9]+)$/) || [])[1] || "").downcase
104
- if extension == ""
119
+ nil # if we can't find anything
120
+ end
121
+
122
+ def use_mime_module(filename)
123
+ # if the user has installed mime/types, we can use that
124
+ # if not, rescue and return nil
125
+ begin
126
+ type = MIME::Types.type_for(filename).first
127
+ type ? type.to_s : nil
128
+ rescue
129
+ nil
130
+ end
131
+ end
132
+
133
+ def use_simple_detection(filename)
134
+ # very rudimentary extension analysis for images
135
+ # first, get the downcased extension, or an empty string if it doesn't exist
136
+ extension = ((filename.match(/\.([a-zA-Z0-9]+)$/) || [])[1] || "").downcase
137
+ case extension
138
+ when ""
105
139
  nil
106
- elsif extension == "jpg" || extension == "jpeg"
140
+ # images
141
+ when "jpg", "jpeg"
107
142
  "image/jpeg"
108
- elsif extension == "png"
143
+ when "png"
109
144
  "image/png"
110
- elsif extension == "gif"
145
+ when "gif"
111
146
  "image/gif"
112
- end
147
+
148
+ # video
149
+ when "3g2"
150
+ "video/3gpp2"
151
+ when "3gp", "3gpp"
152
+ "video/3gpp"
153
+ when "asf"
154
+ "video/x-ms-asf"
155
+ when "avi"
156
+ "video/x-msvideo"
157
+ when "flv"
158
+ "video/x-flv"
159
+ when "m4v"
160
+ "video/x-m4v"
161
+ when "mkv"
162
+ "video/x-matroska"
163
+ when "mod"
164
+ "video/mod"
165
+ when "mov", "qt"
166
+ "video/quicktime"
167
+ when "mp4", "mpeg4"
168
+ "video/mp4"
169
+ when "mpe", "mpeg", "mpg", "tod", "vob"
170
+ "video/mpeg"
171
+ when "nsv"
172
+ "application/x-winamp"
173
+ when "ogm", "ogv"
174
+ "video/ogg"
175
+ when "wmv"
176
+ "video/x-ms-wmv"
113
177
  end
178
+ end
114
179
  end
115
- end
180
+ end
@@ -0,0 +1,11 @@
1
+ module Koala
2
+ module Utils
3
+ def self.deprecate(message)
4
+ begin
5
+ send(:warn, "KOALA: Deprecation warning: #{message}")
6
+ rescue Exception => err
7
+ puts "Unable to issue Koala deprecation warning! #{err.message}"
8
+ end
9
+ end
10
+ end
11
+ end