supernova 0.3.12 → 0.3.13

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.12
1
+ 0.3.13
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ exec %(cd /usr/local/Cellar/solr/3.1.0/libexec/example && java -Dsolr.solr.home=#{File.expand_path("../solr", File.dirname(__FILE__))} -Djetty.port=8985 -jar start.jar)
@@ -1,4 +1,6 @@
1
1
  require "rsolr"
2
+ require "active_support/core_ext/object/blank"
3
+ require "active_support/core_ext/hash"
2
4
 
3
5
  module Supernova
4
6
  KM_TO_METER = 1000.0
@@ -21,6 +23,17 @@ module Supernova
21
23
  self.defined_named_search_scopes << name
22
24
  end
23
25
  end
26
+
27
+ class << self
28
+ def build_ar_like_record(clazz, attributes, original_search_doc = nil)
29
+ record = clazz.new
30
+ record.instance_variable_set("@attributes", attributes)
31
+ record.instance_variable_set("@readonly", true)
32
+ record.instance_variable_set("@new_record", false)
33
+ record.instance_variable_set("@original_search_doc", original_search_doc) if original_search_doc
34
+ record
35
+ end
36
+ end
24
37
  end
25
38
 
26
39
  require "supernova/numeric_extensions"
@@ -2,7 +2,7 @@ class Supernova::Criteria
2
2
  DEFAULT_PER_PAGE = 25
3
3
  FIRST_PAGE = 1
4
4
 
5
- attr_accessor :filters, :search_options, :clazz
5
+ attr_accessor :filters, :search_options, :clazz, :results
6
6
 
7
7
  class << self
8
8
  def method_missing(*args)
@@ -138,8 +138,22 @@ class Supernova::Criteria
138
138
  def to_parameters
139
139
  implement_in_subclass
140
140
  end
141
-
141
+
142
+ def populate
143
+ @results = execute if !populated?
144
+ self
145
+ end
146
+
142
147
  def to_a
148
+ populate
149
+ results
150
+ end
151
+
152
+ def populated?
153
+ instance_variables.include?("@results")
154
+ end
155
+
156
+ def execute
143
157
  implement_in_subclass
144
158
  end
145
159
 
@@ -170,8 +184,9 @@ class Supernova::Criteria
170
184
  end
171
185
 
172
186
  def method_missing(*args, &block)
173
- if args.length == 1 && Array.new.respond_to?(args.first)
174
- to_a.send(args.first, &block)
187
+ if Supernova::Collection.instance_methods.include?(args.first.to_s)
188
+ populate
189
+ @results.send(*args, &block)
175
190
  elsif self.named_scope_defined?(args.first)
176
191
  self.merge(self.search_options[:named_scope_class].send(*args)) # merge named scope and current criteria
177
192
  else
@@ -87,20 +87,11 @@ class Supernova::SolrCriteria < Supernova::Criteria
87
87
  end
88
88
 
89
89
  def build_doc(hash)
90
- return hash if !hash["type"].respond_to?(:constantize)
91
- doc = hash["type"].constantize.new
92
- doc.instance_variable_set("@solr_doc", hash)
93
- atts = convert_doc_attributes(hash)
94
- if doc.instance_variables.include?("@attributes")
95
- doc.instance_variable_set("@attributes", atts.except("type"))
90
+ if hash["type"].respond_to?(:constantize)
91
+ Supernova.build_ar_like_record(hash["type"].constantize, convert_doc_attributes(hash), hash)
96
92
  else
97
- atts.each do |key, value|
98
- doc.send(:"#{key}=", value) if doc.respond_to?(:"#{key}=")
99
- end
93
+ hash
100
94
  end
101
- doc.instance_variable_set("@readonly", true)
102
- doc.instance_variable_set("@new_record", false)
103
- doc
104
95
  end
105
96
 
106
97
  # called in build doc, all hashes have strings as keys!!!
@@ -137,7 +128,7 @@ class Supernova::SolrCriteria < Supernova::Criteria
137
128
  end
138
129
  end
139
130
 
140
- def to_a
131
+ def execute
141
132
  response = Supernova::Solr.connection.post("select", :data => to_params)
142
133
  collection = Supernova::Collection.new(current_page, per_page, response["response"]["numFound"])
143
134
  collection.replace(build_docs(response["response"]["docs"]))
@@ -72,14 +72,43 @@ class Supernova::SolrIndexer
72
72
 
73
73
  def index!
74
74
  index_query(query_to_index) do |row|
75
- row_to_solr(row)
75
+ map_for_solr(row)
76
76
  end
77
77
  end
78
78
 
79
- def row_to_solr(row)
79
+ def map_for_solr(row)
80
+ map_hash_keys_to_solr(
81
+ if self.respond_to?(:extra_attributes_from_record) && self.class.clazz
82
+ row.merge(self.extra_attributes_from_record(Supernova.build_ar_like_record(self.class.clazz, row)).stringify_keys)
83
+ elsif self.respond_to?(:row_to_solr)
84
+ puts "DEPRECATION WARNING: use before_index instead of row_to_solr! in #{caller.first}"
85
+ self.row_to_solr(row)
86
+ else
87
+ self.before_index(row)
88
+ end
89
+ )
90
+ end
91
+
92
+ def before_index(row)
80
93
  row
81
94
  end
82
95
 
96
+ def map_hash_keys_to_solr(hash)
97
+ hash["indexed_at_dt"] = Time.now.utc.iso8601
98
+ hash["id_s"] = [self.class.table_name, hash["id"]].compact.join("/") if hash["id"]
99
+ self.class.field_definitions.each do |field, options|
100
+ if value = hash.delete(field.to_s)
101
+ if options[:type] == :date
102
+ value = Time.utc(value.year, value.month, value.day) if value.is_a?(Date)
103
+ value = value.utc.iso8601
104
+ end
105
+ hash["#{field}_#{self.class.suffix_from_type(options[:type])}"] = value
106
+ end
107
+ end
108
+ hash["type"] = self.class.clazz.to_s if self.class.clazz
109
+ hash
110
+ end
111
+
83
112
  def table_name
84
113
  self.class.table_name || (self.class.clazz && self.class.clazz.respond_to?(:table_name) ? self.class.clazz.table_name : nil)
85
114
  end
@@ -99,7 +128,7 @@ class Supernova::SolrIndexer
99
128
 
100
129
  def defined_fields
101
130
  self.class.field_definitions.map do |field, options|
102
- sql_column_from_field_and_type(field, options[:type]) if options[:virtual] != true
131
+ field.to_s if options[:virtual] != true
103
132
  end.compact
104
133
  end
105
134
 
@@ -219,7 +248,8 @@ class Supernova::SolrIndexer
219
248
  %(cd #{File.dirname(index_file_path)} && curl -s '#{solr_url}/update/json?commit=true' --data-binary @#{File.basename(index_file_path)} -H 'Content-type:application/json')
220
249
  end
221
250
  out = Kernel.send(:`, cmd)
222
- FileUtils.rm_f(self.index_file_path) if out.to_s.include?(%(<int name=\"status\">0</int>))
251
+ raise "unable to index #{index_file_path}: #{out}" if !out.to_s.include?(%(<int name=\"status\">0</int>))
252
+ FileUtils.rm_f(self.index_file_path)
223
253
  out
224
254
  end
225
255
  end
@@ -34,7 +34,7 @@ class Supernova::ThinkingSphinxCriteria < Supernova::Criteria
34
34
  [(self.search_options[:search] || Array.new).join(" "), sphinx_options]
35
35
  end
36
36
 
37
- def to_a
37
+ def execute
38
38
  ThinkingSphinx.search(*self.to_params)
39
39
  end
40
40
 
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
3
  describe "Solr" do
4
4
  before(:each) do
5
5
  Supernova::Solr.instance_variable_set("@connection", nil)
6
- Supernova::Solr.url = "http://localhost:8983/solr/"
6
+ Supernova::Solr.url = "http://localhost:8985/solr/"
7
7
  Supernova::Solr.truncate!
8
8
  Offer.criteria_class = Supernova::SolrCriteria
9
9
  root = Geokit::LatLng.new(47, 11)
@@ -41,7 +41,7 @@ describe "Solr" do
41
41
  has :user_id, :type => :integer
42
42
  has :popularity, :type => :integer
43
43
 
44
- def row_to_solr(row)
44
+ def before_index(row)
45
45
  row["indexed_at_dt"] = Time.now.utc.iso8601
46
46
  row
47
47
  end
@@ -54,9 +54,9 @@ describe "Solr" do
54
54
  offer2 = Offer.create!(:user_id => 2, :popularity => 20)
55
55
  indexer = OfferIndex.new(:db => ActiveRecord::Base.connection)
56
56
  indexer.index!
57
- OfferIndex.search_scope.to_a.first.instance_variable_get("@solr_doc")["indexed_at_dt"].should_not be_nil
58
- OfferIndex.search_scope.to_a.total_entries.should == 2
59
- OfferIndex.search_scope.order("user_id desc").to_a.should == [offer2, offer1]
57
+ OfferIndex.search_scope.first.instance_variable_get("@original_search_doc")["indexed_at_dt"].should_not be_nil
58
+ OfferIndex.search_scope.total_entries.should == 2
59
+ OfferIndex.search_scope.order("user_id desc").populate.results.should == [offer2, offer1]
60
60
  indexer.instance_variable_get("@index_file_path").should be_nil
61
61
  end
62
62
 
@@ -66,9 +66,9 @@ describe "Solr" do
66
66
  indexer = OfferIndex.new(:db => ActiveRecord::Base.connection, :max_rows_to_direct_index => 0)
67
67
  indexer.index!
68
68
  indexer.instance_variable_get("@index_file_path").should_not be_nil
69
- OfferIndex.search_scope.to_a.total_entries.should == 2
70
- OfferIndex.search_scope.to_a.first.instance_variable_get("@solr_doc")["indexed_at_dt"].should_not be_nil
71
- OfferIndex.search_scope.order("user_id desc").to_a.should == [offer2, offer1]
69
+ OfferIndex.search_scope.total_entries.should == 2
70
+ OfferIndex.search_scope.first.instance_variable_get("@original_search_doc")["indexed_at_dt"].should_not be_nil
71
+ OfferIndex.search_scope.order("user_id desc").populate.results.should == [offer2, offer1]
72
72
  File.should_not be_exists(indexer.instance_variable_get("@index_file_path"))
73
73
  end
74
74
 
@@ -78,39 +78,63 @@ describe "Solr" do
78
78
  indexer = OfferIndex.new(:db => ActiveRecord::Base.connection, :max_rows_to_direct_index => 0, :local_solr => true)
79
79
  indexer.index!
80
80
  indexer.instance_variable_get("@index_file_path").should_not be_nil
81
- OfferIndex.search_scope.to_a.first.instance_variable_get("@solr_doc")["indexed_at_dt"].should_not be_nil
82
- OfferIndex.search_scope.to_a.total_entries.should == 2
83
- OfferIndex.search_scope.order("user_id desc").to_a.should == [offer2, offer1]
81
+ OfferIndex.search_scope.first.instance_variable_get("@original_search_doc")["indexed_at_dt"].should_not be_nil
82
+ OfferIndex.search_scope.total_entries.should == 2
83
+ OfferIndex.search_scope.order("user_id desc").populate.results.should == [offer2, offer1]
84
84
  File.should_not be_exists(indexer.instance_variable_get("@index_file_path"))
85
85
  end
86
+
87
+ describe "with extra_attributes_from_doc method defined" do
88
+
89
+ class OfferIndexWitheExtraSearchMethodFromDoc < Supernova::SolrIndexer
90
+ has :user_id, :type => :integer
91
+ has :popularity, :type => :integer
92
+ has :upcased_text, :type => :text, :virtual => true
93
+ has :text, :type => :text
94
+
95
+ clazz Offer
96
+
97
+ def extra_attributes_from_record(record)
98
+ { :upcased_text => record.text.to_s.upcase.presence }
99
+ end
100
+ end
101
+
102
+ it "sets the capitalize_text attribute" do
103
+ Offer.create!(:user_id => 2, :popularity => 20, :text => "lower_text")
104
+ indexer = OfferIndexWitheExtraSearchMethodFromDoc.new(:db => ActiveRecord::Base.connection)
105
+ indexer.index!
106
+ offer = OfferIndexWitheExtraSearchMethodFromDoc.search_scope.first
107
+ offer.instance_variable_get("@original_search_doc")["upcased_text_t"].should == "LOWER_TEXT"
108
+ end
109
+ end
86
110
  end
87
111
 
88
112
  describe "searching" do
89
113
  it "returns the correct current_page when nil" do
90
- new_criteria.to_a.current_page.should == 1
114
+ new_criteria.current_page.should == 1
91
115
  end
92
116
 
93
117
  it "returns the correct page when set" do
94
- new_criteria.paginate(:page => 10).to_a.current_page.should == 10
118
+ new_criteria.paginate(:page => 10).current_page.should == 10
95
119
  end
96
120
 
97
121
  it "the correct per_page when set" do
98
- new_criteria.paginate(:per_page => 10).to_a.per_page.should == 10
122
+ new_criteria.paginate(:per_page => 10).per_page.should == 10
99
123
  end
100
124
 
101
125
  it "the correct per_page when not set" do
102
- new_criteria.to_a.per_page.should == 25
126
+ new_criteria.per_page.should == 25
103
127
  end
104
128
 
105
129
  describe "plain text search" do
106
130
  it "returns the correct entries for 1 term" do
107
- new_criteria.search("text_t:Hans").to_a.map { |h| h["id"] }.should == [1]
108
- new_criteria.search("text_t:Hans").search("text_t:Meyer").to_a.map { |h| h["id"] }.should == [1]
109
- new_criteria.search("text_t:Marek").to_a.map { |h| h["id"] }.should == [2]
131
+ new_criteria.search("text_t:Hans").map { |h| h["id"] }.should == [1]
132
+ new_criteria.search("text_t:Hans").search("text_t:Meyer").map { |h| h["id"] }.should == [1]
133
+ new_criteria.search("text_t:Marek").map { |h| h["id"] }.should == [2]
110
134
  end
111
135
 
112
136
  it "returns the correct options for a combined search" do
113
- new_criteria.search("text_t:Hans", "text_t:Marek").to_a.map.should == []
137
+ new_criteria.search("text_t:Hans", "text_t:Marek").populate.results.should == []
114
138
  end
115
139
  end
116
140
 
@@ -119,14 +143,14 @@ describe "Solr" do
119
143
  "popularity_i" => 10, "location_p" => "47,11"
120
144
  }.each do |key, value|
121
145
  it "sets #{key} to #{value}" do
122
- doc = new_criteria.search("text_t:Hans").to_a.first.instance_variable_get("@solr_doc")[key].should == value
146
+ doc = new_criteria.search("text_t:Hans").first.instance_variable_get("@original_search_doc")[key].should == value
123
147
  end
124
148
  end
125
149
 
126
150
  describe "nearby search" do
127
151
  { 49.kms => 1, 51.kms => 2 }.each do |distance, total_entries|
128
152
  it "returns #{total_entries} for distance #{distance}" do
129
- new_criteria.attribute_mapping(:location => { :type => :location }).near(47, 11).within(distance).to_a.total_entries.should == total_entries
153
+ new_criteria.attribute_mapping(:location => { :type => :location }).near(47, 11).within(distance).total_entries.should == total_entries
130
154
  end
131
155
  end
132
156
  end
@@ -145,25 +169,25 @@ describe "Solr" do
145
169
  :type => "Offer"
146
170
  )
147
171
  Supernova::Solr.connection.commit
148
- raise "There should be 3 docs" if new_criteria.to_a.total_entries != 3
149
- new_criteria.with(:user_id_i.not => nil).to_a.map { |h| h["id"] }.should == [1, 2]
172
+ raise "There should be 3 docs" if new_criteria.total_entries != 3
173
+ new_criteria.with(:user_id_i.not => nil).map { |h| h["id"] }.should == [1, 2]
150
174
  end
151
175
 
152
176
  it "finds the correct values for not specific value" do
153
- new_criteria.with(:user_id_i.not => 1).to_a.map { |h| h["id"] }.should == [2]
177
+ new_criteria.with(:user_id_i.not => 1).map { |h| h["id"] }.should == [2]
154
178
  end
155
179
  end
156
180
 
157
181
  describe "gt and lt searches" do
158
182
  { :gt => [2], :gte => [1, 2], :lt => [], :lte => [1] }.each do |type, ids|
159
183
  it "finds ids #{ids.inspect} for #{type}" do
160
- new_criteria.with(:user_id_i.send(type) => 1).to_a.map { |row| row["id"] }.sort.should == ids
184
+ new_criteria.with(:user_id_i.send(type) => 1).map { |row| row["id"] }.sort.should == ids
161
185
  end
162
186
  end
163
187
  end
164
188
 
165
189
  it "returns the correct objects" do
166
- new_criteria.with(:user_id_i => 1).to_a.first.should be_an_instance_of(Offer)
190
+ new_criteria.with(:user_id_i => 1).first.should be_an_instance_of(Offer)
167
191
  end
168
192
 
169
193
  { :id => 1, :user_id => 1, :enabled => false, :text => "Hans Meyer", :popularity => 10 }.each do |key, value|
@@ -173,45 +197,45 @@ describe "Solr" do
173
197
  :enabled => { :type => :boolean },
174
198
  :popularity => { :type => :integer },
175
199
  :text => { :type => :text}
176
- ).with(:id => "offers/1").to_a.first
200
+ ).with(:id => "offers/1").first
177
201
  doc.send(key).should == value
178
202
  end
179
203
  end
180
204
 
181
205
  it "combines filters" do
182
- new_criteria.with(:user_id_i => 1, :enabled_b => false).to_a.total_entries.should == 1
183
- new_criteria.with(:user_id_i => 1, :enabled_b => true).to_a.total_entries.should == 0
206
+ new_criteria.with(:user_id_i => 1, :enabled_b => false).total_entries.should == 1
207
+ new_criteria.with(:user_id_i => 1, :enabled_b => true).total_entries.should == 0
184
208
  end
185
209
 
186
210
  it "uses without correctly" do
187
- new_criteria.without(:user_id_i => 1).to_a.map(&:id).should == [2]
188
- new_criteria.without(:user_id_i => 2).to_a.map(&:id).should == [1]
189
- new_criteria.without(:user_id_i => 2).without(:user_id_i => 1).to_a.map(&:id).should == []
211
+ new_criteria.without(:user_id_i => 1).map(&:id).should == [2]
212
+ new_criteria.without(:user_id_i => 2).map(&:id).should == [1]
213
+ new_criteria.without(:user_id_i => 2).without(:user_id_i => 1).map(&:id).should == []
190
214
  end
191
215
 
192
216
  it "uses the correct orders" do
193
- new_criteria.order("id desc").to_a.map(&:id).should == [2, 1]
194
- new_criteria.order("id asc").to_a.map(&:id).should == [1, 2]
217
+ new_criteria.order("id desc").map(&:id).should == [2, 1]
218
+ new_criteria.order("id asc").map(&:id).should == [1, 2]
195
219
  end
196
220
 
197
221
  it "uses the correct pagination attributes" do
198
- new_criteria.with(:user_id_i => 1, :enabled_b => false).to_a.total_entries.should == 1
222
+ new_criteria.with(:user_id_i => 1, :enabled_b => false).total_entries.should == 1
199
223
  new_criteria.with(:user_id_i => 1, :enabled_b => false).length.should == 1
200
- new_criteria.with(:user_id_i => 1, :enabled_b => false).paginate(:page => 10).to_a.total_entries.should == 1
224
+ new_criteria.with(:user_id_i => 1, :enabled_b => false).paginate(:page => 10).total_entries.should == 1
201
225
  new_criteria.with(:user_id_i => 1, :enabled_b => false).paginate(:page => 10).length.should == 0
202
226
 
203
- new_criteria.paginate(:per_page => 1, :page => 1).to_a.map(&:id).should == [1]
204
- new_criteria.paginate(:per_page => 1, :page => 2).to_a.map(&:id).should == [2]
227
+ new_criteria.paginate(:per_page => 1, :page => 1).map(&:id).should == [1]
228
+ new_criteria.paginate(:per_page => 1, :page => 2).map(&:id).should == [2]
205
229
  end
206
230
 
207
231
  it "handels empty results correctly" do
208
- results = new_criteria.with(:user_id_i => 1, :enabled_b => true).to_a
232
+ results = new_criteria.with(:user_id_i => 1, :enabled_b => true)
209
233
  results.total_entries.should == 0
210
234
  results.current_page.should == 1
211
235
  end
212
236
 
213
237
  it "only sets specific attributes" do
214
- results = new_criteria.select(:user_id_i).with(:user_id_i => 1).to_a
238
+ results = new_criteria.select(:user_id_i).with(:user_id_i => 1)
215
239
  results.length.should == 1
216
240
  results.first.should == { "id" => "offers/1", "user_id_i" => 1 }
217
241
  end
@@ -3,7 +3,9 @@ require "ostruct"
3
3
 
4
4
  describe "Supernova::Criteria" do
5
5
  let(:scope) { Supernova::Criteria.new }
6
-
6
+ before(:each) do
7
+ scope.stub!(:execute).and_return [].paginate(:page => 1)
8
+ end
7
9
 
8
10
  describe "#initialize" do
9
11
  it "can be initialized" do
@@ -110,11 +112,68 @@ describe "Supernova::Criteria" do
110
112
  end
111
113
 
112
114
  it "to_a raises an implement in subclass error" do
115
+ scope.unstub(:execute)
113
116
  lambda {
114
- scope.to_a
117
+ scope.execute
115
118
  }.should raise_error("implement in subclass")
116
119
  end
117
120
 
121
+ describe "#to_a" do
122
+ it "calls populate" do
123
+ scope.should_receive(:populate)
124
+ scope.to_a
125
+ end
126
+
127
+ it "returns the results" do
128
+ res = double("results")
129
+ scope.instance_variable_set("@results", res)
130
+ scope.to_a.should == res
131
+ end
132
+ end
133
+
134
+ describe "#populate" do
135
+ it "returns self when" do
136
+ scope.stub!(:populated?).and_return true
137
+ scope.populate.should == scope
138
+ end
139
+
140
+ it "does not change @results when already populated" do
141
+ res = double("results")
142
+ scope.instance_variable_set("@results", res)
143
+ scope.stub!(:populated?).and_return true
144
+ scope.populate.should == scope
145
+ scope.instance_variable_get("@results").should == res
146
+ end
147
+
148
+ it "returns the scope when not populated yet" do
149
+ scope.populate.should == scope
150
+ end
151
+
152
+ it "calls execute" do
153
+ scope.should_receive(:execute).and_return []
154
+ scope.populate
155
+ end
156
+
157
+ it "assigns the result of populate" do
158
+ result = double("result")
159
+ scope.stub!(:execute).and_return result
160
+ scope.populate
161
+ scope.instance_variable_get("@results").should == result
162
+ end
163
+ end
164
+
165
+ describe "#populated?" do
166
+ it "returns false when @results not set" do
167
+ scope.should_not be_populated
168
+ end
169
+
170
+ it "returns true when instance variable set" do
171
+ scope.stub!(:execute).and_return []
172
+ scope.populate
173
+ scope.should be_populated
174
+ end
175
+ end
176
+
118
177
  describe "with to_a stubbed" do
119
178
  let(:array_double) { double("array") }
120
179
 
@@ -122,17 +181,19 @@ describe "Supernova::Criteria" do
122
181
  scope.stub!(:to_a).and_return array_double
123
182
  end
124
183
 
125
- [ :first, :each, :count, :last ].each do |method|
184
+ [ :first, :each, :count, :last, :total_entries ].each do |method|
126
185
  it "forwards #{method} to array" do
127
- ret = double("ret")
128
- array_double.should_receive(method).and_return ret
186
+ results = double("ret")
187
+ scope.instance_variable_set("@results", results)
188
+ scope.should_receive(:populated?).and_return false
129
189
  scope.send(method)
130
190
  end
131
191
  end
132
192
 
133
193
  it "hands given blocks in" do
134
194
  array = [1, 2, 3]
135
- scope.stub!(:to_a).and_return array
195
+ scope.instance_variable_set("@results", array)
196
+ scope.stub!(:populated?).and_return true
136
197
  called = []
137
198
  scope.each do |i|
138
199
  called << i
@@ -155,6 +216,13 @@ describe "Supernova::Criteria" do
155
216
  }.should raise_error(NoMethodError)
156
217
  end
157
218
 
219
+ it "forwards all array methods to @results" do
220
+ results = double("results")
221
+ scope.instance_variable_set("@results", results)
222
+ results.should_receive(:index, "1")
223
+ scope.index("1")
224
+ end
225
+
158
226
  it "calls named_scope_defined" do
159
227
  scope.should_receive(:named_scope_defined?).with(:rgne).and_return false
160
228
  scope.method_missing(:rgne) rescue nil
@@ -195,10 +263,6 @@ describe "Supernova::Criteria" do
195
263
  end
196
264
  end
197
265
 
198
- describe "#with_scopes" do
199
-
200
- end
201
-
202
266
  describe "#merge" do
203
267
  let(:criteria) { Supernova::Criteria.new.order("popularity asc").with(:a => 1).conditions(:b => 2).search("New Search") }
204
268
  let(:new_crit) { Supernova::Criteria.new.order("popularity desc").with(:c => 8).conditions(:e => 9).search("Search") }
@@ -163,7 +163,7 @@ describe Supernova::SolrCriteria do
163
163
  end
164
164
  end
165
165
 
166
- describe "#to_a" do
166
+ describe "#execute" do
167
167
  let(:params) { double("params") }
168
168
 
169
169
  before(:each) do
@@ -173,45 +173,45 @@ describe Supernova::SolrCriteria do
173
173
 
174
174
  it "calls to_params" do
175
175
  criteria.should_receive(:to_params).and_return params
176
- criteria.to_a
176
+ criteria.execute
177
177
  end
178
178
 
179
179
  it "calls get with select and params" do
180
180
  rsolr.should_receive(:post).with("select", :data => params).and_return solr_response
181
- criteria.to_a
181
+ criteria.execute
182
182
  end
183
183
 
184
184
  it "returns a Supernova::Collection" do
185
- criteria.to_a.should be_an_instance_of(Supernova::Collection)
185
+ criteria.execute.should be_an_instance_of(Supernova::Collection)
186
186
  end
187
187
 
188
188
  it "sets the correct page when page is nil" do
189
- criteria.to_a.current_page.should == 1
189
+ criteria.execute.current_page.should == 1
190
190
  end
191
191
 
192
192
  it "sets the correct page when page is 1" do
193
- criteria.paginate(:page => 1).to_a.current_page.should == 1
193
+ criteria.paginate(:page => 1).execute.current_page.should == 1
194
194
  end
195
195
 
196
196
  it "sets the correct page when page is 2" do
197
- criteria.paginate(:page => 2).to_a.current_page.should == 2
197
+ criteria.paginate(:page => 2).execute.current_page.should == 2
198
198
  end
199
199
 
200
200
  it "sets the correct per_page when zero" do
201
- criteria.paginate(:page => 2, :per_page => nil).to_a.per_page.should == 25
201
+ criteria.paginate(:page => 2, :per_page => nil).execute.per_page.should == 25
202
202
  end
203
203
 
204
204
  it "sets the custom per_page when given" do
205
- criteria.paginate(:page => 2, :per_page => 10).to_a.per_page.should == 10
205
+ criteria.paginate(:page => 2, :per_page => 10).execute.per_page.should == 10
206
206
  end
207
207
 
208
208
  it "sets the correct total_entries" do
209
- criteria.paginate(:page => 2, :per_page => 10).to_a.total_entries.should == 2
209
+ criteria.paginate(:page => 2, :per_page => 10).execute.total_entries.should == 2
210
210
  end
211
211
 
212
212
  it "calls build_docs with returned docs" do
213
213
  criteria.should_receive(:build_docs).with(docs).and_return []
214
- criteria.to_a
214
+ criteria.execute
215
215
  end
216
216
 
217
217
  it "calls replace on collection wit returned docs" do
@@ -220,7 +220,7 @@ describe Supernova::SolrCriteria do
220
220
  built_docs = double("built docs")
221
221
  criteria.stub!(:build_docs).and_return built_docs
222
222
  col.should_receive(:replace).with(built_docs)
223
- criteria.to_a
223
+ criteria.execute
224
224
  end
225
225
  end
226
226
 
@@ -283,9 +283,7 @@ describe Supernova::SolrCriteria do
283
283
  criteria.build_doc(row).should == row
284
284
  end
285
285
 
286
- it "assigns the attributes returned from convert_doc_attributes to attributes when instance variable exists" do
287
- str = String.new
288
- str.instance_variable_set("@attributes", {})
286
+ it "assigns the attributes returned from convert_doc_attributes to attributes when record responds to attributes=" do
289
287
  atts = { :title => "Hello" }
290
288
  row = { "type" => "Offer" }
291
289
  criteria.should_receive(:convert_doc_attributes).with(row).and_return atts
@@ -293,9 +291,9 @@ describe Supernova::SolrCriteria do
293
291
  doc.instance_variable_get("@attributes").should == atts
294
292
  end
295
293
 
296
- it "sets the original solr_doc" do
297
- solr_doc = { "type" => "Offer", "id" => "offers/id" }
298
- criteria.build_doc(solr_doc).instance_variable_get("@solr_doc").should == solr_doc
294
+ it "sets the original original_search_doc" do
295
+ original_search_doc = { "type" => "Offer", "id" => "offers/id" }
296
+ criteria.build_doc(original_search_doc).instance_variable_get("@original_search_doc").should == original_search_doc
299
297
  end
300
298
 
301
299
  it "should be readonly" do
@@ -6,11 +6,13 @@ describe Supernova::SolrIndexer do
6
6
  let(:to_index) { { :id => 1, :title => "Some Title"} }
7
7
  let(:file_stub) { double("file").as_null_object }
8
8
  let(:solr) { double("solr").as_null_object }
9
+ let(:solr_index_response) { %(<int name="status">0</int>) }
9
10
 
10
11
  let(:indexer) do
11
12
  indexer = Supernova::SolrIndexer.new
12
13
  indexer.db = db
13
14
  indexer.stub!(:system).and_return true
15
+ Kernel.stub!(:`).and_return solr_index_response
14
16
  indexer
15
17
  end
16
18
 
@@ -68,17 +70,81 @@ describe Supernova::SolrIndexer do
68
70
  indexer.index!
69
71
  end
70
72
 
71
- it "calls row_to_solr with all returned rows from sql" do
73
+ it "calls map_for_solr with all returned rows from sql" do
72
74
  row1 = double("row1")
73
75
  row2 = double("row2")
74
76
  indexer.stub!(:query).and_return [row1, row2]
75
77
  indexer.stub!(:query_to_index).and_return "some query"
76
- indexer.should_receive(:row_to_solr).with(row1)
78
+ indexer.should_receive(:map_for_solr).with(row1)
77
79
  indexer.stub!(:index_query).and_yield(row1)
78
80
  indexer.index!
79
81
  end
80
82
  end
81
83
 
84
+ describe "#map_for_solr" do
85
+ let(:row) { { "a" => 1 } }
86
+
87
+ before(:each) do
88
+ indexer.stub!(:puts)
89
+ end
90
+
91
+ it "calls row_to_solr" do
92
+ indexer.should_not_receive(:before_index)
93
+ indexer.should_receive(:row_to_solr).with(row).and_return row
94
+ indexer.map_for_solr(row)
95
+ end
96
+
97
+ it "prints a deprecation warning when using row_to_solr" do
98
+ indexer.stub!(:row_to_solr).with(row).and_return row
99
+ indexer.should_receive(:puts).with(/DEPRECATION WARNING: use before_index instead of row_to_solr! in/)
100
+ indexer.map_for_solr(row)
101
+ end
102
+
103
+ it "calls before_index when row_to_solr is not defined" do
104
+ row = { "a" => 1 }
105
+ indexer.should_receive(:before_index).with(row).and_return row
106
+ indexer.map_for_solr(row)
107
+ end
108
+
109
+ it "calls map_hash_keys_to_solr with result of row_to_solr" do
110
+ dummy_row = double("dummy row")
111
+ indexer.stub!(:row_to_solr).and_return dummy_row
112
+ indexer.should_receive(:map_hash_keys_to_solr).with(dummy_row)
113
+ indexer.map_for_solr({ "a" => 1 })
114
+ end
115
+
116
+ describe "with the index defining extra_attributes_from_record" do
117
+ let(:index) { SolrOfferIndex.new }
118
+ let(:offer_double) { double("Solr Offer", :id => 88).as_null_object }
119
+
120
+ class SolrOfferIndex < Supernova::SolrIndexer
121
+ clazz Offer
122
+ has :created_at, :type => :date
123
+ has :offer_id, :type => :integer
124
+
125
+ def extra_attributes_from_record(doc)
126
+ { :offer_code => "OFFER_#{doc.id}" }
127
+ end
128
+ end
129
+
130
+ it "calls Supernova.build_ar_like_record with correct parameters" do
131
+ Supernova.should_receive(:build_ar_like_record).and_return offer_double
132
+ SolrOfferIndex.new("offer_id" => 77, "type" => "Offer").map_for_solr(row)
133
+ end
134
+
135
+ it "includes the original attributes" do
136
+ index = SolrOfferIndex.new
137
+ index.map_for_solr({ "a" => 2 })["a"].should == 2
138
+ end
139
+
140
+ it "includes the attributes from extra_attributes_from_record" do
141
+ index = SolrOfferIndex.new
142
+ index.map_for_solr({ "a" => 2, "id" => "88" })["offer_code"].should == "OFFER_88"
143
+ hash = { :a => 1, "a" => 2 }
144
+ end
145
+ end
146
+ end
147
+
82
148
  describe "validate_lat" do
83
149
  { nil => nil, 10 => 10.0, 90.1 => nil, 90 => 90, -90.1 => nil, -90 => -90 }.each do |from, to|
84
150
  it "converts #{from} to #{to}" do
@@ -118,9 +184,9 @@ describe Supernova::SolrIndexer do
118
184
  end
119
185
  end
120
186
 
121
- describe "#row_to_solr" do
187
+ describe "#before_index" do
122
188
  it "returns the db row by default" do
123
- indexer.row_to_solr("id" => 1).should == { "id" => 1 }
189
+ indexer.before_index("id" => 1).should == { "id" => 1 }
124
190
  end
125
191
  end
126
192
 
@@ -138,6 +204,56 @@ describe Supernova::SolrIndexer do
138
204
  end
139
205
  end
140
206
 
207
+ describe "#map_hash_keys_to_solr" do
208
+ class CustomSolrIndex < Supernova::SolrIndexer
209
+ has :offer_id, :type => :integer
210
+ has :lat, :type => :float
211
+ has :lng, :type => :float
212
+ has :created_at, :type => :date
213
+ has :checkin_date, :type => :date
214
+ end
215
+
216
+ it "maps float fields" do
217
+ index = CustomSolrIndex.new
218
+ index.map_hash_keys_to_solr("lat" => 49.0)["lat_f"].should == 49.0
219
+ end
220
+
221
+ it "maps time fields to iso8601" do
222
+ index = CustomSolrIndex.new
223
+ time = Time.parse("2011-02-03 11:20:30")
224
+ index.map_hash_keys_to_solr("created_at" => time)["created_at_dt"].should == "2011-02-03T10:20:30Z"
225
+ end
226
+
227
+ it "maps date fields to iso8601" do
228
+ date = Date.new(2011, 1, 2)
229
+ CustomSolrIndex.new.map_hash_keys_to_solr("checkin_date" => date)["checkin_date_dt"].should == "2011-01-02T00:00:00Z"
230
+ end
231
+
232
+ it "sets the indexed_at time" do
233
+ Time.stub!(:now).and_return Time.parse("2011-02-03T11:20:30Z")
234
+ CustomSolrIndex.new.map_hash_keys_to_solr({})["indexed_at_dt"].should == "2011-02-03T11:20:30Z"
235
+ Time.unstub!(:now)
236
+ end
237
+
238
+ it "adds the class as type when class set" do
239
+ clazz = Class.new(Supernova::SolrIndexer)
240
+ clazz.clazz Offer
241
+ clazz.new.map_hash_keys_to_solr({})["type"].should == "Offer"
242
+ end
243
+
244
+ it "uses the table_name as prefix for ids" do
245
+ clazz = Class.new(Supernova::SolrIndexer)
246
+ clazz.table_name :people
247
+ clazz.new.map_hash_keys_to_solr({ "id" => 88 })["id_s"].should == "people/88"
248
+ end
249
+
250
+ it "adds the sets the cla" do
251
+ clazz = Class.new(Supernova::SolrIndexer)
252
+ clazz.clazz Offer
253
+ clazz.new.map_hash_keys_to_solr({})["type"].should == "Offer"
254
+ end
255
+ end
256
+
141
257
  describe "#index_query" do
142
258
  let(:query) { %(SELECT CONCAT("user_", id) AS id, title FROM people WHERE type = 'User') }
143
259
 
@@ -300,13 +416,13 @@ describe Supernova::SolrIndexer do
300
416
 
301
417
  it "calls the correct curl command" do
302
418
  indexer = Supernova::SolrIndexer.new(:index_file_path => "/tmp/some_path.json", :local_solr => true)
303
- Kernel.should_receive(:`).with("curl -s 'http://solr.xx:9333/solr/update/json?commit=true\\&stream.file=/tmp/some_path.json'").and_return "<\"status\">0"
419
+ Kernel.should_receive(:`).with("curl -s 'http://solr.xx:9333/solr/update/json?commit=true\\&stream.file=/tmp/some_path.json'").and_return solr_index_response
304
420
  indexer.do_index_file(:local => true)
305
421
  end
306
422
 
307
423
  it "calls rm on file" do
308
424
  indexer = Supernova::SolrIndexer.new(:index_file_path => "/tmp/some_path.json", :local_solr => true)
309
- Kernel.should_receive(:`).with("curl -s 'http://solr.xx:9333/solr/update/json?commit=true\\&stream.file=/tmp/some_path.json'").and_return %(<int name="status">0</int>)
425
+ Kernel.should_receive(:`).with("curl -s 'http://solr.xx:9333/solr/update/json?commit=true\\&stream.file=/tmp/some_path.json'").and_return solr_index_response
310
426
  FileUtils.should_receive(:rm_f).with("/tmp/some_path.json")
311
427
  indexer.do_index_file(:local => true)
312
428
  end
@@ -315,13 +431,15 @@ describe Supernova::SolrIndexer do
315
431
  indexer = Supernova::SolrIndexer.new(:index_file_path => "/tmp/some_path.json", :local_solr => true)
316
432
  Kernel.should_receive(:`).with("curl -s 'http://solr.xx:9333/solr/update/json?commit=true\\&stream.file=/tmp/some_path.json'").and_return %(<int name="status">1</int>)
317
433
  FileUtils.should_not_receive(:rm_f).with("/tmp/some_path.json")
318
- indexer.do_index_file(:local => true)
434
+ lambda {
435
+ indexer.do_index_file(:local => true)
436
+ }.should raise_error
319
437
  end
320
438
 
321
439
  it "executes the correct curl call when not local" do
322
440
  # curl 'http://localhost:8983/solr/update/json?commit=true' --data-binary @books.json -H 'Content-type:application/json'
323
441
  indexer.index_file_path = "/tmp/some_path.json"
324
- Kernel.should_receive(:`).with("cd /tmp && curl -s 'http://solr.xx:9333/solr/update/json?commit=true' --data-binary @some_path.json -H 'Content-type:application/json'")
442
+ Kernel.should_receive(:`).with("cd /tmp && curl -s 'http://solr.xx:9333/solr/update/json?commit=true' --data-binary @some_path.json -H 'Content-type:application/json'").and_return solr_index_response
325
443
  indexer.do_index_file
326
444
  end
327
445
  end
@@ -394,9 +512,7 @@ describe Supernova::SolrIndexer do
394
512
  custom_indexer.defined_fields
395
513
  end
396
514
 
397
- ["title AS title_t", "artist_id AS artist_id_i", "description AS description_t",
398
- %(IF(created_at IS NULL, NULL, CONCAT(REPLACE(created_at, " ", "T"), "Z")) AS created_at_dt)
399
- ].each do |field|
515
+ ["title", "artist_id", "description", "created_at"].each do |field|
400
516
  it "includes field #{field.inspect}" do
401
517
  custom_indexer.defined_fields.should include(field)
402
518
  end
@@ -406,7 +522,7 @@ describe Supernova::SolrIndexer do
406
522
  clazz = Class.new(Supernova::SolrIndexer)
407
523
  clazz.has :location, :type => :location, :virtual => true
408
524
  clazz.has :title, :type => :string
409
- clazz.new.defined_fields.should == ["title AS title_s"]
525
+ clazz.new.defined_fields.should == ["title"]
410
526
  end
411
527
  end
412
528
 
@@ -84,4 +84,45 @@ describe Supernova do
84
84
  end
85
85
  end
86
86
  end
87
+
88
+ describe "#build_ar_like_record" do
89
+ let(:offer_double) { double("offer").as_null_object }
90
+
91
+ before(:each) do
92
+ Offer.stub!(:new).and_return offer_double
93
+ end
94
+
95
+ it "is callable" do
96
+ Supernova.should respond_to(:build_ar_like_record)
97
+ end
98
+
99
+ it "creates a new instance" do
100
+ Offer.should_receive(:new).and_return offer_double
101
+ Supernova.build_ar_like_record(Offer, {})
102
+ end
103
+
104
+ it "sets the attributes" do
105
+ atts = { :a => 1 }
106
+ offer_double.should_receive(:instance_variable_set).with("@attributes", atts)
107
+ Supernova.build_ar_like_record(Offer, atts)
108
+ end
109
+
110
+ it "returns the record" do
111
+ Supernova.build_ar_like_record(Offer, {}).should == offer_double
112
+ end
113
+
114
+ it "sets readonly to true" do
115
+ Supernova.build_ar_like_record(Offer, {}).instance_variable_get("@readonly").should == true
116
+ end
117
+
118
+ it "sets new_record to false" do
119
+ Supernova.build_ar_like_record(Offer, {}).instance_variable_get("@new_record").should == false
120
+ end
121
+
122
+ it "sets the original_search_doc hash when given" do
123
+ original_search_doc = double("original_search_doc")
124
+ doc = Supernova.build_ar_like_record(Offer, {}, original_search_doc)
125
+ doc.instance_variable_get("@original_search_doc").should == original_search_doc
126
+ end
127
+ end
87
128
  end
@@ -5,13 +5,14 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{supernova}
8
- s.version = "0.3.12"
8
+ s.version = "0.3.13"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tobias Schwab"]
12
- s.date = %q{2011-06-17}
12
+ s.date = %q{2011-06-29}
13
13
  s.description = %q{Unified search scopes}
14
14
  s.email = %q{tobias.schwab@dynport.de}
15
+ s.executables = ["start_solr"]
15
16
  s.extra_rdoc_files = [
16
17
  "LICENSE.txt",
17
18
  "README.rdoc"
@@ -27,6 +28,7 @@ Gem::Specification.new do |s|
27
28
  "Rakefile",
28
29
  "VERSION",
29
30
  "autotest/discover.rb",
31
+ "bin/start_solr",
30
32
  "lib/supernova.rb",
31
33
  "lib/supernova/collection.rb",
32
34
  "lib/supernova/condition.rb",
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: supernova
3
3
  version: !ruby/object:Gem::Version
4
- hash: 11
4
+ hash: 9
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 3
9
- - 12
10
- version: 0.3.12
9
+ - 13
10
+ version: 0.3.13
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tobias Schwab
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-17 00:00:00 Z
18
+ date: 2011-06-29 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  requirement: &id001 !ruby/object:Gem::Requirement
@@ -211,8 +211,8 @@ dependencies:
211
211
  type: :development
212
212
  description: Unified search scopes
213
213
  email: tobias.schwab@dynport.de
214
- executables: []
215
-
214
+ executables:
215
+ - start_solr
216
216
  extensions: []
217
217
 
218
218
  extra_rdoc_files:
@@ -229,6 +229,7 @@ files:
229
229
  - Rakefile
230
230
  - VERSION
231
231
  - autotest/discover.rb
232
+ - bin/start_solr
232
233
  - lib/supernova.rb
233
234
  - lib/supernova/collection.rb
234
235
  - lib/supernova/condition.rb