rbook-bisac 0.5

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.
@@ -0,0 +1,99 @@
1
+
2
+
3
+ module RBook
4
+ module Bisac
5
+
6
+ # Represents a single Bisac File. See RBook::Bisac for a basic
7
+ # usage example.
8
+ class Message
9
+
10
+ attr_accessor :company, :san, :batch, :code, :products
11
+
12
+ # creates a new bisac file in memory
13
+ # params:
14
+ # - company = company name (10 chars)
15
+ # - san = company SAN number (12 chars)
16
+ # - batch = batch name for this file (6 chars)
17
+ # - code = 1 for new titles, 2 for updated titles (1 char)
18
+ def initialize(company, san, batch, code)
19
+ @company = company
20
+ @san = san
21
+ @batch = batch
22
+ @code = code
23
+ @products = []
24
+ end
25
+
26
+ # loads the requested BISAC file into memory.
27
+ # returns a Message object or nil if the file is not
28
+ # a BISAC file
29
+ def self.load(filename)
30
+ msg = self.new(nil,nil,nil,nil)
31
+
32
+ File.open(filename, "r") do |f|
33
+ f.each_line do |l|
34
+ if l[0,10].eql?("**HEADER**")
35
+ msg.company = l[16,10].strip
36
+ msg.san = l[26,12].strip
37
+ msg.batch = l[38,6].strip
38
+ msg.code = l[44,1].strip
39
+ elsif !l[0,11].eql?("**TRAILER**")
40
+ product = RBook::Bisac::Product.from_string(l)
41
+ msg << product unless product.nil?
42
+ end
43
+ end
44
+ end
45
+
46
+ if msg.company.nil? || msg.san.nil? || msg.batch.nil? || msg.code.nil?
47
+ return nil
48
+ else
49
+ return msg
50
+ end
51
+ end
52
+
53
+ # adds a new title to the bisic file. Expects a Rbook::Bisac::Product object.
54
+ def << (item)
55
+ unless item.class == RBook::Bisac::Product
56
+ raise ArgumentError, 'item must be a RBook::Bisac::Product object'
57
+ end
58
+ @products << item
59
+ end
60
+
61
+ # converts this bisac file into a string
62
+ def to_s
63
+ # File Header
64
+ content = "**HEADER**"
65
+ content << Time.now.strftime("%y%m%d")
66
+ content << @company[0,10].ljust(10)
67
+ content << @san[0,12].ljust(12)
68
+ content << @batch[0,6].ljust(6)
69
+ content << @code[0,1].ljust(1)
70
+ content << "**PUBSTAT*"
71
+ content << "040"
72
+ content << "".ljust(201)
73
+ content << "\n"
74
+
75
+ # File Content
76
+ counter = 0
77
+ @products.each do |item|
78
+ content << item.to_s + "\n"
79
+ counter = counter + 1
80
+ end
81
+
82
+ # File Footer
83
+ content << "**TRAILER*"
84
+ content << Time.now.strftime("%y%m%d")
85
+ content << @batch[0,6].ljust(6)
86
+ content << counter.to_s[0,6].ljust(6)
87
+ content << "**PUBSTAT*"
88
+ content << "".ljust(221)
89
+
90
+ return content
91
+ end
92
+
93
+ # writes the content of this bisac file out to the specified file
94
+ def write(filename)
95
+ File.open(filename, "w") { |f| f.puts to_s }
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,96 @@
1
+ require File.dirname(__FILE__) + '/po_line_item'
2
+
3
+ module RBook
4
+ module Bisac
5
+
6
+ # Represents a single BISAC purchase order
7
+ class PO
8
+
9
+ attr_accessor :source_san, :source_suffix, :source_name
10
+ attr_accessor :date, :filename, :format_version
11
+ attr_accessor :destination_san, :destination_suffix
12
+ attr_accessor :po_number, :cancellation_date, :backorder
13
+ attr_accessor :do_not_exceed_action, :do_not_exceed_amount
14
+ attr_accessor :invoice_copies, :special_instructions
15
+ attr_accessor :do_not_ship_before
16
+ attr_accessor :items
17
+
18
+ # creates a new RBook::Bisac::PO object
19
+ def initialize
20
+ @items = []
21
+ end
22
+
23
+ # reads a bisac text file into memory. input should be a string
24
+ # that specifies the file path
25
+ def self.load_from_file(input)
26
+ raise RBook::InvalidFileError, 'Invalid file' unless File.exist?(input)
27
+ data = []
28
+ File.open(input) do |f|
29
+ f.each_line { |l| data << l }
30
+ end
31
+ return self.build_message(data)
32
+ end
33
+
34
+ # creates a RBook::Bisac::PO object from a string. Input should
35
+ # be a complete bisac file as a string
36
+ def self.load_from_string(input)
37
+ data = input.split("\n")
38
+ return self.build_message(data)
39
+ end
40
+
41
+ private
42
+
43
+ def self.build_message(data)
44
+ raise RBook::InvalidFileError, 'File appears to be too short' unless data.size >= 3
45
+ raise RBook::InvalidFileError, 'Missing header information' unless data[0][0,2].eql?("00")
46
+ raise RBook::InvalidFileError, 'Missing footer information' unless data[-1][0,2].eql?("90")
47
+
48
+ msg = PO.new
49
+
50
+ data.each do |line|
51
+
52
+ case line[0,2]
53
+ when "00" # first header
54
+ msg.source_san = line[7,7].strip
55
+ msg.source_suffix = line[14,5].strip
56
+ msg.source_name = line[19,13].strip
57
+ msg.date = line[32,6].strip
58
+ msg.filename = line[38,20].strip
59
+ msg.format_version = line[60,3].strip
60
+ msg.destination_san = line[63,7].strip
61
+ msg.destination_suffix = line[70,5].strip
62
+ when "10" # second header
63
+ msg.po_number = line[7, 12].strip
64
+ msg.cancellation_date = line[50,6].strip
65
+ msg.backorder = line[56,1].eql?("Y") ? true : false
66
+ msg.do_not_exceed_action = line[57,1].strip
67
+ msg.do_not_exceed_amount = line[58,7].strip
68
+ msg.invoice_copies = line[65,2].strip
69
+ msg.special_instructions = line[67,1].eql?("Y") ? true : false
70
+ msg.do_not_ship_before = line[73,6].strip
71
+ when "40" # line item
72
+ # load each lineitem into the message
73
+ item = RBook::Bisac::POLineItem.load_from_string(line)
74
+ msg.items << item
75
+ when "41"
76
+ if line.length > 21
77
+ title = line[21, line.length - 21]
78
+ msg.items.last.title = title.strip unless title.nil?
79
+ end
80
+ when "42"
81
+ if line.length > 21
82
+ author = line[21, line.length - 21]
83
+ msg.items.last.author = author.strip unless author.nil?
84
+ end
85
+ when "90" # footer
86
+ # check the built objects match the file
87
+ end
88
+
89
+ end
90
+
91
+ # return the results
92
+ return msg
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,36 @@
1
+ require 'bigdecimal'
2
+ #require 'rbook/errors'
3
+
4
+ module RBook
5
+ module Bisac
6
+
7
+ # represents a single line on the purchase order. Has attributes like
8
+ # price, qty and description
9
+ class POLineItem
10
+
11
+ attr_accessor :sequence_number, :po_number, :line_item_number
12
+ attr_accessor :isbn, :qty, :catalogue_code, :price
13
+ attr_accessor :author, :title
14
+
15
+ # returns a new RBook::Bisac::POLineItem object using the data passed in as a string
16
+ # refer to the bisac spec for the expected format of the string
17
+ def self.load_from_string(data)
18
+ raise RBook::InvalidArgumentError, 'data must be a string' unless data.kind_of? String
19
+
20
+ item = self.new
21
+
22
+ item.sequence_number = data[2,5].to_i
23
+ item.po_number = data[7,13].strip
24
+ item.line_item_number = data[21,10].strip
25
+ item.isbn = data[31,10].strip
26
+ item.qty = data[41,5].to_i
27
+ item.catalogue_code = data[46,1].strip
28
+ item.price = BigDecimal.new(data[47,6])
29
+
30
+ return item
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,176 @@
1
+ module RBook
2
+ module Bisac
3
+
4
+ # Class to represent a single product line in a Bisac File. See
5
+ # RBook::Bisac for basic usage instructions.
6
+ class Product
7
+
8
+ attr_reader :isbn, :title, :author, :price, :pubdate, :publisher
9
+ attr_reader :imprint, :volumes, :edition, :binding, :volume, :status
10
+
11
+ # Creates a new product object with the requested ISBN. Must be a 10 digit ISBN.
12
+ def initialize(isbn)
13
+ raise ArgumentError, 'isbn must 10 chars or less' if isbn.to_s.length > 10
14
+
15
+ @isbn = isbn.to_s
16
+ @title = ""
17
+ @author = ""
18
+ @price = ""
19
+ @pubdate = ""
20
+ @publisher = ""
21
+ @imprint = ""
22
+ @volumes = ""
23
+ @edition = ""
24
+ @binding = ""
25
+ @volume = ""
26
+ @status = ""
27
+ end
28
+
29
+ # sets the products author. Maximum of 30 chars.
30
+ def author=(author)
31
+ @author = author.to_s
32
+ end
33
+
34
+ # sets the products binding. Maximum of 2 chars.
35
+ def binding=(binding)
36
+ @binding = binding.to_s
37
+ end
38
+
39
+ # sets the products edition number. Maximum of 3 chars.
40
+ def edition=(edition)
41
+ @edition = edition.to_s
42
+ end
43
+
44
+ # takes a single line from a BISAC file and attempts to convert
45
+ # it to a Product object
46
+ def self.from_string(s)
47
+ s = s.to_s
48
+ return nil if s.length < 259
49
+ product = self.new(s[0,10])
50
+ product.title = s[15,30].strip
51
+ product.author = s[46,30].strip
52
+ product.price = s[79,7].strip if s[79,7].strip.match(/\A\d{0,7}\Z/)
53
+ product.pubdate = s[87,6].strip if s[87,6].strip.match(/\A\d{6}\Z/)
54
+ product.publisher = s[94,10].strip
55
+ product.imprint = s[105,14].strip
56
+ product.volumes = s[120,3].strip
57
+ product.edition = s[124,2].strip
58
+ product.binding = s[127,2].strip
59
+ product.volume = s[130,3].strip
60
+ product.status = s[153,3].strip
61
+ return product
62
+ end
63
+
64
+ # Sets the products imprint. Maximum of 14 chars.
65
+ def imprint=(imprint)
66
+ @imprint = imprint.to_s
67
+ end
68
+
69
+ # Sets the price imprint. Maximum of 7 chars. Must by a whole
70
+ # number (represent price in cents).
71
+ def price=(price)
72
+ unless price.to_s.match(/\A\d{0,7}\Z/)
73
+ raise ArgumentError, 'price should be a whole number with no more than 7 digits. (price in cents)'
74
+ end
75
+ @price = price.to_s
76
+ end
77
+
78
+ # Sets the products pubdate. Must be in the form YYMMDD
79
+ def pubdate=(pubdate)
80
+ unless pubdate.to_s.match(/\A\d{6}\Z/)
81
+ raise ArgumentError, 'pubdate should be a date in the form YYMMDD.'
82
+ end
83
+ @pubdate = pubdate.to_s
84
+ end
85
+
86
+ # sets the products publisher. Maximum of 10 chars.
87
+ def publisher=(publisher)
88
+ @publisher = publisher.to_s
89
+ end
90
+
91
+ # sets the products status code. Maximum of 3 chars.
92
+ def status=(status)
93
+ @status = status.to_s
94
+ end
95
+
96
+ # sets the products title. Maximum of 30 chars.
97
+ def title=(title)
98
+ @title = title.to_s
99
+ end
100
+
101
+ # Returns the product as a single line ready for inserting into a BISAC file.
102
+ # Doesn't have a \n on the end
103
+ def to_s
104
+ content = ""
105
+ content << @isbn[0,10].ljust(10) # 10 digit isbn
106
+ content << "1"
107
+ content << "N"
108
+ content << "N"
109
+ content << "B"
110
+ content << "N"
111
+ content << @title[0,30].ljust(30)
112
+ content << "N"
113
+ content << @author[0,30].ljust(30)
114
+ content << "N"
115
+ content << "A" # author role
116
+ content << "N"
117
+ content << @price[0,7].rjust(7,"0") # current price
118
+ content << "N"
119
+ content << @pubdate[0,6].ljust(6) # published date
120
+ content << "N"
121
+ content << @publisher[0,10].ljust(10) # publisher
122
+ content << "N"
123
+ content << @imprint[0,14].ljust(14) #imprint
124
+ content << "N"
125
+ content << @volumes[0,3].rjust(3,"0") # volumes included in this isbn
126
+ content << "N"
127
+ content << @edition[0,2].rjust(2,"0") # edition
128
+ content << "N"
129
+ content << @binding[0,2].rjust(2,"0") # binding
130
+ content << "N"
131
+ content << @volume[0,3].rjust(3,"0") # volume number
132
+ content << "N"
133
+ content << "0000000" # new price
134
+ content << "N"
135
+ content << "000000" # new price effective date
136
+ content << "N"
137
+ content << " " # audience type
138
+ content << "N"
139
+ content << @status[0,3].rjust(3) # status
140
+ content << "N"
141
+ content << " " # available date. only use for status' like NYP
142
+ content << "N"
143
+ content << " " # alternate isbn
144
+ content << "N"
145
+ content << "999999" # out of print date. only use for status == OP
146
+ content << "N"
147
+ content << " " # geographic restrictions
148
+ content << "N"
149
+ content << " " # library of congress catalogue number
150
+ content << "N"
151
+ content << "".ljust(40) # series title
152
+ content << "N"
153
+ content << "0" # price code for current price
154
+ content << "N"
155
+ content << "0" # price code for new price
156
+ content << "N"
157
+ content << "0000000" # freight pass through price
158
+ content << "N"
159
+ content << "000000" # new freight pass through price
160
+ content << "00000" # last changed date
161
+
162
+ return content
163
+ end
164
+
165
+ # sets the products volume number. Maximum of 3 chars.
166
+ def volume=(volume)
167
+ @volume = volume.to_s
168
+ end
169
+
170
+ # sets the number of volumes in the set this product belongs to. Maximum of 3 chars.
171
+ def volumes=(volumes)
172
+ @volumes = volumes.to_s
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module RBook
3
+
4
+ class InvalidRubyVersionError < RuntimeError; end
5
+ class NotLoggedInError < RuntimeError; end
6
+ class InvalidFileError < RuntimeError; end
7
+ class InvalidArgumentError < RuntimeError; end
8
+
9
+ end
@@ -0,0 +1,112 @@
1
+ 00000019013725 Rainbow Book 061112INTERNET.BSC F03901375X
2
+ 1000002 14976 9013725 901375X 061112000000Y 000000001N00020000000
3
+ 4000003 14976 Y000000000102978513220000100000000000000000000550000000
4
+ 4100004 14976 Finding Sanctuary :Monastic St
5
+ 4200005 14976 JAMISON ABBOT CHRISTOPH
6
+ 4000006 14976 Y000000000203407855780000100000000000000000000550000000
7
+ 4100007 14976 In My Own Words: Padre Pio
8
+ 4200008 14976 CHIFFOLO ANTHONY
9
+ 4000009 14976 Y000000000303408635440000100000000000000000000550000000
10
+ 4100010 14976 Knowing God New Edn.With Study
11
+ 4200011 14976 PACKER J I
12
+ 4000012 14976 Y000000000403409111580000100000000000000000000550000000
13
+ 4100013 14976 Everyone I See Is Luckier Than
14
+ 4200014 14976 BEVAN CLARE
15
+ 4000015 14976 Y000000000503409111740000100000000000000000000550000000
16
+ 4100016 14976 I Want To Shout And Stamp Abou
17
+ 4200017 14976 MITTON TONY
18
+ 4000018 14976 Y000000000605670406230000100000000000000000000550000000
19
+ 4100019 14976 Gnostics :Identifying an Ancie
20
+ 4200020 14976 LOGAN, ALASTAIR
21
+ 4000021 14976 Y000000000705670875140000100000000000000000000550000000
22
+ 4100022 14976 Mary For All Christians
23
+ 4200023 14976 MACQUARRIE J
24
+ 4000024 14976 Y000000000806425620750000100000000000000000000550000000
25
+ 4100025 14976 Forgiveness and Other Acts of
26
+ 4200026 14976 DOWRICK STEPHANIE
27
+ 4000027 14976 Y000000000907333174560000100000000000000000000550000000
28
+ 4100028 14976 Honey and The Fires: Ancient s
29
+ 4200029 14976 PULVERS ROGER & ALICE
30
+ 4000030 14976 Y000000001007333179600000100000000000000000000550000000
31
+ 4100031 14976 Mummy Book
32
+ 4200032 14976 PARR TODD
33
+ 4000033 14976 Y000000001107333198230000100000000000000000000550000000
34
+ 4100034 14976 So Many Selves
35
+ 4200035 14976 CAREY GABRIELLE
36
+ 4000036 14976 Y000000001207336159880000100000000000000000000550000000
37
+ 4100037 14976 New Glucose Revolution Pocket
38
+ 4200038 14976 MILLET ET AL
39
+ 4000039 14976 Y000000001307475413530000100000000000000000000550000000
40
+ 4100040 14976 Achieving Emotional Literacy
41
+ 4200041 14976 STEINER CLAUDE
42
+ 4000042 14976 Y000000001407499210990000100000000000000000000550000000
43
+ 4100043 14976 How Meditation Heals
44
+ 4200044 14976 HARRISON ERIC
45
+ 4000045 14976 Y000000001507499242170000100000000000000000000550000000
46
+ 4100046 14976 Other Side and Back :A Psychic
47
+ 4200047 14976 BROWNE SYLVIA
48
+ 4000048 14976 Y000000001607502213130000100000000000000000000550000000
49
+ 4100049 14976 I'm Worried :Your Emotions Pb
50
+ 4200050 14976 MOSES BRIAN
51
+ 4000051 14976 Y000000001707502213210000100000000000000000000550000000
52
+ 4100052 14976 It's Not Fair :Your Feelings P
53
+ 4200053 14976 MOSES BRIAN
54
+ 4000054 14976 Y000000001807538205440000100000000000000000000550000000
55
+ 4100055 14976 Memory and Identity :Personal
56
+ 4200056 14976 PAUL JOHN II
57
+ 4000057 14976 Y000000001908192198190000100000000000000000000550000000
58
+ 4100058 14976 Gift of Faith (Morehouse) :Sho
59
+ 4200059 14976 NEFF LAVONNE
60
+ 4000060 14976 Y0000000020082646520X0000200000000000000000000550000000
61
+ 4100061 14976 Pastoral Prayers
62
+ 4200062 14976 DEADMAN R
63
+ 4000063 14976 Y000000002108264685000000200000000000000000000550000000
64
+ 4100064 14976 Dignity of Difference P/B: How
65
+ 4200065 14976 SACKS JONATHAN
66
+ 4000066 14976 Y000000002208264733770000100000000000000000000550000000
67
+ 4100067 14976 Celebrating Life :Finding happ
68
+ 4200068 14976 SACKS JONATHAN
69
+ 4000069 14976 Y000000002308264748100000100000000000000000000550000000
70
+ 4100070 14976 From Optimism to Hope
71
+ 4200071 14976 SACKS JONATHAN
72
+ 4000072 14976 Y000000002408264785570000100000000000000000000550000000
73
+ 4100073 14976 Persistence of Faith :Religion
74
+ 4200074 14976 JONATHAN SACKS
75
+ 4000075 14976 Y000000002508264794210000100000000000000000000550000000
76
+ 4100076 14976 Shape of the Liturgy New Ed.
77
+ 4200077 14976 DIX GREGORY
78
+ 4000078 14976 Y000000002608601242580000200000000000000000000550000000
79
+ 4100079 14976 Essence of Prayer
80
+ 4200080 14976 BURROWS RUTH
81
+ 4000081 14976 Y000000002715633841400000100000000000000000000550000000
82
+ 4100082 14976 Theological Perspectives On Go
83
+ 4200083 14976 MILBANK & WRD
84
+ 4000084 14976 Y000000002817411479210000200000000000000000000550000000
85
+ 4100085 14976 David Suzuki
86
+ 4200086 14976 SUZUKI DAVID
87
+ 4000087 14976 Y000000002917411484640000100000000000000000000550000000
88
+ 4100088 14976 Holism and Complementary Medic
89
+ 4200089 14976 DI STEFANO VINCENT
90
+ 4000090 14976 Y000000003018418129270000100000000000000000000550000000
91
+ 4100091 14976 Personal power animals :for gu
92
+ 4200092 14976 GAUDING MADONNA
93
+ 4000093 14976 Y000000003118644894130000100000000000000000000550000000
94
+ 4100094 14976 Earth Time
95
+ 4200095 14976 SUZUKI DAVID
96
+ 4000096 14976 Y000000003218650838440000100000000000000000000550000000
97
+ 4100097 14976 Facing The Fifties
98
+ 4200098 14976 O'CONNOR PETER
99
+ 4000099 14976 Y000000003318650891760000100000000000000000000550000000
100
+ 4100100 14976 Long Way from Rome A: Why the
101
+ 4200101 14976 MCGILLION CHRIS (ED)
102
+ 4000102 14976 Y000000003418891089600000100000000000000000000550000000
103
+ 4100103 14976 14 Presentations for Fall (God
104
+ 4200104 14976 BERRYMAN JEROME W
105
+ 4000105 14976 Y000000003518891089790000100000000000000000000550000000
106
+ 4100106 14976 20 Presentations for Winter (G
107
+ 4200107 14976 BERRYMAN JEROME W
108
+ 4000108 14976 Y000000003618891089870000100000000000000000000550000000
109
+ 4100109 14976 20 Presentations for Spring (G
110
+ 4200110 14976 BERRYMAN JEROME W
111
+ 5000111 14976 0011000000000360000000040
112
+ 90001120000000000036000010000000040000010000100000000000003600001000000000000000