rbook-bisac 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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