summon 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.specification +58 -0
  2. data/History.txt +4 -0
  3. data/Manifest.txt +52 -0
  4. data/PostInstall.txt +4 -0
  5. data/README.rdoc +77 -0
  6. data/Rakefile +25 -0
  7. data/bin/summon +10 -0
  8. data/bin/summonh +19 -0
  9. data/ispec/integration_spec.rb +61 -0
  10. data/lib/summon.rb +41 -0
  11. data/lib/summon/cli.rb +136 -0
  12. data/lib/summon/log.rb +40 -0
  13. data/lib/summon/schema.rb +109 -0
  14. data/lib/summon/schema/availability.rb +14 -0
  15. data/lib/summon/schema/citation.rb +11 -0
  16. data/lib/summon/schema/date.rb +23 -0
  17. data/lib/summon/schema/document.rb +84 -0
  18. data/lib/summon/schema/error.rb +4 -0
  19. data/lib/summon/schema/facet.rb +51 -0
  20. data/lib/summon/schema/query.rb +96 -0
  21. data/lib/summon/schema/range.rb +52 -0
  22. data/lib/summon/schema/search.rb +37 -0
  23. data/lib/summon/schema/suggestion.rb +5 -0
  24. data/lib/summon/service.rb +44 -0
  25. data/lib/summon/transport.rb +13 -0
  26. data/lib/summon/transport/canned.json +2327 -0
  27. data/lib/summon/transport/canned.rb +9 -0
  28. data/lib/summon/transport/errors.rb +12 -0
  29. data/lib/summon/transport/headers.rb +55 -0
  30. data/lib/summon/transport/http.rb +114 -0
  31. data/lib/summon/transport/qstring.rb +49 -0
  32. data/script/console +10 -0
  33. data/script/destroy +14 -0
  34. data/script/generate +14 -0
  35. data/spec/spec.opts +1 -0
  36. data/spec/spec_helper.rb +19 -0
  37. data/spec/summon/log_spec.rb +28 -0
  38. data/spec/summon/schema/availability_spec.rb +31 -0
  39. data/spec/summon/schema/citation_spec.rb +34 -0
  40. data/spec/summon/schema/date_spec.rb +12 -0
  41. data/spec/summon/schema/document_spec.rb +235 -0
  42. data/spec/summon/schema/facet_spec.rb +115 -0
  43. data/spec/summon/schema/query_spec.rb +163 -0
  44. data/spec/summon/schema/range_spec.rb +45 -0
  45. data/spec/summon/schema/search_spec.rb +62 -0
  46. data/spec/summon/schema_spec.rb +143 -0
  47. data/spec/summon/service_spec.rb +18 -0
  48. data/spec/summon/transport/headers_spec.rb +47 -0
  49. data/spec/summon/transport/http_spec.rb +29 -0
  50. data/spec/summon/transport/qstring_spec.rb +24 -0
  51. data/spec/summon_spec.rb +48 -0
  52. data/summon.gemspec +41 -0
  53. metadata +145 -0
data/.specification ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: summon
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors: []
7
+
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-15 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib
26
+ - lib/summon
27
+ - lib/summon.rb
28
+ has_rdoc: true
29
+ homepage:
30
+ licenses: []
31
+
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - bin
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: "0"
43
+ version:
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ requirements: []
51
+
52
+ rubyforge_project:
53
+ rubygems_version: 1.3.4
54
+ signing_key:
55
+ specification_version: 3
56
+ summary:
57
+ test_files: []
58
+
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 1.0.0 2009-09-28
2
+
3
+ * 1 major enhancement:
4
+ * Initial Release
data/Manifest.txt ADDED
@@ -0,0 +1,52 @@
1
+ .specification
2
+ History.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.rdoc
6
+ Rakefile
7
+ bin/summon
8
+ bin/summonh
9
+ ispec/integration_spec.rb
10
+ lib/summon.rb
11
+ lib/summon/cli.rb
12
+ lib/summon/log.rb
13
+ lib/summon/schema.rb
14
+ lib/summon/schema/availability.rb
15
+ lib/summon/schema/citation.rb
16
+ lib/summon/schema/date.rb
17
+ lib/summon/schema/document.rb
18
+ lib/summon/schema/error.rb
19
+ lib/summon/schema/facet.rb
20
+ lib/summon/schema/query.rb
21
+ lib/summon/schema/range.rb
22
+ lib/summon/schema/search.rb
23
+ lib/summon/schema/suggestion.rb
24
+ lib/summon/service.rb
25
+ lib/summon/transport.rb
26
+ lib/summon/transport/canned.json
27
+ lib/summon/transport/canned.rb
28
+ lib/summon/transport/errors.rb
29
+ lib/summon/transport/headers.rb
30
+ lib/summon/transport/http.rb
31
+ lib/summon/transport/qstring.rb
32
+ script/console
33
+ script/destroy
34
+ script/generate
35
+ spec/spec.opts
36
+ spec/spec_helper.rb
37
+ spec/summon/log_spec.rb
38
+ spec/summon/schema/availability_spec.rb
39
+ spec/summon/schema/citation_spec.rb
40
+ spec/summon/schema/date_spec.rb
41
+ spec/summon/schema/document_spec.rb
42
+ spec/summon/schema/facet_spec.rb
43
+ spec/summon/schema/query_spec.rb
44
+ spec/summon/schema/range_spec.rb
45
+ spec/summon/schema/search_spec.rb
46
+ spec/summon/schema_spec.rb
47
+ spec/summon/service_spec.rb
48
+ spec/summon/transport/headers_spec.rb
49
+ spec/summon/transport/http_spec.rb
50
+ spec/summon/transport/qstring_spec.rb
51
+ spec/summon_spec.rb
52
+ summon.gemspec
data/PostInstall.txt ADDED
@@ -0,0 +1,4 @@
1
+
2
+ For comprehensive documentation on Summon API options and usage visit:
3
+
4
+ http://api.summon.serialssolutions.com
data/README.rdoc ADDED
@@ -0,0 +1,77 @@
1
+ = summon
2
+
3
+ * http://summon.rubyforge.org
4
+ * http://www.serialssolutions.com/summon
5
+ * http://api.summon.serialssolutions.com
6
+
7
+ == DESCRIPTION:
8
+
9
+ Ruby language bindings for Serials Solutions Summon Unified Discovery Service
10
+
11
+ == FEATURES/PROBLEMS:
12
+
13
+ * perform searches using the Summon public API from within ruby.
14
+
15
+ == SYNOPSIS:
16
+
17
+ === From the Command Line
18
+
19
+ #help
20
+ summon --help
21
+
22
+ #simple text query
23
+ summon "Will Shakespeare"
24
+
25
+ #books only please!
26
+ summon "Will Shakespeare" --s.cmd="addFacetValueFilters(ContentType, Book)"
27
+
28
+ #lots of results.
29
+ summon "Will Shakespeare" --s.cmd="addFacetValueFilters(ContentType, Book) setPageSize(100)"
30
+
31
+ === Inside Code
32
+
33
+ require 'summon'
34
+ @service = Summon::Service.new(:access_id => 'myaccessid', :secret_key => 'mysecretkey')
35
+
36
+ #search using parameters
37
+ search = @service.search("s.q" => "Elephants", "s.fvf" => "ContentType,Book")
38
+ puts search.record_count #=> 137
39
+ search.documents.each do |doc|
40
+ puts doc.title
41
+ end
42
+
43
+ #search using commands
44
+ search = @service.search("s.cmd" => "addTextQuery(Elephants) addFacetValueFilters(ContentType,Book)")
45
+
46
+ == REQUIREMENTS:
47
+
48
+ * ruby >= 1.8.6
49
+
50
+ == INSTALL:
51
+
52
+ * sudo gem install summon
53
+
54
+ == LICENSE:
55
+
56
+ (The MIT License)
57
+
58
+ Copyright (c) 2009 Serials Solutions LLC
59
+
60
+ Permission is hereby granted, free of charge, to any person obtaining
61
+ a copy of this software and associated documentation files (the
62
+ 'Software'), to deal in the Software without restriction, including
63
+ without limitation the rights to use, copy, modify, merge, publish,
64
+ distribute, sublicense, and/or sell copies of the Software, and to
65
+ permit persons to whom the Software is furnished to do so, subject to
66
+ the following conditions:
67
+
68
+ The above copyright notice and this permission notice shall be
69
+ included in all copies or substantial portions of the Software.
70
+
71
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
72
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
73
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
74
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
75
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
76
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
77
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/summon'
6
+
7
+ Hoe.plugin :newgem
8
+
9
+ # Generate all the Rake tasks
10
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
11
+ $hoe = Hoe.spec 'summon' do
12
+ self.developer 'Charles Lowell', 'cowboyd@thefrontside.net'
13
+ self.post_install_message = File.read('PostInstall.txt')
14
+ self.rubyforge_name = self.name
15
+ self.extra_deps = [
16
+ ['json_pure','>= 1.1.7']
17
+ ]
18
+ self.extra_dev_deps = [
19
+ ['rspec', '>= 1.2.7']
20
+ ]
21
+ self.version = Summon::VERSION
22
+ end
23
+
24
+ require 'newgem/tasks'
25
+ Dir['tasks/**/*.rake'].each { |t| load t }
data/bin/summon ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-7-14.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require 'rubygems'
7
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/summon")
8
+ require "summon/cli"
9
+
10
+ Summon::CLI.execute(STDOUT, ARGV)
data/bin/summonh ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/summon")
5
+ require "summon/cli"
6
+
7
+ raise "usage summonh <http query string>" unless ARGV.size == 1
8
+
9
+ params = ARGV[0].split("&").inject({}) do |hash, term|
10
+ k,v = term.split("=")
11
+ hash[CGI.unescape(k)] = CGI.unescape(v)
12
+ hash
13
+ end
14
+
15
+ @config = Summon::CLI::Config.load
16
+ @summon = Summon::Service.new(@config.options)
17
+ @summon.send(:connect, "/search", params) do |result|
18
+ puts JSON.pretty_generate(result)
19
+ end
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/../spec/spec_helper'
2
+ require 'cgi'
3
+ require 'summon/cli'
4
+
5
+ describe "summon" do
6
+ attr_reader :summon
7
+
8
+ before :all do
9
+ @config = Summon::CLI::Config.load
10
+ @summon = Summon::Service.new(@config.options)
11
+ end
12
+
13
+ describe "search" do
14
+ it "should do an empty query" do
15
+ search = summon.search
16
+
17
+ search.record_count.should > 0
18
+ search.should_not be_empty
19
+ end
20
+
21
+ it "should do a text query" do
22
+ search = summon.search "s.q" => "Probability models of recidivism\\\\\\: an exploration"
23
+ search.should_not be_empty
24
+ query(search).should == "s.q=Probability models of recidivism\\\\\\: an exploration"
25
+ end
26
+
27
+ it "should do one command" do
28
+ search = summon.search "s.cmd" => "addTextFilter(Author\\:\\(Linster\\, Richard L\\))"
29
+ query(search).should == "s.fq=Author:(Linster, Richard L)"
30
+ search.should_not be_empty
31
+ end
32
+
33
+ it "should do an interesting search by pieces" do
34
+ text_query = "Probability models of recidivism\\: an exploration"
35
+ command = "addTextFilter(Author_t\\:\\(Linster\\, Richard L\\))"
36
+
37
+ search = summon.search "s.q" => text_query, "s.cmd" => command
38
+
39
+ query(search).should == "s.fq=Author:(Linster, Richard L)&" +
40
+ "s.q=Probability models of recidivism\\: an exploration"
41
+ end
42
+
43
+ it "should modify a search" do
44
+ search = summon.search "s.q" => "Probability models of recidivism\\: an exploration"
45
+ search = summon.modify_search search, "addTextFilter(Author\\:\\(Linster\\, Richard L\\))"
46
+
47
+ query(search).should == "s.fq=Author:(Linster, Richard L)&" +
48
+ "s.q=Probability models of recidivism\\: an exploration"
49
+ end
50
+ end
51
+
52
+ def find(list, search_by, value)
53
+ item = list.find{|i| i.send(search_by) == value}
54
+ item.should_not be_nil
55
+ item
56
+ end
57
+
58
+ def query(search)
59
+ CGI::unescape(search.query.query_string)
60
+ end
61
+ end
data/lib/summon.rb ADDED
@@ -0,0 +1,41 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'cgi'
6
+ require 'json'
7
+
8
+ module Summon
9
+ VERSION = "1.0.0"
10
+ require 'summon/log'
11
+ require 'summon/service'
12
+ require 'summon/transport'
13
+ require 'summon/schema'
14
+ require 'summon/schema/query'
15
+ require 'summon/schema/search'
16
+ require 'summon/schema/facet'
17
+ require 'summon/schema/range'
18
+ require 'summon/schema/document'
19
+ require 'summon/schema/date'
20
+ require 'summon/schema/suggestion'
21
+ require 'summon/schema/availability'
22
+ require 'summon/schema/citation'
23
+ require 'summon/schema/error'
24
+
25
+ def self.escape(value)
26
+ value.gsub(/(,|\(|\)|\{|\}|\$|\:)/, '\\\\\1').gsub("\\", '\\')
27
+ end
28
+
29
+ def self.unescape(value)
30
+ value.gsub(/\\(.)/, '\1')
31
+ end
32
+ end
33
+
34
+ unless Object.method_defined?(:tap)
35
+ class Object
36
+ def tap(&block)
37
+ yield self
38
+ return self
39
+ end
40
+ end
41
+ end
data/lib/summon/cli.rb ADDED
@@ -0,0 +1,136 @@
1
+ require 'optparse'
2
+ require 'json'
3
+ require 'fileutils'
4
+
5
+ module Summon
6
+ class CLI
7
+ def self.execute(stdout, arguments=[])
8
+ config = Config.load
9
+ options = {
10
+ :log => {:level => :warn}
11
+ }
12
+ params = {
13
+ "s.pn" => 1,
14
+ "s.ps" => 10,
15
+ "s.q" => [],
16
+ "s.fq" => [],
17
+ "s.st" => [],
18
+ "s.cmd" => [],
19
+ "s.ff" => [],
20
+ "s.fvf" => [],
21
+ "s.fvgf" => [],
22
+ "s.rff" => [],
23
+ "s.hs" => "*",
24
+ "s.he" => "*"
25
+ }
26
+ raw = nil
27
+
28
+ mandatory_options = %w( )
29
+
30
+ parser = OptionParser.new do |opts|
31
+ opts.banner = <<-BANNER.gsub(/^ /,'')
32
+ Summon Unified Discovery Service
33
+
34
+ Usage: #{File.basename($0)} [options] [text query]*
35
+ See: http://api.summon.serialssolutions.com/help for details
36
+
37
+ BANNER
38
+ opts.separator "API query parameters"
39
+ opts.on("--s.cmd=COMMAND", String, "Command: pass a command to the API") {|cmd| params["s.cmd"] << cmd}
40
+ opts.on("--s.q=QUERY", String, "Query Term: add an all-field text query to the search") {|q| params["s.q"] << q}
41
+ opts.on("--s.fq=QUERY", String, "Filter Query: add a filter query which does not affect document relevance") {|fq| params["s.fq"] << fq}
42
+ opts.on("--s.st=FIELD,VALUE", String, "Search Term: add a query term to a field") {|t| params["s.st"] << t}
43
+ opts.on("--s.ff=FIELD,MODE,PAGE", String, "Facet Field: request facet counts for the given Field") {|f| params['s.ff'] << f}
44
+ opts.on("--s.fvf=FIELD,VALUE,NEGATED", "Facet Value Filter: applies an exact-value filter for a facet value within a facetable field: e.g. ContentType,Book") {|fvf| params["s.fvf"] << fvf}
45
+ opts.on("--s.fvgf=FIELD,MODE,VALUES", "Facet Value Group Filter: advanced functionality. see official api docs. e.g. 'SubjectTerms,or,northern+america,u.s.,canada'")
46
+ opts.on("--s.rff=<facetField>, <minValue>:<maxValue>[:<inclusive>][, <minValue>:<maxValue>[:<inclusive>]]*", "Range Facet Field: e.g. PublicationDate,1971:1980,1981:1990,1991:2000,2001:2010") {|rff| params["s.rff"] << rff}
47
+ opts.on("--s.rf=<fieldName>, <minValue>:<maxValue>[:<inclusive>]", "Range Filter: filter the result list to values lying within the range. e.g. PublicationDate,1971:1980")
48
+ opts.on("--s.hl=BOOLEAN", "Highlight: turn highlighting on or off: e.g. --s.hl=false") {|hl| params["s.hl"] = hl}
49
+ opts.on("--s.hs=DELIMITER", String, "Highlight Start: demarcate the beginning of a term hit. default is *") {|d| params["s.hs"] = d}
50
+ opts.on("--s.he=DELIMITER", String, "Highlight End: demarcate the beginning of a term hit. default is *") {|d| params["s.he"] = d}
51
+ opts.on("--s.ps=INT", Integer, "Page Size: number of documents to return per page") {|n| params["s.ps"] = n}
52
+ opts.on("--s.pn=INT", Integer, "Page Number: start the results at this page, starting with 1") {|n| params["s.pn"] = n}
53
+ opts.on("--s.ho=BOOLEAN", "Holdings Only: restrict this search to my institution's holdings only ") {|n| params["s.ho"] = n}
54
+ opts.on("--s.sort=FIELD:DIRECTION", "Sort: specifiy sort order (e.g. PublicationDate:DESC)") {|s| params["s.sort"] = s}
55
+ opts.on("--s.dym=BOOLEAN", "Did You Mean?: enables or disables search suggestions.") {|dym| params["s.dym"] = dym}
56
+
57
+ opts.separator ""
58
+ opts.separator "Configuration Options"
59
+ opts.on("-u", "--url=KEY", String, "Summon API Base URL", "Default: http://api.summon.serialssolutions.com",
60
+ "Default: ~/.summonrc[url]") {|key| options[:url] = key}
61
+ opts.on("-i", "--access-id=ID", String,
62
+ "Summon API Access ID",
63
+ "Default: ~/.summonrc[access_id]") { |id| options[:access_id] = id }
64
+ opts.on("-k", "--secret-key=KEY", String, "Summon API Secret Key", "Default: ~/.summonrc[secret_key]") {|key| options[:secret_key] = key}
65
+ opts.on("-c --sersol-client-id=CLIENT_HASH", String, "Specific Serials Solutions Client ID to use when making this query",
66
+ "Only useful when your access id is authorized to query multiple accounts") {|id| options[:client_key] = config.client_key(id)}
67
+
68
+ opts.on("-g", "--get=URL", "Takes a raw summon url, and queries the api without first performing any encoding.") {|url| raw = url}
69
+ opts.on("--verbose", "output more request information") {options[:log].merge! :level => :info }
70
+ opts.on("--debug", "output very detailed information") {options[:log].merge! :level => :debug }
71
+ opts.on("-h", "--help",
72
+ "Show this help message.") { stdout.puts opts; exit }
73
+
74
+ begin
75
+ opts.parse!(arguments)
76
+ rescue OptionParser::ParseError => e
77
+ puts e.message; exit
78
+ end
79
+
80
+ params["s.q"] << ARGV.join(' ') unless ARGV.empty?
81
+
82
+ if mandatory_options && mandatory_options.find { |option| options[option.to_sym].nil? }
83
+ stdout.puts opts; exit
84
+ end
85
+ end
86
+ begin
87
+ service = Summon::Service.new(config.options.merge options)
88
+ puts JSON.pretty_generate(raw ? service.transport.urlget(raw) : service.transport.get("/search", params))
89
+ rescue Summon::Transport::TransportError => e
90
+ puts e.message
91
+ end
92
+
93
+ end
94
+
95
+
96
+ class Config
97
+ attr_reader :url, :access_id, :secret_key
98
+
99
+ def initialize
100
+ @clients = {}
101
+ end
102
+
103
+ def self.load
104
+ summonrc = "#{ENV['HOME']}/.summonrc"
105
+ FileUtils.touch summonrc unless File.exists?(summonrc)
106
+ new.tap do |this|
107
+ this.instance_eval(File.read(summonrc), summonrc, 1)
108
+ end
109
+ end
110
+
111
+ def url(url)
112
+ @url = url
113
+ end
114
+
115
+ def access_id(access_id)
116
+ @access_id = access_id
117
+ end
118
+
119
+ def secret_key(secret_key)
120
+ @secret_key = secret_key
121
+ end
122
+
123
+ def clients(h)
124
+ @clients = h
125
+ end
126
+
127
+ def client_key(name)
128
+ @clients[name.to_sym] || name
129
+ end
130
+
131
+ def options
132
+ {:url => @url, :access_id => @access_id, :secret_key => @secret_key}
133
+ end
134
+ end
135
+ end
136
+ end