voyager 0.0.1
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/lib/voyager.rb +17 -0
- data/lib/voyager/activerecord/base.rb +6 -0
- data/lib/voyager/activerecord/bib/item.rb +12 -0
- data/lib/voyager/activerecord/bib/text.rb +10 -0
- data/lib/voyager/activerecord/circ/transaction.rb +14 -0
- data/lib/voyager/activerecord/item.rb +21 -0
- data/lib/voyager/activerecord/item/barcode.rb +12 -0
- data/lib/voyager/sip/basic_client.rb +60 -0
- data/lib/voyager/sip/client.rb +134 -0
- data/lib/voyager/version.rb +3 -0
- data/voyager.gemspec +25 -0
- metadata +100 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/lib/voyager.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# ActiveRecord
|
2
|
+
require 'active_record'
|
3
|
+
require 'voyager/activerecord/base'
|
4
|
+
require 'voyager/activerecord/bib/text'
|
5
|
+
require 'voyager/activerecord/bib/item'
|
6
|
+
require 'voyager/activerecord/circ/transaction'
|
7
|
+
require 'voyager/activerecord/item'
|
8
|
+
require 'voyager/activerecord/item/barcode'
|
9
|
+
|
10
|
+
# SIP Client
|
11
|
+
require 'socket'
|
12
|
+
require 'voyager/sip/basic_client'
|
13
|
+
require 'voyager/sip/client'
|
14
|
+
|
15
|
+
module Voyager
|
16
|
+
# Your code goes here...
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Voyager
|
2
|
+
module AR
|
3
|
+
module Circ
|
4
|
+
class Transaction < Voyager::AR::Base
|
5
|
+
# ActiveRecord Configuration
|
6
|
+
set_table_name 'circ_transactions'
|
7
|
+
set_primary_key 'circ_transactions_id'
|
8
|
+
|
9
|
+
# AR Associations
|
10
|
+
belongs_to :item, :class_name => 'Voyager::AR::Item'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Voyager
|
2
|
+
module AR
|
3
|
+
class Item < Voyager::AR::Base
|
4
|
+
# ActiveRecord Configuration
|
5
|
+
set_table_name 'item'
|
6
|
+
set_primary_key 'item_id'
|
7
|
+
|
8
|
+
# Associations
|
9
|
+
has_one :barcode, :foreign_key => 'item_id', :class_name => 'Voyager::AR::Item::Barcode'
|
10
|
+
has_one :circ_transaction, :class_name => 'Voyager::AR::Circ::Transaction'
|
11
|
+
|
12
|
+
has_one :bib_text, :through => :bib_item, :class_name => 'Voyager::AR::Bib::Text'
|
13
|
+
has_one :bib_item, :foreign_key => 'item_id', :class_name => 'Voyager::AR::Bib::Item'
|
14
|
+
|
15
|
+
# Class Methods
|
16
|
+
def charged?
|
17
|
+
circ_transaction.nil? ? false : true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
# Rudimentary SIP Client
|
4
|
+
|
5
|
+
module Voyager
|
6
|
+
module SIP
|
7
|
+
module BasicSipClient
|
8
|
+
VERBOSE = true
|
9
|
+
TIMESTAMP_FORMAT = "%Y%m%d %H%M%S"
|
10
|
+
@socket
|
11
|
+
|
12
|
+
def initialize(host, port)
|
13
|
+
Rails::logger.info "SIPClient: Connecting with #{host}:#{port}" if VERBOSE
|
14
|
+
@socket = TCPSocket::new(host, port)
|
15
|
+
|
16
|
+
if block_given?
|
17
|
+
begin
|
18
|
+
yield self
|
19
|
+
ensure
|
20
|
+
# When given a socket, we automatically close connection
|
21
|
+
close
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def send(msg)
|
27
|
+
Rails::logger.info "SIPClient: Sending #{msg}" if VERBOSE
|
28
|
+
@socket.write("#{msg.strip}\r")
|
29
|
+
|
30
|
+
# If a block is given, yield the response
|
31
|
+
if block_given?
|
32
|
+
yield recv
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def recv
|
37
|
+
msg = ""
|
38
|
+
until msg =~ /\r/
|
39
|
+
result = IO::select([@socket], nil, nil)
|
40
|
+
|
41
|
+
for inp in result[0]
|
42
|
+
msg << @socket.recv(300);
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
Rails::logger.info "SIPClient: Recieved #{msg.strip}" if VERBOSE
|
47
|
+
msg
|
48
|
+
end
|
49
|
+
|
50
|
+
def close
|
51
|
+
Rails::logger.info "SIPClient: Connection closed." if VERBOSE
|
52
|
+
@socket.close
|
53
|
+
end
|
54
|
+
|
55
|
+
def timestamp
|
56
|
+
Time.now.strftime(TIMESTAMP_FORMAT)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module Voyager
|
2
|
+
module SIP
|
3
|
+
class Client
|
4
|
+
include BasicSipClient
|
5
|
+
#--
|
6
|
+
# Sends a Login Message
|
7
|
+
#
|
8
|
+
# Example login request:
|
9
|
+
# 9300CNusername|COpassword|CPCIRC|
|
10
|
+
# Example login response:
|
11
|
+
# 941
|
12
|
+
#++
|
13
|
+
def login(username, password, location)
|
14
|
+
msg = "9300CN#{username}|CO#{password}|CP#{location}|\r"
|
15
|
+
|
16
|
+
send(msg) do |response|
|
17
|
+
response = parse_login(response)
|
18
|
+
|
19
|
+
raise "Invalid login: #{response[:raw]}" unless response[:ok] == '1'
|
20
|
+
|
21
|
+
if block_given?
|
22
|
+
yield response
|
23
|
+
end
|
24
|
+
|
25
|
+
response
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_login(msg)
|
30
|
+
match = msg.match(/^94(0|1)\r$/)
|
31
|
+
if match
|
32
|
+
{:raw => msg,
|
33
|
+
:id => 94,
|
34
|
+
:ok => match[1]}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
#--
|
39
|
+
# Sends a Create Bib Message
|
40
|
+
#
|
41
|
+
# Example create bib request:
|
42
|
+
# 8120100813 133505AO|MFSelfchk|AJThe transformation of learning : / *77087*|AB77087|AC|^M
|
43
|
+
# Example create bib response:
|
44
|
+
# 821MJ633124|MA630995|AFCreate Bib successful.|^M
|
45
|
+
#++
|
46
|
+
def create_bib(operator, title, barcode)
|
47
|
+
msg = "81#{self.timestamp}|AO|MF#{operator}|AJ#{title}|AB#{barcode}|AC|"
|
48
|
+
|
49
|
+
send(msg) do |response|
|
50
|
+
response = parse_create_bib(response)
|
51
|
+
|
52
|
+
# Bib/MFHD/Item create failed
|
53
|
+
raise "Bib/MFHD/Item create failed: #{response['raw']}" if response[:ok] == '0'
|
54
|
+
|
55
|
+
# Bib was created, but not the item
|
56
|
+
#
|
57
|
+
# This is possible if we have a duplicate barcode, however we
|
58
|
+
# check for that. Possible race condition, though unlikely.
|
59
|
+
#
|
60
|
+
# Store the bib_id for troubleshooting
|
61
|
+
raise "Duplicate barcode. A bib exists without an item and should be deleted: #{response['raw']}" if response[:item_id].empty?
|
62
|
+
|
63
|
+
|
64
|
+
if block_given?
|
65
|
+
yield response
|
66
|
+
end
|
67
|
+
|
68
|
+
response
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_create_bib(msg)
|
73
|
+
# 821MJ640267|MA818432|AFCreate Bib successful.|
|
74
|
+
# [2] - OK? 1:yes, 0:no
|
75
|
+
# MJ - Item number
|
76
|
+
# MA - Bib number
|
77
|
+
# AF - Print screen
|
78
|
+
match = msg.match(/^82(.)MJ(.*)\|MA(.*)\|AF(.*)\|\r$/)
|
79
|
+
if match
|
80
|
+
{:raw => msg,
|
81
|
+
:id => 82,
|
82
|
+
:ok => match[1],
|
83
|
+
:item_id => match[2],
|
84
|
+
:bib_id => match[3],
|
85
|
+
:print_msg => match[4]
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def item_status(item_identifier)
|
91
|
+
msg = "17#{self.timestamp}|AO|AB#{item_identifier}|AC|"
|
92
|
+
|
93
|
+
send(msg) do |response|
|
94
|
+
response = parse_item_status(response)
|
95
|
+
|
96
|
+
if block_given?
|
97
|
+
yield response
|
98
|
+
end
|
99
|
+
|
100
|
+
response
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_item_status(msg)
|
105
|
+
# 1803000120110408 150732CF0|AH|CJ|AB96917|AJBokml - Test for Nostos *96917*|BGHealey_Library|AQCirculation Desk|AFItem Info retrieved successfully.|
|
106
|
+
match = msg.match(/^18([0-9]{2})([0-9]{2})([0-9]{2})(.{18})(.*)\r$/)
|
107
|
+
# 1 - Circulation Status
|
108
|
+
# 2 - Security Marker
|
109
|
+
# 3 - Fee Type
|
110
|
+
# 4 - Transaction Date
|
111
|
+
# 5 - Variable Fields
|
112
|
+
var_fields = msg.scan(/([A-Z]{2})([^|]*)\|/)
|
113
|
+
if match
|
114
|
+
r = {:raw => msg,
|
115
|
+
:id => 18,
|
116
|
+
:circ_status => match[1],
|
117
|
+
:security_marker => match[2],
|
118
|
+
:fee_type => match[3],
|
119
|
+
:transaction_date => match[4],
|
120
|
+
}
|
121
|
+
var_fields.each do |field|
|
122
|
+
r[field.first.to_sym] = field.last
|
123
|
+
end
|
124
|
+
r
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Utilities
|
129
|
+
def timestamp
|
130
|
+
Time.now.strftime("%Y%m%d %H%M%S")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
data/voyager.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "voyager/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "voyager"
|
7
|
+
s.version = Voyager::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Brice Stacey"]
|
10
|
+
s.email = ["bricestacey@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/bricestacey/voyager"
|
12
|
+
s.summary = %q{ActiveRecord bindings for Voyager ILS}
|
13
|
+
s.description = %q{ActiveRecord bindings for Voyager ILS}
|
14
|
+
|
15
|
+
s.rubyforge_project = "voyager"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_dependency('activerecord')
|
23
|
+
s.add_dependency('activerecord-oracle_enhanced-adapter')
|
24
|
+
s.add_dependency('ruby-oci8')
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: voyager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brice Stacey
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-15 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activerecord
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activerecord-oracle_enhanced-adapter
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: "0"
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: ruby-oci8
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id003
|
48
|
+
description: ActiveRecord bindings for Voyager ILS
|
49
|
+
email:
|
50
|
+
- bricestacey@gmail.com
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- .gitignore
|
59
|
+
- Gemfile
|
60
|
+
- Rakefile
|
61
|
+
- lib/voyager.rb
|
62
|
+
- lib/voyager/activerecord/base.rb
|
63
|
+
- lib/voyager/activerecord/bib/item.rb
|
64
|
+
- lib/voyager/activerecord/bib/text.rb
|
65
|
+
- lib/voyager/activerecord/circ/transaction.rb
|
66
|
+
- lib/voyager/activerecord/item.rb
|
67
|
+
- lib/voyager/activerecord/item/barcode.rb
|
68
|
+
- lib/voyager/sip/basic_client.rb
|
69
|
+
- lib/voyager/sip/client.rb
|
70
|
+
- lib/voyager/version.rb
|
71
|
+
- voyager.gemspec
|
72
|
+
homepage: https://github.com/bricestacey/voyager
|
73
|
+
licenses: []
|
74
|
+
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: "0"
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: "0"
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project: voyager
|
95
|
+
rubygems_version: 1.7.2
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: ActiveRecord bindings for Voyager ILS
|
99
|
+
test_files: []
|
100
|
+
|