shared_book 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,3 @@
1
+ === 0.0.1 2010-11-08
2
+
3
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ lib/shared_book.rb
6
+ spec/lib/shared_book_spec.rb
7
+ spec/spec.opts
8
+ spec/spec_helper.rb
9
+ tasks/rspec.rake
data/README.rdoc ADDED
@@ -0,0 +1,100 @@
1
+ = shared_book
2
+
3
+ * http://github.com/xunker/shared_book
4
+
5
+ == DESCRIPTION:
6
+
7
+ A Ruby Gem to connect to the SharedBook.com publishing API.
8
+
9
+ This version provides 1:1 method call structure to the SharedBook rest-like API.
10
+
11
+ == SYNOPSIS:
12
+
13
+ s = SharedBook.new(
14
+ :product_api_key => "xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-x",
15
+ :product_secret_word => "xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-x",
16
+ :auth_token => "xxxxx_xxx",
17
+ :get_session_token => true
18
+ )
19
+
20
+ s.bmscreate_init('test book',
21
+ [{:chapterTitle => 'chapter 1', :chapterText => 'text'}, {:chapterTitle => 'chapter 2', :chapterText => 'text'}]
22
+ )
23
+
24
+ s.bmscreate_publish
25
+
26
+ s.bms_addComment(:comment_title => 'comment', :comment_text => 'text', :owner_name => 'George')
27
+
28
+ s.bms_addPhoto_by_url(:file_url => 'http://www.example.com/images/logo.png', :owner_name => 'Geroge')
29
+ s.bms_addPhoto_by_handle(:file_name => "../images/angry_squirrel.jpg", :file_mime => "image/jpeg", :owner_name => 'Gerogee')
30
+ s.bms_setFrontCoverPhoto(:file_name => "../images/angry_squirrel.jpg", :file_mime => "image/jpeg", :owner_name => 'Gerogee')
31
+ s.bms_setBackCoverPhoto(:file_name => "../images/angry_squirrel.jpg", :file_mime => "image/jpeg", :owner_name => 'Gerogee')
32
+
33
+ s.bms_publish
34
+ s.bookcreate_init
35
+ s.bookcreate_setDedication(:dedication_text => 'Dedicated!')
36
+ s.bookcreate_publish
37
+ s.book_preview
38
+
39
+ == USAGE:
40
+
41
+ Before calling SharedBook.new you will need to send you web client to the URL provided by SharedBook.auth_login_url. This URL will return the user to your SharedBook.com return URL and it will have a unique auth token appended to the query string. You must retrieve that token and pass it in to SharedBook.new as :auth_token.
42
+
43
+ For testing you can call SharedBook.new with an :auth_token value of 'auto' which will automatically get this token for you but the preview URL given my book_preview will be inaccessible. :auth_token => 'auto' should never, ever, ever be used for production purposes.
44
+
45
+ During a real usage scenario, the order of operations should follow roughtly:
46
+
47
+ s = SharedBook.new
48
+ s.bmscreate_init
49
+ s.bmscreate_publish
50
+ s.bms_addComment
51
+ s.bms_addPhoto_by_url
52
+ s.bms_addPhoto_by_handle
53
+ s.bms_setFrontCoverPhoto
54
+ s.bms_setBackCoverPhoto
55
+ s.bms_publish
56
+ s.bookcreate_init
57
+ s.bookcreate_setDedication
58
+ s.bookcreate_publish
59
+ s.book_preview
60
+
61
+ == REQUIREMENTS:
62
+
63
+ This library is useless unless you have an account at SharedBook.com.
64
+
65
+ You will need to know your the API key and secret word for your product
66
+ and pass them in to the constructor.
67
+
68
+ == INSTALL:
69
+
70
+ gem install shared_book
71
+
72
+ == TODO:
73
+
74
+ * Add the ability to post a picture using data from a string, not just a URL or filename.
75
+ * Add a more ruby-like method for creating a new SharedBook object.
76
+
77
+ == LICENSE:
78
+
79
+ (The MIT License)
80
+
81
+ Copyright (c) 2010 Matthew Nielsen
82
+
83
+ Permission is hereby granted, free of charge, to any person obtaining
84
+ a copy of this software and associated documentation files (the
85
+ 'Software'), to deal in the Software without restriction, including
86
+ without limitation the rights to use, copy, modify, merge, publish,
87
+ distribute, sublicense, and/or sell copies of the Software, and to
88
+ permit persons to whom the Software is furnished to do so, subject to
89
+ the following conditions:
90
+
91
+ The above copyright notice and this permission notice shall be
92
+ included in all copies or substantial portions of the Software.
93
+
94
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
95
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
96
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
97
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
98
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
99
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
100
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ gem 'multipart-post', '>= 1.0.1'
4
+ require 'hoe'
5
+ require 'fileutils'
6
+ require './lib/shared_book'
7
+
8
+ VERSION = "0.1.0"
9
+
10
+ Hoe.plugin :newgem
11
+
12
+ $hoe = Hoe.spec 'shared_book' do
13
+ self.developer 'Matthew Nielsen', 'xunker@pyxidis.org'
14
+ end
15
+
16
+ require 'newgem/tasks'
17
+ Dir['tasks/**/*.rake'].each { |t| load t }
18
+
19
+ task :default => :spec
@@ -0,0 +1,280 @@
1
+ # order of calls
2
+ #
3
+ # s = SharedBook.new { ... }
4
+ # s.get_session_token .. or .. pass :get_session_token => true to .new
5
+ # s.bmscreate_init('test book', {:chapterTitle => 'chapter 1', :chapterText => 'text'})
6
+ # ... or ...
7
+ # s.bmscreate_init('test book', [{:chapterTitle => 'chapter 1', :chapterText => 'text'}, {:chapterTitle => 'chapter 2', :chapterText => 'text'}])
8
+ # s.bmscreate_publish
9
+ # s.bms_addComment { ... }
10
+ # s.bms_addPhoto_by_url { ... }
11
+ # s.bms_addPhoto_by_handle { ... }
12
+ # s.bms_setFrontCoverPhoto { ... }
13
+ # s.bms_setBackCoverPhoto { ... }
14
+ # s.bms_publish
15
+ # s.bookcreate_init
16
+ # s.bookcreate_setDedication { ... }
17
+ # s.bookcreate_publish
18
+ # s.book_preview
19
+
20
+ class SharedBookError < StandardError; end
21
+ class MissingCredentialError < SharedBookError; end
22
+ class MissingAuthTokenError < MissingCredentialError; def to_s; "Please supply auth_token given from '/auth/login'"; end; end
23
+ class MissingSessionTokenError < MissingCredentialError; end
24
+ class MissingProductApiKeyError < MissingCredentialError; def to_s; "Please supply product_api_key"; end; end
25
+ class MissingProductSecretWordError < MissingCredentialError; def to_s; "Please supply product_secret_word"; end; end
26
+ class ResponseError < SharedBookError; end
27
+
28
+ class SharedBook
29
+ require 'rubygems'
30
+ require 'kconv'
31
+ require 'net/http'
32
+ require 'net/http/post/multipart'
33
+ require 'digest/md5'
34
+ require 'uri'
35
+
36
+ URL = "http://api.sharedbook.com/v0.6"
37
+ DEFAULT_THEME = 'vanilla'
38
+
39
+ attr_reader :product_api_key, :session_token, :auth_token, :product_secret_word, :bms_id, :book_id, :comment_ids, :photo_ids, :front_cover_photo_id, :back_cover_photo_id, :book_url
40
+ attr_accessor :book_title, :articles
41
+
42
+ def initialize(opts = {})
43
+ if opts[:product_api_key]
44
+ @product_api_key = opts[:product_api_key]
45
+ else
46
+ raise MissingProductApiKeyError
47
+ end
48
+
49
+ if opts[:product_secret_word]
50
+ @product_secret_word = opts[:product_secret_word]
51
+ else
52
+ raise MissingProductSecretWordError
53
+ end
54
+
55
+ if opts[:auth_token]
56
+ @auth_token = if opts[:auth_token] == 'auto'
57
+ get_new_auth_token
58
+ else
59
+ opts[:auth_token]
60
+ end
61
+ else
62
+ raise MissingAuthTokenError
63
+ end
64
+
65
+ if opts[:session_token]
66
+ @session_token = opts[:session_token]
67
+ else
68
+ @session_token = auth_getSessionToken if opts[:get_session_token]
69
+ end
70
+
71
+ end
72
+
73
+ def self.auth_login_url
74
+ "#{URL}/auth/login"
75
+ end
76
+
77
+ def auth_getSessionToken
78
+ return session_token if session_token # you can't call the service twice with the same auth_token
79
+ response = get_url("#{URL}/auth/getSessionToken", :apiKey => @product_api_key, :authToken => @auth_token)
80
+ parse_response(response, /\<sessionToken\>(.*)\<\/sessionToken\>/)
81
+ end
82
+
83
+ def session_token
84
+ @session_token
85
+ end
86
+
87
+ def bmscreate_init(book_title=@book_title, articles=@articles, theme=DEFAULT_THEME)
88
+ article_hash = if articles.class == Array
89
+ hh = {}
90
+ articles.each_with_index do |article, i|
91
+ hh["chapterTitle#{i+1}".to_sym] = article[:chapterTitle]
92
+ hh["chapterText#{i+1}".to_sym] = article[:chapterText]
93
+ end
94
+ hh
95
+ else
96
+ articles # expected to be hash with :chapterTitle and :chapterText
97
+ end
98
+ response = post_url("#{URL}/bmscreate/init", {:bookTitle => book_title}.merge(article_hash))
99
+ @bms_id = parse_response(response, /\<bms id=\"(\d+)\"/)
100
+ end
101
+
102
+ def bmscreate_publish(bms_id = @bms_id)
103
+ response = post_url("#{URL}/bmscreate/publish", {:bmsId => bms_id})
104
+ parse_response(response, /status=\"ok\"/) { true }
105
+ end
106
+
107
+ def bms_addComment(opts = {})
108
+ required = {:bmsId => opts[:bms_id] || @bms_id, :commentTitle => opts[:comment_title], :commentText => opts[:comment_text],
109
+ :chapterNumber => opts[:chapter_number].to_s, :ownerName => opts[:owner_name]}
110
+ optional = {:time => opts[:time], :commentId => opts[:comment_id]}.reject{|k,v| v.nil?}
111
+
112
+ response = post_url("#{URL}/bms/addComment", required.merge(optional))
113
+
114
+ parse_response(response, /comment id=\"(.*)\"/) do |match|
115
+ @comment_ids ||= []
116
+ @comment_ids << match[1]
117
+ @comment_ids.last
118
+ end
119
+ end
120
+
121
+ def bms_addPhoto_by_url(opts = {})
122
+ required = {:bmsId => opts[:bms_id] || @bms_id, :url => opts[:file_url], :ownerName => opts[:owner_name]}
123
+ optional = {:time => opts[:time], :caption => opts[:caption], :photoId => opts[:photo_id], :photoOrdinal => opts[:photo_ordinal]}.reject{|k,v| v.nil?}
124
+
125
+ response = post_url("#{URL}/bms/addPhoto", required.merge(optional))
126
+ parse_response(response, /\<photo id=\"(.*)\"/) do |match|
127
+ @photo_ids ||= []
128
+ @photo_ids << match[1]
129
+ @photo_ids.last
130
+ end
131
+ end
132
+
133
+ def bms_addPhoto_by_handle(opts = {})
134
+ parse_response(post_photo_data("#{URL}/bms/addPhoto", opts), /\<photo id=\"(.*)\"/) do |match|
135
+ @photo_ids ||= []
136
+ @photo_ids << match[1]
137
+ @photo_ids.last
138
+ end
139
+ end
140
+
141
+ def bms_setFrontCoverPhoto(opts = {})
142
+ @front_cover_photo_id = set_cover_photo(:front, opts)
143
+ end
144
+
145
+ def bms_setBackCoverPhoto(opts = {})
146
+ @back_cover_photo_id = set_cover_photo(:back, opts)
147
+ end
148
+
149
+ def bms_publish(bms_id = @bms_id)
150
+ response = post_url("#{URL}/bms/publish", {:bmsId => bms_id})
151
+ parse_response(response, /status=\"ok\"/) { true }
152
+ end
153
+
154
+ def bookcreate_init(bms_id = @bms_id)
155
+ response = post_url("#{URL}/bookcreate/init", {:bmsId => bms_id})
156
+ parse_response(response, /status=\"ok\"/) { true }
157
+ end
158
+
159
+ def bookcreate_setDedication(opts = {})
160
+ response = post_url("#{URL}/bookcreate/setDedication", {:bmsId => opts[:bms_id] || @bms_id, :dedicationText => opts[:dedication_text]})
161
+ parse_response(response, /status=\"ok\"/) { true }
162
+ end
163
+
164
+ def bookcreate_publish(bms_id = @bms_id)
165
+ response = post_url("#{URL}/bookcreate/publish", {:bmsId => bms_id})
166
+ @book_id = parse_response(response, /\<book id=\"(\d+)\" \/\>/)
167
+ end
168
+
169
+ def book_preview(bms_id = @bms_id, book_id = @book_id)
170
+ query = {
171
+ :bmsId => bms_id.to_s, :bookId => book_id.to_s, :redirect => 'false', :apiKey => @product_api_key,
172
+ :sessionToken => session_token, :authToken => @auth_token
173
+ }
174
+ query.merge!(generate_signature(query))
175
+ response = get_url("#{URL}/book/preview", query)
176
+ @book_url = parse_response(response, /\<url\>(.*)\<\/url\>/).gsub(/&amp\;/, "&")
177
+ end
178
+
179
+ private
180
+
181
+ def parse_response(response, pattern, &block)
182
+ if match = response.match(pattern)
183
+ if block_given?
184
+ yield match
185
+ else
186
+ match[1]
187
+ end
188
+ else
189
+ raise ResponseError, response
190
+ end
191
+ end
192
+
193
+ def get_url(path, query = {})
194
+ url = URI.parse("#{path}?#{query_params(query)}")
195
+
196
+ req = Net::HTTP::Get.new([url.path, url.query].join('?'))
197
+ res = Net::HTTP.start(url.host, url.port) {|http|
198
+ http.request(req)
199
+ }
200
+ res.body
201
+ end
202
+
203
+ def post_url(path, query = {})
204
+ query.merge!({:apiKey => @product_api_key, :sessionToken => session_token, :authToken => @auth_token})
205
+ query.merge!(generate_signature(query)) # must be added last
206
+
207
+ res = Net::HTTP.post_form(URI.parse(path), query)
208
+
209
+ case res
210
+ when Net::HTTPSuccess, Net::HTTPRedirection
211
+ res.body
212
+ else
213
+ raise ResponseError, res.error! #res.error!?
214
+ end
215
+
216
+ end
217
+
218
+ def post_url_file(path, query = {})
219
+ query.merge!({:apiKey => @product_api_key, :sessionToken => session_token, :authToken => @auth_token})
220
+ query.merge!(generate_signature(query)) # must be added last
221
+
222
+ url = URI.parse(path)
223
+ req = Net::HTTP::Post::Multipart.new url.path, query
224
+ res = Net::HTTP.start(url.host, url.port) do |http|
225
+ http.request(req)
226
+ end
227
+
228
+ case res
229
+ when Net::HTTPSuccess, Net::HTTPRedirection
230
+ res.body
231
+ else
232
+ raise ResponseError, res.error! #res.error!?
233
+ end
234
+
235
+ end
236
+
237
+ def query_params(query={})
238
+ query.map{|k,v| [k, v].join('=')}.join('&')
239
+ end
240
+
241
+ def generate_signature(query)
242
+ str=""
243
+ query.keys.sort{|a,b| a.to_s <=> b.to_s}.each do |key|
244
+ str << key.to_s+query[key] unless query[key].class == File
245
+ end
246
+
247
+ {:signature => Digest::MD5.hexdigest(@product_secret_word.toutf8+str.toutf8)}
248
+ end
249
+
250
+ def get_new_auth_token
251
+ if ENV['RAILS_ENV']
252
+ puts "*** YOU SHOULD ONLY USE THIS IN DEVELOPMENT MODE ***" unless ENV['RAILS_ENV'] == 'development'
253
+ end
254
+
255
+ res = Net::HTTP.post_form(URI.parse(SharedBook.auth_login_url), {:apiKey => @product_api_key})
256
+
257
+ case res
258
+ when Net::HTTPSuccess, Net::HTTPRedirection
259
+ res.body.match(/\?authToken=(.*)\"\>/)[1]
260
+ else
261
+ raise ResponseError, res.error! #res.error!?
262
+ end
263
+ end
264
+
265
+ def set_cover_photo(cover, opts={})
266
+ raise "only Front or Back cover" unless %w[front back].include?(cover.to_s)
267
+
268
+ parse_response(post_photo_data("#{URL}/bms/set#{cover.to_s.capitalize}CoverPhoto", opts), /\<photo id=\"(.*)\"/)
269
+ end
270
+
271
+ def post_photo_data(url, opts={})
272
+ required = {:bmsId => opts[:bms_id] || @bms_id, :file_name => opts[:file_name], :file_mime => opts[:file_mime], :ownerName => opts[:owner_name]}
273
+ optional = {:time => opts[:time], :caption => opts[:caption], :photoId => opts[:photo_id], :photoOrdinal => opts[:photo_ordinal]}.reject{|k,v| v.nil?}
274
+
275
+ File.open(opts[:file_name]) do |file_upload|
276
+ post_url_file(url, required.merge(optional).merge({"photo" => UploadIO.new(file_upload, opts[:file_mime], opts[:file_name])}))
277
+ end
278
+ end
279
+
280
+ end
@@ -0,0 +1,398 @@
1
+ require 'spec_helper'
2
+
3
+ def gem_root
4
+ File.dirname(__FILE__) + '/../..'
5
+ end
6
+
7
+ def post_should_get_response_error(method, arg=nil)
8
+ @sharedbook.should_receive(:post_url).and_return(@bad_return)
9
+ method_should_get_response_error(method, arg)
10
+ end
11
+
12
+ def get_should_get_response_error(method, arg=nil)
13
+ @sharedbook.should_receive(:get_url).and_return(@bad_return)
14
+ method_should_get_response_error(method, arg)
15
+ end
16
+
17
+ def method_should_get_response_error(method, arg=nil)
18
+ lambda { arg ? @sharedbook.send(method, arg) : @sharedbook.send(method) }.should raise_error(ResponseError)
19
+ end
20
+
21
+ describe SharedBook do
22
+ before(:each) do
23
+ @valid = {:product_api_key => 'x', :product_secret_word => 'x', :auth_token => 'x', :session_token => 'aabbcc'}
24
+ @sharedbook = SharedBook.new(@valid)
25
+ @bad_return = "<p>B0RKEN!</p>"
26
+ end
27
+
28
+ describe '.new' do
29
+ it 'should require product_api_key, product_secret_word and auth_token' do
30
+ lambda { SharedBook.new(:product_api_key => 'x', :product_secret_word => 'x') }.should raise_error(MissingAuthTokenError)
31
+
32
+ lambda { SharedBook.new(:product_api_key => 'x', :auth_token => 'x') }.should raise_error(MissingProductSecretWordError)
33
+
34
+ lambda { SharedBook.new(:auth_token => 'x', :product_secret_word => 'x') }.should raise_error(MissingProductApiKeyError)
35
+ end
36
+ end
37
+
38
+ describe '.auth_login_url' do
39
+ it 'should return the login url that the web user should be sent to' do
40
+ SharedBook.auth_login_url.should == "#{SharedBook::URL}/auth/login"
41
+ end
42
+ end
43
+
44
+ describe '#auth_getSessionToken' do
45
+ before(:each) do
46
+ @sharedbook = SharedBook.new(@valid.merge(:session_token => nil))
47
+ end
48
+
49
+ it 'should get the session token for a given auth_token' do
50
+ # expect
51
+ @sharedbook.should_receive(:get_url).with("#{SharedBook::URL}/auth/getSessionToken", {:apiKey=>"x", :authToken=>"x"}).and_return(
52
+ "<auth.getSessionToken status=\"ok\">\n\t<sessionToken>a1b2c3</sessionToken>\n</auth.getSessionToken>"
53
+ )
54
+
55
+ @sharedbook.auth_getSessionToken.should == "a1b2c3"
56
+ end
57
+
58
+ it "should return the already-known session ID if we already know it" do
59
+ # given
60
+ @sharedbook.stub(:session_token).and_return('xyz')
61
+
62
+ # expect
63
+ @sharedbook.should_not_receive(:get_url)
64
+
65
+ # when
66
+ @sharedbook.auth_getSessionToken.should == 'xyz'
67
+ end
68
+
69
+ it "should raise ResponseError if the response cannot be parsed" do
70
+ # expect
71
+ get_should_get_response_error(:auth_getSessionToken)
72
+ end
73
+ end
74
+
75
+ describe '#session_token' do
76
+ it "should return the current session token" do
77
+ # given
78
+ sharedbook = SharedBook.new(@valid.merge(:session_token => nil))
79
+ # expect
80
+ sharedbook.session_token.should be_nil
81
+
82
+ # given
83
+ sharedbook = SharedBook.new(@valid.merge(:session_token => 'abc'))
84
+ # expect
85
+ sharedbook.session_token.should == 'abc'
86
+ end
87
+ end
88
+
89
+ describe '#bmscreate_init' do
90
+ before(:all) do
91
+ @return = "<bmscreate.init status=\"ok\">\n\t<bms id=\"11235\" />\n</bmscreate.init>"
92
+ end
93
+ context 'with one article' do
94
+ it 'should post to the init url and return a bms_id' do
95
+ # expect
96
+ @sharedbook.should_receive(:post_url).with(
97
+ "#{SharedBook::URL}/bmscreate/init",
98
+ {:bookTitle=>"book title", :chapterTitle=>"chapter 1", :chapterText=>"text"}
99
+ ).and_return(@return)
100
+
101
+ # given
102
+ @sharedbook.bmscreate_init("book title", {:chapterTitle => 'chapter 1', :chapterText => 'text'}).should == '11235'
103
+ @sharedbook.bms_id.should == "11235"
104
+ end
105
+ end
106
+ context 'with more than one article' do
107
+ it 'should post to the init url and return a bms_id' do
108
+ # expect
109
+ @sharedbook.should_receive(:post_url).with(
110
+ "#{SharedBook::URL}/bmscreate/init",
111
+ {:chapterText1=>"text", :bookTitle=>"book title", :chapterTitle2=>"chapter 2", :chapterText2=>"text", :chapterTitle1=>"chapter 1"}
112
+ ).and_return(@return)
113
+
114
+ # given
115
+ @sharedbook.bmscreate_init("book title", [
116
+ {:chapterTitle => 'chapter 1', :chapterText => 'text'}, {:chapterTitle => 'chapter 2', :chapterText => 'text'}
117
+ ]).should == '11235'
118
+ end
119
+ end
120
+
121
+ it 'should raise ResponseError if the return cannot be parsed' do
122
+ # expect
123
+ @sharedbook.should_receive(:post_url).and_return(@bad_return)
124
+
125
+ # given
126
+ lambda {
127
+ @sharedbook.bmscreate_init("book title", {:chapterTitle => 'chapter 1', :chapterText => 'text'}).should == '11235'
128
+ }.should raise_error(ResponseError)
129
+ end
130
+ end
131
+
132
+ describe '#bmscreate_publish' do
133
+ it 'should post the the publish url' do
134
+ # expect
135
+ @sharedbook.should_receive(:post_url).with(
136
+ "#{SharedBook::URL}/bmscreate/publish",
137
+ {:bmsId=>123}
138
+ ).and_return("<publish status=\"ok\" />")
139
+
140
+ # given
141
+ @sharedbook.bmscreate_publish(123).should be_true
142
+ end
143
+
144
+ it 'should raise ResponseError if the return cannot be parsed' do
145
+ # expect
146
+ post_should_get_response_error(:bmscreate_publish, 123)
147
+ end
148
+ end
149
+
150
+ describe '#bms_addComment' do
151
+ it 'should post the add comment url and return a comment id' do
152
+ # expect
153
+ @sharedbook.should_receive(:post_url).with(
154
+ "#{SharedBook::URL}/bms/addComment",
155
+ {:ownerName=>"Joe", :commentTitle=>"title", :bmsId=>123, :commentText=>"text", :chapterNumber=>"1"}
156
+ ).and_return(
157
+ "<bms.addComment status=\"ok\">\n\t<comment id=\"x1y1z1\" />\n</bms.addComment>"
158
+ )
159
+
160
+ # given
161
+ @sharedbook.bms_addComment({
162
+ :bms_id => 123, :comment_title => 'title', :comment_text => 'text', :chapter_number => 1, :owner_name => 'Joe'
163
+ }).should == 'x1y1z1'
164
+ @sharedbook.comment_ids.should == ["x1y1z1"]
165
+ end
166
+ it 'should raise ResponseError if the return cannot be parsed' do
167
+ # expect
168
+ @sharedbook.should_receive(:post_url).and_return(@bad_return)
169
+
170
+ # given
171
+ lambda { @sharedbook.bms_addComment({
172
+ :bms_id => 123, :comment_title => 'title', :comment_text => 'text', :chapter_number => 1, :owner_name => 'Joe'
173
+ }) }.should raise_error(ResponseError)
174
+ end
175
+ end
176
+
177
+ describe '#bms_addPhoto_by_url' do
178
+ before(:each) do
179
+ @image = "http://examp.le/x.jpg"
180
+ @valid_post = {:bms_id => 123, :file_url => @image, :chapter_number => 1, :owner_name => 'Joe'}
181
+ end
182
+
183
+ it 'should post to the add photo url with the url of the photo to add and return a photo id' do
184
+ # expect
185
+ @sharedbook.should_receive(:post_url).with(
186
+ "#{SharedBook::URL}/bms/addPhoto",
187
+ {:ownerName=>"Joe", :bmsId=>123, :url => @image}
188
+ ).and_return(
189
+ "<bms.addPhoto status=\"ok\">\n\t<photo id=\"q1w2e3\" />\n</bms.addPhoto>"
190
+ )
191
+
192
+ # given
193
+ @sharedbook.bms_addPhoto_by_url(@valid_post).should == 'q1w2e3'
194
+ @sharedbook.photo_ids.should == ["q1w2e3"]
195
+ end
196
+ it 'should raise ResponseError if the return cannot be parsed' do
197
+ # expect
198
+ @sharedbook.should_receive(:post_url).and_return(@bad_return)
199
+
200
+ # given
201
+ lambda { @sharedbook.bms_addPhoto_by_url(@valid_post) }.should raise_error(ResponseError)
202
+ end
203
+ end
204
+
205
+ describe '#bms_addPhoto_by_handle' do
206
+ before(:each) do
207
+ @image = gem_root+"/spec/images/angry_squirrel.jpg"
208
+ @valid_post = {:bms_id => 123, :file_name => @image, :file_mime => "image/jpeg", :chapter_number => 1, :owner_name => 'Joe'}
209
+ UploadIO.stub!(:new)
210
+ end
211
+
212
+ it 'should post to the add photo url with the data of the photo to add and return a photo id' do
213
+ # expect
214
+ @sharedbook.should_receive(:post_url_file).with(
215
+ "#{SharedBook::URL}/bms/addPhoto",
216
+ {"photo"=>nil, :ownerName=>"Joe", :file_name=>@image, :file_mime=>"image/jpeg", :bmsId=>123}
217
+ ).and_return(
218
+ "<bms.addPhoto status=\"ok\">\n\t<photo id=\"q1w2e3\" />\n</bms.addPhoto>"
219
+ )
220
+
221
+ # given
222
+ @sharedbook.bms_addPhoto_by_handle(@valid_post).should == 'q1w2e3'
223
+ @sharedbook.photo_ids.should == ["q1w2e3"]
224
+ end
225
+ it 'should raise ResponseError if the return cannot be parsed' do
226
+ # expect
227
+ @sharedbook.should_receive(:post_url_file).and_return(@bad_return)
228
+
229
+ # given
230
+ lambda { @sharedbook.bms_addPhoto_by_handle(@valid_post) }.should raise_error(ResponseError)
231
+ end
232
+ end
233
+
234
+ describe '#bms_setFrontCoverPhoto' do
235
+ before(:each) do
236
+ @image = gem_root+"/spec/images/angry_squirrel.jpg"
237
+ @id = "q1w2e3"
238
+ @valid_post = {:bms_id => 123, :file_name => @image, :file_mime => "image/jpeg", :chapter_number => 1, :owner_name => 'Joe'}
239
+ UploadIO.stub!(:new)
240
+ end
241
+
242
+ it 'should post the image data to the url for setting the front cover and return an id' do
243
+ # expect
244
+ @sharedbook.should_receive(:post_url_file).with(
245
+ "#{SharedBook::URL}/bms/setFrontCoverPhoto",
246
+ {"photo"=>nil, :ownerName=>"Joe", :file_name=>@image, :file_mime=>"image/jpeg", :bmsId=>123}
247
+ ).and_return(
248
+ "<bms.addPhoto status=\"ok\">\n\t<photo id=\"#{@id}\" />\n</bms.addPhoto>"
249
+ )
250
+
251
+ # given
252
+ @sharedbook.bms_setFrontCoverPhoto(@valid_post).should == @id
253
+ @sharedbook.front_cover_photo_id.should == @id
254
+ end
255
+ it 'should raise ResponseError if the return cannot be parsed' do
256
+ # expect
257
+ @sharedbook.should_receive(:post_url_file).and_return(@bad_return)
258
+
259
+ # given
260
+ lambda { @sharedbook.bms_setFrontCoverPhoto(@valid_post) }.should raise_error(ResponseError)
261
+ end
262
+ end
263
+
264
+ describe '#bms_setBackCoverPhoto' do
265
+ before(:each) do
266
+ @image = gem_root+"/spec/images/angry_squirrel.jpg"
267
+ @id = "q1w2e3"
268
+ @valid_post = {:bms_id => 123, :file_name => @image, :file_mime => "image/jpeg", :chapter_number => 1, :owner_name => 'Joe'}
269
+ UploadIO.stub!(:new)
270
+ end
271
+
272
+ it 'should post the image data to the url for setting the front cover and return an id' do
273
+ # expect
274
+ @sharedbook.should_receive(:post_url_file).with(
275
+ "#{SharedBook::URL}/bms/setBackCoverPhoto",
276
+ {"photo"=>nil, :ownerName=>"Joe", :file_name=>@image, :file_mime=>"image/jpeg", :bmsId=>123}
277
+ ).and_return(
278
+ "<bms.addPhoto status=\"ok\">\n\t<photo id=\"#{@id}\" />\n</bms.addPhoto>"
279
+ )
280
+
281
+ # given
282
+ @sharedbook.bms_setBackCoverPhoto(@valid_post).should == @id
283
+ @sharedbook.back_cover_photo_id.should == @id
284
+ end
285
+ it 'should raise ResponseError if the return cannot be parsed' do
286
+ # expect
287
+ @sharedbook.should_receive(:post_url_file).and_return(@bad_return)
288
+
289
+ # given
290
+ lambda { @sharedbook.bms_setBackCoverPhoto(@valid_post) }.should raise_error(ResponseError)
291
+ end
292
+ end
293
+
294
+ describe '#bms_publish' do
295
+ it 'should post the the publish url' do
296
+ # expect
297
+ @sharedbook.should_receive(:post_url).with(
298
+ "#{SharedBook::URL}/bms/publish",
299
+ {:bmsId=>123}
300
+ ).and_return("<bms.publish status=\"ok\" />")
301
+
302
+ # given
303
+ @sharedbook.bms_publish(123).should be_true
304
+ end
305
+
306
+ it 'should raise ResponseError if the return cannot be parsed' do
307
+ # expect
308
+ post_should_get_response_error(:bms_publish, 123)
309
+ end
310
+ end
311
+
312
+ describe '#bookcreate_init' do
313
+ it 'should post the the publish url' do
314
+ # expect
315
+ @sharedbook.should_receive(:post_url).with(
316
+ "#{SharedBook::URL}/bookcreate/init",
317
+ {:bmsId=>123}
318
+ ).and_return("<bookcreate.init status=\"ok\" />")
319
+
320
+ # given
321
+ @sharedbook.bookcreate_init(123).should be_true
322
+ end
323
+
324
+ it 'should raise ResponseError if the return cannot be parsed' do
325
+ # expect
326
+ post_should_get_response_error(:bookcreate_init, 123)
327
+ end
328
+ end
329
+
330
+ describe '#bookcreate_setDedication' do
331
+ it 'should post the the publish url' do
332
+ # expect
333
+ @sharedbook.should_receive(:post_url).with(
334
+ "#{SharedBook::URL}/bookcreate/setDedication",
335
+ {:dedicationText=>"Dedicated!", :bmsId=>123}
336
+ ).and_return("<bookcreate.setDedication status=\"ok\" />")
337
+
338
+ # given
339
+ @sharedbook.bookcreate_setDedication({:bms_id => 123, :dedication_text => "Dedicated!"}).should be_true
340
+ end
341
+
342
+ it 'should raise ResponseError if the return cannot be parsed' do
343
+ # expect
344
+ post_should_get_response_error(:bookcreate_setDedication, 123)
345
+ end
346
+ end
347
+
348
+ describe '#bookcreate_publish' do
349
+ it 'should post to the bookcreate publish url and return a book_id' do
350
+ # expect
351
+ @sharedbook.should_receive(:post_url).with(
352
+ "#{SharedBook::URL}/bookcreate/publish",
353
+ {:bmsId=>123}
354
+ ).and_return(
355
+ "<bookcreate.publish status=\"ok\" />\n\t<book id=\"54321\" />\n</bookcreate.init>"
356
+ )
357
+
358
+ # given
359
+ @sharedbook.bookcreate_publish(123).should == "54321"
360
+ @sharedbook.book_id.should == "54321"
361
+ end
362
+
363
+ it 'should raise ResponseError if the return cannot be parsed' do
364
+ # expect
365
+ post_should_get_response_error(:bookcreate_publish, 123)
366
+ end
367
+ end
368
+
369
+ describe '#book_preview' do
370
+ before(:all) do
371
+ @preview_url = 'http://www.examp.le/12345'
372
+ end
373
+ it 'should post to the book preview url and return a preview url' do
374
+ # expect
375
+ @sharedbook.should_receive(:get_url).with(
376
+ "#{SharedBook::URL}/book/preview",
377
+ {:sessionToken=>"aabbcc", :apiKey=>"x", :authToken=>"x", :bookId=>"321", :redirect=>"false", :bmsId=>"123", :signature=>"5949fa652c1dba35111d978541ecb44e"}
378
+ ).and_return(
379
+ "<book.preview status=\"ok\">\n\t<url>#{@preview_url}</url></book.preview>"
380
+ )
381
+
382
+ # given
383
+ @sharedbook.book_preview(123,321).should == @preview_url
384
+ @sharedbook.book_url.should == @preview_url
385
+
386
+ end
387
+
388
+ it 'should raise ResponseError if the return cannot be parsed' do
389
+ # expect
390
+ @sharedbook.should_receive(:get_url).and_return(@bad_return)
391
+
392
+ # given
393
+ lambda { @sharedbook.book_preview(123,321) }.should raise_error(ResponseError)
394
+
395
+ end
396
+ end
397
+
398
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ gem 'rspec'
6
+ require 'spec'
7
+ end
8
+
9
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
10
+ require 'shared_book'
data/tasks/rspec.rake ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'spec'
3
+ rescue LoadError
4
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
5
+ require 'spec'
6
+ end
7
+ begin
8
+ require 'spec/rake/spectask'
9
+ rescue LoadError
10
+ puts <<-EOS
11
+ To use rspec for testing you must install rspec gem:
12
+ gem install rspec
13
+ EOS
14
+ exit(0)
15
+ end
16
+
17
+ desc "Run the specs under spec/models"
18
+ Spec::Rake::SpecTask.new do |t|
19
+ t.spec_opts = ['--options', "spec/spec.opts"]
20
+ t.spec_files = FileList['spec/**/*_spec.rb']
21
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shared_book
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Matthew Nielsen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-08 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rubyforge
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 4
34
+ version: 2.0.4
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: hoe
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 21
46
+ segments:
47
+ - 2
48
+ - 6
49
+ - 1
50
+ version: 2.6.1
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: |-
54
+ A Ruby Gem to connect to the SharedBook.com publishing API.
55
+
56
+ This version provides 1:1 method call structure to the SharedBook rest-like API.
57
+ email:
58
+ - xunker@pyxidis.org
59
+ executables: []
60
+
61
+ extensions: []
62
+
63
+ extra_rdoc_files:
64
+ - History.txt
65
+ - Manifest.txt
66
+ files:
67
+ - History.txt
68
+ - Manifest.txt
69
+ - README.rdoc
70
+ - Rakefile
71
+ - lib/shared_book.rb
72
+ - spec/lib/shared_book_spec.rb
73
+ - spec/spec.opts
74
+ - spec/spec_helper.rb
75
+ - tasks/rspec.rake
76
+ has_rdoc: true
77
+ homepage: http://github.com/xunker/shared_book
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --main
83
+ - README.rdoc
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project: shared_book
107
+ rubygems_version: 1.3.7
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: A Ruby Gem to connect to the SharedBook.com publishing API
111
+ test_files: []
112
+