purzelrakete-boomloop 0.0.5 → 0.0.9

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