summon 1.0.0

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.
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