supplejack_client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +16 -0
  5. data/Guardfile +24 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +153 -0
  8. data/Rakefile +9 -0
  9. data/lib/generators/locales/en.yml +11 -0
  10. data/lib/generators/supplejack/install_generator.rb +28 -0
  11. data/lib/generators/templates/README +19 -0
  12. data/lib/generators/templates/supplejack_client.rb +120 -0
  13. data/lib/supplejack/config.rb +116 -0
  14. data/lib/supplejack/controllers/helpers.rb +172 -0
  15. data/lib/supplejack/engine.rb +20 -0
  16. data/lib/supplejack/exceptions.rb +17 -0
  17. data/lib/supplejack/facet.rb +33 -0
  18. data/lib/supplejack/item.rb +94 -0
  19. data/lib/supplejack/item_relation.rb +73 -0
  20. data/lib/supplejack/log_subscriber.rb +58 -0
  21. data/lib/supplejack/paginated_collection.rb +61 -0
  22. data/lib/supplejack/record.rb +147 -0
  23. data/lib/supplejack/request.rb +95 -0
  24. data/lib/supplejack/search.rb +346 -0
  25. data/lib/supplejack/url_formats/item_hash.rb +208 -0
  26. data/lib/supplejack/user.rb +132 -0
  27. data/lib/supplejack/user_set.rb +349 -0
  28. data/lib/supplejack/user_set_relation.rb +143 -0
  29. data/lib/supplejack/util.rb +120 -0
  30. data/lib/supplejack/version.rb +10 -0
  31. data/lib/supplejack_client.rb +29 -0
  32. data/spec/spec_helper.rb +23 -0
  33. data/spec/supplejack/controllers/helpers_spec.rb +277 -0
  34. data/spec/supplejack/facet_spec.rb +44 -0
  35. data/spec/supplejack/item_relation_spec.rb +111 -0
  36. data/spec/supplejack/item_spec.rb +115 -0
  37. data/spec/supplejack/log_subscriber_spec.rb +40 -0
  38. data/spec/supplejack/paginated_collection_spec.rb +43 -0
  39. data/spec/supplejack/record_spec.rb +255 -0
  40. data/spec/supplejack/request_spec.rb +195 -0
  41. data/spec/supplejack/search_spec.rb +727 -0
  42. data/spec/supplejack/url_formats/item_hash_spec.rb +341 -0
  43. data/spec/supplejack/user_set_relation_spec.rb +149 -0
  44. data/spec/supplejack/user_set_spec.rb +465 -0
  45. data/spec/supplejack/user_spec.rb +159 -0
  46. data/supplejack_client.gemspec +30 -0
  47. metadata +159 -0
@@ -0,0 +1,61 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ module Supplejack
9
+ class PaginatedCollection
10
+ instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|object_id/ }
11
+
12
+ attr_reader :current_page, :per_page
13
+ attr_accessor :total_count
14
+ alias :total_entries :total_count
15
+ alias :total_entries= :total_count=
16
+ alias :limit_value :per_page
17
+
18
+ def initialize(collection, page, per_page, total)
19
+ @collection = collection
20
+ @current_page = page
21
+ @per_page = per_page
22
+ @total_count = total
23
+ end
24
+
25
+ def total_pages
26
+ (total_count.to_f / per_page).ceil
27
+ end
28
+ alias :num_pages :total_pages
29
+
30
+ def first_page?
31
+ current_page == 1
32
+ end
33
+
34
+ def last_page?
35
+ current_page >= total_pages
36
+ end
37
+
38
+ def previous_page
39
+ current_page > 1 ? (current_page - 1) : nil
40
+ end
41
+
42
+ def next_page
43
+ current_page < total_pages ? (current_page + 1) : nil
44
+ end
45
+
46
+ def out_of_bounds?
47
+ current_page > total_pages
48
+ end
49
+
50
+ def offset
51
+ (current_page - 1) * per_page
52
+ end
53
+
54
+ private
55
+
56
+ def method_missing(method, *args, &block)
57
+ @collection.send(method, *args, &block)
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,147 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'supplejack/search'
9
+
10
+ module Supplejack
11
+ module Record
12
+ extend ActiveSupport::Concern
13
+
14
+ attr_accessor :attributes
15
+
16
+ included do
17
+ extend Supplejack::Request
18
+ extend ActiveModel::Naming
19
+ include ActiveModel::Conversion
20
+ end
21
+
22
+ def initialize(attributes={})
23
+ if attributes.is_a?(String)
24
+ attributes = JSON.parse(attributes) rescue {}
25
+ end
26
+ @attributes = attributes.symbolize_keys rescue {}
27
+ end
28
+
29
+ def id
30
+ id = @attributes[:id] || @attributes[:record_id]
31
+ id.to_i
32
+ end
33
+
34
+ def to_param
35
+ self.id
36
+ end
37
+
38
+ def title
39
+ @attributes[:title].present? ? @attributes[:title] : "Untitled"
40
+ end
41
+
42
+ # Returns a array of hashes containing all the record attributes and
43
+ # the schema each attribute belongs to. To set what fields belong to
44
+ # each schema there is a config option to set supplejack_fields and admin_fields
45
+ #
46
+ # @example
47
+ # record.metadata => [{:name => "location", :schema => "supplejack", :value => "Wellington" }, ...]
48
+ #
49
+ def metadata
50
+ metadata = []
51
+
52
+ ['supplejack', 'admin'].each do |schema|
53
+ Supplejack.send("#{schema}_fields").each do |field|
54
+ if @attributes.has_key?(field)
55
+ values = @attributes[field]
56
+ values ||= [] unless !!values == values #Testing if boolean
57
+ values = [values] unless values.is_a?(Array)
58
+ field = field.to_s.camelcase(:lower) if schema == "dcterms"
59
+ field = field.to_s.sub(/#{schema}_/, '')
60
+ values.each do |value|
61
+ metadata << {:name => field, :schema => schema, :value => value }
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ metadata
68
+ end
69
+
70
+ def format
71
+ raise NoMethodError, "undefined method 'format' for Supplejack::Record:Module" unless @attributes.has_key?(:format)
72
+ @attributes[:format]
73
+ end
74
+
75
+ # Some of the records in the API return an array of values, but in practice
76
+ # most of them have on only one value. What this does is just convert the array
77
+ # to a string for the methods defined in the configuration.
78
+ Supplejack.single_value_methods.each do |method|
79
+ define_method("#{method}") do
80
+ values = @attributes[method]
81
+ values.is_a?(Array) ? values.first : values
82
+ end
83
+ end
84
+
85
+ [:next_page, :previous_page, :next_record, :previous_record].each do |pagination_field|
86
+ define_method(pagination_field) do
87
+ @attributes[pagination_field]
88
+ end
89
+ end
90
+
91
+ def persisted?
92
+ true
93
+ end
94
+
95
+ def method_missing(symbol, *args, &block)
96
+ raise NoMethodError, "undefined method '#{symbol.to_s}' for Supplejack::Record:Module" unless @attributes.has_key?(symbol)
97
+ @attributes[symbol]
98
+ end
99
+
100
+ module ClassMethods
101
+
102
+ # Finds a record or array of records from the Supplejack API
103
+ #
104
+ # @params [ Integer, Array ] id A integer or array of integers representing the ID of records
105
+ # @params [ Hash ] options Search options used to perform a search in order to get the next/previous
106
+ # records within the search results.
107
+ #
108
+ # @return [ Supplejack::Record ] A record or array of records initialized with the class of where the Supplejack::Record module was included
109
+ #
110
+ def find(id_or_array, options={})
111
+ if id_or_array.is_a?(Array)
112
+ options = {:record_ids => id_or_array, :fields => Supplejack.fields.join(',') }
113
+ response = get("/records/multiple", options)
114
+ response["records"].map {|attributes| new(attributes) }
115
+ else
116
+ begin
117
+ # handle malformed id's before requesting anything.
118
+ id = id_or_array.to_i
119
+ raise(Supplejack::MalformedRequest, "'#{id_or_array}' is not a valid record id") if id <= 0
120
+
121
+ # Do not send any parameters in the :search key when the user didn't specify any options
122
+ # And also always send the :fields parameter
123
+ #
124
+ search_klass = Supplejack::Search
125
+ search_klass = Supplejack.search_klass.classify.constantize if Supplejack.search_klass.present?
126
+
127
+ any_options = options.try(:any?)
128
+
129
+ search = search_klass.new(options)
130
+ search_options = search.api_params
131
+ search_options = search.merge_extra_filters(search_options)
132
+
133
+ options = {:search => search_options}
134
+ options[:fields] = options[:search].delete(:fields)
135
+ options.delete(:search) unless any_options
136
+
137
+ response = get("/records/#{id}", options)
138
+ new(response['record'])
139
+ rescue RestClient::ResourceNotFound => e
140
+ raise Supplejack::RecordNotFound, "Record with ID #{id_or_array} was not found"
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ end
147
+ end
@@ -0,0 +1,95 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'rest-client'
9
+
10
+ module Supplejack
11
+ module Request
12
+ extend ActiveSupport::Concern
13
+
14
+ def get(path, params={}, options={})
15
+ params ||= {}
16
+ url = full_url(path, options[:format], params)
17
+
18
+ started = Time.now
19
+ payload = {:path => path, :params => params, :options => options}
20
+
21
+ begin
22
+ result = RestClient::Request.execute(:url => url, :method => :get, :timeout => timeout(options))
23
+ result = JSON.parse(result) if result
24
+ rescue StandardError => e
25
+ payload[:exception] = [e.class.name, e.message]
26
+ raise e
27
+ ensure
28
+ duration = (Time.now - started)*1000 # Convert to miliseconds
29
+ solr_request_params = result["search"]['solr_request_params'] if result && result['search']
30
+ @subscriber = Supplejack::LogSubscriber.new
31
+ @subscriber.log_request(duration, payload, solr_request_params)
32
+ end
33
+
34
+ result
35
+ end
36
+
37
+ def post(path, params={}, payload={}, options={})
38
+ payload ||= {}
39
+ log_request(:post, path, params, payload) do
40
+ response = RestClient::Request.execute(:url => full_url(path, nil, params), :method => :post, :payload => payload.to_json, :timeout => timeout(options), :headers => {:content_type => :json, :accept => :json})
41
+ JSON.parse(response) rescue {}.to_json
42
+ end
43
+ end
44
+
45
+ def delete(path, params={}, options={})
46
+ log_request(:delete, path, params, {}) do
47
+ RestClient::Request.execute(:url => full_url(path, nil, params), :method => :delete, :timeout => timeout(options))
48
+ end
49
+ end
50
+
51
+ def put(path, params={}, payload={}, options={})
52
+ payload ||= {}
53
+ log_request(:put, path, params, payload) do
54
+ response = RestClient::Request.execute(:url => full_url(path, nil, params), :method => :put, :payload => payload.to_json, :timeout => timeout(options), :headers => {:content_type => :json, :accept => :json})
55
+ JSON.parse(response) rescue {}.to_json
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def full_url(path, format=nil, params={})
62
+ params ||= {}
63
+ format = format ? format : 'json'
64
+ params[:api_key] ||= Supplejack.api_key
65
+ params[:debug] = true if Supplejack.enable_debugging
66
+
67
+ Supplejack.api_url + path + ".#{format.to_s}" + '?' + params.to_query
68
+ end
69
+
70
+ def timeout(options={})
71
+ timeout = Supplejack.timeout.to_i == 0 ? 30 : Supplejack.timeout.to_i
72
+ options[:timeout] || timeout
73
+ end
74
+
75
+ def log_request(method, path, params={}, payload={})
76
+ information = {path: path}
77
+ information[:params] = params
78
+ information[:payload] = payload
79
+ information[:method] = method
80
+
81
+ begin
82
+ started = Time.now
83
+ yield
84
+ rescue StandardError => e
85
+ information[:exception] = [e.class.name, e.message]
86
+ raise e
87
+ ensure
88
+ duration = (Time.now - started)*1000 # Convert to miliseconds
89
+ @subscriber = Supplejack::LogSubscriber.new
90
+ @subscriber.log_request(duration, information, {})
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,346 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'supplejack/request'
9
+ require 'digest/md5'
10
+
11
+ module Supplejack
12
+ class Search
13
+ include Supplejack::Request
14
+
15
+ attr_accessor :results, :text, :page, :per_page, :pagination_limit, :direction, :sort, :filters, :record_type, :record_klass
16
+ attr_accessor :url_format, :without, :and, :or, :params, :api_params
17
+
18
+ def initialize(params={})
19
+ @params = params.clone rescue {}
20
+ @params[:facets] ||= Supplejack.facets.join(',')
21
+ @params[:facets_per_page] ||= Supplejack.facets_per_page
22
+ [:action, :controller].each {|p| @params.delete(p) }
23
+
24
+ @text = @params[:text]
25
+ @geo_bbox = @params[:geo_bbox]
26
+ @record_type = @params[:record_type]
27
+ @record_type = @record_type.to_i unless @record_type == "all"
28
+ @page = (@params[:page] || 1).to_i
29
+ @per_page = (@params[:per_page] || Supplejack.per_page).to_i
30
+ @pagination_limit = @params[:pagination_limit] || Supplejack.pagination_limit
31
+ @sort = @params[:sort]
32
+ @direction = @params[:direction]
33
+ @url_format = Supplejack.url_format_klass.new(@params, self)
34
+ @filters = @url_format.filters
35
+ @api_params = @url_format.to_api_hash
36
+ @record_klass = @params[:record_klass] || Supplejack.record_klass
37
+
38
+ # Do not execute the actual search right away, it should be lazy loaded
39
+ # when the user needs one of the following values.
40
+ @total = nil
41
+ @results = nil
42
+ @facets = nil
43
+
44
+ Supplejack.search_attributes.each do |attribute|
45
+ # We have to define the attribute accessors for the filters at initialization of the search instance
46
+ # otherwise because the rails initializer is run after the gem was loaded, only the default
47
+ # Supplejack.search_attributes set in the Gem would be defined.
48
+
49
+ self.class.send(:attr_accessor, attribute)
50
+ self.send("#{attribute}=", @filters[attribute]) unless @filters[attribute] == 'all'
51
+ end
52
+ end
53
+
54
+ # Returns by default a array of two element arrays with all the active filters
55
+ # in the search object and their values
56
+ #
57
+ # @example Return a array of filters
58
+ # search = Search.new(:i => {:content_partner => "Matapihi", :category => ["Images", "Videos"]})
59
+ # search.filters => [[:content_partner, "Matapihi"], [:category, "Images"], [:category, "Videos"]]
60
+ #
61
+ # @return [ Array<Array> ] Array with two element arrays, each with the filter and its value.
62
+ #
63
+ def filters(options={})
64
+ options.reverse_merge!(:format => :array, :except => [])
65
+ return @filters if options[:format] == :hash
66
+
67
+ filters = []
68
+ @filters.each do |key, value|
69
+ unless options[:except].include?(key)
70
+ if value.is_a?(Array)
71
+ value.each do |v|
72
+ filters << [key, v]
73
+ end
74
+ else
75
+ filters << [key, value]
76
+ end
77
+ end
78
+ end
79
+
80
+ return filters
81
+ end
82
+
83
+ def options(filter_options={})
84
+ @url_format.options(filter_options)
85
+ end
86
+
87
+ # Returns an array of facets for the current search criteria sorted by
88
+ # the order specified in +Supplejack.facets+
89
+ #
90
+ # @example facets return format
91
+ # search.facets => [Supplejack::Facet]
92
+ #
93
+ # @param [ Hash ] options Supported options: :drill_dates
94
+ #
95
+ # @return [ Array<Supplejack::Facet> ] Every element in the array is a Supplejack::Facet object, and responds to name and values
96
+ #
97
+ def facets(options={})
98
+ return @facets if @facets
99
+ self.execute_request
100
+
101
+ facets = @response['search']['facets'] || {}
102
+
103
+ facet_array = facets.sort_by {|facet, rows| Supplejack.facets.find_index(facet.to_sym) || 100 }
104
+ @facets = facet_array.map {|name, values| Supplejack::Facet.new(name, values) }
105
+ end
106
+
107
+ def facet(value)
108
+ self.facets.find { |facet| facet.name == value }
109
+ end
110
+
111
+ # Returns a array of +Supplejack::Record+ objects wrapped in a Paginated Collection
112
+ # which provides methods for will_paginate and kaminari to work properly
113
+ #
114
+ # It will initialize the +Supplejack::Record+ objects with the class stored in
115
+ # +Supplejack.record_klass+, so that you can override any method provided by the +Supplejack::Record+
116
+ # module or create new methods. You can also provide a +:record_klass+ option
117
+ # when initialing a +Supplejack::Search+ object to override the record_klass on a per request basis.
118
+ #
119
+ # @return [ Array ] Array of +Supplejack::Record+ objects
120
+ #
121
+ def results
122
+ return @results if @results
123
+ self.execute_request
124
+
125
+ if @response['search']['results'].respond_to?(:map)
126
+ records = @response['search']['results'].map do |attributes|
127
+ @record_klass.classify.constantize.new(attributes)
128
+ end
129
+ else
130
+ records = []
131
+ end
132
+
133
+ last_page = [pagination_limit || total, total].min
134
+ @results = Supplejack::PaginatedCollection.new(records, page, per_page, last_page)
135
+ end
136
+
137
+ # Returns the total amount of records for the current search filters
138
+ #
139
+ # @returns [ Integer ] Number of records that match the current search criteria
140
+ #
141
+ def total
142
+ return @total if @total
143
+ self.execute_request
144
+ @total = @response['search']['result_count'].to_i
145
+ end
146
+
147
+ def record?
148
+ self.record_type == 0
149
+ end
150
+
151
+ # Calculates counts for specific queries using solr's facet.query
152
+ #
153
+ # @example Request images with a large_thumbnail_url and of record_type = 1:
154
+ # search.counts({"photos" => {:large_thumbnail_url => "all", :record_type => 1}})
155
+ # @example Returns the following hash:
156
+ # {"photos" => 100}
157
+ #
158
+ # @param [Hash{String => Hash{String => String}}] a hash with query names as keys and a hash with filters as values.
159
+ # @return [Hash{String => Integer}] A hash with the query names as keys and the result count for every query as values
160
+ #
161
+ def counts(query_parameters={})
162
+ if Supplejack.enable_caching
163
+ cache_key = Digest::MD5.hexdigest(counts_params(query_parameters).to_query)
164
+ Rails.cache.fetch(cache_key, :expires_in => 1.day) do
165
+ fetch_counts(query_parameters)
166
+ end
167
+ else
168
+ fetch_counts(query_parameters)
169
+ end
170
+ end
171
+
172
+ def fetch_counts(query_parameters={})
173
+ begin
174
+ response = get(request_path, counts_params(query_parameters))
175
+ counts_hash = response['search']['facets']['counts']
176
+ rescue StandardError => e
177
+ counts_hash = {}
178
+ end
179
+
180
+ # When the search doesn't match any facets for the specified filters, Sunspot doesn't return any facets
181
+ # at all. Here we add those keys with a value of 0.
182
+ #
183
+ query_parameters.each_pair do |count_name, count_filters|
184
+ counts_hash[count_name.to_s] = 0 unless counts_hash[count_name.to_s]
185
+ end
186
+
187
+ counts_hash
188
+ end
189
+
190
+ # Returns a hash with all the parameters required by the counts method
191
+ #
192
+ def counts_params(query_parameters={})
193
+ query_with_filters = {}
194
+ query_parameters.each_pair do |count_name, count_filters|
195
+ count_filters = count_filters.symbolize_keys
196
+ query_record_type = count_filters[:record_type].to_i
197
+ type = query_record_type == 0 ? :items : :headings
198
+ filters = self.url_format.and_filters(type).dup
199
+
200
+ without_filters = self.url_format.without_filters(type).dup
201
+ without_filters = Hash[without_filters.map {|key, value| ["-#{key}".to_sym, value]}]
202
+
203
+ filters.merge!(without_filters)
204
+ query_with_filters.merge!({count_name.to_sym => Supplejack::Util.deep_merge(filters, count_filters) })
205
+ end
206
+
207
+ params = {:facet_query => query_with_filters, :record_type => "all"}
208
+ params[:text] = self.url_format.text
209
+ params[:text] = self.text if self.text.present?
210
+ # params[:geo_bbox] = self.geo_bbox if self.geo_bbox.present?
211
+ params[:query_fields] = self.url_format.query_fields
212
+ params = merge_extra_filters(params)
213
+ params
214
+ end
215
+
216
+ # Gets the type facet unrestricted by the current type filter
217
+ #
218
+ # @return [Hash{String => Integer}] A hash of type names and counts
219
+ #
220
+ def categories(options={})
221
+ return @categories if @categories
222
+ @categories = facet_values('category', options)
223
+ end
224
+
225
+ # Gets the facet values unrestricted by the current filter
226
+ #
227
+ # @return [Hash{String => Integer}] A hash of facet names and counts
228
+ #
229
+ def fetch_facet_values(facet_name, options={})
230
+ options.reverse_merge!(:all => true, :sort => nil)
231
+ memoized_values = instance_variable_get("@#{facet_name}_values")
232
+ return memoized_values if memoized_values
233
+
234
+ begin
235
+ response = get(request_path, facet_values_params(facet_name, options))
236
+ @facet_values = response["search"]["facets"]["#{facet_name}"]
237
+ rescue StandardError => e
238
+ response = {"search" => {"result_count" => 0}}
239
+ @facet_values = {}
240
+ end
241
+
242
+ @facet_values["All"] = response["search"]["result_count"] if options[:all]
243
+
244
+ facet = Supplejack::Facet.new(facet_name, @facet_values)
245
+ @facet_values = facet.values(options[:sort])
246
+
247
+ instance_variable_set("@#{facet_name}_values", @facet_values)
248
+ @facet_values
249
+ end
250
+
251
+ # Returns a hash with all the parameters required by the facet_values
252
+ # method
253
+ #
254
+ def facet_values_params(facet_name, options={})
255
+ memoized_values = instance_variable_get("@#{facet_name}_params")
256
+ return memoized_values if memoized_values
257
+
258
+ filters = self.url_format.and_filters
259
+ filters.delete(facet_name.to_sym)
260
+
261
+ facet_params = self.api_params
262
+ facet_params[:and] = filters
263
+ facet_params[:facets] = "#{facet_name}"
264
+ facet_params[:per_page] = 0
265
+ facet_params[:facets_per_page] = options[:facets_per_page] if options[:facets_per_page]
266
+
267
+ facet_params = merge_extra_filters(facet_params)
268
+
269
+ instance_variable_set("@#{facet_name}_params", facet_params)
270
+ facet_params
271
+ end
272
+
273
+ def facet_values(facet_name, options={})
274
+ if Supplejack.enable_caching
275
+ cache_key = Digest::MD5.hexdigest(facet_values_params(facet_name).to_query)
276
+ Rails.cache.fetch(cache_key, :expires_in => 1.day) do
277
+ fetch_facet_values(facet_name, options)
278
+ end
279
+ else
280
+ fetch_facet_values(facet_name, options)
281
+ end
282
+ end
283
+
284
+ def request_path
285
+ '/records'
286
+ end
287
+
288
+ def execute_request
289
+ return @response if @response
290
+
291
+ @api_params = merge_extra_filters(@api_params)
292
+
293
+ begin
294
+ if Supplejack.enable_caching && self.cacheable?
295
+ cache_key = Digest::MD5.hexdigest("#{request_path}?#{@api_params.to_query}")
296
+ @response = Rails.cache.fetch(cache_key, expires_in: 1.hour) do
297
+ get(request_path, @api_params)
298
+ end
299
+ else
300
+ @response = get(request_path, @api_params)
301
+ end
302
+ rescue StandardError => e
303
+ @response = {'search' => {}}
304
+ end
305
+ end
306
+
307
+ def cacheable?
308
+ return false if text.present? || page > 1
309
+ return true
310
+ end
311
+
312
+ # Convienence method to find out if the search object has any specific filter
313
+ # applied to it. It works for both single and multiple value filters.
314
+ # This methods are actually defined on method_missing.
315
+ #
316
+ # @exampe Return true when the search has a category filter set to images
317
+ # search = Search.new(:i => {:category => ["Images"]})
318
+ # search.has_category?("Images") => true
319
+
320
+ def has_filter_and_value?(filter, value)
321
+ actual_value = *self.send(filter)
322
+ return false unless actual_value
323
+ actual_value.include?(value)
324
+ end
325
+
326
+ def method_missing(symbol, *args, &block)
327
+ if symbol.to_s.match(/has_(.+)\?/) && Supplejack.search_attributes.include?($1.to_sym)
328
+ return has_filter_and_value?($1, args.first)
329
+ end
330
+ end
331
+
332
+ # Adds any filters defined in the :or, :and or :without attr_accessors
333
+ # By setting them directly it allows to nest any conditions that is not
334
+ # normally possible though the item_hash URL format.
335
+ #
336
+ def merge_extra_filters(existing_filters)
337
+ and_filters = self.and.try(:any?) ? {:and => self.and} : {}
338
+ or_filters = self.or.try(:any?) ? {:or => self.or} : {}
339
+ without_filters = self.without.try(:any?) ? {:without => self.without} : {}
340
+ extra_filters = and_filters.merge(or_filters).merge(without_filters)
341
+
342
+ Util.deep_merge(existing_filters, extra_filters)
343
+ end
344
+
345
+ end
346
+ end