purzelrakete-boomloop 0.0.5 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,2 +1,5 @@
1
+ 0.0.6, 05.06.2008
2
+ * Everything working much better
3
+
1
4
  0.0.1, 06.05.2008.
2
- * First checkin
5
+ * First checkin
data/bin/boomloop CHANGED
@@ -33,8 +33,8 @@ error(parser) unless options.secret && options.key
33
33
 
34
34
  # negotiate the tokens
35
35
  client = Boomloop::Authentication::Client.new("api", Boomloop::Authentication::Store::YAMLStore.new)
36
- request_token = client.consume(options.key, options.secret) rescue error("Invalid. Check your credentials at #{ Boomloop::Resources::Base.api_base }/oauth/applications.")
37
- puts "\nalmost ready! go to #{ request_token.authorize_url.gsub(/\/\/oauth/, '/oauth') }\n\n press enter when you're done... <waiting>\n"
36
+ request_token = client.consume(options.key, options.secret) rescue error("Invalid. Check your credentials at #{ Boomloop::Resources::Base.boomloop_base }/oauth/applications.")
37
+ puts "\nalmost ready! go to #{ request_token.authorize_url }\n\n press enter when you're done... <waiting>\n"
38
38
 
39
39
  while !@access_token && gets
40
40
  @access_token = client.activate rescue nil
data/lib/boomloop.rb CHANGED
@@ -9,8 +9,17 @@ module Boomloop
9
9
  class ApiError < ::StandardError; end
10
10
  class AuthorizationError < ApiError; end
11
11
  class ValidationError < ApiError; end
12
+ class RedirectError < ApiError; end
12
13
  end
13
14
 
14
- Dir.glob(File.dirname(__FILE__) + "/**/*.rb").each do |file|
15
- require file
16
- end
15
+ $:.unshift File.dirname(__FILE__)
16
+
17
+ require 'boomloop/authentication/client'
18
+ require 'boomloop/authentication/credentials'
19
+ require 'boomloop/authentication/store/base'
20
+ require 'boomloop/authentication/store/yaml_store'
21
+ require 'boomloop/resources/base'
22
+ require 'boomloop/resources/event'
23
+ require 'boomloop/resources/event_series'
24
+ require 'boomloop/resources/place'
25
+ require 'boomloop/resources/ticket_category'
@@ -63,8 +63,8 @@ module Boomloop
63
63
  connection.get(url, options.merge({ "X-Consumer-Key" => self.consumer.key }))
64
64
  end
65
65
 
66
- def post(url, params = "")
67
- connection.post(url, params, { "X-Consumer-Key" => self.consumer.key })
66
+ def post(url, params = "", headers = {})
67
+ connection.post(url, params, { "X-Consumer-Key" => self.consumer.key, 'Accept'=>'application/xml', 'Content-Type' => 'application/xml' }.merge(headers))
68
68
  end
69
69
 
70
70
  def raise_error_if_not_authenticated(credentials)
@@ -5,19 +5,6 @@ module Boomloop
5
5
  attr_accessor :client
6
6
  end
7
7
 
8
- def self.api_base
9
- @@api_base ||= (ENV["BOOMLOOP_API_URL"] ? ENV["BOOMLOOP_API_URL"].gsub(/\/$/, "") : 'http://api.boomloop.com' )
10
- end
11
-
12
- def self.client
13
- @@client ||= init
14
- end
15
-
16
- # get oauth token
17
- def self.init
18
- self.client = Boomloop::Authentication::Client.new("api", Boomloop::Authentication::Store::YAMLStore.new)
19
- end
20
-
21
8
  def save
22
9
  returned = self.class.create(self)
23
10
  self.instance_variable_set("@table", returned.to_hash)
@@ -28,12 +15,33 @@ module Boomloop
28
15
  links = to_hash.keys.select { |key| key =~ /resource-url$/ }
29
16
  end
30
17
 
18
+ def valid?
19
+ self.errors.blank?
20
+ end
21
+
31
22
  def to_hash
32
23
  instance_variable_get("@table")
33
24
  end
34
25
 
35
- def to_xml
36
- self.to_hash.to_xml
26
+ def to_xml(options = {})
27
+ self.to_hash.to_xml(options)
28
+ end
29
+
30
+ def self.api_base
31
+ @@api_base ||= (ENV["BOOMLOOP_API_URL"] ? ENV["BOOMLOOP_API_URL"].gsub(/\/$/, "") : 'http://api.boomloop.com' )
32
+ end
33
+
34
+ def self.boomloop_base
35
+ @@boomloop_base ||= self.api_base.gsub /(^http:\/\/)(api.)(.*)/, '\1' + '\3'
36
+ end
37
+
38
+ def self.client
39
+ @@client ||= init
40
+ end
41
+
42
+ # get oauth token
43
+ def self.init
44
+ self.client = Boomloop::Authentication::Client.new("api", Boomloop::Authentication::Store::YAMLStore.new)
37
45
  end
38
46
 
39
47
  def self.singular
@@ -45,6 +53,12 @@ module Boomloop
45
53
  end
46
54
 
47
55
  # REST methods
56
+ def self.show(url)
57
+ res = client.get(url)
58
+ attrs = from_response(res)
59
+ self.new(attrs[self.singular])
60
+ end
61
+
48
62
  def self.index(params = {})
49
63
  url = "#{ api_base }/#{ self.plural }"
50
64
  url += "?#{ params.to_query }" unless params.empty?
@@ -53,24 +67,33 @@ module Boomloop
53
67
  construct_resource_collection(attrs)
54
68
  end
55
69
 
56
- def self.show(url)
57
- res = client.get(url)
70
+ def self.create(resource)
71
+ resource = resource.kind_of?(self) ? resource : self.new(resource)
72
+
73
+ url = "#{ api_base }/#{ self.plural }"
74
+ res = client.post(url, resource.to_xml(:root => singular.to_sym))
58
75
  attrs = from_response(res)
59
76
  self.new(attrs[self.singular])
60
77
  end
61
78
 
62
- def self.create(resource)
63
- url = "#{ api_base }/#{ self.plural }"
64
- res = client.post(url, resource.to_hash.to_query)
65
- self.new(from_response(res)[self.singular])
79
+ def self.follow_redirect(response)
80
+ res = client.get(response.location)
81
+ from_response(res, response)
66
82
  end
67
83
 
68
- def self.from_response(response)
84
+ def self.from_response(response, old_response=nil)
69
85
  xml = response.body
70
86
 
71
87
  case response.code.to_i
88
+ when 422
89
+ @result = { self.singular => Hash.from_xml(xml) }
72
90
  when 401
73
91
  raise Boomloop::AuthorizationError.new
92
+ when 301
93
+ raise Boomloop::RedirectError.new("Redirect URL seem to be the same as the original. I do not follow!") if
94
+ old_response and old_response.location and response.location and response.location == old_response.location
95
+
96
+ follow_redirect(response)
74
97
  when 200...299
75
98
  begin
76
99
  @result = Hash.from_xml(xml)
@@ -81,8 +104,8 @@ module Boomloop
81
104
  raise Boomloop::ValidationError.new("There was a problem with your request: \n\n #{ response }") if @result["errors"]
82
105
  else
83
106
  raise Boomloop::ApiError.new("boomloop responded with #{ response.class } (#{ response.code }): #{ response.body }")
84
- end
85
-
107
+ end
108
+
86
109
  @result
87
110
  end
88
111
 
@@ -94,14 +117,6 @@ module Boomloop
94
117
  when Hash : self.new(collection[self.singular])
95
118
  end
96
119
  end
97
-
98
- def self.transitive_create(resource)
99
- if resource.child_resources
100
- child_resources.each { |child| transitive_create(child) }
101
- else
102
- create(resource)
103
- end
104
- end
105
120
  end
106
121
  end
107
122
  end
File without changes
File without changes
File without changes
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/test_helper.rb'
2
2
 
3
- context "a boomloop resource" do
3
+ context "A boomloop resource" do
4
4
 
5
5
  setup do
6
6
  Boomloop::Authentication::Client.class_eval { def raise_error_if_not_authenticated(*args); end }
@@ -101,19 +101,32 @@ context "a boomloop resource" do
101
101
 
102
102
  specify "should be savable when calling .save on a Monster instance" do
103
103
  intended_resource_url = "#{ Boomloop::Resources::Base.api_base }/monsters/samuel"
104
- intended_attributes = { :size => :laughable, :affect => :mordant, :name => "samuel" }
104
+ intended_attributes = { :affect => :mordant, :name => "samuel", :size => :laughable }
105
+ intended_ingoing_xml = <<-XML
106
+ <?xml version="1.0" encoding="UTF-8"?>
107
+ <monster>
108
+ <name>samuel</name>
109
+ <size type="symbol">laughable</size>
110
+ <affect type="symbol">mordant</affect>
111
+ </monster>
112
+ XML
113
+
114
+ intended_return_xml = <<-XML
115
+ <?xml version="1.0" encoding="UTF-8"?>
116
+ <monster>
117
+ <name>samuel</name>
118
+ <affect type="symbol">mordant</affect>
119
+ <size type="symbol">laughable</size>
120
+ <resource-url>#{ Boomloop::Resources::Base.api_base }/monsters/samuel</resource-url>
121
+ </monster>
122
+ XML
123
+
105
124
  response = mock()
106
125
  response.expects(:code).returns 201
107
- response.expects(:body).returns(<<-XML)
108
- <monster>
109
- <resource_url>#{ intended_resource_url }</resource_url>
110
- <size>laughable</size>
111
- <affect>mordant</affect>
112
- <name>samuel</name>
113
- </monster>
114
- XML
126
+ response.expects(:body).returns(intended_return_xml)
127
+
128
+ @client.expects(:post).with("#{ Boomloop::Resources::Base.api_base }/monsters", intended_ingoing_xml).at_least_once.returns(response)
115
129
 
116
- @client.expects(:post).with("#{ Boomloop::Resources::Base.api_base }/monsters", intended_attributes.to_query).at_least_once.returns(response)
117
130
  monster = Boomloop::Resources::Monster.new(intended_attributes)
118
131
  monster.save
119
132
  monster.resource_url.should.equal intended_resource_url
@@ -146,4 +159,104 @@ context "a boomloop resource" do
146
159
  Boomloop::Resources::Monster.show("#{ Boomloop::Resources::Base.api_base }/monsters/fred")
147
160
  end
148
161
  end
162
+
163
+ specify "should follow and return the new resource XML if redirected via 301 on create and resource is a duplicate" do
164
+ intended_resource_url = "#{ Boomloop::Resources::Base.api_base }/monsters/samuel"
165
+ intended_attributes = { :size => :huge, :affect => :sanguine, :name => "samuel" }
166
+
167
+ response = mock()
168
+ response.expects(:code).returns 301
169
+ response.expects(:location).returns intended_resource_url
170
+ response.expects(:body).returns(<<-XML)
171
+ <duplicates>
172
+ <resource_url>#{ intended_resource_url }</resource_url>
173
+ </duplicates>
174
+ XML
175
+
176
+ intended_response = mock()
177
+ intended_response.expects(:code).returns 200
178
+ intended_response.expects(:body).returns(<<-XML)
179
+ <monster>
180
+ <resource_url>#{ intended_resource_url }</resource_url>
181
+ <size>huge</size>
182
+ <affect>sanguine</affect>
183
+ <name>samuel</name>
184
+ </monster>
185
+ XML
186
+
187
+ @client.expects(:post).with("#{ Boomloop::Resources::Base.api_base }/monsters", intended_attributes.to_xml(:root => :monster)).at_least_once.returns(response)
188
+ @client.expects(:get).with(intended_resource_url).at_least_once.returns(intended_response)
189
+
190
+ monster = Boomloop::Resources::Monster.new(intended_attributes)
191
+ monster.save
192
+ monster.resource_url.should.equal intended_resource_url
193
+ end
194
+
195
+ specify "should follow and return the new resource XML if redirected via 301 on show" do
196
+ called_resource_url = "#{ Boomloop::Resources::Base.api_base }/monsters/fred"
197
+ intended_resource_url = "#{ Boomloop::Resources::Base.api_base }/monsters/samuel"
198
+
199
+ response = mock()
200
+ response.expects(:code).returns 301
201
+ response.expects(:location).returns intended_resource_url
202
+ response.expects(:body).returns(<<-XML)
203
+ <duplicates>
204
+ <resource_url>#{ intended_resource_url }</resource_url>
205
+ </duplicates>
206
+ XML
207
+
208
+ second_response = mock()
209
+ second_response.expects(:code).returns 200
210
+ second_response.expects(:body).returns(<<-XML)
211
+ <monster>
212
+ <resource_url>#{ intended_resource_url }</resource_url>
213
+ <size>huge</size>
214
+ <affect>sanguine</affect>
215
+ <name>samuel</name>
216
+ </monster>
217
+ XML
218
+
219
+ @client.expects(:get).with(any_of(equals(called_resource_url), equals(intended_resource_url))).at_least_once.returns(response, second_response)
220
+ monster = Boomloop::Resources::Monster.show(called_resource_url)
221
+ end
222
+
223
+ specify "should raise an exception if response redirects to own resource_url" do
224
+ intended_resource_url = "#{ Boomloop::Resources::Base.api_base }/monsters/samuel"
225
+
226
+ response = mock()
227
+ response.expects(:code).at_least_once.returns 301
228
+ response.expects(:location).at_least_once.returns intended_resource_url
229
+ response.expects(:body).at_least_once.returns(<<-XML)
230
+ <duplicates>
231
+ <resource_url>#{ intended_resource_url }</resource_url>
232
+ </duplicates>
233
+ XML
234
+
235
+ @client.expects(:get).with(intended_resource_url).times(2).returns(response)
236
+ should.raise Boomloop::RedirectError do
237
+ Boomloop::Resources::Monster.show(intended_resource_url)
238
+ end
239
+ end
240
+
241
+ specify "should have errors if boomloop API returns errors" do
242
+ monster_attributes = { :title => "Foo" }
243
+
244
+ response = mock
245
+ response.expects(:code).returns 422
246
+ response.expects(:body).returns(<<-XML)
247
+ <errors>
248
+ <error>Location boomloop braucht eine Location.</error>
249
+ <error>Start date trying to parse nil date, bad stuff.</error>
250
+ <error>Location boomloop braucht eine Location.</error>
251
+ <error>Start hour Zeit bitte in HH:MM Format.</error>
252
+ <error>Beschreibung Hast Du keine Informationen zu diesem Event?</error>
253
+ <error>trying to parse nil date, bad stuff.</error>
254
+ </errors>
255
+ XML
256
+
257
+ @client.expects(:post).with("#{ Boomloop::Resources::Base.api_base }/monsters", monster_attributes.to_xml(:root => :monster)).returns(response)
258
+
259
+ monster = Boomloop::Resources::Monster.create(monster_attributes)
260
+ monster.should.not.be.valid
261
+ end
149
262
  end
@@ -1,3 +1,5 @@
1
+ require 'boomloop/resources/base'
2
+
1
3
  module Boomloop
2
4
  module Resources
3
5
  class Monster < Base
data/test/test_helper.rb CHANGED
@@ -5,4 +5,5 @@ require 'mocha'
5
5
  current_dir = File.dirname(__FILE__)
6
6
 
7
7
  require current_dir + '/../lib/boomloop'
8
- require current_dir + '/mocks/monster'
8
+ require current_dir + '/mocks/monster'
9
+ require current_dir + '/mocks/planet'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: purzelrakete-boomloop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rany Keddo
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-27 00:00:00 -07:00
12
+ date: 2008-06-18 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -31,17 +31,17 @@ extra_rdoc_files:
31
31
  - History.txt
32
32
  - README.txt
33
33
  files:
34
- - lib/authentication/client.rb
35
- - lib/authentication/credentials.rb
36
- - lib/authentication/store/base.rb
37
- - lib/authentication/store/yaml_store.rb
34
+ - lib/boomloop/authentication/client.rb
35
+ - lib/boomloop/authentication/credentials.rb
36
+ - lib/boomloop/authentication/store/base.rb
37
+ - lib/boomloop/authentication/store/yaml_store.rb
38
38
  - lib/boomloop.rb
39
- - lib/resources/base.rb
40
- - lib/resources/event.rb
41
- - lib/resources/event_series.rb
42
- - lib/resources/place.rb
43
- - lib/resources/ticket_category.rb
44
- - lib/version.rb
39
+ - lib/boomloop/resources/base.rb
40
+ - lib/boomloop/resources/event.rb
41
+ - lib/boomloop/resources/event_series.rb
42
+ - lib/boomloop/resources/place.rb
43
+ - lib/boomloop/resources/ticket_category.rb
44
+ - lib/boomloop/version.rb
45
45
  - bin/boomloop
46
46
  - History.txt
47
47
  - README.txt