supernova 0.3.12 → 0.3.13

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/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