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/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,11 @@
1
+ class Summon::Citation < Summon::Schema
2
+ attr :name
3
+ attr :label
4
+ attr :short_name
5
+ attr :caption
6
+ attr :text
7
+
8
+ def self.parse_results(results)
9
+ results["Results"]["Citations"]["Citation"].map {|result| new(result) }
10
+ end
11
+ 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,4 @@
1
+ class Summon::Error < Summon::Schema
2
+ attr :suggestion, :transform => :Suggestion
3
+ attr :message
4
+ end
@@ -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