koala 1.0.0 → 1.2.0

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 (54) hide show
  1. data/.autotest +12 -0
  2. data/.gitignore +3 -1
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +62 -2
  5. data/Gemfile +8 -0
  6. data/Rakefile +0 -1
  7. data/autotest/discover.rb +1 -0
  8. data/koala.gemspec +13 -14
  9. data/lib/koala/batch_operation.rb +74 -0
  10. data/lib/koala/graph_api.rb +145 -132
  11. data/lib/koala/graph_batch_api.rb +97 -0
  12. data/lib/koala/graph_collection.rb +59 -0
  13. data/lib/koala/http_service.rb +176 -0
  14. data/lib/koala/oauth.rb +191 -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 -17
  18. data/lib/koala/uploadable_io.rb +153 -87
  19. data/lib/koala/utils.rb +11 -0
  20. data/lib/koala/version.rb +3 -0
  21. data/lib/koala.rb +59 -217
  22. data/readme.md +92 -53
  23. data/spec/cases/{api_base_spec.rb → api_spec.rb} +31 -6
  24. data/spec/cases/error_spec.rb +32 -0
  25. data/spec/cases/graph_and_rest_api_spec.rb +12 -21
  26. data/spec/cases/graph_api_batch_spec.rb +582 -0
  27. data/spec/cases/graph_api_spec.rb +11 -14
  28. data/spec/cases/graph_collection_spec.rb +116 -0
  29. data/spec/cases/http_service_spec.rb +446 -0
  30. data/spec/cases/koala_spec.rb +54 -0
  31. data/spec/cases/oauth_spec.rb +319 -213
  32. data/spec/cases/realtime_updates_spec.rb +45 -31
  33. data/spec/cases/rest_api_spec.rb +23 -7
  34. data/spec/cases/test_users_spec.rb +123 -75
  35. data/spec/cases/uploadable_io_spec.rb +120 -37
  36. data/spec/cases/utils_spec.rb +10 -0
  37. data/spec/fixtures/cat.m4v +0 -0
  38. data/spec/fixtures/facebook_data.yml +26 -24
  39. data/spec/fixtures/mock_facebook_responses.yml +203 -78
  40. data/spec/spec_helper.rb +30 -5
  41. data/spec/support/graph_api_shared_examples.rb +149 -118
  42. data/spec/support/json_testing_fix.rb +42 -0
  43. data/spec/support/koala_test.rb +187 -0
  44. data/spec/support/mock_http_service.rb +62 -58
  45. data/spec/support/ordered_hash.rb +205 -0
  46. data/spec/support/rest_api_shared_examples.rb +139 -15
  47. data/spec/support/uploadable_io_shared_examples.rb +2 -8
  48. metadata +90 -114
  49. data/lib/koala/http_services.rb +0 -146
  50. data/spec/cases/http_services/http_service_spec.rb +0 -54
  51. data/spec/cases/http_services/net_http_service_spec.rb +0 -350
  52. data/spec/cases/http_services/typhoeus_service_spec.rb +0 -144
  53. data/spec/support/live_testing_data_helper.rb +0 -40
  54. 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)
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,16 +63,15 @@ 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
 
62
74
  def create_network(network_size, installed = true, permissions = '')
63
- network_size = 50 if network_size > 50 # FB's max is 50
64
75
  users = (0...network_size).collect { create(installed, permissions) }
65
76
  friends = users.clone
66
77
  users.each do |user|
@@ -73,9 +84,14 @@ module Koala
73
84
  end
74
85
  return users
75
86
  end
76
-
87
+
88
+ def graph_api
89
+ Koala::Utils.deprecate("the TestUsers.graph_api accessor is deprecated and will be removed in a future version; please use .api instead.")
90
+ @api
91
+ end
92
+
77
93
  protected
78
-
94
+
79
95
  def accounts_path
80
96
  @accounts_path ||= "/#{@app_id}/accounts/test-users"
81
97
  end
@@ -1,115 +1,181 @@
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
+ :parse_io
45
+ ]
46
+
47
+ def parse_init_mixed_param(mixed, content_type = nil)
48
+ PARSE_STRATEGIES.each do |method|
49
+ send(method, mixed, content_type)
50
+ return if @io_or_path && @content_type
41
51
  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
52
+ end
53
+
54
+ # Expects a parameter of type ActionDispatch::Http::UploadedFile
55
+ def self.rails_3_param?(uploaded_file)
56
+ uploaded_file.respond_to?(:content_type) and uploaded_file.respond_to?(:tempfile) and uploaded_file.tempfile.respond_to?(:path)
57
+ end
58
+
59
+ def parse_rails_3_param(uploaded_file, content_type = nil)
60
+ if UploadableIO.rails_3_param?(uploaded_file)
61
+ @io_or_path = uploaded_file.tempfile.path
62
+ @content_type = content_type || uploaded_file.content_type
63
+ @filename = uploaded_file.original_filename
49
64
  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
65
+ end
66
+
67
+ # Expects a Sinatra hash of file info
68
+ def self.sinatra_param?(file_hash)
69
+ file_hash.kind_of?(Hash) and file_hash.has_key?(:type) and file_hash.has_key?(:tempfile)
70
+ end
71
+
72
+ def parse_sinatra_param(file_hash, content_type = nil)
73
+ if UploadableIO.sinatra_param?(file_hash)
74
+ @io_or_path = file_hash[:tempfile]
75
+ @content_type = content_type || file_hash[:type] || detect_mime_type(tempfile)
76
+ @filename = file_hash[:filename]
57
77
  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
78
+ end
79
+
80
+ # takes a file object
81
+ def self.file_param?(file)
82
+ file.kind_of?(File)
83
+ end
84
+
85
+ def parse_file_object(file, content_type = nil)
86
+ if UploadableIO.file_param?(file)
87
+ @io_or_path = file
88
+ @content_type = content_type || detect_mime_type(file.path)
89
+ @filename = File.basename(file.path)
65
90
  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
91
+ end
92
+
93
+ def parse_string_path(path, content_type = nil)
94
+ if path.kind_of?(String)
95
+ @io_or_path = path
96
+ @content_type = content_type || detect_mime_type(path)
97
+ @filename = File.basename(path)
72
98
  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
99
+ end
100
+
101
+ def parse_io(io, content_type = nil)
102
+ if io.respond_to?(:read)
103
+ @io_or_path = io
104
+ @content_type = content_type
87
105
  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
106
+ end
107
+
108
+ MIME_TYPE_STRATEGIES = [
109
+ :use_mime_module,
110
+ :use_simple_detection
111
+ ]
112
+
113
+ def detect_mime_type(filename)
114
+ if filename
115
+ MIME_TYPE_STRATEGIES.each do |method|
116
+ result = send(method, filename)
117
+ return result if result
97
118
  end
98
119
  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 == ""
120
+ nil # if we can't find anything
121
+ end
122
+
123
+ def use_mime_module(filename)
124
+ # if the user has installed mime/types, we can use that
125
+ # if not, rescue and return nil
126
+ begin
127
+ type = MIME::Types.type_for(filename).first
128
+ type ? type.to_s : nil
129
+ rescue
130
+ nil
131
+ end
132
+ end
133
+
134
+ def use_simple_detection(filename)
135
+ # very rudimentary extension analysis for images
136
+ # first, get the downcased extension, or an empty string if it doesn't exist
137
+ extension = ((filename.match(/\.([a-zA-Z0-9]+)$/) || [])[1] || "").downcase
138
+ case extension
139
+ when ""
105
140
  nil
106
- elsif extension == "jpg" || extension == "jpeg"
141
+ # images
142
+ when "jpg", "jpeg"
107
143
  "image/jpeg"
108
- elsif extension == "png"
144
+ when "png"
109
145
  "image/png"
110
- elsif extension == "gif"
146
+ when "gif"
111
147
  "image/gif"
112
- end
148
+
149
+ # video
150
+ when "3g2"
151
+ "video/3gpp2"
152
+ when "3gp", "3gpp"
153
+ "video/3gpp"
154
+ when "asf"
155
+ "video/x-ms-asf"
156
+ when "avi"
157
+ "video/x-msvideo"
158
+ when "flv"
159
+ "video/x-flv"
160
+ when "m4v"
161
+ "video/x-m4v"
162
+ when "mkv"
163
+ "video/x-matroska"
164
+ when "mod"
165
+ "video/mod"
166
+ when "mov", "qt"
167
+ "video/quicktime"
168
+ when "mp4", "mpeg4"
169
+ "video/mp4"
170
+ when "mpe", "mpeg", "mpg", "tod", "vob"
171
+ "video/mpeg"
172
+ when "nsv"
173
+ "application/x-winamp"
174
+ when "ogm", "ogv"
175
+ "video/ogg"
176
+ when "wmv"
177
+ "video/x-ms-wmv"
113
178
  end
179
+ end
114
180
  end
115
- end
181
+ 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
@@ -0,0 +1,3 @@
1
+ module Koala
2
+ VERSION = "1.2.0"
3
+ end