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 CHANGED
@@ -8,8 +8,10 @@ lib/dnz.rb
8
8
  lib/dnz/attributes.rb
9
9
  lib/dnz/client.rb
10
10
  lib/dnz/facet.rb
11
+ lib/dnz/facet_array.rb
11
12
  lib/dnz/result.rb
12
13
  lib/dnz/search.rb
14
+ lib/dnz/error/invalid_api_key.rb
13
15
  script/console
14
16
  script/destroy
15
17
  script/generate
data/README.rdoc CHANGED
@@ -18,13 +18,13 @@
18
18
 
19
19
  == INSTALL:
20
20
 
21
- * sudo gem install dnz-client / sudo gem install boost-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:/www.boost.co.nz)
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
- :search => Set.new([
30
- :search_text,
31
- :api_key,
32
- :num_results,
33
- :start,
34
- :sort,
35
- :direction,
36
- :facets,
37
- :facet_num_results,
38
- :facet_start,
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.map{|k,v| '%s=%s' % [k,v] }.join('&')
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
- open(url)
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 ARGS.has_key?(path) && !Set.new(options.keys).subset?(ARGS[path])
127
- raise ArgumentError.new("Valid options for #{path} are: #{ARGS[path].to_a.join(', ')}, provided: #{options.keys.join(', ')}")
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
@@ -0,0 +1,13 @@
1
+ module DNZ
2
+ class InvalidApiKeyError < RuntimeError
3
+ attr_reader :api_key
4
+
5
+ def initialize(api_key)
6
+ @api_key = api_key
7
+ end
8
+
9
+ def to_s
10
+ "Invalid API key: #{api_key}"
11
+ end
12
+ end
13
+ 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 << value if value.valid?
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, @search_options)
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
@@ -4,5 +4,5 @@ $:.unshift(File.dirname(__FILE__)) unless
4
4
  require 'dnz/client'
5
5
 
6
6
  module Dnz
7
- VERSION = '0.0.2'
7
+ VERSION = '0.0.3'
8
8
  end
@@ -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
@@ -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.2
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