dnz-client 0.0.4 → 0.0.5

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/History.txt CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.0.5 2009-09-15
2
+
3
+ * Support for custom_search API calls.
4
+ * Experimental support for beta API v2.
5
+ * WARNING: Client.new signature has changed.
6
+
1
7
  === 0.0.4 2009-09-14
2
8
 
3
9
  * Bring memoizable from ActiveSupport into DNZ namespace.
data/lib/dnz/client.rb CHANGED
@@ -23,7 +23,7 @@ module DNZ
23
23
 
24
24
  APIS = {
25
25
  :search => 'records/${version}.xml/',
26
- :custom_search => 'custom_searches/${version}/${title}.xml'
26
+ :custom_search => 'custom_searches/${version}/${custom_search}.xml'
27
27
  }
28
28
 
29
29
  ARGS = {
@@ -37,9 +37,43 @@ module DNZ
37
37
  :direction,
38
38
  :facets,
39
39
  :facet_num_results,
40
- :facet_start,
40
+ :facet_start
41
+ ]),
42
+ :custom_search => Set.new([
43
+ :custom_search,
44
+ :search_text,
45
+ :api_key,
46
+ :num_results,
47
+ :start,
48
+ :sort,
49
+ :direction
50
+ ])
51
+ },
52
+ :v2 => {
53
+ :search => Set.new([
54
+ :search_text,
55
+ :api_key,
56
+ :num_results,
57
+ :start,
58
+ :sort,
59
+ :direction,
60
+ :facets,
61
+ :facet_num_results,
62
+ :facet_start
63
+ ]),
64
+ :custom_search => Set.new([
65
+ :custom_search,
66
+ :search_text,
67
+ :api_key,
68
+ :num_results,
69
+ :start,
70
+ :sort,
71
+ :direction,
72
+ :facets,
73
+ :facet_num_results,
74
+ :facet_start
41
75
  ])
42
- }
76
+ }
43
77
  }
44
78
 
45
79
  # List of available facets that can be passed to search
@@ -56,7 +90,7 @@ module DNZ
56
90
  # search.results.each do |result|
57
91
  # puts result.title
58
92
  # end
59
- def initialize(api_key, base_url = 'http://api.digitalnz.org', version = 'v1')
93
+ def initialize(api_key, version = 'v1', base_url = 'http://api.digitalnz.org')
60
94
  @api_key = api_key
61
95
  @base_url = base_url
62
96
  @version = version
@@ -81,6 +115,7 @@ module DNZ
81
115
  # * <tt>:start</tt> - The starting offset of the results.
82
116
  # * <tt>:facets</tt> - The facets to return for this search.
83
117
  # * <tt>:filter</tt> - A hash of filters to apply to the results
118
+ # * <tt>:custom_search</tt> - The name of a custom search created at http://digitalnz.org
84
119
  #
85
120
  # ==== Example
86
121
  # search = client.search('rubgy', :num_results => 50)
@@ -109,17 +144,12 @@ module DNZ
109
144
  def fetch(api, options = {})
110
145
  validate_options(api, options)
111
146
 
112
- options = options.reverse_merge(:api_key => self.api_key)
113
-
114
- #api_url = APIS[url]
115
- #matches = (/\$\{(.*?)\}/)
116
- #
117
- #
118
- #
119
-
120
- # qs = options.map{|k,v| '%s=%s' % [k,v] }.join('&')
121
- qs = options.to_query
122
- url = self.base_url + '/' + APIS[api].gsub('${version}', self.version) + '?' + qs
147
+ options = options.reverse_merge(
148
+ :api_key => self.api_key,
149
+ :version => self.version
150
+ )
151
+
152
+ url = create_url(api, options)
123
153
 
124
154
  begin
125
155
  open(url)
@@ -133,14 +163,40 @@ module DNZ
133
163
  end
134
164
 
135
165
  private
136
-
137
- def validate_options(path, options = {})
166
+
167
+ # Create a URL for a given API call with a hash of option
168
+ #
169
+ # * <tt>api</tt> - The api call to make. This must be listed in the APIS constant.
170
+ # * <tt>options</tt> - A hash of options.
171
+ def create_url(api, options)
138
172
  options = options.symbolize_keys
139
173
 
140
- version_args = ARGS[@version.to_sym]
174
+ path = APIS[api].dup
175
+ variable_regex = /\$\{(.+?)\}/m
176
+
177
+ while match = variable_regex.match(path)
178
+ variable_name = $1.to_sym
179
+
180
+ if options.has_key?(variable_name)
181
+ path.sub!(variable_regex, options.delete(variable_name))
182
+ else
183
+ raise ArgumentError.new("Required argument missing: #{variable_name}")
184
+ end
185
+ end
186
+
187
+ url = self.base_url + '/' + path
188
+ url + '?' + options.to_query
189
+ end
141
190
 
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(', ')}")
191
+ # Validate an options hash for a given api
192
+ def validate_options(api, options = {})
193
+ options = options.symbolize_keys
194
+ version_args = ARGS[@version.to_sym]
195
+
196
+ if !version_args
197
+ raise ArgumentError.new("Invalid version API call: #{@version}, #{api}")
198
+ elsif version_args.has_key?(api) && !Set.new(options.keys).subset?(version_args[api])
199
+ raise ArgumentError.new("Valid options for #{api} are: #{version_args[api].to_a.join(', ')}, provided: #{options.keys.join(', ')}")
144
200
  end
145
201
  end
146
202
  end
data/lib/dnz/search.rb CHANGED
@@ -4,6 +4,8 @@ require 'dnz/facet_array'
4
4
  require 'dnz/facet'
5
5
  require 'dnz/memoizable'
6
6
 
7
+ # Load will_paginate if it's available
8
+ # to provide pagination
7
9
  begin
8
10
  gem 'mislav-will_paginate' rescue nil
9
11
  require 'will_paginate/collection' rescue nil
@@ -18,6 +20,7 @@ module DNZ
18
20
  # search = client.search('text')
19
21
  # puts "%d results found on %d pages" % [search.result_count, search.pages]
20
22
  class Search
23
+ # The total number of results returned by the search
21
24
  attr_reader :result_count
22
25
 
23
26
  extend DNZ::Memoizable
@@ -88,9 +91,16 @@ module DNZ
88
91
  :pages => self.pages
89
92
  }.inspect
90
93
  end
94
+
95
+ # Return true if this search is using a custom search engine
96
+ def custom_search?
97
+ !@search_options.has_key?(:custom_search)
98
+ end
91
99
 
92
100
  private
93
101
 
102
+ # Turn the filter hash into an array of strings
103
+ # in the format key:"value"
94
104
  def parsed_search_filter
95
105
  filter = @search_options[:filter]
96
106
  filter = {} unless filter.is_a?(Hash)
@@ -99,6 +109,7 @@ module DNZ
99
109
  end
100
110
  memoize :parsed_search_filter
101
111
 
112
+ # Join the search text with any filters with " AND "
102
113
  def parsed_search_text
103
114
  if parsed_search_filter.any?
104
115
  ([text] + parsed_search_filter).join(' AND ')
@@ -107,12 +118,15 @@ module DNZ
107
118
  end
108
119
  end
109
120
 
121
+ # The facets option gets turned into a comma separated string
110
122
  def parsed_search_facets
111
123
  search_facets = @search_options[:facets] || []
112
124
  search_facets = search_facets.join(',') if search_facets.is_a?(Array)
113
125
  search_facets
114
126
  end
115
127
 
128
+ # Turn the options into options acceptable for an API call.
129
+ # Removes the filter option and parses the other options.
116
130
  def parsed_search_options
117
131
  parsed_options = @search_options.dup
118
132
  parsed_options.delete(:filter)
@@ -124,16 +138,28 @@ module DNZ
124
138
  end
125
139
  memoize :parsed_search_options
126
140
 
141
+ # Return a Nokogiri document for the XML
127
142
  def doc
128
143
  @doc ||= Nokogiri::XML(@xml)
129
144
  end
145
+
146
+ # Choose which API call to make, either search or
147
+ # custom_search if a custom search engine is specified.
148
+ def execute_action
149
+ if custom_search?
150
+ :search
151
+ else
152
+ :custom_search
153
+ end
154
+ end
130
155
 
156
+ # Execute the search by making the API call
131
157
  def execute
132
- @doc = nil
133
- @results = nil
134
- @facets = nil
135
- @xml = @client.send(:fetch, :search, parsed_search_options)
158
+ reset
159
+
160
+ @xml = @client.send(:fetch, execute_action, parsed_search_options)
136
161
 
162
+ # Parse the results
137
163
  parse_attributes
138
164
  parse_facets
139
165
  parse_results
@@ -141,13 +167,22 @@ module DNZ
141
167
 
142
168
  self
143
169
  end
170
+
171
+ # Reset important instance variables
172
+ def reset
173
+ @doc = nil
174
+ @results = nil
175
+ @facets = nil
176
+ end
144
177
 
178
+ # Replace the results array with a paginated array
145
179
  def paginate_results
146
180
  @results = WillPaginate::Collection.create(self.page, num_results_requested, self.result_count) do |pager|
147
181
  pager.replace @results
148
182
  end
149
183
  end
150
184
 
185
+ # Parse important global attributes into instance variables
151
186
  def parse_attributes
152
187
  %w(num-results-requested result-count start).each do |node|
153
188
  if child = doc.root.xpath(node).first
@@ -158,13 +193,15 @@ module DNZ
158
193
  end
159
194
  end
160
195
 
196
+ # Parse the results into an array of DNZ::Result
161
197
  def parse_results
162
198
  @results = []
163
199
  doc.xpath('//results/result').each do |result_xml|
164
200
  @results << DNZ::Result.new(result_xml)
165
201
  end
166
202
  end
167
-
203
+
204
+ # Parse the facets into an array of DNZ::FacetArray
168
205
  def parse_facets
169
206
  @facets = FacetArray.new
170
207
 
data/lib/dnz.rb CHANGED
@@ -4,5 +4,5 @@ $:.unshift(File.dirname(__FILE__)) unless
4
4
  require 'dnz/client'
5
5
 
6
6
  module Dnz
7
- VERSION = '0.0.4'
7
+ VERSION = '0.0.5'
8
8
  end
@@ -10,6 +10,91 @@ describe Client do
10
10
  @search = mock(:search)
11
11
  DNZ::Search.stub!(:new).and_return(@search)
12
12
  end
13
+
14
+ describe 'APIs' do
15
+ describe 'v1' do
16
+ before do
17
+ @version = 'v1'
18
+ @client = Client.new('abc', @version)
19
+ @client.stub!(:open) # make sure open is never called
20
+ end
21
+
22
+ describe 'search' do
23
+ [:search_text,:api_key,:num_results,:start,:sort,:direction,:facets,:facet_num_results,:facet_start].each do |option|
24
+ it "should allow #{option}" do
25
+ lambda do
26
+ @client.send(:fetch, :search, {option => "test"})
27
+ end.should_not raise_error(ArgumentError)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe 'custom_search' do
33
+ it 'should require custom_search' do
34
+ lambda do
35
+ @client.send(:fetch, :custom_search, {})
36
+ end.should raise_error(ArgumentError, "Required argument missing: custom_search")
37
+ end
38
+
39
+ [:search_text,:api_key,:num_results,:start,:sort,:direction].each do |option|
40
+ it "should allow #{option}" do
41
+ lambda do
42
+ @client.send(:fetch, :custom_search, {:custom_search => "test", option => "test"})
43
+ end.should_not raise_error(ArgumentError)
44
+ end
45
+ end
46
+ [:facets,:facet_num_results,:facet_start].each do |option|
47
+ it "should not allow #{option}" do
48
+ lambda do
49
+ @client.send(:fetch, :custom_search, {:custom_search => "test", option => "test"})
50
+ end.should raise_error(ArgumentError)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ describe 'v2' do
57
+ before do
58
+ @version = 'v2'
59
+ @client = Client.new('abc', @version)
60
+ @client.stub!(:open) # make sure open is never called
61
+ end
62
+
63
+ describe 'search' do
64
+ [:search_text,:api_key,:num_results,:start,:sort,:direction,:facets,:facet_num_results,:facet_start].each do |option|
65
+ it "should allow #{option}" do
66
+ lambda do
67
+ @client.send(:fetch, :search, {})
68
+ end.should_not raise_error(ArgumentError)
69
+ end
70
+ end
71
+ end
72
+
73
+ describe 'custom_search' do
74
+ it 'should require custom_search' do
75
+ lambda do
76
+ @client.send(:fetch, :custom_search, {})
77
+ end.should raise_error(ArgumentError, "Required argument missing: custom_search")
78
+ end
79
+
80
+ [:search_text,:api_key,:num_results,:start,:sort,:direction].each do |option|
81
+ it "should allow #{option}" do
82
+ lambda do
83
+ @client.send(:fetch, :custom_search, {:custom_search => "test", option => "test"})
84
+ end.should_not raise_error(ArgumentError)
85
+ end
86
+ end
87
+ [:facets,:facet_num_results,:facet_start].each do |option|
88
+ it "should allow #{option}" do
89
+ lambda do
90
+ @client.send(:fetch, :custom_search, {:custom_search => "test", option => "test"})
91
+ end.should_not raise_error(ArgumentError)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+
13
98
 
14
99
  describe '#search' do
15
100
  it 'should create a new search object and return it' do
data/spec/spec.opts CHANGED
@@ -1,4 +1,5 @@
1
1
  --colour
2
2
  --format progress
3
3
  --loadby mtime
4
- --reverse
4
+ --reverse
5
+ --debugger
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
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Wells
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-14 00:00:00 +13:00
12
+ date: 2009-10-15 00:00:00 +13:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency