library_stdnums 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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Bill Dueber
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = library_stdnums
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Bill Dueber. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "library_stdnums"
8
+ gem.summary = %Q{Normalize and compute checkdigits for ISBN, ISSN, and LCCN}
9
+ gem.description = %Q{Normalization and checksum computation for ISBN (10 and 13), ISSN, and LCCN}
10
+ gem.email = "bill@dueber.com"
11
+ gem.homepage = "http://github.com/billdueber/library_stdnums"
12
+ gem.authors = ["Bill Dueber"]
13
+ gem.add_development_dependency "bacon", ">= 0"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.pattern = 'spec/**/*_spec.rb'
26
+ spec.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |spec|
32
+ spec.libs << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :spec => :check_dependencies
43
+
44
+ task :default => :spec
45
+
46
+ begin
47
+ require 'yard'
48
+ YARD::Rake::YardocTask.new
49
+ rescue LoadError
50
+ task :yardoc do
51
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
52
+ end
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,130 @@
1
+ module StdNum
2
+
3
+ STDNUMPAT = /^.*?(\d[\d\-]+[xX]?)/
4
+
5
+ # Extract the most likely looking number from the string. This will be the first
6
+ # string of digits-and-hyphens-and-maybe-a-trailing-X, with the hypens removed
7
+ def self.extractNumber str
8
+ match = STDNUMPAT.match str
9
+ return nil unless match
10
+ return match[1].gsub(/\-/, '').upcase
11
+ end
12
+
13
+
14
+
15
+ module ISBN
16
+
17
+ # Compute check digits for 10 or 13-digit ISBNs. See algorithm at
18
+ # http://en.wikipedia.org/wiki/International_Standard_Book_Number
19
+ # @param [String] isbn The ISBN (we'll try to clean it up if possible)
20
+ # @return [String] the one-character checkdigit
21
+ def self.checkdigit isbn
22
+ isbn = StdNum.extractNumber isbn
23
+ size = isbn.size
24
+ return nil unless size == 10 or size == 13
25
+ checkdigit = 0
26
+ if size == 10
27
+ digits = isbn[0..8].split(//).map {|i| i.to_i}
28
+ (1..9).each do |i|
29
+ checkdigit += digits[i-1] * i
30
+ end
31
+ checkdigit = checkdigit % 11
32
+ return 'X' if checkdigit == 10
33
+ return checkdigit.to_s
34
+ else # size == 13
35
+ digits = isbn[0..11].split(//).map {|i| i.to_i}
36
+ 6.times do
37
+ checkdigit += digits.shift
38
+ checkdigit += digits.shift * 3
39
+ end
40
+ return (10 - (checkdigit % 10)).to_s
41
+ end
42
+ end
43
+
44
+ # Check to see if the checkdigit is correct
45
+ # @param [String] isbn The ISBN (we'll try to clean it up if possible)
46
+ # @return [Boolean] Whether or not the checkdigit is correct
47
+ def self.valid? isbn
48
+ isbn = StdNum.extractNumber isbn
49
+ size = isbn.size
50
+ return false unless (size == 10 or size == 13)
51
+ return isbn[-1..-1] == self.checkdigit(isbn)
52
+ end
53
+
54
+ # To convert to an ISBN13, throw a '978' on the front and
55
+ # compute the checkdigit
56
+ # We leave 13-digit numbers alone, figuring they're already ok,
57
+ # and return nil on anything that's not the right length
58
+ # @param [String] isbn The ISBN (we'll try to clean it up if possible)
59
+ # @return [String] The converted 13-character ISBN, nil if something looks wrong, or whatever was passed in if it already looked like a 13-digit ISBN
60
+ def self.convert_to_13 isbn
61
+ isbn = StdNum.extractNumber isbn
62
+ size = isbn.size
63
+ return isbn if size == 13
64
+ return nil unless size == 10
65
+
66
+ prefix = '978' + isbn[0..8]
67
+ return prefix + self.checkdigit(prefix + '0')
68
+ end
69
+
70
+
71
+ # Convert to 10 if it's 13 digits and the first three digits are 978.
72
+ # Pass through anything 10-digits, and return nil for everything else.
73
+ # @param [String] isbn The ISBN (we'll try to clean it up if possible)
74
+ # @return [String] The converted 10-character ISBN, nil if something looks wrong, or whatever was passed in if it already looked like a 10-digit ISBN
75
+ def self.convert_to_10 isbn
76
+ isbn = StdNum.extractNumber isbn
77
+ size = isbn.size
78
+ return isbn if size == 10
79
+ return nil unless size == 13
80
+ return nil unless isbn[0..2] == '978'
81
+
82
+ prefix = isbn[3..11]
83
+ return prefix + self.checkdigit(prefix + '0')
84
+ end
85
+ end
86
+
87
+ module ISSN
88
+
89
+ # Compute the checkdigit of an ISSN
90
+ # @param [String] issn The ISSN (we'll try to clean it up if possible)
91
+ # @return [String] the one-character checkdigit
92
+ def self.checkdigit issn
93
+ issn = StdNum.extractNumber issn
94
+ return nil unless issn.size == 8
95
+ digits = issn[0..6].split(//).map {|i| i.to_i}
96
+ checkdigit = 0
97
+ (0..6).each do |i|
98
+ checkdigit += digits[i] * (8 - i)
99
+ end
100
+ checkdigit = checkdigit % 11
101
+ return 0 if checkdigit == 0
102
+ checkdigit = 11 - checkdigit
103
+ return 'X' if checkdigit == 10
104
+ return checkdigit.to_s
105
+ end
106
+
107
+
108
+ end
109
+
110
+ module LCCN
111
+ # Normalize based on data at http://www.loc.gov/marc/lccn-namespace.html#syntax
112
+ # @param [String] str The LCCN to normalize
113
+ # @return [String] the normalized LCCN, or nil if it looks malformed
114
+ def self.normalize str
115
+ str.gsub!(/\s/, '')
116
+ str.gsub!(/\/.*$/, '')
117
+ if str =~ /^(.*?)\-(.+)/
118
+ pre = $1
119
+ post = $2
120
+ return nil unless post =~ /^\d+$/ # must be all digits
121
+ return "%s%06d" % [pre, post]
122
+ end
123
+ return str
124
+ end
125
+ end
126
+
127
+
128
+ end
129
+
130
+
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Extract" do
4
+ it "should leave a number alone" do
5
+ StdNum.extractNumber('123456').should.equal '123456'
6
+ end
7
+
8
+ it "should skip leading and trailing crap" do
9
+ StdNum.extractNumber(' 12345 (online)').should.equal '12345'
10
+ end
11
+
12
+ it "should allow hyphens" do
13
+ StdNum.extractNumber(' 1-234-5').should.equal '12345'
14
+ end
15
+
16
+ it "should return nil on a non-match" do
17
+ StdNum.extractNumber('bill dueber').should.equal nil
18
+ end
19
+
20
+ it "should allow a trailing X" do
21
+ StdNum.extractNumber('1-234-5-X').should.equal '12345X'
22
+ end
23
+
24
+ it "should upcase any trailing X" do
25
+ StdNum.extractNumber('1-234-x').should.equal '1234X'
26
+ end
27
+
28
+ it "only allows a single trailing X" do
29
+ StdNum.extractNumber('1234-X-X').should.equal '1234X'
30
+ end
31
+
32
+ end
33
+
34
+
35
+ describe "ISBN" do
36
+ it "computes 10-digit checksum" do
37
+ StdNum::ISBN.checkdigit('0-306-40615-X').should.equal '2'
38
+ end
39
+
40
+ it "correctly uses X for checksum" do
41
+ StdNum::ISBN.checkdigit('061871460X').should.equal 'X'
42
+ end
43
+
44
+ it "finds a zero checkdigit" do
45
+ StdNum::ISBN.checkdigit('0139381430').should.equal '0'
46
+ end
47
+
48
+ it "computes 13-digit checksum" do
49
+ StdNum::ISBN.checkdigit('9780306406157').should.equal '7'
50
+ end
51
+
52
+ it "finds a good number valid" do
53
+ StdNum::ISBN.valid?('9780306406157').should.equal true
54
+ end
55
+
56
+ it "finds a bad number false" do
57
+ StdNum::ISBN.valid?('9780306406154').should.equal false
58
+ end
59
+
60
+ it "returns nil when computing checksum for bad ISBN" do
61
+ StdNum::ISBN.checkdigit('12345').should.equal nil
62
+ end
63
+
64
+ it "converts 10 to 13" do
65
+ StdNum::ISBN.convert_to_13('0-306-40615-2').should.equal '9780306406157'
66
+ end
67
+
68
+ it "passes through 13 digit number instead of converting to 13" do
69
+ StdNum::ISBN.convert_to_13('9780306406157').should.equal '9780306406157'
70
+ end
71
+
72
+ it "converts 13 to 10" do
73
+ StdNum::ISBN.convert_to_10('978-0-306-40615-7').should.equal '0306406152'
74
+ end
75
+
76
+ end
77
+
78
+
79
+
80
+ describe 'ISSN' do
81
+ it "computes checksum" do
82
+ StdNum::ISSN.checkdigit('0378-5955').should.equal '5'
83
+ end
84
+ end
85
+
86
+
87
+ describe 'LCCN' do
88
+
89
+ # Tests take from http://www.loc.gov/marc/lccn-namespace.html#syntax
90
+ test = {
91
+ "n78-890351" => "n78890351",
92
+ "n78-89035" => "n78089035",
93
+ "n 78890351 " => "n78890351",
94
+ " 85000002 " => "85000002",
95
+ "85-2 " => "85000002",
96
+ "2001-000002" => "2001000002",
97
+ "75-425165//r75" => "75425165",
98
+ " 79139101 /AC/r932" => "79139101"
99
+ }
100
+
101
+ test.each do |k, v|
102
+ it "normalizes #{k}" do
103
+ StdNum::LCCN.normalize(k.dup).should.equal v
104
+ end
105
+ end
106
+
107
+
108
+ end
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'bacon'
3
+ begin
4
+ require 'greeneggs'
5
+ rescue LoadError
6
+ end
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ require 'library_stdnums'
10
+
11
+ Bacon.summary_on_exit
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: library_stdnums
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
+ - Bill Dueber
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-13 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bacon
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: yard
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id002
49
+ description: Normalization and checksum computation for ISBN (10 and 13), ISSN, and LCCN
50
+ email: bill@dueber.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files:
56
+ - LICENSE
57
+ - README.rdoc
58
+ files:
59
+ - .document
60
+ - .gitignore
61
+ - LICENSE
62
+ - README.rdoc
63
+ - Rakefile
64
+ - VERSION
65
+ - lib/library_stdnums.rb
66
+ - spec/library_stdnums_spec.rb
67
+ - spec/spec_helper.rb
68
+ has_rdoc: true
69
+ homepage: http://github.com/billdueber/library_stdnums
70
+ licenses: []
71
+
72
+ post_install_message:
73
+ rdoc_options:
74
+ - --charset=UTF-8
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ hash: 3
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ required_rubygems_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
+ requirements: []
96
+
97
+ rubyforge_project:
98
+ rubygems_version: 1.3.7
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Normalize and compute checkdigits for ISBN, ISSN, and LCCN
102
+ test_files:
103
+ - spec/library_stdnums_spec.rb
104
+ - spec/spec_helper.rb