summon 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.specification +58 -0
- data/History.txt +4 -0
- data/Manifest.txt +52 -0
- data/PostInstall.txt +4 -0
- data/README.rdoc +77 -0
- data/Rakefile +25 -0
- data/bin/summon +10 -0
- data/bin/summonh +19 -0
- data/ispec/integration_spec.rb +61 -0
- data/lib/summon.rb +41 -0
- data/lib/summon/cli.rb +136 -0
- data/lib/summon/log.rb +40 -0
- data/lib/summon/schema.rb +109 -0
- data/lib/summon/schema/availability.rb +14 -0
- data/lib/summon/schema/citation.rb +11 -0
- data/lib/summon/schema/date.rb +23 -0
- data/lib/summon/schema/document.rb +84 -0
- data/lib/summon/schema/error.rb +4 -0
- data/lib/summon/schema/facet.rb +51 -0
- data/lib/summon/schema/query.rb +96 -0
- data/lib/summon/schema/range.rb +52 -0
- data/lib/summon/schema/search.rb +37 -0
- data/lib/summon/schema/suggestion.rb +5 -0
- data/lib/summon/service.rb +44 -0
- data/lib/summon/transport.rb +13 -0
- data/lib/summon/transport/canned.json +2327 -0
- data/lib/summon/transport/canned.rb +9 -0
- data/lib/summon/transport/errors.rb +12 -0
- data/lib/summon/transport/headers.rb +55 -0
- data/lib/summon/transport/http.rb +114 -0
- data/lib/summon/transport/qstring.rb +49 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/summon/log_spec.rb +28 -0
- data/spec/summon/schema/availability_spec.rb +31 -0
- data/spec/summon/schema/citation_spec.rb +34 -0
- data/spec/summon/schema/date_spec.rb +12 -0
- data/spec/summon/schema/document_spec.rb +235 -0
- data/spec/summon/schema/facet_spec.rb +115 -0
- data/spec/summon/schema/query_spec.rb +163 -0
- data/spec/summon/schema/range_spec.rb +45 -0
- data/spec/summon/schema/search_spec.rb +62 -0
- data/spec/summon/schema_spec.rb +143 -0
- data/spec/summon/service_spec.rb +18 -0
- data/spec/summon/transport/headers_spec.rb +47 -0
- data/spec/summon/transport/http_spec.rb +29 -0
- data/spec/summon/transport/qstring_spec.rb +24 -0
- data/spec/summon_spec.rb +48 -0
- data/summon.gemspec +41 -0
- metadata +145 -0
data/lib/summon/log.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Summon
|
4
|
+
class Log
|
5
|
+
|
6
|
+
attr_reader :impl
|
7
|
+
|
8
|
+
def initialize(spec = nil)
|
9
|
+
@impl = create spec
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(name, *args, &block)
|
13
|
+
@impl.respond_to?(name) ? @impl.send(name, *args, &block) : super(name, *args, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to?(name)
|
17
|
+
@impl.respond_to?(name) || super(name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create(spec)
|
23
|
+
case spec
|
24
|
+
when nil
|
25
|
+
Logger.new($stderr).tap do |this|
|
26
|
+
this.level = Logger::WARN
|
27
|
+
end
|
28
|
+
when Hash
|
29
|
+
level = spec[:level] || "warn"
|
30
|
+
init = spec[:initialize] || [$stderr]
|
31
|
+
Logger.new(*init).tap do |this|
|
32
|
+
this.level = Logger.const_get(level.to_s.upcase)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
spec
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
|
2
|
+
module Summon
|
3
|
+
class Schema
|
4
|
+
def self.inherited(mod)
|
5
|
+
class << mod
|
6
|
+
include Summon::Schema::Initializer
|
7
|
+
include Summon::Schema::ClassMethods
|
8
|
+
end
|
9
|
+
mod.module_eval do
|
10
|
+
include Summon::Schema::InstanceMethods
|
11
|
+
end
|
12
|
+
mod.summon!
|
13
|
+
end
|
14
|
+
|
15
|
+
module Initializer
|
16
|
+
def new(values = {})
|
17
|
+
dup = {}
|
18
|
+
for k, v in values
|
19
|
+
dup[k.to_s] = v
|
20
|
+
end
|
21
|
+
instance = allocate
|
22
|
+
instance.instance_eval do
|
23
|
+
@src = values
|
24
|
+
end
|
25
|
+
for attribute in @attrs
|
26
|
+
instance.instance_variable_set("@#{attribute.name}", attribute.get(dup))
|
27
|
+
end
|
28
|
+
instance
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
|
34
|
+
def attr(name, options = {})
|
35
|
+
if name.to_s =~ /^(.*)\?$/
|
36
|
+
name = $1
|
37
|
+
options[:boolean] = true
|
38
|
+
end
|
39
|
+
symbol = name.to_sym
|
40
|
+
@attrs << ::Summon::Schema::Attr.new(symbol, options)
|
41
|
+
define_method(name) do |*args|
|
42
|
+
self.instance_variable_get("@#{name}")
|
43
|
+
end
|
44
|
+
if options[:boolean]
|
45
|
+
define_method("#{name}?") do
|
46
|
+
send(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def attrs
|
52
|
+
@attrs
|
53
|
+
end
|
54
|
+
|
55
|
+
def summon!
|
56
|
+
@attrs = []
|
57
|
+
attr_reader :src
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module InstanceMethods
|
62
|
+
def to_json(*a)
|
63
|
+
self.class.attrs.inject({}) do |json, attr|
|
64
|
+
json.merge attr.name => self.send(attr.name)
|
65
|
+
end.to_json(*a)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Attr
|
70
|
+
attr_reader :name
|
71
|
+
def initialize(name, options)
|
72
|
+
@name = name
|
73
|
+
@boolean = options[:boolean]
|
74
|
+
@camel_name = camelize(name.to_s)
|
75
|
+
@pascal_name = @camel_name.gsub(/^\w/) {|first| first.upcase}
|
76
|
+
@transform = options[:transform]
|
77
|
+
@json_name = options[:json_name].to_s if options[:json_name]
|
78
|
+
@json_name = "is#{@pascal_name}" if @boolean unless @json_name
|
79
|
+
@single = options[:single].nil? ? !(name.to_s.downcase =~ /s$/) : options[:single]
|
80
|
+
end
|
81
|
+
|
82
|
+
def get(json)
|
83
|
+
raw = json[@json_name || @camel_name]
|
84
|
+
raw = json[@pascal_name] if raw.nil?
|
85
|
+
if raw.nil?
|
86
|
+
@single ? nil : []
|
87
|
+
else
|
88
|
+
raw = @single && raw.kind_of?(Array) ? raw.first : raw
|
89
|
+
transform(raw) || raw
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def camelize(str)
|
94
|
+
str.gsub /(\w)_(\w)/ do
|
95
|
+
"#{$1}#{$2.upcase}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def transform(raw)
|
100
|
+
if @transform
|
101
|
+
ctor = proc do |h|
|
102
|
+
::Summon.const_get(@transform).new(h)
|
103
|
+
end
|
104
|
+
raw.kind_of?(Array) ? raw.map(&ctor) : ctor.call(raw)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Summon::Availability < Summon::Schema
|
2
|
+
attr :token
|
3
|
+
attr :status
|
4
|
+
attr :status_message
|
5
|
+
attr :location
|
6
|
+
attr :location_string
|
7
|
+
attr :call_number
|
8
|
+
|
9
|
+
def self.parse_results(results)
|
10
|
+
results["Result"]["RecordSummary"].map do |record|
|
11
|
+
new(record["Record"].merge(:token => record["ID"]))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
class Summon::Date < Summon::Schema
|
3
|
+
attr :day
|
4
|
+
attr :month
|
5
|
+
attr :text
|
6
|
+
attr :year
|
7
|
+
|
8
|
+
def day
|
9
|
+
@day.to_i if @day
|
10
|
+
end
|
11
|
+
|
12
|
+
def month
|
13
|
+
@month.to_i if @month
|
14
|
+
end
|
15
|
+
|
16
|
+
def year
|
17
|
+
@year.to_i if @year
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s(options = {})
|
21
|
+
"Date: #{text}"
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
|
2
|
+
class Summon::Document < Summon::Schema
|
3
|
+
attr :id, :json_name => "ID"
|
4
|
+
attr :title
|
5
|
+
attr :subtitle
|
6
|
+
attr :publication_title
|
7
|
+
attr :publication_series_title
|
8
|
+
attr :content_type
|
9
|
+
|
10
|
+
attr :authors, :json_name => "Author"
|
11
|
+
attr :publishers, :json_name => "Publisher"
|
12
|
+
attr :volume
|
13
|
+
attr :issue
|
14
|
+
attr :start_page
|
15
|
+
attr :end_page
|
16
|
+
attr :page_count
|
17
|
+
attr :publication_date, :json_name => "PublicationDate_xml", :transform => :Date
|
18
|
+
|
19
|
+
attr :dissertation_advisor
|
20
|
+
attr :dissertation_category
|
21
|
+
attr :dissertation_degree
|
22
|
+
attr :dissertation_degree_date
|
23
|
+
attr :dissertation_degree_date_decade
|
24
|
+
attr :dissertation_degree_date_century
|
25
|
+
attr :dissertation_degree_date_year
|
26
|
+
attr :dissertation_school
|
27
|
+
|
28
|
+
attr :library
|
29
|
+
attr :call_number, :json_name => "LCCallNum"
|
30
|
+
attr :doi, :json_name => "DOI"
|
31
|
+
attr :isbns, :json_name => "ISBN"
|
32
|
+
attr :issns, :json_name => "ISSN"
|
33
|
+
attr :patent_number
|
34
|
+
|
35
|
+
|
36
|
+
attr :subject_terms
|
37
|
+
attr :genres, :json_name => "Genre"
|
38
|
+
attr :languages, :json_name => "Language"
|
39
|
+
|
40
|
+
attr :snippet #highlight
|
41
|
+
attr :abstract
|
42
|
+
attr :fulltext, :boolean => true, :json_name => "hasFullText"
|
43
|
+
attr :url
|
44
|
+
attr :open_url
|
45
|
+
attr :subject_terms
|
46
|
+
|
47
|
+
attr :thumbnail_small, :json_name => "Thumbnail-s"
|
48
|
+
attr :thumbnail_medium, :json_name => "Thumbnail-m"
|
49
|
+
attr :thumbnail_large, :json_name => "Thumbnail-l"
|
50
|
+
attr :availability_token
|
51
|
+
|
52
|
+
|
53
|
+
def isbn
|
54
|
+
@isbns.first
|
55
|
+
end
|
56
|
+
|
57
|
+
def pages?
|
58
|
+
@start_page || @page_count
|
59
|
+
end
|
60
|
+
|
61
|
+
def publication_date
|
62
|
+
@publication_date || Summon::Date.new({})
|
63
|
+
end
|
64
|
+
|
65
|
+
def publisher
|
66
|
+
@publishers.first
|
67
|
+
end
|
68
|
+
|
69
|
+
def authors
|
70
|
+
@authors.map {|n| Summon::Author.new(n)}
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s(options = {})
|
74
|
+
"Title: #{title}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
class Summon::Author < Struct.new(:name)
|
80
|
+
def name(*args)
|
81
|
+
super()
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
class Summon::Facet < Summon::Schema
|
3
|
+
attr :page_size
|
4
|
+
attr :display_name
|
5
|
+
attr :field_name
|
6
|
+
attr :combine_mode
|
7
|
+
attr :page_number
|
8
|
+
attr :counts, :transform => :FacetCount
|
9
|
+
attr :remove_command
|
10
|
+
attr :remove_value_filters_command
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"Facet(#{display_name}, #{field_name})"
|
14
|
+
end
|
15
|
+
|
16
|
+
def range?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def empty?
|
21
|
+
@counts.empty?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Summon::FacetCount < Summon::Schema
|
26
|
+
attr :value
|
27
|
+
attr :count
|
28
|
+
attr :negated?
|
29
|
+
attr :applied?
|
30
|
+
attr :further_limiting?
|
31
|
+
attr :apply_command
|
32
|
+
attr :apply_negated_command
|
33
|
+
attr :remove_command
|
34
|
+
|
35
|
+
def escaped_value
|
36
|
+
Summon.escape(@value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def excluded?
|
40
|
+
negated?
|
41
|
+
end
|
42
|
+
|
43
|
+
def included?
|
44
|
+
applied? && !excluded?
|
45
|
+
end
|
46
|
+
|
47
|
+
def abs_value
|
48
|
+
value.sub /^-/, ''
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class Summon::Query < Summon::Schema
|
2
|
+
|
3
|
+
attr :page_number
|
4
|
+
attr :page_size
|
5
|
+
attr :search_terms
|
6
|
+
attr :query_string
|
7
|
+
attr :facet_value_filters, :transform => :FacetValueFilter
|
8
|
+
attr :facet_value_group_filters, :transform => :FacetValueGroupFilter
|
9
|
+
attr :text_queries
|
10
|
+
attr :text_filters
|
11
|
+
attr :range_filters, :transform => :RangeFilter
|
12
|
+
attr :facet_fields
|
13
|
+
attr :sorts, :transform => :Sort, :json_name => "sort"
|
14
|
+
attr :params
|
15
|
+
attr :holdings_only_enabled?
|
16
|
+
|
17
|
+
def sort
|
18
|
+
@sorts.first
|
19
|
+
end
|
20
|
+
|
21
|
+
def date_min
|
22
|
+
date_filter do |f|
|
23
|
+
val = f.range.min_value
|
24
|
+
return val == "*" ? nil : val.to_i
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def date_max
|
29
|
+
date_filter do |f|
|
30
|
+
val = f.range.max_value
|
31
|
+
return val == "*" ? nil : val.to_i
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def date_filter
|
36
|
+
range_filters.find {|f| f.field_name == "PublicationDate"}.tap do |this|
|
37
|
+
yield this if this && block_given?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_hash
|
42
|
+
return {} if query_string.nil? || query_string == ""
|
43
|
+
params = query_string.split("&").inject({}) do |params, param|
|
44
|
+
name, value = param.split("=")
|
45
|
+
name = CGI.unescape(name)
|
46
|
+
value = CGI.unescape(value)
|
47
|
+
params.tap do
|
48
|
+
case params[name]
|
49
|
+
when nil
|
50
|
+
params[name] = value
|
51
|
+
when String
|
52
|
+
params[name] = [params[name], value]
|
53
|
+
else
|
54
|
+
params[name] << value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Summon::Sort < Summon::Schema
|
62
|
+
attr :field_name
|
63
|
+
attr :sort_order
|
64
|
+
end
|
65
|
+
|
66
|
+
class Summon::FacetValueFilter < Summon::Schema
|
67
|
+
attr :negated?
|
68
|
+
attr :value
|
69
|
+
attr :field_name
|
70
|
+
attr :remove_command
|
71
|
+
attr :negate_command
|
72
|
+
end
|
73
|
+
|
74
|
+
class Summon::FacetValueGroupFilter < Summon::Schema
|
75
|
+
attr :combine_mode
|
76
|
+
attr :tag
|
77
|
+
attr :field_name
|
78
|
+
attr :remove_command
|
79
|
+
attr :values, :transform => :FacetValueGroupFilterValue
|
80
|
+
end
|
81
|
+
|
82
|
+
class Summon::FacetValueGroupFilterValue < Summon::Schema
|
83
|
+
attr :value
|
84
|
+
attr :remove_command
|
85
|
+
end
|
86
|
+
|
87
|
+
class Summon::RangeFilter < Summon::Schema
|
88
|
+
attr :field_name
|
89
|
+
attr :range, :transform => :Range
|
90
|
+
attr :remove_command
|
91
|
+
end
|
92
|
+
|
93
|
+
class Summon::Range < Summon::Schema
|
94
|
+
attr :min_value
|
95
|
+
attr :max_value
|
96
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
class Summon::RangeFacet < Summon::Schema
|
3
|
+
attr :display_name
|
4
|
+
attr :field_name
|
5
|
+
attr :remove_command
|
6
|
+
attr :counts, :transform => :RangeCount
|
7
|
+
|
8
|
+
def clear_filters_command
|
9
|
+
"removeRangeFilter(#{field_name})"
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
# #COMPATIBILITY
|
17
|
+
#
|
18
|
+
# alias_method :name, :display_name
|
19
|
+
# alias_method :label, :display_name
|
20
|
+
# alias_method :facets, :counts
|
21
|
+
# alias_method :ranges, :counts
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class Summon::RangeCount < Summon::Schema
|
26
|
+
attr :count
|
27
|
+
attr :applied?
|
28
|
+
attr :apply_command
|
29
|
+
attr :range
|
30
|
+
|
31
|
+
def min
|
32
|
+
@range["minValue"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def max
|
36
|
+
@range["maxValue"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def abs_value
|
40
|
+
""
|
41
|
+
end
|
42
|
+
|
43
|
+
def apply_negated_command
|
44
|
+
""
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method :included?, :applied?
|
48
|
+
|
49
|
+
def excluded?
|
50
|
+
false
|
51
|
+
end
|
52
|
+
end
|