isbndb 1.5.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module ISBNdb
2
-
2
+
3
3
  private
4
4
  # The Result object is a true testament of metaprogramming. Almost every method of the Result
5
5
  # is dynamically generated through the build_result() method. All attribtues of the XML are
@@ -9,52 +9,51 @@ module ISBNdb
9
9
  class Result
10
10
  # Initialize simply calls build_result. Because the method definition is recusive, it must
11
11
  # be moved into a separate helper.
12
- def initialize(top_node)
13
- build_result(top_node)
12
+ def initialize(json = {})
13
+ @store = build_result(json)
14
14
  end
15
-
16
- # Because a result may or may not contain a specified key, we always return nil for
15
+
16
+ # Because a result may or may not contain a specified key, we always return nil for
17
17
  # consistency. This allows developers to easily check for .nil? instead of checking for
18
- # a miriad of exceptions throughout their code.
18
+ # a myriad of exceptions throughout their code.
19
19
  def method_missing(m, *args, &block)
20
- nil
20
+ @store[m.to_s.underscore]
21
21
  end
22
-
23
- # Pretty preint the Result including the number of singleton methods that exist. If
22
+
23
+ # Return a list of all "methods" this class responds to
24
+ def instance_methods
25
+ @store.collect{ |key,value| key.to_s }
26
+ end
27
+
28
+ # Pretty print the Result including the number of singleton methods that exist. If
24
29
  # you want the ACTUAL singleton methods, call @result.singleton_methods.
25
30
  def to_s
26
- @singleton_methods ||= []
27
- "#<Result @num_singleton_methods=#{@singleton_methods.size}>"
31
+ "#<Result @num_singleton_methods=#{@store.size}>"
28
32
  end
29
-
30
- private
31
- # This is the `magical` method. It essentially parses each attribute of the XML as well as
32
- # the content of each XML node, dynamically sends a method to the instance with that attribute's
33
- # or content's value. Not to be outdone, it recursively iterates over all children too!
34
- def build_result(top_node)
35
- top_node.attributes.each do |attribute|
36
- singleton.send(:define_method, method_name(attribute.name)) { attribute.value } unless attribute.value.strip.empty?
37
- end
38
-
39
- if top_node.children?
40
- top_node.children.each { |child| build_result(child) }
41
- else
42
- singleton.send(:define_method, method_name(top_node.parent.name)) { top_node.content.strip.chomp(',') } unless top_node.content.strip.empty?
43
- end
33
+
34
+ def inspect
35
+ "#<Result #{@store.collect{ |key,value| ':' + key.to_s + ' => ' + value.inspect }.join(', ')}>"
44
36
  end
45
-
46
- # This helper function reduces code redundancy and maintains consistency by formatting
47
- # all method names the same. All method names are stripped of any trailing whitespaces,
48
- # converted from CamelCase to under_score, and converted to a symbol
49
- def method_name(name)
50
- name.strip.underscore.to_sym
37
+
38
+ def ==(result)
39
+ self.inspect == result.inspect
51
40
  end
52
-
53
- # We need a singleton reference to the current _instance_ so that we can dynamically define
54
- # methods. This is just a simple helper that returns the singleton class of the current
55
- # object instance.
56
- def singleton
57
- class << self; self end
41
+
42
+ private
43
+ def build_result(json)
44
+ result = {}
45
+
46
+ json.each do |key,value|
47
+ result[key.to_s.underscore] = if value.is_a?(Hash)
48
+ build_result(value)
49
+ elsif value.blank?
50
+ nil
51
+ else
52
+ value
53
+ end
54
+ end
55
+
56
+ result
58
57
  end
59
58
  end
60
- end
59
+ end
@@ -1,12 +1,16 @@
1
1
  module ISBNdb
2
-
2
+
3
3
  private
4
4
  # The ResultSet is a collection of Result objects with helper methods for pagination. It
5
5
  # allows for easy paginating through multiple pages of results as well as jumping to a
6
6
  # specific results page.
7
7
  class ResultSet
8
8
  include Enumerable
9
-
9
+ include HTTParty
10
+
11
+ base_uri 'http://isbndb.com/api'
12
+ headers 'Content-Type' => 'text/xml'
13
+
10
14
  # This method creates instance variables for @uri, @collection, and @current_page. It then
11
15
  # attempts to parse the XML at the gieven URI. If it cannot parse the URI for any reason,
12
16
  # it will raise an ISBNdb::InvalidURIError. Next, the results are checked for an any error
@@ -14,76 +18,71 @@ module ISBNdb
14
18
  # Finally, this method then actually builds the ResultSet.
15
19
  def initialize(uri, collection, current_page = 1)
16
20
  @uri = uri
17
- @collection = collection
21
+ @collection = collection.to_s.titleize.singularize
18
22
  @current_page = current_page
19
- @xml = parse_xml
20
-
23
+ @parsed_response = self.class.get(@uri).parsed_response['ISBNdb']
24
+
21
25
  check_results
22
26
  build_results
23
27
  end
24
-
28
+
29
+ def size
30
+ @results.size
31
+ end
32
+
25
33
  # Because ResultSet extends Enumerable, we need to define the each method. This allows users
26
34
  # to call methods like .first, .last, [5], and .each on the ResultSet, making it behave like
27
35
  # a primitive array.
28
36
  def each(&block)
29
37
  @results.each &block
30
38
  end
31
-
39
+
32
40
  # Jump to a specific page. This method will return nil if the specified page does not exist.
33
41
  def go_to_page(page)
34
42
  get_total_pages unless @total_pages
35
- return nil if page < 1 || page > @total_pages
43
+ return nil if page.to_i < 1 || page.to_i > @total_pages
36
44
  ISBNdb::ResultSet.new("#{@uri}&page_number=#{page}", @collection, page)
37
45
  end
38
-
46
+
39
47
  # Go to the next page. This method will return nil if a next page does not exist.
40
48
  def next_page
41
49
  go_to_page(@current_page+1)
42
50
  end
43
-
51
+
44
52
  # Go to the previous page. This method will return nil if a previous page does not exist.
45
53
  def prev_page
46
54
  go_to_page(@current_page-1)
47
55
  end
48
-
56
+
49
57
  # Pretty prints the Result set information.
50
58
  def to_s
51
- "#<ResultSet @collection=#{@collection}, total_results=#{@results.size}>"
59
+ "#<ResultSet::#{@collection} :total_results => #{@results.size}>"
52
60
  end
53
-
54
- private
55
- # Try and parses and returns the XML from the given URI. If the parsing fails for any reason, this method
56
- # raises ISBNdb::InvalidURIError.
57
- def parse_xml
58
- begin
59
- LibXML::XML::Parser.file(@uri).parse
60
- rescue
61
- raise ISBNdb::InvalidURIError
62
- end
61
+
62
+ def ==(result_set)
63
+ self.size == result_set.size && self.instance_variable_get('@results') == result_set.instance_variable_get('@results')
63
64
  end
64
-
65
- # Check the results for an error message. If one exists, raise an ISBNdb::AccessKeyError for now.
65
+
66
+ private
67
+ # Check the results for an error message. If one exists, raise an ISBNdb::AccessKeyError for now.
66
68
  # Currently the API does not differentiate between an overloaded API key and an invalid one
67
69
  # (it returns the same exact response), so there only exists one exception for now...
68
70
  def check_results
69
- raise ISBNdb::AccessKeyError unless @xml.find("ErrorMessage").first.nil?
71
+ raise ISBNdb::AccessKeyError if @parsed_response['ErrorMessage']
70
72
  end
71
-
73
+
72
74
  # Iterate over #{@collection}List/#{@collection}Data (ex. BookList/BookData) and build a result with
73
75
  # each child. This method works because the API always returns #{@collection}List followed by a subset
74
76
  # of #{@collection}Data. These results are all pushed into the @results array for accessing.
75
77
  def build_results
76
- @results = []
77
- list = @xml.find("#{@collection}List/#{@collection}Data")
78
- return [nil] if list.empty?
79
- list.collect { |node| @results << Result.new(node) }
78
+ @results = @parsed_response["#{@collection}List"]["#{@collection}Data"].collect{ |json| Result.new(json) }
80
79
  end
81
-
80
+
82
81
  # This helper method is mainly designed for use with the go_to_page(page) method. It parses the XML
83
82
  # and returns the total number of pages that exist for this result set.
84
83
  def get_total_pages
85
- list = @xml.find("#{@collection}List").first.attributes
84
+ list = @parsed_response["#{@collection}List"]
86
85
  @total_pages = (list['total_results'].to_f/list['page_size'].to_f).ceil
87
86
  end
88
87
  end
89
- end
88
+ end
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <ISBNdb server_time="2012-06-16T19:19:05Z">
4
+ <ErrorMessage>Access key error</ErrorMessage>
5
+ </ISBNdb>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <ISBNdb server_time="2012-06-16T20:26:27Z">
4
+ <AuthorList total_results="401" page_size="10" page_number="1" shown_results="10">
5
+ <AuthorData person_id="abraham_seth">
6
+ <Name>Abraham, Seth</Name>
7
+ <Details first_name="Seth" last_name="Abraham" dates="" has_books="1" />
8
+ </AuthorData>
9
+ <AuthorData person_id="abramson_seth_f">
10
+ <Name>Abramson, Seth F.</Name>
11
+ <Details first_name="Seth" last_name="Abramson" dates="1926-" has_books="1" />
12
+ </AuthorData>
13
+ <AuthorData person_id="adams_seth">
14
+ <Name>Adams, Seth</Name>
15
+ <Details first_name="Seth" last_name="Adams" dates="" has_books="1" />
16
+ </AuthorData>
17
+ <AuthorData person_id="seth_afikorah_danquah">
18
+ <Name>Seth Afikorah-Danquah</Name>
19
+ <Details first_name="Seth" last_name="Afikorah-Danquah" dates="" has_books="1" />
20
+ </AuthorData>
21
+ <AuthorData person_id="agata_seth_h">
22
+ <Name>Agata, Seth H</Name>
23
+ <Details first_name="Seth" last_name="Agata" dates="" has_books="1" />
24
+ </AuthorData>
25
+ <AuthorData person_id="agbo_seth_a">
26
+ <Name>Agbo, Seth A.</Name>
27
+ <Details first_name="Seth" last_name="Agbo" dates="1951-" has_books="1" />
28
+ </AuthorData>
29
+ <AuthorData person_id="allcorn_seth">
30
+ <Name>Allcorn, Seth</Name>
31
+ <Details first_name="Seth" last_name="Allcorn" dates="" has_books="1" />
32
+ </AuthorData>
33
+ <AuthorData person_id="ames_seth">
34
+ <Name>Ames, Seth</Name>
35
+ <Details first_name="Seth" last_name="Ames" dates="1805-1881." has_books="1" />
36
+ </AuthorData>
37
+ <AuthorData person_id="anderson_seth_b">
38
+ <Name>Anderson, Seth B.</Name>
39
+ <Details first_name="Seth" last_name="Anderson" dates="" has_books="1" />
40
+ </AuthorData>
41
+ <AuthorData person_id="seth_andrew_a01">
42
+ <Name>Seth Andrew</Name>
43
+ <Details first_name="Seth" last_name="Andrew" dates="" has_books="1" />
44
+ </AuthorData>
45
+ </AuthorList>
46
+ </ISBNdb>
@@ -0,0 +1,76 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <ISBNdb server_time="2012-06-16T20:10:13Z">
4
+ <BookList total_results="1664" page_size="10" page_number="1" shown_results="10">
5
+ <BookData book_id="100th_day_of_school_a04" isbn="1590543947" isbn13="9781590543948">
6
+ <Title>100th Day of School</Title>
7
+ <TitleLong>100th Day of School (Hello Reader Level 2)</TitleLong>
8
+ <AuthorsText>Angela Shelf Medearis, </AuthorsText>
9
+ <PublisherText publisher_id="fitzgerald_books">Fitzgerald Books</PublisherText>
10
+ <Details change_time="2011-03-08T18:28:45Z" price_time="2012-05-29T16:45:46Z" edition_info="Unknown Binding; 2007-01" language="" physical_description_text="6.0&quot;x9.0&quot;x0.5&quot;; 0.4 lb; 32 pages" lcc_number="" dewey_decimal_normalized="" dewey_decimal="" />
11
+ </BookData>
12
+ <BookData book_id="100th_day_the" isbn="0439330173" isbn13="9780439330176">
13
+ <Title>100th Day, The</Title>
14
+ <TitleLong>100th Day, The (level 1) (Hello Reader Level 1)</TitleLong>
15
+ <AuthorsText>Alayne Pick, Grace Maccarone, Laura Freeman (Illustrator)</AuthorsText>
16
+ <PublisherText publisher_id="cartwheel">Cartwheel</PublisherText>
17
+ <Details change_time="2006-02-27T21:34:15Z" price_time="2012-05-29T16:46:08Z" edition_info="Paperback; 2002-12-01" language="" physical_description_text="32 pages" lcc_number="" dewey_decimal_normalized="" dewey_decimal="" />
18
+ </BookData>
19
+ <BookData book_id="2011_hello_kitty_engagement_calendar" isbn="1423803558" isbn13="9781423803553">
20
+ <Title>2011 Hello Kitty Engagement Calendar</Title>
21
+ <TitleLong></TitleLong>
22
+ <AuthorsText>Day Dream (Contributor)</AuthorsText>
23
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
24
+ <Details change_time="2010-09-21T21:20:39Z" price_time="2012-03-24T04:44:44Z" edition_info="Calendar; 2010-08-01" language="" physical_description_text="7.0&quot;x8.5&quot;x0.9&quot;; 1.1 lb" lcc_number="" dewey_decimal_normalized="741" dewey_decimal="741" />
25
+ </BookData>
26
+ <BookData book_id="2011_hello_kitty_wall_calendar" isbn="1423803981" isbn13="9781423803980">
27
+ <Title>2011 Hello Kitty Wall Calendar</Title>
28
+ <TitleLong></TitleLong>
29
+ <AuthorsText>Day Dream (Contributor)</AuthorsText>
30
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
31
+ <Details change_time="2010-09-21T18:46:02Z" price_time="2012-04-25T20:26:40Z" edition_info="Calendar; 2010-08-01" language="" physical_description_text="10.8&quot;x11.8&quot;x0.2&quot;; 0.3 lb" lcc_number="" dewey_decimal_normalized="" dewey_decimal="" />
32
+ </BookData>
33
+ <BookData book_id="2012_hello_kitty_2_year_pocket_planner_calendar" isbn="1423809424" isbn13="9781423809425">
34
+ <Title>2012 Hello Kitty 2 Year Pocket Planner Calendar</Title>
35
+ <TitleLong></TitleLong>
36
+ <AuthorsText>Day Dream, </AuthorsText>
37
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
38
+ <Details change_time="2012-01-17T22:23:43Z" price_time="2012-03-04T05:32:03Z" edition_info="Calendar; 2011-07-01" language="" physical_description_text="3.5&quot;x6.2&quot;x0.0&quot;; 0.1 lb" lcc_number="" dewey_decimal_normalized="636" dewey_decimal="636" />
39
+ </BookData>
40
+ <BookData book_id="2012_hello_kitty_juvenile_activity_calendar" isbn="1423811194" isbn13="9781423811190">
41
+ <Title>2012 Hello Kitty Juvenile Activity Calendar</Title>
42
+ <TitleLong></TitleLong>
43
+ <AuthorsText>Day Dream, </AuthorsText>
44
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
45
+ <Details change_time="2012-05-15T00:52:54Z" price_time="2012-06-16T20:10:13Z" edition_info="Calendar; 2011-07-01" language="" physical_description_text="11.0&quot;x12.0&quot;x0.3&quot;; 0.8 lb" lcc_number="" dewey_decimal_normalized="741" dewey_decimal="741" />
46
+ </BookData>
47
+ <BookData book_id="2012_hello_kitty_mini_calendar" isbn="1423809165" isbn13="9781423809166">
48
+ <Title>2012 Hello Kitty Mini Calendar</Title>
49
+ <TitleLong></TitleLong>
50
+ <AuthorsText>Day Dream, </AuthorsText>
51
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
52
+ <Details change_time="2012-01-17T22:24:12Z" price_time="2012-02-09T06:11:01Z" edition_info="Calendar; 2011-07-01" language="" physical_description_text="6.2&quot;x6.9&quot;x0.2&quot;; 0.1 lb" lcc_number="" dewey_decimal_normalized="741" dewey_decimal="741" />
53
+ </BookData>
54
+ <BookData book_id="2012_hello_kitty_wall_calendar" isbn="1423809696" isbn13="9781423809692">
55
+ <Title>2012 Hello Kitty Wall Calendar</Title>
56
+ <TitleLong></TitleLong>
57
+ <AuthorsText>Day Dream, </AuthorsText>
58
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
59
+ <Details change_time="2012-01-17T22:14:27Z" price_time="2012-02-09T06:02:14Z" edition_info="Calendar; 2011-07-01" language="" physical_description_text="10.6&quot;x11.8&quot;x0.2&quot;; 0.5 lb" lcc_number="" dewey_decimal_normalized="741" dewey_decimal="741" />
60
+ </BookData>
61
+ <BookData book_id="2012_hello_kitty_weekly_engagement_calendar" isbn="1423809092" isbn13="9781423809098">
62
+ <Title>2012 Hello Kitty Weekly Engagement Calendar</Title>
63
+ <TitleLong></TitleLong>
64
+ <AuthorsText>Day Dream, </AuthorsText>
65
+ <PublisherText publisher_id="day_dream">Day Dream</PublisherText>
66
+ <Details change_time="2012-01-17T22:14:34Z" price_time="2012-02-08T12:37:05Z" edition_info="Calendar; 2011-07-01" language="" physical_description_text="7.2&quot;x8.5&quot;x0.9&quot;; 1.0 lb" lcc_number="" dewey_decimal_normalized="741" dewey_decimal="741" />
67
+ </BookData>
68
+ <BookData book_id="2_grrrls_hello_gorgeous" isbn="0439187370" isbn13="9780439187374">
69
+ <Title>Hello gorgeous</Title>
70
+ <TitleLong>Hello gorgeous: a guide to style</TitleLong>
71
+ <AuthorsText>by Kristen Kemp</AuthorsText>
72
+ <PublisherText publisher_id="scholastic">New York : Scholastic, c2000.</PublisherText>
73
+ <Details change_time="2009-09-29T18:09:13Z" price_time="2011-03-25T22:20:05Z" edition_info="(pbk.) :$3.99" language="eng" physical_description_text="64 p. : ill. (some col.) ; 20 cm." lcc_number="" dewey_decimal_normalized="" dewey_decimal="" />
74
+ </BookData>
75
+ </BookList>
76
+ </ISBNdb>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <ISBNdb server_time="2012-06-16T20:36:49Z">
4
+ <CategoryList total_results="99" page_size="10" page_number="1" shown_results="10">
5
+ <CategoryData category_id="society.religion.christianity.denominations.catholicism.literature.fiction" parent_id="society.religion.christianity.denominations.catholicism.literature">
6
+ <Name>Fiction</Name>
7
+ <Details summary="" depth="6" element_count="30" />
8
+ </CategoryData>
9
+ <CategoryData category_id="society.religion.christianity.denominations.catholicism.clergy.fiction" parent_id="society.religion.christianity.denominations.catholicism.clergy">
10
+ <Name>Fiction</Name>
11
+ <Details summary="" depth="6" element_count="16" />
12
+ </CategoryData>
13
+ <CategoryData category_id="society.family.family_members.fathers.single_and_divorced.fiction" parent_id="society.family.family_members.fathers.single_and_divorced">
14
+ <Name>Fiction</Name>
15
+ <Details summary="" depth="5" element_count="7" />
16
+ </CategoryData>
17
+ <CategoryData category_id="society.family.family_members.mothers.single_and_divorced.fiction" parent_id="society.family.family_members.mothers.single_and_divorced">
18
+ <Name>Fiction</Name>
19
+ <Details summary="" depth="5" element_count="25" />
20
+ </CategoryData>
21
+ <CategoryData category_id="society.family.family_members.mothers.working_mothers.fiction" parent_id="society.family.family_members.mothers.working_mothers">
22
+ <Name>Fiction</Name>
23
+ <Details summary="" depth="5" element_count="6" />
24
+ </CategoryData>
25
+ <CategoryData category_id="society.family.family_members.mothers.teenage_mothers.fiction" parent_id="society.family.family_members.mothers.teenage_mothers">
26
+ <Name>Fiction</Name>
27
+ <Details summary="" depth="5" element_count="4" />
28
+ </CategoryData>
29
+ <CategoryData category_id="society.sexuality.homosexuality.lesbians.literature.fiction" parent_id="society.sexuality.homosexuality.lesbians.literature">
30
+ <Name>Fiction</Name>
31
+ <Details summary="" depth="5" element_count="24" />
32
+ </CategoryData>
33
+ <CategoryData category_id="arts.music.styles.jazz.musicians.fiction" parent_id="arts.music.styles.jazz.musicians">
34
+ <Name>Fiction</Name>
35
+ <Details summary="" depth="5" element_count="6" />
36
+ </CategoryData>
37
+ <CategoryData category_id="regions.france.history.famous_people.napoleon_i.fiction" parent_id="regions.france.history.famous_people.napoleon_i">
38
+ <Name>Fiction</Name>
39
+ <Details summary="" depth="5" element_count="4" />
40
+ </CategoryData>
41
+ <CategoryData category_id="science.biology.fields.genetics.literature.fiction" parent_id="science.biology.fields.genetics.literature">
42
+ <Name>Fiction</Name>
43
+ <Details summary="" depth="5" element_count="3" />
44
+ </CategoryData>
45
+ </CategoryList>
46
+ </ISBNdb>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <ISBNdb server_time="2012-06-16T18:32:28Z">
4
+ <KeyStats granted="2" access_key="ABC123" requests="5" limit="0" />
5
+ <BookList total_results="0" page_size="10" page_number="1" shown_results="0"></BookList>
6
+ </ISBNdb>
@@ -0,0 +1,46 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <ISBNdb server_time="2012-06-16T20:42:39Z">
4
+ <PublisherList total_results="276" page_size="10" page_number="1" shown_results="10">
5
+ <PublisherData publisher_id="taylor_francis_a01">
6
+ <Name>: Taylor &amp; Francis</Name>
7
+ <Details location="London [etc.]" />
8
+ </PublisherData>
9
+ <PublisherData publisher_id="a_a_balkema_taylor_francis">
10
+ <Name>A.A. Balkema/Taylor &amp; Francis</Name>
11
+ <Details location="Leiden, The Netherlands" />
12
+ </PublisherData>
13
+ <PublisherData publisher_id="albert_francis_perrin">
14
+ <Name>Albert Francis Perrin</Name>
15
+ <Details location="Ottawa" />
16
+ </PublisherData>
17
+ <PublisherData publisher_id="auerbach_publications_tayl_a02">
18
+ <Name>Auerbach Publications, Taylor &amp; Francis Group</Name>
19
+ <Details location="Boca Raton, FL" />
20
+ </PublisherData>
21
+ <PublisherData publisher_id="auerbach_publications_tayl_a01">
22
+ <Name>Auerbach Publications/Taylor &amp; Francis</Name>
23
+ <Details location="Boca Raton, FL" />
24
+ </PublisherData>
25
+ <PublisherData publisher_id="auerbach_publications_taylor_f">
26
+ <Name>Auerbach Publications/Taylor &amp; Francis Group</Name>
27
+ <Details location="Boca Raton, FL" />
28
+ </PublisherData>
29
+ <PublisherData publisher_id="auerbach_taylor_francis">
30
+ <Name>Auerbach/Taylor&amp;Francis</Name>
31
+ <Details location="Boca Raton" />
32
+ </PublisherData>
33
+ <PublisherData publisher_id="b_francis">
34
+ <Name>B. Francis</Name>
35
+ <Details location="[Coolangatta, Qld.]" />
36
+ </PublisherData>
37
+ <PublisherData publisher_id="balkema_taylor_francis">
38
+ <Name>Balkema/Taylor &amp; Francis</Name>
39
+ <Details location="[London]" />
40
+ </PublisherData>
41
+ <PublisherData publisher_id="beau_francis_press">
42
+ <Name>Beau Francis Press</Name>
43
+ <Details location="" />
44
+ </PublisherData>
45
+ </PublisherList>
46
+ </ISBNdb>