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 +27 -0
@@ 1,27 @@
+ValidatesAsIssn
+===============
+
+This Ruby on Rails plugin implements an ActiveRecord validation helper
+called validates_as_issn. The helper validates that the string provided
+is a valid ISSN.
+
+Installation
+------------
+
+ruby script/plugin install http://svn.deeden.co.uk/validates_as_issn/trunk
+
+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_issn :issn
+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_issn 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_issn plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'ValidatesAsIssn'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
A => data/invalid.txt +4 -0
@@ 1,4 @@
+1351-0109
+1351-01XX
+0963-2718
+1752-7016<
\ No newline at end of file
A => data/valid.txt +4 -0
@@ 1,4 @@
+1351-010X
+1534-0309
+0963-2719
+1752-7015<
\ No newline at end of file
A => init.rb +1 -0
@@ 1,1 @@
+require 'validates_as_issn'<
\ No newline at end of file
A => lib/validates_as_issn.rb +45 -0
@@ 1,45 @@
+module ISSN
+ class Validations
+ def self.clean(issn)
+ issn.upcase.gsub(/[^0-9X]/, '')
+ end
+
+ def self.looks_valid?(issn)
+ return /^\d{7}[0-9X]$/.match(issn)
+ end
+
+ def self.is_valid?(issn)
+ issn = clean(issn)
+ return false unless looks_valid?(issn)
+
+ bits = issn.split(//).collect { |b| b.to_i }
+ checksum = bits[7] == 'X' ? 10 : bits[7]
+
+ sum = 0
+ 8.downto(2) do |pos|
+ sum += pos * bits.shift
+ end
+
+ return checksum == (11 - (sum % 11)) % 10
+ end
+ end
+end
+
+module ActiveRecord
+ module Validations
+ module ClassMethods
+ def validates_as_issn(*attr_names)
+ configuration = {
+ :message => 'is not a valid ISSN'
+ }
+ configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
+
+ validates_each(attr_names, configuration) do |record, attr_name, value|
+ unless ISSN::Validations.is_valid?(value)
+ record.errors.add(attr_name, configuration[:message])
+ end
+ end
+ end
+ end
+ end
+end
A => test/validates_as_issn_test.rb +46 -0
@@ 1,46 @@
+require 'test/unit'
+
+begin
+ require File.dirname(__FILE__) + '/../../../../config/boot'
+ require 'active_record'
+ require 'validates_as_issn'
+rescue LoadError
+ require 'rubygems'
+ gem 'activerecord'
+ require 'active_record'
+ require File.dirname(__FILE__) + '/../lib/validates_as_issn'
+end
+
+class TestRecord < ActiveRecord::Base
+ def self.columns; []; end
+ attr_accessor :issn
+ validates_as_issn :issn
+end
+
+class ValidatesAsIssnTest < Test::Unit::TestCase
+ # Check that valid issns are validated as such.
+ def test_should_validate
+ issns = IO.readlines(File.dirname(__FILE__) + '/../data/valid.txt')
+
+ issns.each do |issn|
+ issn.chomp!
+ assert TestRecord.new(:issn => issn).valid?, "#{issn} should be valid."
+ assert TestRecord.new(:issn => issn.downcase).valid?, "#{issn.downcase} should be valid."
+ assert TestRecord.new(:issn => issn.gsub('[^0-9X]', '')).valid?, "#{issn.gsub('[^0-9X]', '')} should be valid."
+ assert TestRecord.new(:issn => issn.downcase.gsub('[^0-9X]', '')).valid?, "#{issn.downcase.gsub('[^0-9X]', '')} should be valid."
+ end
+ end
+
+ # Check that invalid issns are validated as such.
+ def test_should_not_validate
+ issns = IO.readlines(File.dirname(__FILE__) + '/../data/invalid.txt')
+
+ issns.each do |issn|
+ issn.chomp!
+ assert !TestRecord.new(:issn => issn).valid?, "#{issn} should be valid."
+ assert !TestRecord.new(:issn => issn.downcase).valid?, "#{issn.downcase} should be invalid."
+ assert !TestRecord.new(:issn => issn.gsub('[^0-9X]', '')).valid?, "#{issn.gsub('[^0-9X]', '')} should be invalid."
+ assert !TestRecord.new(:issn => issn.downcase.gsub('[^0-9X]', '')).valid?, "#{issn.downcase.gsub('[^0-9X]', '')} should be invalid."
+ end
+ end
+end