dnz-client 0.0.2 → 0.0.3
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.
- data/Manifest.txt +2 -0
- data/README.rdoc +2 -2
- data/lib/dnz/client.rb +34 -18
- data/lib/dnz/error/invalid_api_key.rb +13 -0
- data/lib/dnz/facet.rb +25 -3
- data/lib/dnz/facet_array.rb +16 -0
- data/lib/dnz/search.rb +40 -8
- data/lib/dnz.rb +1 -1
- data/spec/dnz/client_spec.rb +29 -1
- data/spec/dnz/search_spec.rb +16 -1
- metadata +3 -1
data/Manifest.txt
CHANGED
data/README.rdoc
CHANGED
@@ -18,13 +18,13 @@
|
|
18
18
|
|
19
19
|
== INSTALL:
|
20
20
|
|
21
|
-
* sudo gem install dnz-client
|
21
|
+
* sudo gem install dnz-client
|
22
22
|
|
23
23
|
== LICENSE:
|
24
24
|
|
25
25
|
(The MIT License)
|
26
26
|
|
27
|
-
Copyright (c) 2009 Boost New Media (http
|
27
|
+
Copyright (c) 2009 Boost New Media (http://www.boost.co.nz)
|
28
28
|
|
29
29
|
Permission is hereby granted, free of charge, to any person obtaining
|
30
30
|
a copy of this software and associated documentation files (the
|
data/lib/dnz/client.rb
CHANGED
@@ -3,6 +3,7 @@ require 'open-uri'
|
|
3
3
|
require 'set'
|
4
4
|
require 'active_support'
|
5
5
|
require 'dnz/search'
|
6
|
+
require 'dnz/error/invalid_api_key'
|
6
7
|
|
7
8
|
module DNZ
|
8
9
|
# This is a simple client for accessing the digitalnz.org API
|
@@ -26,17 +27,19 @@ module DNZ
|
|
26
27
|
}
|
27
28
|
|
28
29
|
ARGS = {
|
29
|
-
:
|
30
|
-
:
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
30
|
+
:v1 => {
|
31
|
+
:search => Set.new([
|
32
|
+
:search_text,
|
33
|
+
:api_key,
|
34
|
+
:num_results,
|
35
|
+
:start,
|
36
|
+
:sort,
|
37
|
+
:direction,
|
38
|
+
:facets,
|
39
|
+
:facet_num_results,
|
40
|
+
:facet_start,
|
41
|
+
])
|
42
|
+
}
|
40
43
|
}
|
41
44
|
|
42
45
|
# List of available facets that can be passed to search
|
@@ -77,6 +80,7 @@ module DNZ
|
|
77
80
|
# * <tt>:num_results</tt> - The number of results to return in this call. Defaults to 20.
|
78
81
|
# * <tt>:start</tt> - The starting offset of the results.
|
79
82
|
# * <tt>:facets</tt> - The facets to return for this search.
|
83
|
+
# * <tt>:filter</tt> - A hash of filters to apply to the results
|
80
84
|
#
|
81
85
|
# ==== Example
|
82
86
|
# search = client.search('rubgy', :num_results => 50)
|
@@ -87,7 +91,8 @@ module DNZ
|
|
87
91
|
options.reverse_merge!(
|
88
92
|
:search_text => text,
|
89
93
|
:num_results => 20,
|
90
|
-
:start => 0
|
94
|
+
:start => 0,
|
95
|
+
:filter => {}
|
91
96
|
)
|
92
97
|
|
93
98
|
# Select the correct page
|
@@ -112,19 +117,30 @@ module DNZ
|
|
112
117
|
#
|
113
118
|
#
|
114
119
|
|
115
|
-
|
116
|
-
qs = options.
|
120
|
+
# qs = options.map{|k,v| '%s=%s' % [k,v] }.join('&')
|
121
|
+
qs = options.to_query
|
117
122
|
url = self.base_url + '/' + APIS[api].gsub('${version}', self.version) + '?' + qs
|
118
|
-
|
123
|
+
|
124
|
+
begin
|
125
|
+
open(url)
|
126
|
+
rescue OpenURI::HTTPError => e
|
127
|
+
if e.to_s =~ /^401/
|
128
|
+
raise InvalidApiKeyError.new(self.api_key)
|
129
|
+
else
|
130
|
+
raise
|
131
|
+
end
|
132
|
+
end
|
119
133
|
end
|
120
134
|
|
121
135
|
private
|
122
136
|
|
123
137
|
def validate_options(path, options = {})
|
124
|
-
options.symbolize_keys
|
138
|
+
options = options.symbolize_keys
|
139
|
+
|
140
|
+
version_args = ARGS[@version.to_sym]
|
125
141
|
|
126
|
-
if
|
127
|
-
raise ArgumentError.new("Valid options for #{path} are: #{
|
142
|
+
if version_args.has_key?(path) && !Set.new(options.keys).subset?(version_args[path])
|
143
|
+
raise ArgumentError.new("Valid options for #{path} are: #{version_args[path].to_a.join(', ')}, provided: #{options.keys.join(', ')}")
|
128
144
|
end
|
129
145
|
end
|
130
146
|
end
|
data/lib/dnz/facet.rb
CHANGED
@@ -5,17 +5,39 @@ module DNZ
|
|
5
5
|
attr_reader :name
|
6
6
|
attr_reader :values
|
7
7
|
attr_reader :search
|
8
|
+
|
9
|
+
include Enumerable
|
8
10
|
|
9
11
|
def initialize(client, search, doc)
|
10
12
|
@name = doc.xpath('facet-field').text
|
11
|
-
@values =
|
13
|
+
@values = {}
|
12
14
|
@search = search
|
13
15
|
|
14
16
|
doc.xpath('values').first.children.each do |value_doc|
|
15
17
|
value = DNZ::FacetValue.new(client, self, value_doc)
|
16
|
-
@values
|
18
|
+
@values[value.name] = value if value.valid?
|
17
19
|
end
|
18
20
|
end
|
21
|
+
|
22
|
+
def values
|
23
|
+
@values.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](index)
|
27
|
+
@values[index]
|
28
|
+
end
|
29
|
+
|
30
|
+
def each
|
31
|
+
@values.each {|key, value| yield value }
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
values.join(', ')
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
'[ %s ]' % values.collect(&:inspect).join(', ')
|
40
|
+
end
|
19
41
|
end
|
20
42
|
|
21
43
|
class FacetValue
|
@@ -42,7 +64,7 @@ module DNZ
|
|
42
64
|
end
|
43
65
|
|
44
66
|
def to_s
|
45
|
-
self.name
|
67
|
+
'%s => %d' % [self.name, self.count]
|
46
68
|
end
|
47
69
|
end
|
48
70
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module DNZ
|
2
|
+
# Subclass of Array that allows retrieval by facet name
|
3
|
+
class FacetArray < Array
|
4
|
+
def names
|
5
|
+
self.collect(&:name).uniq
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](value)
|
9
|
+
if value.is_a?(String) || value.is_a?(Symbol)
|
10
|
+
self.detect{|f| f.name == value.to_s }
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/dnz/search.rb
CHANGED
@@ -18,6 +18,8 @@ module DNZ
|
|
18
18
|
# puts "%d results found on %d pages" % [search.result_count, search.pages]
|
19
19
|
class Search
|
20
20
|
attr_reader :result_count
|
21
|
+
|
22
|
+
extend ActiveSupport::Memoizable
|
21
23
|
|
22
24
|
# Constructor for Search class. Do not call this directly, instead use the <tt>Client.search</tt> method.
|
23
25
|
def initialize(client, search_options)
|
@@ -39,11 +41,6 @@ module DNZ
|
|
39
41
|
|
40
42
|
# An array of results. If the mislav-will_paginate gem is installed this will return a paginated array.
|
41
43
|
def results
|
42
|
-
if @results.nil?
|
43
|
-
parse_results
|
44
|
-
paginate_results if defined? WillPaginate::Collection
|
45
|
-
end
|
46
|
-
|
47
44
|
@results
|
48
45
|
end
|
49
46
|
|
@@ -56,7 +53,6 @@ module DNZ
|
|
56
53
|
# puts '%d results in category %s' % [category.count, category.name]
|
57
54
|
# end
|
58
55
|
def facets
|
59
|
-
parse_facets if @facets.nil?
|
60
56
|
@facets
|
61
57
|
end
|
62
58
|
|
@@ -93,6 +89,39 @@ module DNZ
|
|
93
89
|
end
|
94
90
|
|
95
91
|
private
|
92
|
+
|
93
|
+
def parsed_search_filter
|
94
|
+
filter = @search_options[:filter]
|
95
|
+
filter = {} unless filter.is_a?(Hash)
|
96
|
+
filter.symbolize_keys!
|
97
|
+
filter.map{|k,v| '%s:"%s"' % [k,v]}
|
98
|
+
end
|
99
|
+
memoize :parsed_search_filter
|
100
|
+
|
101
|
+
def parsed_search_text
|
102
|
+
if parsed_search_filter.any?
|
103
|
+
([text] + parsed_search_filter).join(' AND ')
|
104
|
+
else
|
105
|
+
text
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def parsed_search_facets
|
110
|
+
search_facets = @search_options[:facets] || []
|
111
|
+
search_facets = search_facets.join(',') if search_facets.is_a?(Array)
|
112
|
+
search_facets
|
113
|
+
end
|
114
|
+
|
115
|
+
def parsed_search_options
|
116
|
+
parsed_options = @search_options.dup
|
117
|
+
parsed_options.delete(:filter)
|
118
|
+
|
119
|
+
parsed_options[:search_text] = parsed_search_text
|
120
|
+
parsed_options[:facets] = parsed_search_facets
|
121
|
+
|
122
|
+
parsed_options
|
123
|
+
end
|
124
|
+
memoize :parsed_search_options
|
96
125
|
|
97
126
|
def doc
|
98
127
|
@doc ||= Nokogiri::XML(@xml)
|
@@ -102,9 +131,12 @@ module DNZ
|
|
102
131
|
@doc = nil
|
103
132
|
@results = nil
|
104
133
|
@facets = nil
|
105
|
-
@xml = @client.send(:fetch, :search,
|
134
|
+
@xml = @client.send(:fetch, :search, parsed_search_options)
|
106
135
|
|
107
136
|
parse_attributes
|
137
|
+
parse_facets
|
138
|
+
parse_results
|
139
|
+
paginate_results if defined? WillPaginate::Collection
|
108
140
|
|
109
141
|
self
|
110
142
|
end
|
@@ -132,7 +164,7 @@ module DNZ
|
|
132
164
|
end
|
133
165
|
end
|
134
166
|
|
135
|
-
def parse_facets
|
167
|
+
def parse_facets
|
136
168
|
@facets = FacetArray.new
|
137
169
|
|
138
170
|
doc.xpath('//facets/facet').each do |facet_xml|
|
data/lib/dnz.rb
CHANGED
data/spec/dnz/client_spec.rb
CHANGED
@@ -28,9 +28,37 @@ describe Client do
|
|
28
28
|
@client.should_receive(:open).with do |url|
|
29
29
|
url.should include('http://api.digitalnz.org/records/v1.xml/?')
|
30
30
|
url.should include('api_key=abc')
|
31
|
-
url.should include('search_text
|
31
|
+
url.should include('search_text=%2A%3A%2A')
|
32
32
|
end
|
33
33
|
@client.fetch(:search, :search_text => '*:*')
|
34
34
|
end
|
35
|
+
|
36
|
+
it 'should raise an InvalidApiKey error if the status is 401' do
|
37
|
+
@client.should_receive(:open).and_raise(OpenURI::HTTPError.new('401 Unauthorized', nil))
|
38
|
+
lambda do
|
39
|
+
@client.fetch(:search, :search_text => '*:*')
|
40
|
+
end.should raise_error(InvalidApiKeyError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#categories' do
|
45
|
+
before do
|
46
|
+
@categories = mock(:categories)
|
47
|
+
@category = mock(:facet)
|
48
|
+
@category.stub!(:values).and_return(@categories)
|
49
|
+
@facets = mock(:facets)
|
50
|
+
@facets.stub!(:[]).with('category').and_return(@category)
|
51
|
+
@search.stub!(:facets).and_return(@facets)
|
52
|
+
@client.stub!(:search).and_return(@search)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should run a search for categories facet' do
|
56
|
+
@client.should_receive(:search).with('*:*', :facets => 'category', :facet_num_results => 100).and_return(@search)
|
57
|
+
@client.categories
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should return the categories facet' do
|
61
|
+
@client.categories.should == @categories
|
62
|
+
end
|
35
63
|
end
|
36
64
|
end
|
data/spec/dnz/search_spec.rb
CHANGED
@@ -34,7 +34,7 @@ describe Search do
|
|
34
34
|
|
35
35
|
describe 'Search.new' do
|
36
36
|
it 'should call @client.fetch' do
|
37
|
-
@client.should_receive(:fetch).with(:search, :search_text => 'test').and_return(@xml)
|
37
|
+
@client.should_receive(:fetch).with(:search, :search_text => 'test', :facets => "").and_return(@xml)
|
38
38
|
Search.new(@client, @options)
|
39
39
|
end
|
40
40
|
|
@@ -52,4 +52,19 @@ describe Search do
|
|
52
52
|
Search.new(@client, @options).facets.should be_a(FacetArray)
|
53
53
|
end
|
54
54
|
end
|
55
|
+
|
56
|
+
describe 'filtering' do
|
57
|
+
before do
|
58
|
+
@options = {:search_text => 'test', :filter => {:category => 'Images'}}
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should call @client.fetch with the search text set to \'test AND category:"Images"\'' do
|
62
|
+
@client.should_receive(:fetch).with(
|
63
|
+
:search,
|
64
|
+
:search_text => 'test AND category:"Images"',
|
65
|
+
:facets => ""
|
66
|
+
).and_return(@xml)
|
67
|
+
Search.new(@client, @options)
|
68
|
+
end
|
69
|
+
end
|
55
70
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dnz-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Wells
|
@@ -65,8 +65,10 @@ files:
|
|
65
65
|
- lib/dnz/attributes.rb
|
66
66
|
- lib/dnz/client.rb
|
67
67
|
- lib/dnz/facet.rb
|
68
|
+
- lib/dnz/facet_array.rb
|
68
69
|
- lib/dnz/result.rb
|
69
70
|
- lib/dnz/search.rb
|
71
|
+
- lib/dnz/error/invalid_api_key.rb
|
70
72
|
- script/console
|
71
73
|
- script/destroy
|
72
74
|
- script/generate
|