shared_book 0.1.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.
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
+