~srushe/validates_as_isbn_old

d286c1115f795f9134c33f36abbe5cf8a8a7d8a1 — Steve Rushe 13 years ago
First commit to repository (version 0.2).
A  => CHANGELOG +11 -0
@@ 1,11 @@
0.2 (8th March 2008)
--------------------

  * Update to remove require_gem.
  * Changed format of CHANGELOG.
  * First public release.

0.1 (30th June 2007)
--------------------

  * Initial version.
\ No newline at end of file

A  => README +28 -0
@@ 1,28 @@
ValidatesAsIsbn
===============

This Ruby on Rails plugin implements an ActiveRecord validation helper
called validates_as_isbn. The helper validates that the string provided
is a valid ISBN. It works on both the original 10-character ISBN, or
the newer 13-character version.

Installation
------------

ruby script/plugin install http://svn.deeden.co.uk/validates_as_isbn

Full documentation on script/plugin can be obtained by invoking the plugin
script with no arguments:

ruby script/plugin

You may need to restart your webserver in order to load the plugin files.

Usage
-----

In your model file do something like:

class MyClass < ActiveRecord::Base
  validates_as_isbn :isbn
end
\ No newline at end of file

A  => Rakefile +22 -0
@@ 1,22 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

desc 'Default: run unit tests.'
task :default => :test

desc 'Test the validates_as_isbn plugin.'
Rake::TestTask.new(:test) do |t|
  t.libs << 'lib'
  t.pattern = 'test/**/*_test.rb'
  t.verbose = true
end

desc 'Generate documentation for the validates_as_isbn plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
  rdoc.rdoc_dir = 'rdoc'
  rdoc.title    = 'ValidatesAsIsbn'
  rdoc.options << '--line-numbers' << '--inline-source'
  rdoc.rdoc_files.include('README')
  rdoc.rdoc_files.include('lib/**/*.rb')
end

A  => data/invalid.txt +23 -0
@@ 1,23 @@
0300088745
352805747
3932588745
3827317184
3898641148
393258853X
3446215910
3333336363
3446194345
3446216347
34462
344621
3446216
00071039
000710559
0007111291
0007612630
0020199853
0020209855
0020345546
0020389657
abcdefghij
xxxxxxxxxx
\ No newline at end of file

A  => data/valid_isbn_10.txt +997 -0
@@ 1,997 @@
0000000000
0000000027
0000000035
0000000043
0000000051
000000006X
0000000078
0000000086
0000000094
0000000132
0000000329
0100000010
0101021429
0101049927
0101339224
010138890X
0101390408
0101453426
0101474105
0101644922
0102645981
0103000003
0200000004
0200000039
0200000063
0200000071
0200000616
020000106X
0200001310
0200001337
0200001515
0200714252
0200714848
0300000014
0300000022
0300000049
0300000138
0300000170
0300000197
0300000316
0300000375
0300000383
0300000391
030010006X
0400377012
0400649861
0401101746
0401254968
0401819337
0402122119
0402123115
040212491X
0402140184
0402141210
0403001293
050001034X
0500010587
0500010625
0500010633
0500010676
0500010684
0500010862
0500010870
0500010919
0500010951
0500011648
0600000079
0600000370
0600000427
0600000532
0600000761
060000077X
0600000788
0600000796
0600000834
0600000893
0600000915
0700001530
0700001557
0700001859
070000193X
0700012915
0700113622
0700222413
0700222634
0700222898
0700222901
0700223096
0800000080
0800100891
0800100964
0800101529
080010255X
0800103106
0800103688
0800106105
0800106903
0800109872
0800141687
0900000015
0900000066
0900000139
090000021X
0900000457
0900000821
0900000848
0900000902
0900001224
0900001283
0900009020
1000001563
1001000145
1011561239
1015010830
1029289506
1030722382
1050550277
106277101X
1064602061
111100000X
1111028001
1111046204
1111050988
1111096112
1111099553
1111261350
1111411727
1111549745
1111734186
1112330089
1210354489
1224565304
1225019710
1225703603
1231018992
1245002341
1298611067
1299004768
1299231845
129926218X
1308005830
1314567845
1326584944
1334132658
1338668072
1340021021
1345020147
1345022816
1345023413
1375430939
139915110X
1400000289
1400000300
1400000327
1400000696
1400000785
1400000823
1400000831
1400000904
1400000912
1400001188
140000229X
150001060X
1500506575
1503524450
1505838738
1506007724
1518789552
1520003307
1538101173
1550010506
1550010689
1550010921
1600010415
1600010423
160001044X
1600010458
1600010474
1600019927
1600021131
1600050042
1600060226
1600060250
1600090079
1700000829
1701130092
171450042X
1718108001
1718112440
1723158518
1739425057
1740092384
1740092643
1740092686
1740092805
1800788312
1805210076
1805290223
1807281965
1816043710
1830280058
1840000031
184000004X
1840000279
1840000414
1840000627
1900007029
1900007223
1900027011
1900029030
1900032082
1900032104
1900032147
1900032155
1900032368
1900032856
190012100X
2000021891
2000021905
2000021913
2000022669
200002422X
2000025196
2000026877
2008052060
2010003454
2010005392
2010021398
2100000918
210000302X
2100012851
2100014315
2100016520
2100016814
2100017276
2100020277
2100026003
2100026259
2100049682
2200012861
2200013728
2200014155
2200014317
2200016026
2200016522
2200016794
2200017049
2200019343
2200019890
220021622X
2300131196
2310011452
2350040178
2350050009
2350120163
2350120635
2350121054
2350180050
2350210197
2350781291
235078133X
2402559721
2441659790
2493435117
2495000258
2495000266
2495005209
2495010725
249501075X
2495010954
2501000307
2501000560
2501000595
2501001311
2501001516
2501002059
2501002644
2501002822
250100342X
2501005473
2501006488
2600001832
2600002057
2600005145
2600006893
2600007520
2600009221
2600010998
2601002654
260103131X
2603007246
2603010859
2700001117
2700001974
2700002369
2700002733
2700003160
2700010566
2700011015
2700011058
2700011422
270001197X
2700019121
2800100060
2800100087
2800100095
2800100133
2800100141
280010015X
2800100168
2800100176
2800100184
2800100249
2800100362
2900130808
2900200067
2900269016
2900269075
290029715X
2900297192
2900297214
2900319331
2900339049
2901076343
2901422330
3000004270
3000005447
3000005765
300001375X
3000024492
3000024816
3000026231
3000026304
3000058338
3000060073
3000087079
3100001362
3100002377
3100003373
3100004205
3100005139
3100006208
3100011066
310001524X
3100015401
3100044134
3100108310
3200002867
3200003898
3200006021
3201000426
3201009385
3201009512
3201010774
3201010960
3201015393
3203750139
320375875X
3303152438
3305000740
3305001216
3305002557
3309001695
3310001954
3310002349
3310002942
331000306X
3310003221
3310007103
3401002031
3401002112
3401002236
3401002325
340100252X
3401002597
3401002600
3401002619
3401011944
3401012428
3401012703
3500245404
3501001002
3501003099
3501004001
3501011288
3501011547
3501012055
3501013736
3501013973
3501014260
350101452X
3600301004
3600301063
3601004836
3602145549
360214691X
3607000018
3608206000
3608206205
3608860037
3608896201
3608911332
3700102569
370010331X
3700103417
3700103743
3700104642
3700107854
3700119976
3700121210
3700122241
3700126735
3700129238
3800012103
3800014327
3800014904
3800015501
3800016036
3800016125
3800016362
3800016559
3800020238
3800020440
380002053X
3900036349
3900051127
390010042X
3900134014
3900134251
3900134286
3900134340
3900134448
3900134472
3900134715
3900252173
4000002538
4000013815
4000020153
4000029010
4000033344
4000041576
4000072749
4000224417
400023403X
4000801112
4001110121
4101001014
4101001022
4101001235
4101001340
4101001359
4101001413
4101001421
410100143X
4101005036
4101005087
4101010048
4245008350
4250085104
4250270157
4250980138
4253010695
4253059759
4253071112
4253071163
4253071171
425307118X
4253071236
4306043479
4306043886
4306044211
4306044297
430604436X
4306044734
4306050173
4306051625
4306093778
4309015050
4309408362
4400002780
4400013006
4400107825
4400481212
440086250X
4401013468
4401015584
4401611527
4401618009
4402052491
4403500293
4501321601
4501540109
4502335215
450369992X
4517234150
4524211756
4524214232
4527002074
4529027813
4529032108
4529039927
4620305774
4620792330
4621038656
4621039733
4621045199
4621052055
4621074687
4622039702
4635062171
463506302X
4636174828
472746621X
4750311235
4750316202
4750319600
4750587028
4753413071
4756102107
4756118089
4756132243
4756702716
4756704034
4803020000
4803404089
4804033173
4804611061
4804611347
4804612262
4804750215
4805301066
480530152X
4805303174
4805303948
4900090891
4900105295
4900211230
4900211508
4900211516
4900211524
4900211532
4900422029
4900480053
490050212X
4900737097
5010004488
5010004550
5010004984
5010005131
501001114X
5010011557
5010019752
5010019833
5010029529
5010032996
5010042665
5100000317
510360745X
5103683954
5105453956
5110014000
5120001378
5120004725
5130218841
5150002623
5170044399
5170045972
5200000181
5200000386
5200000467
5200000483
520000067X
5200001420
5200002044
5200002249
5200003318
5200003512
5200007135
5300001333
5300005568
5300005851
5300013587
5300016276
5300020494
5300022209
530101224X
5303000702
5306002900
5352000125
5410001591
5415004637
5415004734
541500677X
5415012990
5415014268
5415014306
5415014322
5415014543
5415015795
5420015439
5504400651
5504401267
5504401313
550440178X
5504402964
5511003115
5511006610
5530835112
5550007738
5550031906
5550033089
5605000039
5610004276
5615008490
5628020583
5640014105
5648007388
5670705157
5670705661
5691003054
5699005862
569902784X
5700000059
5700001292
5700001314
5700002078
5700002825
570000299X
5702702840
5702704916
5702709497
5704209173
5710756121
580150169X
5801571183
5802600209
5804400878
5804600605
5806700534
5812201137
5815300012
5815900400
5815900591
5815900966
5900033422
590003612X
5900118053
5900134016
5900161064
5900310019
5900370127
5900939235
5900959201
5901229010
5901250028
6000006756
6000020724
6000020740
6000058152
6000067291
6000091133
6000109199
6000111177
6000120915
6000137338
600014329X
6101644340
6105372906
610621350X
6125010392
6125030474
6125038831
6161834669
6300157563
6300174921
6300181286
6300181367
6300182320
630018403X
6300185095
6300214052
6300214699
6300215814
6300217388
6441660040
6451152749
6694701292
6706705535
6710000990
6712040418
6712118972
671221078X
6714003501
6714003994
6714005997
6714006993
6718204439
6841256840
684130750X
6848010863
6848181838
7010003718
7010085722
7018980917
7018981069
7018981514
702001271X
7020012876
7020025943
7020031331
7020043275
7020044360
7100000335
7100000432
7100000440
7100000904
7100000939
7100000947
7100003113
7100007038
710001056X
7100026016
7101012191
7200002194
7200010308
7200015830
7200032271
7200046566
7204009932
7205048907
7207034393
7213007599
721400528X
7215964965
7300066763
7300066909
7301002351
7301015054
7301018940
7301026188
7301027737
7301028156
7301035195
730202832X
7356150042
7410959444
7418310013
7472016772
7500101430
7500101449
7500101473
7500101481
7500105568
7500302312
7500404204
7500407726
750061070X
7500612575
7501555087
7629437404
7632401466
7671336459
7673263262
7673499967
7674356663
7770020557
777710473X
7777777238
7777777289
7778006995
7780038181
7800029921
7800030180
7800050076
7800050807
7800050963
7800051102
7800051145
7800051188
780005134X
7800053229
7800055574
7900363416
7900730001
790128000X
7901280077
7910904444
8000000830
8000002817
800000450X
8000005875
8000006413
8000007584
8000008742
8000014106
8004191649
8004239978
8020004181
8101867163
8109005640
8109010725
8110724779
8114675357
8120202538
8120300114
8120301722
812030585X
8120306686
8120310071
8200013146
8200013677
8200013871
8200014924
8200016420
8200017273
8200019608
8200028585
8200029182
8200032159
824211272X
8300006575
8300008659
8300022627
8301001623
8301032391
8301037326
8301057750
830109530X
8301110694
8301114878
8301135042
840001801X
8400036387
8400051505
8400056906
8400058704
8400065638
8400071212
8400076591
8400078683
8401012090
8401012449
8500000686
8500000708
8500001240
8500001267
8500001712
850000178X
8500001844
8500001895
8500003901
8500006889
8500007923
8601011349
8601012086
8601015115
8601016413
8603001308
8603003602
8603990360
8603990581
8607003097
8607013564
861115584X
8700000612
8700001945
8700002216
8700007129
8700014044
8700019127
8700029270
8700032913
8700042358
8700146021
8800454771
8800811418
8800852637
8800852750
8800853579
8800860613
8800861024
8801020422
880102729X
8802050775
8804083956
8900005456
8900049011
8900070134
8900820745
890105180X
8901056372
8901057069
8901732173
8908167358
8930081150
8931003897
9000001420
9000009189
9000013968
9000014336
9000019451
9000020867
900002210X
9000026083
9000031745
9001093922
9001121004
9100100196
9100100315
9100100390
9100100447
9100100471
9100100528
9100100692
9100100773
9100100919
9100101044
910114605X
9200115969
9200129382
9200943055
9210001346
9210020251
9210120507
9210120604
9211002990
9211007933
9211008638
922101598X
9309192380
9310100648
9312073516
9316881404
9329700292
9343200129
934641684X
9350515067
9394583815
9408180003
941895429X
9423340180
9488144076
9500080176
9500120275
9500170000
9500201259
9500258994
9500262967
9500263432
9500263661
950026370X
9500274493
9500285118
9600105480
9600300577
9600310599
9600329915
9600339058
9600400423
9600400512
9600404801
960040481X
9600404836
9600404844
9700300331
9700302555
9700313662
970040174X
9700500969
9700502023
9700502384
9700502740
9700503046
9700503577
9700513378
9800100814
9800101063
9800101799
9800103708
980010397X
9800105891
9800110852
9800737170
9802040126
9802081515
9802222267
9900003292
9900052110
9900730879
9900730925
9900733207
990073369X
9901000033
9901981476
9907659754
9914537308
9949101131

A  => data/valid_isbn_13.txt +27 -0
@@ 1,27 @@
9780571232796
9781845293079
9780451460899
9780370329215
9780553384079
9780007219155
9780575079144
9780575079120
9780434014088
9780739439708
9780575079410
9780007148929
9780007232178
9781594146015
9780575077836
9780007252237
9780571236596
9780375838583
9780958685696
9780575076952
9781741146585
9781741146608
9781741146592
9781741140811
9780330422451
9781862916326
9780979534904
\ No newline at end of file

A  => init.rb +1 -0
@@ 1,1 @@
require 'validates_as_isbn'
\ No newline at end of file

A  => lib/validates_as_isbn.rb +53 -0
@@ 1,53 @@
module ISBN
  class Validations  
    def self.clean(isbn)
      isbn.upcase.gsub(/[^0-9X]/, '')
    end

    def self.looks_valid?(isbn)
      return /^(?:\d{9}[0-9X]|97[89]\d{10})$/.match(isbn)
    end

    def self.is_valid?(isbn)
      isbn = clean(isbn)
      return false unless looks_valid?(isbn)

      bits = isbn.split(//)
      sum = 0
      if isbn.length == 10
        10.downto(2) do |pos|
          sum += pos * bits.shift.to_i
        end
        checksum = bits[0] == 'X' ? 10 : bits[0].to_i
        return ((sum + checksum) % 11) == 0
      else
        0.step(10, 2) do |pos|
          sum += bits[pos].to_i
        end
        1.step(11, 2) do |pos|
          sum += bits[pos].to_i * 3
        end
        return ((sum + bits[12].to_i) % 10) == 0
      end
    end 
  end
end

module ActiveRecord
  module Validations
    module ClassMethods
      def validates_as_isbn(*attr_names)
        configuration = {
          :message  => 'is not a valid ISBN'
        }
        configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)

        validates_each(attr_names, configuration) do |record, attr_name, value|
          unless ISBN::Validations.is_valid?(value)
            record.errors.add(attr_name, configuration[:message])
          end
        end
      end
    end
  end
end
\ No newline at end of file

A  => test/validates_as_isbn_test.rb +59 -0
@@ 1,59 @@
require 'test/unit'

begin
  require File.dirname(__FILE__) + '/../../../../config/boot'
  require 'active_record'
  require 'validates_as_isbn'
rescue LoadError
  require 'rubygems'
  gem 'activerecord'
  require 'active_record'
  require File.dirname(__FILE__) + '/../lib/validates_as_isbn'
end

class TestRecord < ActiveRecord::Base
  def self.columns; []; end
  attr_accessor :isbn
  validates_as_isbn :isbn
end

class ValidatesAsIsbnTest < Test::Unit::TestCase
  # Check that valid 10-digit isbns are validated as such.
  def test_should_validate_10_digit_isbn
    isbns = IO.readlines(File.dirname(__FILE__) + '/../data/valid_isbn_10.txt')

    isbns.each do |isbn|
      isbn.chomp!
      assert TestRecord.new(:isbn => isbn).valid?, "#{isbn} should be valid."
      assert TestRecord.new(:isbn => isbn.downcase).valid?, "#{isbn.downcase} should be valid."
      assert TestRecord.new(:isbn => isbn.gsub('[^0-9X]', '')).valid?, "#{isbn.gsub('[^0-9X]', '')} should be valid."
      assert TestRecord.new(:isbn => isbn.downcase.gsub('[^0-9X]', '')).valid?, "#{isbn.downcase.gsub('[^0-9X]', '')} should be valid."
    end
  end

  # Check that valid 13-digit isbns are validated as such.
  def test_should_validate_13_digit_isbn
    isbns = IO.readlines(File.dirname(__FILE__) + '/../data/valid_isbn_13.txt')

    isbns.each do |isbn|
      isbn.chomp!
      assert TestRecord.new(:isbn => isbn).valid?, "#{isbn} should be valid."
      assert TestRecord.new(:isbn => isbn.downcase).valid?, "#{isbn.downcase} should be valid."
      assert TestRecord.new(:isbn => isbn.gsub('[^0-9X]', '')).valid?, "#{isbn.gsub('[^0-9X]', '')} should be valid."
      assert TestRecord.new(:isbn => isbn.downcase.gsub('[^0-9X]', '')).valid?, "#{isbn.downcase.gsub('[^0-9X]', '')} should be valid."
    end
  end

  # Check that invalid isbns are validated as such.
  def test_should_not_validate
    isbns = IO.readlines(File.dirname(__FILE__) + '/../data/invalid.txt')

    isbns.each do |isbn|
      isbn.chomp!
      assert !TestRecord.new(:isbn => isbn).valid?, "#{isbn} should be invalid."
      assert !TestRecord.new(:isbn => isbn.downcase).valid?, "#{isbn.downcase} should not be invalid."
      assert !TestRecord.new(:isbn => isbn.gsub('[^0-9X]', '')).valid?, "#{isbn.gsub('[^0-9X]', '')} should be invalid."
      assert !TestRecord.new(:isbn => isbn.downcase.gsub('[^0-9X]', '')).valid?, "#{isbn.downcase.gsub('[^0-9X]', '')} should be invalid."
    end
  end
end