isbnify 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ce182ccf75b38d868b634282cbcc0daf145f0f9
4
+ data.tar.gz: 2db0e7a91ea2e89e09aab45d52a4cf7bae1982f9
5
+ SHA512:
6
+ metadata.gz: 0b4a6877ee5abcfa908d07f7f8c86272d047ed47c00ee0900ded4ecdddbdfc548dd3df47049cad775b8f20dfbe2262fbf09e68fa9e93d04a461b3918d3354acc
7
+ data.tar.gz: fa04c7d41f0c2f92171582c1a30795fe5c16d5a7da94be3ac3f0624abef598f5e879adb9c75eabc540d13b0356a7f96e03ab9d35fea13d8b7c95c2241293aa38
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .DS_Store
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ spec/cassettes
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1,2 @@
1
+ rvm use 2.0.0-p247
2
+ rvm --create gemset use $(basename $(pwd))
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in isbnify.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Christoph Seydel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ isbnify
2
+ =======
3
+
4
+ hyphenate, verify and create ISBN13 numbers
5
+
6
+ ### Functionality
7
+ * creates valid ISBN13 fake numbers, allready hyphinated
8
+ * check ISBN13 validity of a given number
9
+ * hyphinate valid ISBN13 number
10
+
11
+
12
+ ### Implementation
13
+ Include Isbnify into your controller and use the helper actions or call object Isbnify::ISBN class actions.
14
+
15
+
16
+ ### Functions
17
+
18
+ ``create_isbn`` or ``Isbnify::ISBN.create_isbn``
19
+
20
+ Returns a random, yet valid fake ISBN allready hyphinated.
21
+
22
+ ``hyphinate_isbn(String)`` or ``Isbnify::ISBN.hyphinate_isbn(String)``
23
+
24
+ Returns hyphinated ISBN or error message as String.
25
+
26
+ ``valid_isbn?(String)`` or ``Isbnify::ISBN.valid_isbn?(String)``
27
+
28
+ Returns true or false, whether a given ISBN is valid or not. It can be passed as hyphinated ISBN or not.
29
+
30
+
31
+ ### Useable method calls
32
+
33
+ ```
34
+ class ApplicationController < ActionController
35
+ include Isbnify
36
+
37
+ def some_method
38
+ hyphinate_isbn("9783404166695") # returns "978-3-404-16669-5"
39
+ Isbnify::ISBN.hyphinate_isbn("9783404166695") # returns "978-3-404-16669-5"
40
+ end
41
+
42
+ def some_other_method
43
+ valid_isbn?("9783404166695") # returns true
44
+ Isbnify::ISBN.valid_isbn?("9783404166695") # returns true
45
+ end
46
+
47
+ def some_last_method
48
+ create_isbn # returns random well-formed isbn number
49
+ Isbnify::ISBN.create_isbn # returns random well-formed isbn number
50
+ end
51
+
52
+ end
53
+ ```
54
+
55
+ ### Ruby Versions
56
+
57
+ This gem was developed and tested with versions 1.9.3 and 2.0.0
58
+
59
+ ### Contributing
60
+
61
+ 1. Fork it
62
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
63
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
64
+ 4. Push to the branch (`git push origin my-new-feature`)
65
+ 5. Create new Pull Request
66
+
67
+ ### Copyright
68
+
69
+ Copyright (c) 2013 Christoph Seydel. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task :default => :spec
data/isbnify.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'isbnify/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "isbnify"
8
+ spec.version = Isbnify::VERSION
9
+ spec.authors = ["cseydel"]
10
+ spec.email = ["christoph.seydel@me.com"]
11
+ spec.description = %q{This gem verifies, creates and hyphinates ISBN13 numbers}
12
+ spec.summary = %q{This gem verifies, creates and hyphinates ISBN13 numbers}
13
+ spec.homepage = "https://github.com/cseydel/isbnify"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "nokogiri"
22
+
23
+ spec.add_development_dependency "bundler", ">= 1.2.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec", "~> 2.14.1"
26
+ spec.add_development_dependency "fakeweb", "~> 1.3.0"
27
+ spec.add_development_dependency "vcr", "~> 2.6.0"
28
+ spec.add_development_dependency "simplecov", "~> 0.7.1"
29
+ spec.add_development_dependency "simplecov-gem-adapter", "~> 1.0.1"
30
+ end
data/lib/isbnify.rb ADDED
@@ -0,0 +1,25 @@
1
+ require "isbnify/version"
2
+ require "isbnify/isbn"
3
+ require "isbnify/iisbna"
4
+
5
+ module Isbnify
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def hyphinate_isbn(isbn_string = nil)
13
+ ISBN.hyphinate_isbn(isbn_string)
14
+ end
15
+
16
+ def valid_isbn?(isbn_string = nil)
17
+ ISBN.valid_isbn?(isbn_string)
18
+ end
19
+
20
+ def create_isbn
21
+ ISBN.create_isbn
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,168 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+
4
+ module Isbnify
5
+ class IISBNA
6
+
7
+ attr_accessor :country_group, :group_hash, :isbn, :prefix, :publisher, :range_hash
8
+
9
+ def initialize(isbn = "")
10
+ @isbn = isbn.to_s
11
+ @range_hash = xml_parse
12
+ end
13
+
14
+ def hyphinate
15
+ return "invalid international ISBN13 number" unless valid_prefix? && valid_group? && valid_publisher?
16
+ hyphinate_with_values
17
+ end
18
+
19
+ def create_valid_isbn
20
+ random_group = range_hash[:"ISBNRangeMessage"][:"RegistrationGroups"][:"Group"].shuffle[0]
21
+ group = random_group[:Prefix]
22
+ rule = random_group[:Rules][:Rule].is_a?(Array) ? random_group[:Rules][:Rule].shuffle[0] : random_group[:Rules][:Rule]
23
+ r_publisher = random_publisher(rule)
24
+ create_isbn(group, r_publisher)
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def create_isbn(group, r_publisher)
31
+ product_size = 12 - group.gsub("-","").size - r_publisher.size
32
+ product = "9999999999"[0..(product_size - 1)]
33
+ checksum = Isbnify::ISBN.create_isbn_checksum(group.gsub("-","") + r_publisher + product)
34
+ return "#{group}-#{r_publisher}-#{product}-#{checksum}"
35
+ end
36
+
37
+ def group_prefix_included?(prefix, checkstring)
38
+ 5.times do |n|
39
+ return true if prefix == "#{@prefix}-#{checkstring[0..(n-1)]}"
40
+ end
41
+ false
42
+ end
43
+
44
+ def hyphinate_with_values
45
+ product_id = isbn.gsub(/#{@prefix}#{@country_group}#{@publisher}/, "")
46
+ return "#{@prefix}-#{@country_group}-#{@publisher}-#{product_id[0..-2]}-#{product_id[-1]}"
47
+ end
48
+
49
+ def in_range?(rule, string)
50
+ truncated_range(rule[:Range], rule[:Length], string[valid_range(rule[:Length])])
51
+ end
52
+
53
+ def open_url_with_file_fallback
54
+ begin
55
+ xml_to_hash(open("http://www.isbn-international.org/agency?rmxml=1").read)
56
+ rescue
57
+ xml_to_hash(open(File.expand_path("../../../vendor/lib/RangeMessage.xml", __FILE__)).read)
58
+ end
59
+ end
60
+
61
+ def random_publisher(rule)
62
+ array = rule[:Range].split("-")
63
+ new_publisher = (rand(array[1].to_i - array[0].to_i) + array[0].to_i).to_s
64
+ return new_publisher[valid_range(rule[:Length])]
65
+ end
66
+
67
+ def truncated_range(range, length, needle)
68
+ array = range.split("-")
69
+ needle.to_i.between?((array[0][valid_range(length)]).to_i, (array[1][valid_range(length)]).to_i)
70
+ end
71
+
72
+ def valid_group?
73
+ sanitized_string = isbn.gsub(/^#{@prefix}/, "")
74
+ range_hash[:"ISBNRangeMessage"][:"RegistrationGroups"][:"Group"].each do |group_hash|
75
+ if group_prefix_included?(group_hash[:Prefix], sanitized_string)
76
+ @group_hash = group_hash
77
+ return @country_group = group_hash[:Prefix].gsub(/^#{@prefix}-/, "")
78
+ end
79
+ end
80
+ false
81
+ end
82
+
83
+ def valid_prefix?
84
+ range_hash[:"ISBNRangeMessage"][:"EAN.UCCPrefixes"][:"EAN.UCC"].each do |prefix_hash|
85
+ return @prefix = prefix_hash[:Prefix] if prefix_hash.has_value?(isbn[0..2])
86
+ end
87
+ false
88
+ end
89
+
90
+ def valid_publisher?
91
+ sanitized_string = isbn.gsub(/#{@prefix}#{@country_group}/, "")
92
+ @group_hash[:Rules][:Rule].each do |rule|
93
+ return @publisher = sanitized_string[valid_range(rule[:Length])] if in_range?(rule, sanitized_string)
94
+ end
95
+ end
96
+
97
+ def valid_range(length)
98
+ (0..(length.to_i - 1))
99
+ end
100
+
101
+
102
+ # XML parser methods
103
+ # modified from http://stackoverflow.com/questions/1230741/convert-a-nokogiri-document-to-a-ruby-hash/1231297#123129
104
+ #
105
+
106
+ def xml_parse
107
+ open_url_with_file_fallback.to_hash
108
+ end
109
+
110
+ def xml_to_hash(xml_io)
111
+ result = Nokogiri::XML(xml_io)
112
+ return { result.root.name.to_sym => xml_node_to_hash(result.root)}
113
+ end
114
+
115
+ def xml_node_to_hash(node)
116
+ if node.element?
117
+ parse_element(node)
118
+ else
119
+ return node.content.to_s
120
+ end
121
+ end
122
+
123
+ def parse_element(node)
124
+ result_hash = {}
125
+ attributes = parse_attributes(node)
126
+ if node.children.size > 0
127
+ return result_hash = parse_children(node, attributes, result_hash)
128
+ else
129
+ return attributes
130
+ end
131
+ end
132
+
133
+ def parse_attributes(node)
134
+ if node.attributes != {}
135
+ attributes = {}
136
+ node.attributes.keys.each do |key|
137
+ attributes[node.attributes[key].name.to_sym] = node.attributes[key].value
138
+ end
139
+ end
140
+ attributes
141
+ end
142
+
143
+ def parse_children(node, attributes, result_hash)
144
+ node.children.each do |child|
145
+ result = xml_node_to_hash(child)
146
+ if child.name == "text"
147
+ unless child.next_sibling || child.previous_sibling
148
+ return result unless attributes
149
+ result_hash[child.name.to_sym] = result
150
+ end
151
+ elsif result_hash[child.name.to_sym]
152
+ if result_hash[child.name.to_sym].is_a?(Object::Array)
153
+ result_hash[child.name.to_sym] << result
154
+ else
155
+ result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << result
156
+ end
157
+ else
158
+ result_hash[child.name.to_sym] = result
159
+ end
160
+ end
161
+ if attributes
162
+ result_hash = attributes.merge(result_hash)
163
+ end
164
+ return result_hash
165
+ end
166
+
167
+ end
168
+ end
@@ -0,0 +1,106 @@
1
+ module Isbnify
2
+ class ISBN
3
+
4
+ attr_accessor :param
5
+
6
+ def initialize(param = nil)
7
+ @param = param
8
+ end
9
+
10
+ class << self
11
+ def create_isbn
12
+ self.new.create_isbn
13
+ end
14
+
15
+ def create_isbn_checksum(number = nil)
16
+ self.new(number).create_isbn_checksum
17
+ end
18
+
19
+ def hyphinate_isbn(isbn_string = nil)
20
+ self.new(isbn_string).hyphinate_isbn
21
+ end
22
+
23
+ def valid_isbn?(isbn_string = nil)
24
+ self.new(isbn_string).valid_isbn?
25
+ end
26
+ end
27
+
28
+ def create_isbn
29
+ Isbnify::IISBNA.new.create_valid_isbn
30
+ end
31
+
32
+ def create_isbn_checksum
33
+ checksum_argument_validation(param)
34
+ return calculate_checksum(param)
35
+ end
36
+
37
+ def hyphinate_isbn
38
+ string_argument_validations(param)
39
+ return "invalid ISBN given" unless valid_isbn?
40
+ Isbnify::IISBNA.new(param).hyphinate
41
+ end
42
+
43
+ def valid_isbn?
44
+ string_argument_validations(param)
45
+ validate_checksum
46
+ end
47
+
48
+
49
+ private
50
+
51
+ def calculate_checksum(string)
52
+ (10 - (inject_with_index(string_to_array(string)) % 10)) % 10
53
+ end
54
+
55
+ def calculate_sum(sum, number, index)
56
+ sum + exponate_number_with_index(number, index)
57
+ end
58
+
59
+ def checksum_argument_validation(isbn_string)
60
+ validate_presence_of_attribute(isbn_string)
61
+ validate_type_of_attribute(isbn_string, "String")
62
+ end
63
+
64
+ def exponate_number_with_index(number, index)
65
+ number * (3 ** ((index + 1) % 2))
66
+ end
67
+
68
+ def inject_with_index(array)
69
+ array.each_index.inject(0){ |sum, index| calculate_sum(sum, array[index].to_i, index + 1) }
70
+ end
71
+
72
+ def sanitize_isbn_string
73
+ param.gsub(/[^0-9]/, "")
74
+ end
75
+
76
+ def string_argument_validations(isbn_string)
77
+ validate_presence_of_attribute(isbn_string)
78
+ validate_type_of_attribute(isbn_string, "String")
79
+ validates_isbn13_length
80
+ end
81
+
82
+ def string_to_array(string)
83
+ string[0..-2].split("")
84
+ end
85
+
86
+ def validate_checksum
87
+ validate_with_sanitized_string(sanitize_isbn_string)
88
+ end
89
+
90
+ def validate_with_sanitized_string(sanitized_string)
91
+ sanitized_string[-1].to_i == calculate_checksum(sanitized_string)
92
+ end
93
+
94
+ def validates_isbn13_length
95
+ raise ArgumentError, "expected argument to include exactly 13 digits" unless sanitize_isbn_string.length == 13
96
+ end
97
+
98
+ def validate_presence_of_attribute(attribute)
99
+ raise ArgumentError, "expected argument not to be nil" if attribute.nil?
100
+ end
101
+
102
+ def validate_type_of_attribute(attribute, type)
103
+ raise ArgumentError, "expected argument to be #{type}" unless attribute.is_a?(Module.const_get(type))
104
+ end
105
+ end
106
+ end