koha 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
@@ -0,0 +1,3 @@
1
+ ## v0.0.1
2
+
3
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in koha.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 fitz
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,84 @@
1
+ # Koha [![Build Status](https://travis-ci.org/cfitz/koha.png?branch=master)](https://travis-ci.org/cfitz/koha) [![Code Climate](https://codeclimate.com/github/cfitz/koha.png)](https://codeclimate.com/github/cfitz/koha)
2
+
3
+ A simple ruby wrapper for the Koha ILS RESTFUL API.
4
+
5
+ ### Prerequisites
6
+
7
+ You must install the RESTFUL api code to your instance of Koha. This project can be found here =>
8
+ http://git.biblibre.com/?p=koha-restful;a=summary
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'koha'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install koha
23
+
24
+ ## Usage
25
+
26
+ Here a quick irb walk-through...
27
+
28
+ > $ irb
29
+
30
+ > 1.9.3-p286 :001 > require 'koha'
31
+ => true
32
+
33
+ Make a Connection...
34
+
35
+ > 1.9.3-p286 :002 > k = Koha.connect({:url => "http://my.library.se/cgi-bin/koha/rest.pl"} )
36
+ > => #<Koha::Client:0x007fbcec9b6fd0 @uri=#<URI::HTTP:0x007fbcec9c3a00 URL:http://my.library.se/cgi-bin/koha/rest.pl/>, @proxy=nil, @connection=#<Koha::Connection:0x007fbcec9b6ff8>, @options={:url=>"http://my.library.se/cgi-bin/koha/rest.pl"}>
37
+
38
+ now get branch information...
39
+
40
+ > 1.9.3-p286 :003 > k.branches
41
+ > => [ { "name"=>"World Maritime University Library", "code"=>"WMU"}, { "name"=>"ebrary", "code"=>"EBR" } ]
42
+
43
+
44
+ find a bibliographic record
45
+ > 1.9.3-p286 :006 > k.find_biblio("17454")
46
+ > => [{"withdrawn"=>"1", "biblioitemnumber"=>"17454", .......
47
+
48
+ check is an biblio is holdable.....
49
+ > 1.9.3-p286 :009 > k.biblio_holdable?("17454" )
50
+ > => false
51
+
52
+ ... for a specific user ...
53
+ > 1.9.3-p286 :011 > k.biblio_holdable?("17454", :borrowernumber => "544" )
54
+ > => true
55
+
56
+
57
+ ... by user name ...
58
+ > 1.9.3-p286 :012 > k.biblio_holdable?("17454", :borrowername => "cf" )
59
+ > => true
60
+
61
+ get the user's holds.
62
+ > 1.9.3-p286 :013 > k.user_holds :borrowername => "cf"
63
+ > => [{"itemnumber"=>nil, "branchname"=>"World Maritime University Library", "itemcallnumber"=>nil, "hold_id"=>nil, "reservedate"=>"2013-02-20", "barcode"=>nil, "found"=>nil, "biblionumber"=>"76356", "cancellationdate"=>nil, "title"=>"Asian approaches to international law and the legacy of colonialism and imperialism :", "rank"=>"1", "branchcode"=>"WMU"}]
64
+
65
+ or get the user's issues
66
+
67
+ > 1.9.3-p286 :014 > k.user_issues :borrowernumber => "544"
68
+ > => [{"itemnumber"=>"42414", "itemcallnumber"=>"KD1819 .H54 2003", "barcode"=>"022593", "date_due"=>"2013-03-11T23:59:00", "renewable"=>true, "issuedate"=>"2012-11-21T00:00:00", "biblionumber"=>"17454", "title"=>"Maritime law", "borrowernumber"=>"544", "branchcode"=>"WMU"}]
69
+
70
+
71
+
72
+ ### Development
73
+
74
+ Checkout the code. rake will run the tests. rake coverage will generate coverage. rake yard will generate documentation.
75
+
76
+
77
+ ## Contributing
78
+
79
+ 1. Fork it
80
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
81
+ 2.5 Write the code and add tests.
82
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create new Pull Request
@@ -0,0 +1,21 @@
1
+ require 'rake'
2
+ require 'bundler/gem_tasks'
3
+
4
+ require 'rubygems/package_task'
5
+
6
+ task :environment do
7
+ require File.dirname(__FILE__) + '/lib/koha_client'
8
+ end
9
+
10
+ Dir['tasks/**/*.rake'].each { |t| load t }
11
+
12
+ task :default => ['spec:api']
13
+
14
+
15
+
16
+ task :coverage do
17
+ # add simplecov
18
+ ENV["COVERAGE"] = 'yes'
19
+ # run the specs
20
+ Rake::Task['spec:api'].execute
21
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require "koha"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "koha"
8
+ s.summary = "A Ruby client for Koha ILSDI interface"
9
+ s.description = %q{Easy interface for the Koha ILSDI API (https://github.com/Koha-Community/Koha/blob/master/C4/ILSDI/Services.pm) }
10
+ s.version = Koha.version
11
+ s.authors = ["chris fitzpatrick"]
12
+ s.email = ["chrisfitzpat@gmail.com"]
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_development_dependency 'simplecov', '0.7.1'
19
+ s.add_development_dependency 'yard', '~> 0.8.4.1'
20
+ s.add_development_dependency 'webmock', '~> 1.9.3'
21
+ s.add_development_dependency 'rake', '~> 10.0.3'
22
+ s.add_development_dependency 'rdoc', '~> 3.9.5'
23
+ s.add_development_dependency 'rspec', '~> 2.6.0'
24
+ s.add_development_dependency 'redcarpet'
25
+ end
@@ -0,0 +1,20 @@
1
+ $: << "#{File.dirname(__FILE__)}" unless $:.include? File.dirname(__FILE__)
2
+
3
+ require 'rubygems'
4
+
5
+ module Koha
6
+
7
+ %W(Client Error Connection Uri Version).each{|n| autoload n.to_sym, "koha/#{n.downcase}"}
8
+
9
+ def self.version; "0.0.1" end
10
+
11
+ VERSION = self.version
12
+
13
+ def self.connect *args
14
+ driver = Class === args[0] ? args[0] : Koha::Connection
15
+ opts = Hash === args[-1] ? args[-1] : {}
16
+ Client.new driver.new, opts
17
+ end
18
+
19
+
20
+ end
@@ -0,0 +1,210 @@
1
+ require 'json'
2
+
3
+ class Koha::Client
4
+
5
+ attr_reader :connection, :uri, :proxy, :options
6
+
7
+ def initialize connection, options = {}
8
+ @proxy = @uri = nil
9
+ @connection = connection
10
+ unless false === options[:url]
11
+ url = options[:url] ? options[:url].dup : 'http://localhost/cgi-bin/koha/rest.pl/'
12
+ url << "/" unless url[-1] == ?/
13
+ @uri = Koha::Uri.create url
14
+ if options[:proxy]
15
+ proxy_url = options[:proxy].dup
16
+ proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
17
+ @proxy = Koha::Uri.create proxy_url if proxy_url
18
+ end
19
+ end
20
+ @options = options
21
+ end
22
+
23
+ # returns the uri proxy if present,
24
+ # otherwise just the uri object.
25
+ def base_uri
26
+ @proxy ? @proxy : @uri
27
+ end
28
+
29
+ # Create the get, post, and head methods
30
+ %W(get post put).each do |meth|
31
+ class_eval <<-RUBY
32
+ def #{meth} path, opts = {}, &block
33
+ send_and_receive path, opts.merge(:method => :#{meth}), &block
34
+ end
35
+ RUBY
36
+ end
37
+
38
+
39
+ # +send_and_receive+ is the main request method responsible for sending requests to the +connection+ object.
40
+ #
41
+ # "path" : A string value that directs the client to the API REST method
42
+ # "opts" : A hash, which can contain the following keys:
43
+ # :method : optional - the http method (:get, :post or :put)
44
+ # :params : optional - the query string params in hash form
45
+ # All other options are passed right along to the connection's +send_and_receive+ method (:get, :post, or :put)
46
+ #
47
+ #
48
+ # creates a request context hash,
49
+ # sends it to the connection's +execute+ method
50
+ # which returns a simple hash,
51
+ # then passes the request/response into +adapt_response+.
52
+ def send_and_receive path, opts
53
+ request_context = build_request path, opts
54
+ [:open_timeout, :read_timeout].each do |k|
55
+ request_context[k] = @options[k]
56
+ end
57
+ execute request_context
58
+ end
59
+
60
+ # send a request to the connection to be submitted
61
+ def execute request_context
62
+ raw_response = connection.execute self, request_context
63
+ adapt_response(request_context, raw_response) unless raw_response.nil?
64
+ end
65
+
66
+ # +build_request+ accepts a path and options hash,
67
+ # then prepares a normalized hash to return for sending
68
+ # +build_request+ sets up the uri/query string
69
+ # and converts the +data+ arg to form-urlencoded,
70
+ # returns a hash with the following keys:
71
+ # :method
72
+ # :params
73
+ # :uri
74
+ # :path
75
+ # :query
76
+
77
+ def build_request path, opts
78
+ raise "path must be a string or symbol, not #{path.inspect}" unless [String,Symbol].include?(path.class)
79
+ path = path.to_s
80
+ opts[:proxy] = proxy unless proxy.nil?
81
+ opts[:method] ||= :get
82
+ opts[:params] ||= {}
83
+ opts[:params][:borrowernumber] = opts[:borrowernumber] if opts[:borrowernumber]
84
+ opts[:params][:user_name] = opts[:borrowername] if opts[:borrowername]
85
+ query = Koha::Uri.to_params(opts[:params]) unless opts[:params].empty?
86
+ opts[:query] = query
87
+ opts[:path] = path
88
+ if base_uri
89
+ opts[:uri] = URI.join(base_uri, path.to_s)
90
+ opts[:uri].merge!("?#{query}" ) if query
91
+ end
92
+ opts
93
+ end
94
+
95
+
96
+ # A mixin for used by #adapt_response
97
+ module Context
98
+ attr_accessor :request, :response
99
+ end
100
+
101
+ # This method will evaluate the :body value
102
+ # if the request has an :evaulte value, the response is send to
103
+ # the evaluate_json_response, which returns just the node.
104
+ # this is a convience to simply return "false" or "true" and not a bunch of stupid
105
+ # json that has nothing that makes any god-damn sense.
106
+ # ... otherwise, the json is simply returned.
107
+ def adapt_response request, response
108
+ raise "The response does not have the correct keys => :body, :headers, :status" unless
109
+ %W(body headers status) == response.keys.map{|k|k.to_s}.sort
110
+ raise Koha::Error::Http.new request, response unless [200,302].include? response[:status]
111
+ result = request[:evaluate] ? evaluate_json_response(request, response, request[:evaluate]) : response[:body]
112
+ result.extend Context
113
+ result.request, result.response = request, response
114
+ result
115
+ end
116
+
117
+
118
+
119
+ ##### KOHA REST API METHODS #######
120
+
121
+ ### Info Methods ###
122
+
123
+ # returns a hash of [ { :code => "1", :name => "Our Branch Name "}]
124
+ def branches opts= {}
125
+ JSON.parse(get "branches", opts)
126
+ end
127
+
128
+
129
+ ### USER Methods ###
130
+
131
+ # returns a hash of all the users
132
+ def all_users opts= {}
133
+ JSON.parse(get "user/all", opts )
134
+ end
135
+
136
+ # returns a hash of patrons enrolled today
137
+ def today_users opts= {}
138
+ JSON.parse(get "user/today", opts)
139
+ end
140
+
141
+
142
+ def user_holds opts= {}
143
+ path, opts = build_user_path("holds", opts)
144
+ JSON.parse(get path, opts)
145
+ end
146
+
147
+ def user_issues opts= {}
148
+ path, opts = build_user_path("issues", opts)
149
+ JSON.parse(get path, opts)
150
+ end
151
+
152
+ def build_user_path rest_method, opts= {}
153
+ raise ArgumentError unless ( opts[:borrowernumber] or opts[:borrowername] ) #we have to be passed either a name or number
154
+ borrowernumber, borrowername = opts.delete(:borrowernumber), opts.delete(:borrowername)
155
+ path = borrowernumber ? "user/byid/#{borrowernumber}/#{rest_method}" : "user/#{borrowername}/#{rest_method}"
156
+ return path, opts
157
+ end
158
+
159
+
160
+ ### Biblio and Item Methods ###
161
+
162
+ # This method will get the item record from koha given the biblionumber
163
+ def find_biblio biblionumber, opts = {}
164
+ biblionumber = biblionumber.to_s
165
+ JSON.parse(get "biblio/#{biblionumber}/items", opts)
166
+ end
167
+
168
+ # wrapper to check if a biblio is holdable
169
+ # take a koha biblio number and standard client opts
170
+ def biblio_holdable?(biblionumber, opts = {})
171
+ is_holdable?(:biblio, biblionumber, opts )
172
+ end
173
+
174
+ # wrapper to check a biblio items holdabale statues.
175
+ # takes a koha bilionumber and standard client opts
176
+ # this returns just a hash of the items and their status, no need to evaulate.
177
+ def biblio_items_holdable?(biblionumber, opts = {} )
178
+ opts ||= {}
179
+ opts[:holdable] = "items_holdable_status"
180
+ opts[:evaluate] ||= false
181
+ is_holdable?(:biblio, biblionumber, opts)
182
+ end
183
+
184
+ # wrapper to check is an item is holdable
185
+ def item_holdable?(itemnumber, opts = {})
186
+ is_holdable?(:item, itemnumber, opts)
187
+ end
188
+
189
+ def is_holdable?(koha_type, identifier, opts = {} )
190
+ opts ||= {}
191
+ opts[:evaluate] = :is_holdable unless opts[:evaluate] == false
192
+ holdable = opts[:holdable] ? opts[:holdable] : "holdable"
193
+ koha_type = koha_type.to_s == "item" ? "item" : "biblio"
194
+ identifier = identifier.to_s
195
+ get "#{koha_type}/#{identifier}/#{holdable}", opts
196
+ end
197
+
198
+ protected
199
+
200
+ # this is used to retrun a ruby primitive based on a json node. For example holdable returns { "is_holdable" : true, "reasons" : [] }
201
+ # and we just want true.
202
+ def evaluate_json_response request, response, node
203
+ json = JSON.parse(response[:body])
204
+ json[node.to_s]
205
+ end
206
+
207
+
208
+
209
+
210
+ end
@@ -0,0 +1,70 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+
5
+ # taken from RSOLR's http client
6
+ class Koha::Connection
7
+
8
+ def execute client, request_context
9
+ h = http request_context[:uri], request_context[:proxy], request_context[:read_timeout], request_context[:open_timeout]
10
+ request = setup_raw_request request_context
11
+ request.body = request_context[:data] if request_context[:method] == :post and request_context[:data]
12
+ begin
13
+ response = h.request request
14
+ charset = response.type_params["charset"]
15
+ {:status => response.code.to_i, :headers => response.to_hash, :body => force_charset(response.body, charset)}
16
+ rescue Errno::ECONNREFUSED => e
17
+ raise(Errno::ECONNREFUSED.new(request_context.inspect))
18
+ # catch the undefined closed? exception -- this is a confirmed ruby bug
19
+ rescue NoMethodError
20
+ $!.message == "undefined method `closed?' for nil:NilClass" ?
21
+ raise(Errno::ECONNREFUSED.new) :
22
+ raise($!)
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ # This returns a singleton of a Net::HTTP or Net::HTTP.Proxy request object.
29
+ def http uri, proxy = nil, read_timeout = nil, open_timeout = nil
30
+ @http ||= (
31
+ http = if proxy
32
+ proxy_user, proxy_pass = proxy.userinfo.split(/:/) if proxy.userinfo
33
+ Net::HTTP.Proxy(proxy.host, proxy.port, proxy_user, proxy_pass).new uri.host, uri.port
34
+ else
35
+ Net::HTTP.new uri.host, uri.port
36
+ end
37
+ http.use_ssl = uri.port == 443 || uri.instance_of?(URI::HTTPS)
38
+ http.read_timeout = read_timeout if read_timeout
39
+ http.open_timeout = open_timeout if open_timeout
40
+ http
41
+ )
42
+ end
43
+
44
+ #
45
+ def setup_raw_request request_context
46
+ http_method = case request_context[:method]
47
+ when :get
48
+ Net::HTTP::Get
49
+ when :post
50
+ Net::HTTP::Post
51
+ when :put
52
+ Net::HTTP::Put
53
+ else
54
+ raise "Only :get, :post and :head http method types are allowed."
55
+ end
56
+ headers = request_context[:headers] || {}
57
+ raw_request = http_method.new request_context[:uri].request_uri
58
+ raw_request.initialize_http_header headers
59
+ raw_request.basic_auth(request_context[:uri].user, request_context[:uri].password) if request_context[:uri].user && request_context[:uri].password
60
+ raw_request
61
+ end
62
+
63
+ private
64
+
65
+ def force_charset body, charset
66
+ return body unless charset and body.respond_to?(:force_encoding)
67
+ body.force_encoding(charset)
68
+ end
69
+
70
+ end
@@ -0,0 +1,75 @@
1
+ module Koha::Error
2
+
3
+
4
+
5
+ class Http < RuntimeError
6
+
7
+
8
+ # ripped right from ActionPack
9
+ # Defines the standard HTTP status codes, by integer, with their
10
+ # corresponding default message texts.
11
+ # Source: http://www.iana.org/assignments/http-status-codes
12
+ STATUS_CODES = {
13
+ 100 => "Continue",
14
+ 101 => "Switching Protocols",
15
+ 102 => "Processing",
16
+
17
+ 200 => "OK",
18
+ 201 => "Created",
19
+ 202 => "Accepted",
20
+ 203 => "Non-Authoritative Information",
21
+ 204 => "No Content",
22
+ 205 => "Reset Content",
23
+ 206 => "Partial Content",
24
+ 207 => "Multi-Status",
25
+ 226 => "IM Used",
26
+
27
+ 300 => "Multiple Choices",
28
+ 301 => "Moved Permanently",
29
+ 302 => "Found",
30
+ 303 => "See Other",
31
+ 304 => "Not Modified",
32
+ 305 => "Use Proxy",
33
+ 307 => "Temporary Redirect",
34
+
35
+ 400 => "Bad Request",
36
+ 401 => "Unauthorized",
37
+ 402 => "Payment Required",
38
+ 403 => "Forbidden",
39
+ 404 => "Not Found",
40
+ 405 => "Method Not Allowed",
41
+ 406 => "Not Acceptable",
42
+ 407 => "Proxy Authentication Required",
43
+ 408 => "Request Timeout",
44
+ 409 => "Conflict",
45
+ 410 => "Gone",
46
+ 411 => "Length Required",
47
+ 412 => "Precondition Failed",
48
+ 413 => "Request Entity Too Large",
49
+ 414 => "Request-URI Too Long",
50
+ 415 => "Unsupported Media Type",
51
+ 416 => "Requested Range Not Satisfiable",
52
+ 417 => "Expectation Failed",
53
+ 422 => "Unprocessable Entity",
54
+ 423 => "Locked",
55
+ 424 => "Failed Dependency",
56
+ 426 => "Upgrade Required",
57
+
58
+ 500 => "Internal Server Error",
59
+ 501 => "Not Implemented",
60
+ 502 => "Bad Gateway",
61
+ 503 => "Service Unavailable",
62
+ 504 => "Gateway Timeout",
63
+ 505 => "HTTP Version Not Supported",
64
+ 507 => "Insufficient Storage",
65
+ 510 => "Not Extended"
66
+ }
67
+
68
+ def initialize request, response
69
+ @request, @response = request, response
70
+ end
71
+
72
+ end
73
+
74
+
75
+ end
@@ -0,0 +1,59 @@
1
+ require 'uri'
2
+
3
+ module Koha::Uri
4
+
5
+ def create url
6
+ ::URI.parse url[-1] == ?/ ? url : "#{url}/"
7
+ end
8
+
9
+ # Returns a query string param pair as a string.
10
+ # Both key and value are escaped.
11
+ def build_param(k,v, escape = true)
12
+ escape ?
13
+ "#{escape_query_value(k)}=#{escape_query_value(v)}" :
14
+ "#{k}=#{v}"
15
+ end
16
+
17
+ # Return the bytesize of String; uses String#size under Ruby 1.8 and
18
+ # String#bytesize under 1.9.
19
+ if ''.respond_to?(:bytesize)
20
+ def bytesize(string)
21
+ string.bytesize
22
+ end
23
+ else
24
+ def bytesize(string)
25
+ string.size
26
+ end
27
+ end
28
+
29
+ # Creates a ILSDI based query string.
30
+ # Keys that have arrays values are set multiple times:
31
+ # params_to_solr(:service => 'foo', :biblionumbers => ['1', '2'])
32
+ # is converted to:
33
+ # ?service=foo&biblinumbers=1+2
34
+ def to_params(params, escape = true)
35
+ mapped = params.map do |k, v|
36
+ next if v.to_s.empty?
37
+ if v.class == Array
38
+ build_param k, v.join("+"), false
39
+ else
40
+ build_param k, v, escape
41
+ end
42
+ end
43
+ mapped.compact.join("&")
44
+ end
45
+
46
+
47
+ # Performs URI escaping so that you can construct proper
48
+ # query strings faster. Use this rather than the cgi.rb
49
+ # version since it's faster.
50
+ # (Stolen from Rack).
51
+ def escape_query_value(s)
52
+ s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/u) {
53
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
54
+ }.tr(' ', '+')
55
+ end
56
+
57
+ extend self
58
+
59
+ end
@@ -0,0 +1,6 @@
1
+ module Koha
2
+
3
+ VERSION = "0.0.1"
4
+
5
+
6
+ end
@@ -0,0 +1,172 @@
1
+ require 'spec_helper'
2
+ describe "Koha::Client" do
3
+
4
+ module ClientHelper
5
+ def client
6
+ @client ||= (
7
+ connection = Koha::Connection.new
8
+ Koha::Client.new connection, :url => "http://localhost/koha", :read_timeout => 42, :open_timeout=>43
9
+ )
10
+ end
11
+ end
12
+
13
+ context "initialize" do
14
+ it "should accept whatevs and set it as the @connection" do
15
+ Koha::Client.new(:whatevs).connection.should == :whatevs
16
+ end
17
+
18
+
19
+ end
20
+
21
+ context "send_and_receive" do
22
+ include ClientHelper
23
+ it "should forward these method calls the #connection object" do
24
+ [:get, :post, :put].each do |meth|
25
+ client.connection.should_receive(:execute).
26
+ and_return({:status => 200, :body => "{}", :headers => {}})
27
+ client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
28
+ end
29
+ end
30
+
31
+ it "should be timeout aware" do
32
+ [:get, :post, :put].each do |meth|
33
+ client.connection.should_receive(:execute).with(client, hash_including(:read_timeout => 42, :open_timeout=>43))
34
+ client.send_and_receive '', :method => meth, :params => {}, :data => nil, :headers => {}
35
+ end
36
+ end
37
+ end
38
+
39
+ context "post" do
40
+ include ClientHelper
41
+ it "should pass the expected params to the connection's #execute method" do
42
+ request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
43
+ client.connection.should_receive(:execute).
44
+ with(client, hash_including(request_opts)).
45
+ and_return(
46
+ :body => "",
47
+ :status => 200,
48
+ :headers => {"Content-Type"=>"text/plain"}
49
+ )
50
+ client.post "biblio/update", request_opts
51
+ end
52
+ end
53
+
54
+
55
+
56
+ context "adapt_response" do
57
+ include ClientHelper
58
+
59
+
60
+ end
61
+
62
+ context "build_request" do
63
+ include ClientHelper
64
+ it 'should return a request context array' do
65
+ result = client.build_request('select',
66
+ :method => :get,
67
+ :params => {},
68
+ :borrowernumber => "512",
69
+ :borrowername => "cf"
70
+ )
71
+ [/user_name=cf/, /borrowernumber=512/].each do |pattern|
72
+ result[:query].should match pattern
73
+ end
74
+ end
75
+
76
+
77
+
78
+ # Check responses to see how the KOHA RESTFUL API v1.0 responds.
79
+ context "koha methods" do
80
+ include ClientHelper
81
+
82
+ # branches
83
+ it "should call the REST API branch method with #client" do
84
+ stub_request(:get, "http://localhost/koha/branches").to_return(:status => 200, :body =>
85
+ "[{\"name\":\"World Maritime University Library\",\"code\":\"WMU\"},{\"name\":\"ebrary\",\"code\":\"EBR\"}]",
86
+ :headers => {})
87
+ client.branches
88
+ WebMock.should have_requested(:get, "http://localhost/koha/branches")
89
+ end
90
+
91
+
92
+ # user
93
+ it "should call the user/all method for #all_users" do
94
+ stub_request(:get, "http://localhost/koha/user/all").to_return(:status => 200, :body =>
95
+ "[{\"categorycode\":\"T\",\"B_address\":\"\",\"contactnote\":\"\",\"ethnicity\":null,\"email\":\"foo@wmu.se\",\"password\":\"1+1898juif\",\"B_country\":\"\",\"borrowernumber\":\"5\",\"lost\":\"0\",\"branchcode\":\"WMU\",\"streettype\":null,\"altcontactaddress3\":\"\",\"contactfirstname\":null,\"title\":\"\",\"attributes\":[],\"ethnotes\":null,\"relationship\":null,\"mobile\":\"\",\"fax\":\"\",\"altcontactphone\":\"\",\"contactname\":\"WMU\",\"country\":\"Sweden\",\"dateenrolled\":\"2010-02-03\",\"altcontactstate\":null,\"guarantorid\":\"0\",\"address2\":\"\",\"borrowernotes\":\"\",\"dateexpiry\":\"2018-05-03\",\"sort2\":\"\",\"contacttitle\":null,\"phonepro\":\"+46-40-35 63 90\",\"smsalertnumber\":null,\"B_streetnumber\":null,\"emailpro\":\"mbf@wmu.se\",\"firstname\":\"Michael\",\"altcontactcountry\":\"\",\"gonenoaddress\":\"0\",\"othernames\":\"\",\"state\":null,\"dateofbirth\":null,\"altcontactaddress2\":\"\",\"B_streettype\":null,\"debarred\":null,\"B_state\":null,\"address\":\"PO Box 500\",\"B_address2\":\"\",\"privacy\":\"1\",\"streetnumber\":\"\",\"surname\":\"BALDAUF\",\"cardnumber\":\"MBF\",\"altcontactsurname\":\"\",\"altcontactzipcode\":\"\",\"opacnote\":\"\",\"altcontactfirstname\":\"\",\"userid\":\"mbf\",\"B_zipcode\":\"\",\"B_email\":\"\",\"city\":\"Malm\",\"B_phone\":\"\",\"debarredcomment\":null,\"initials\":\"MB\",\"sort1\":\"\",\"flags\":null,\"zipcode\":\"20124\",\"phone\":\"\",\"sex\":\"M\",\"altcontactaddress1\":\"\",\"B_city\":\"\"}]"
96
+ )
97
+ client.all_users
98
+ WebMock.should have_requested(:get, "http://localhost/koha/user/all")
99
+ end
100
+
101
+
102
+ it "should call the user/all method for #all_users" do
103
+ stub_request(:get, "http://localhost/koha/user/today").to_return(:status => 200, :body =>
104
+ "[{\"categorycode\":\"T\",\"B_address\":\"\",\"contactnote\":\"\",\"ethnicity\":null,\"email\":\"foo@wmu.se\",\"password\":\"1+1898juif\",\"B_country\":\"\",\"borrowernumber\":\"5\",\"lost\":\"0\",\"branchcode\":\"WMU\",\"streettype\":null,\"altcontactaddress3\":\"\",\"contactfirstname\":null,\"title\":\"\",\"attributes\":[],\"ethnotes\":null,\"relationship\":null,\"mobile\":\"\",\"fax\":\"\",\"altcontactphone\":\"\",\"contactname\":\"WMU\",\"country\":\"Sweden\",\"dateenrolled\":\"2010-02-03\",\"altcontactstate\":null,\"guarantorid\":\"0\",\"address2\":\"\",\"borrowernotes\":\"\",\"dateexpiry\":\"2018-05-03\",\"sort2\":\"\",\"contacttitle\":null,\"phonepro\":\"+46-40-35 63 90\",\"smsalertnumber\":null,\"B_streetnumber\":null,\"emailpro\":\"mbf@wmu.se\",\"firstname\":\"Michael\",\"altcontactcountry\":\"\",\"gonenoaddress\":\"0\",\"othernames\":\"\",\"state\":null,\"dateofbirth\":null,\"altcontactaddress2\":\"\",\"B_streettype\":null,\"debarred\":null,\"B_state\":null,\"address\":\"PO Box 500\",\"B_address2\":\"\",\"privacy\":\"1\",\"streetnumber\":\"\",\"surname\":\"BALDAUF\",\"cardnumber\":\"MBF\",\"altcontactsurname\":\"\",\"altcontactzipcode\":\"\",\"opacnote\":\"\",\"altcontactfirstname\":\"\",\"userid\":\"mbf\",\"B_zipcode\":\"\",\"B_email\":\"\",\"city\":\"Malm\",\"B_phone\":\"\",\"debarredcomment\":null,\"initials\":\"MB\",\"sort1\":\"\",\"flags\":null,\"zipcode\":\"20124\",\"phone\":\"\",\"sex\":\"M\",\"altcontactaddress1\":\"\",\"B_city\":\"\"}]"
105
+ )
106
+ client.today_users
107
+ WebMock.should have_requested(:get, "http://localhost/koha/user/today")
108
+ end
109
+
110
+ it "should call the user holds method for #user_holds" do
111
+ stub_request(:get, "http://localhost/koha/user/byid/1/holds").to_return(:status => 200, :body =>
112
+ "[{\"itemnumber\":null,\"branchname\":\"World Maritime University Library\",\"itemcallnumber\":null,\"hold_id\":null,\"reservedate\":\"2013-02-20\",\"barcode\":null,\"found\":null,\"biblionumber\":\"76356\",\"cancellationdate\":null,\"title\":\"Asian approaches to international law and the legacy of colonialism and imperialism :\",\"rank\":\"1\",\"branchcode\":\"WMU\"}]" )
113
+ client.user_holds(:borrowernumber => "1")
114
+ WebMock.should have_requested(:get, "http://localhost/koha/user/byid/1/holds")
115
+ end
116
+
117
+
118
+ it "should call the user issues method for #user_issues" do
119
+ stub_request(:get, "http://localhost/koha/user/cf/issues").to_return(:status => 200, :body =>
120
+ "[{\"itemnumber\":\"42414\",\"itemcallnumber\":\"KD1819 .H54 2003\",\"barcode\":\"022593\",\"date_due\":\"2013-03-11T23:59:00\",\"renewable\":true,\"issuedate\":\"2012-11-21T00:00:00\",\"biblionumber\":\"17454\",\"title\":\"Maritime law\",\"borrowernumber\":\"544\",\"branchcode\":\"WMU\"}]" )
121
+ client.user_issues(:borrowername => "cf")
122
+ WebMock.should have_requested(:get, "http://localhost/koha/user/cf/issues")
123
+ end
124
+
125
+
126
+ # biblio and items
127
+
128
+ it "should call the biblio items method for #find_biblio" do
129
+ stub_request(:get, "http://localhost/koha/biblio/1/items").to_return(:status => 200, :body =>
130
+ "[{\"withdrawn\":\"0\",\"biblioitemnumber\":\"1\",\"restricted\":null,\"wthdrawn\":\"0\",\"holdingbranchname\":\"World Maritime University Library\",\"notforloan\":\"0\",\"replacementpricedate\":\"2010-02-05\",\"itemnumber\":\"1\",\"ccode\":null,\"itemnotes\":null,\"location\":\"GEN\",\"itemcallnumber\":\"HD30.3 .A32 1989\",\"stack\":null,\"date_due\":\"\",\"barcode\":\"011376\",\"itemlost\":\"0\",\"uri\":null,\"materials\":null,\"datelastseen\":\"2010-02-05\",\"price\":null,\"issues\":null,\"homebranch\":\"WMU\",\"replacementprice\":null,\"more_subfields_xml\":null,\"cn_source\":\"lcc\",\"homebranchname\":\"World Maritime University Library\",\"booksellerid\":null,\"biblionumber\":\"1\",\"renewals\":null,\"holdingbranch\":\"WMU\",\"timestamp\":\"2012-11-28 07:47:23\",\"damaged\":\"0\",\"stocknumber\":null,\"cn_sort\":\"HD_00030_3_A32_1989\",\"reserves\":null,\"dateaccessioned\":\"2010-02-05\",\"datelastborrowed\":null,\"enumchron\":null,\"copynumber\":\"1\",\"permanent_location\":null,\"onloan\":null,\"paidfor\":null,\"itype\":\"BOOK\"}]" )
131
+ client.find_biblio("1")
132
+ WebMock.should have_requested(:get, "http://localhost/koha/biblio/1/items")
133
+ end
134
+
135
+
136
+ it "should call the biblio items method for #find_biblio" do
137
+ stub_request(:get, "http://localhost/koha/biblio/1/items").to_return(:status => 200, :body =>
138
+ "[{\"withdrawn\":\"0\",\"biblioitemnumber\":\"1\",\"restricted\":null,\"wthdrawn\":\"0\",\"holdingbranchname\":\"World Maritime University Library\",\"notforloan\":\"0\",\"replacementpricedate\":\"2010-02-05\",\"itemnumber\":\"1\",\"ccode\":null,\"itemnotes\":null,\"location\":\"GEN\",\"itemcallnumber\":\"HD30.3 .A32 1989\",\"stack\":null,\"date_due\":\"\",\"barcode\":\"011376\",\"itemlost\":\"0\",\"uri\":null,\"materials\":null,\"datelastseen\":\"2010-02-05\",\"price\":null,\"issues\":null,\"homebranch\":\"WMU\",\"replacementprice\":null,\"more_subfields_xml\":null,\"cn_source\":\"lcc\",\"homebranchname\":\"World Maritime University Library\",\"booksellerid\":null,\"biblionumber\":\"1\",\"renewals\":null,\"holdingbranch\":\"WMU\",\"timestamp\":\"2012-11-28 07:47:23\",\"damaged\":\"0\",\"stocknumber\":null,\"cn_sort\":\"HD_00030_3_A32_1989\",\"reserves\":null,\"dateaccessioned\":\"2010-02-05\",\"datelastborrowed\":null,\"enumchron\":null,\"copynumber\":\"1\",\"permanent_location\":null,\"onloan\":null,\"paidfor\":null,\"itype\":\"BOOK\"}]" )
139
+ client.find_biblio("1")
140
+ WebMock.should have_requested(:get, "http://localhost/koha/biblio/1/items")
141
+ end
142
+
143
+ it "should call the biblio holdable for #biblio_holdable?" do
144
+ stub_request(:get, "http://localhost/koha/biblio/1/holdable").to_return(:status => 200, :body =>
145
+ "{\"is_holdable\":true,\"reasons\":[]}" )
146
+ client.biblio_holdable?("1").should be_true
147
+ WebMock.should have_requested(:get, "http://localhost/koha/biblio/1/holdable")
148
+ end
149
+
150
+ # show how to use a borrowername
151
+ it "should call the biblio holdable items for #biblio_items_holdable?" do
152
+ stub_request(:get, "http://localhost/koha/biblio/1/items_holdable_status?user_name=cf").to_return(:status => 200, :body =>
153
+ "{\"is_holdable\":true,\"reasons\":[]}" )
154
+ client.biblio_items_holdable?("1", :borrowername => "cf" ).should be_true
155
+ WebMock.should have_requested(:get, "http://localhost/koha/biblio/1/items_holdable_status?user_name=cf")
156
+ end
157
+
158
+ # show how to use a borrowernumber
159
+ it "should call the items holdable API method for #item_holdable?" do
160
+ stub_request(:get, "http://localhost/koha/item/74297/holdable?user_name=cf").to_return(:status => 200, :body =>
161
+ "{\"is_holdable\":false,\"reasons\":[]}" )
162
+ client.item_holdable?("74297", :borrowername => "cf" ).should be_false
163
+ WebMock.should have_requested(:get, "http://localhost/koha/item/74297/holdable?user_name=cf")
164
+ end
165
+
166
+
167
+
168
+ end
169
+
170
+
171
+ end
172
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+ require 'base64'
3
+
4
+ describe "Koha::Connection" do
5
+
6
+ context "setup_raw_request" do
7
+
8
+ before(:each) do
9
+ @c = Koha::Connection.new
10
+ @base_url = "http://localhost:8983/koha/rest.pl"
11
+ @client = Koha::Client.new @c, :url => @base_url
12
+ end
13
+
14
+ it "should set up a get request" do
15
+ req = @c.send :setup_raw_request, {:headers => {"content-type" => "application/json"}, :method => :get, :uri => URI.parse(@base_url + "/biblio/1/items?borrowername=cf")}
16
+ headers = {}
17
+ req.each_header{|k,v| headers[k] = v}
18
+ req.method.should == "GET"
19
+ headers.should == {"content-type"=>"application/json"}
20
+ end
21
+
22
+ it "should set up a post request" do
23
+ req = @c.send :setup_raw_request, {:headers => {"content-type" => "application/json"}, :method => :post, :uri => URI.parse(@base_url + "/biblio/1/items?borrowername=cf")}
24
+ headers = {}
25
+ req.each_header{|k,v| headers[k] = v}
26
+ req.method.should == "POST"
27
+ headers.should == {"content-type"=>"application/json"}
28
+ end
29
+
30
+ it "should set up a post request" do
31
+ req = @c.send :setup_raw_request, {:headers => {"content-type" => "application/json"}, :method => :put, :uri => URI.parse(@base_url + "/biblio/1/items?borrowername=cf")}
32
+ headers = {}
33
+ req.each_header{|k,v| headers[k] = v}
34
+ req.method.should == "PUT"
35
+ headers.should == {"content-type"=>"application/json"}
36
+ end
37
+
38
+ it "should raise error if something weird is set as method" do
39
+ expect { @c.send :setup_raw_request, {:headers => {"content-type" => "application/json"}, :method => :head, :uri => URI.parse(@base_url + "/biblio/1/items?borrowername=cf")} }.to raise_error("Only :get, :post and :head http method types are allowed.")
40
+
41
+ end
42
+
43
+ end
44
+
45
+ context "read timeout configuration" do
46
+ let(:client) { mock.as_null_object }
47
+
48
+ let(:http) { mock(Net::HTTP).as_null_object }
49
+
50
+ subject { Koha::Connection.new }
51
+
52
+ before do
53
+ Net::HTTP.stub(:new) { http }
54
+ end
55
+
56
+ it "should configure Net:HTTP read_timeout" do
57
+ http.should_receive(:read_timeout=).with(42)
58
+ subject.execute client, {:uri => URI.parse("http://localhost/some_uri"), :method => :get, :read_timeout => 42}
59
+ end
60
+
61
+ it "should use Net:HTTP default read_timeout if not specified" do
62
+ http.should_not_receive(:read_timeout=)
63
+ subject.execute client, {:uri => URI.parse("http://localhost/some_uri"), :method => :get}
64
+ end
65
+ end
66
+
67
+ context "open timeout configuration" do
68
+ let(:client) { mock.as_null_object }
69
+
70
+ let(:http) { mock(Net::HTTP).as_null_object }
71
+
72
+ subject { Koha::Connection.new }
73
+
74
+ before do
75
+ Net::HTTP.stub(:new) { http }
76
+ end
77
+
78
+ it "should configure Net:HTTP open_timeout" do
79
+ http.should_receive(:open_timeout=).with(42)
80
+ subject.execute client, {:uri => URI.parse("http://localhost/some_uri"), :method => :get, :open_timeout => 42}
81
+ end
82
+
83
+ it "should use Net:HTTP default open_timeout if not specified" do
84
+ http.should_not_receive(:open_timeout=)
85
+ subject.execute client, {:uri => URI.parse("http://localhost/some_uri"), :method => :get}
86
+ end
87
+ end
88
+
89
+ context "connection refused" do
90
+ let(:client) { mock.as_null_object }
91
+
92
+ let(:http) { mock(Net::HTTP).as_null_object }
93
+ let(:request_context) {
94
+ {:uri => URI.parse("http://localhost/some_uri"), :method => :get, :open_timeout => 42}
95
+ }
96
+ subject { Koha::Connection.new }
97
+
98
+ before do
99
+ Net::HTTP.stub(:new) { http }
100
+ end
101
+
102
+ it "should configure Net:HTTP open_timeout" do
103
+ http.should_receive(:request).and_raise(Errno::ECONNREFUSED)
104
+ lambda {
105
+ subject.execute client, request_context
106
+ }.should raise_error(Errno::ECONNREFUSED, /#{request_context}/)
107
+ end
108
+
109
+ # there is a strange ruby bug about closing a connection....this catches it.
110
+ it "should raise NoMethodError if a non-method is called" do
111
+ http.should_receive(:request).and_raise(NoMethodError)
112
+ lambda {
113
+ subject.execute client, request_context
114
+ }.should raise_error(NoMethodError, /NoMethodError/)
115
+ end
116
+
117
+ it "should raise NoMethodError if a non-method is called" do
118
+ error = NoMethodError.new("undefined method `closed?' for nil:NilClass")
119
+ http.should_receive(:request).and_raise(error)
120
+ lambda {
121
+ subject.execute client, request_context
122
+ }.should raise_error(Errno::ECONNREFUSED)
123
+ end
124
+
125
+ end
126
+
127
+ describe "basic auth support" do
128
+ let(:http) { mock(Net::HTTP).as_null_object }
129
+
130
+ before do
131
+ Net::HTTP.stub(:new) { http }
132
+ end
133
+
134
+ it "sets the authorization header" do
135
+ http.should_receive(:request) do |request|
136
+ request.fetch('authorization').should == "Basic #{Base64.encode64("joe:pass")}".strip
137
+ mock(Net::HTTPResponse).as_null_object
138
+ end
139
+ Koha::Connection.new.execute nil, :uri => URI.parse("http://joe:pass@localhost/koha/rest.pl"), :method => :get
140
+ end
141
+ end
142
+
143
+ describe "proxy support" do
144
+ it "set the proxy" do
145
+ @mock_proxy = mock('Proxy')
146
+ @mock_proxy.should_receive(:new).with("localhost", 80).and_return( mock(Net::HTTP).as_null_object)
147
+ Net::HTTP.should_receive(:Proxy).with("proxy", 80, nil, nil).and_return(@mock_proxy)
148
+ Koha::Connection.new.execute nil, :uri => URI.parse("http://localhost/koha/rest.pl"), :method => :get, :proxy => URI.parse("http://proxy/pass.pl")
149
+ end
150
+ end
151
+
152
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ describe "Koha::Uri" do
3
+
4
+ context "class-level methods" do
5
+
6
+ let(:uri){ Koha::Uri }
7
+
8
+ it "should return a URI object with a trailing slash" do
9
+ u = uri.create 'http://koha.org'
10
+ u.path[0].should == ?/
11
+ end
12
+
13
+ it "should return the bytesize of a string" do
14
+ uri.bytesize("test").should == 4
15
+ end
16
+
17
+
18
+
19
+ context "escape_query_value" do
20
+
21
+ it 'should escape &' do
22
+ uri.to_params(:biblionumber => "&").should == 'biblionumber=%26'
23
+ end
24
+
25
+ it 'should escape &' do
26
+ uri.to_params({:biblionumber => "&" }, false).should == 'biblionumber=&'
27
+ end
28
+
29
+ it 'should escape &' do
30
+ uri.to_params(:biblionumber => [1,2,3] ).should == 'biblionumber=1+2+3'
31
+ end
32
+
33
+
34
+ it 'should convert spaces to +' do
35
+ uri.to_params(:biblionumber => "me and you").should == 'biblionumber=me+and+you'
36
+ end
37
+
38
+ it 'should escape comlex queries, part 1' do
39
+ my_params = {'fq' => '{!raw f=field_name}crazy+\"field+value'}
40
+ expected = 'fq=%7B%21raw+f%3Dfield_name%7Dcrazy%2B%5C%22field%2Bvalue'
41
+ uri.to_params(my_params).should == expected
42
+ end
43
+
44
+ it 'should escape complex queries, part 2' do
45
+ my_params = {'q' => '+popularity:[10 TO *] +section:0'}
46
+ expected = 'q=%2Bpopularity%3A%5B10+TO+%2A%5D+%2Bsection%3A0'
47
+ uri.to_params(my_params).should == expected
48
+ end
49
+
50
+ it 'should escape properly' do
51
+ uri.escape_query_value('+').should == '%2B'
52
+ uri.escape_query_value('This is a test').should == 'This+is+a+test'
53
+ uri.escape_query_value('<>/\\').should == '%3C%3E%2F%5C'
54
+ uri.escape_query_value('"').should == '%22'
55
+ uri.escape_query_value(':').should == '%3A'
56
+ end
57
+
58
+ it 'should escape brackets' do
59
+ uri.escape_query_value('{').should == '%7B'
60
+ uri.escape_query_value('}').should == '%7D'
61
+ end
62
+
63
+ it 'should escape exclamation marks!' do
64
+ uri.escape_query_value('!').should == '%21'
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,8 @@
1
+ require File.expand_path('../../lib/koha', __FILE__)
2
+ require 'webmock/rspec'
3
+ if ENV["COVERAGE"] == 'yes'
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_filter 'spec'
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ require "rubygems"
2
+ require 'rspec'
3
+ require 'rspec/core/rake_task'
4
+
5
+ namespace :spec do
6
+
7
+ desc 'run api specs (mock out Koha dependency)'
8
+ RSpec::Core::RakeTask.new(:api) do |t|
9
+
10
+ t.pattern = [File.join('spec', 'spec_helper.rb')]
11
+ t.pattern += FileList[File.join('spec', 'api', '**', '*_spec.rb')]
12
+
13
+ t.verbose = true
14
+ t.rspec_opts = ['--color']
15
+ end
16
+ =begin
17
+ desc 'run integration specs'
18
+ RSpec::Core::RakeTask.new(:integration) do |t|
19
+
20
+ t.pattern = [File.join('spec', 'spec_helper.rb')]
21
+ t.pattern += FileList[File.join('spec', 'integration', '**', '*_spec.rb')]
22
+
23
+ t.verbose = true
24
+ t.rspec_opts = ['--color']
25
+ end
26
+ =end
27
+
28
+ end
@@ -0,0 +1,6 @@
1
+ require 'yard'
2
+
3
+ YARD::Rake::YardocTask.new do |t|
4
+ t.files = ['lib/**/*.rb'] # optional
5
+ t.options = ['--any', '--extra', '--opts'] # optional
6
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: koha
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - chris fitzpatrick
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: simplecov
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.7.1
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.7.1
30
+ - !ruby/object:Gem::Dependency
31
+ name: yard
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.8.4.1
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.8.4.1
46
+ - !ruby/object:Gem::Dependency
47
+ name: webmock
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.9.3
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.9.3
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 10.0.3
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 10.0.3
78
+ - !ruby/object:Gem::Dependency
79
+ name: rdoc
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 3.9.5
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 3.9.5
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 2.6.0
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 2.6.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: redcarpet
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: ! 'Easy interface for the Koha ILSDI API (https://github.com/Koha-Community/Koha/blob/master/C4/ILSDI/Services.pm) '
127
+ email:
128
+ - chrisfitzpat@gmail.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - .gitignore
134
+ - .travis.yml
135
+ - CHANGELOG
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - koha.gemspec
141
+ - lib/koha.rb
142
+ - lib/koha/client.rb
143
+ - lib/koha/connection.rb
144
+ - lib/koha/error.rb
145
+ - lib/koha/uri.rb
146
+ - lib/koha/version.rb
147
+ - spec/api/client_spec.rb
148
+ - spec/api/connection_spec.rb
149
+ - spec/api/uri_spec.rb
150
+ - spec/spec_helper.rb
151
+ - tasks/spec.rake
152
+ - tasks/yard.rake
153
+ homepage:
154
+ licenses: []
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ none: false
167
+ requirements:
168
+ - - ! '>='
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ requirements: []
172
+ rubyforge_project:
173
+ rubygems_version: 1.8.24
174
+ signing_key:
175
+ specification_version: 3
176
+ summary: A Ruby client for Koha ILSDI interface
177
+ test_files: []
178
+ has_rdoc: