dnz-client 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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