forked from OpenNeo/impress
basic specs for items, including search
This commit is contained in:
parent
fb73d23db8
commit
851aa49091
9 changed files with 301 additions and 4 deletions
|
@ -1,3 +1,64 @@
|
||||||
class Item
|
class Item < ActiveRecord::Base
|
||||||
|
set_table_name 'objects' # Neo & PHP Impress call them objects, but the class name is a conflict (duh!)
|
||||||
|
set_inheritance_column 'inheritance_type' # PHP Impress used "type" to describe category
|
||||||
|
|
||||||
# Not defining validations, since this app is currently read-only
|
# Not defining validations, since this app is currently read-only
|
||||||
|
|
||||||
|
def species_support_ids
|
||||||
|
@species_support_ids_array ||= read_attribute('species_support_ids').split(',').map(&:to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
def species_support_ids=(replacement)
|
||||||
|
replacement = replacement.join(',') if replacement.is_a?(Array)
|
||||||
|
write_attribute('species_support_ids', replacement)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.search(query)
|
||||||
|
query_conditions = [Condition.new]
|
||||||
|
in_phrase = false
|
||||||
|
query.each_char do |c|
|
||||||
|
if c == ' ' && !in_phrase
|
||||||
|
query_conditions << Condition.new
|
||||||
|
elsif c == '"'
|
||||||
|
in_phrase = !in_phrase
|
||||||
|
elsif c == ':' && !in_phrase
|
||||||
|
query_conditions.last.to_property!
|
||||||
|
else
|
||||||
|
query_conditions.last << c
|
||||||
|
end
|
||||||
|
end
|
||||||
|
query_conditions.inject(self) do |scope, condition|
|
||||||
|
condition.narrow(scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
class Condition < String
|
||||||
|
attr_reader :property
|
||||||
|
|
||||||
|
def to_property!
|
||||||
|
@property = self.clone
|
||||||
|
self.replace ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def narrow(scope)
|
||||||
|
if @property == 'species'
|
||||||
|
species = Species.find_by_name(self)
|
||||||
|
# TODO: add a many-to-many table to handle this relationship
|
||||||
|
scope.where('species_support_ids = ? OR species_support_ids LIKE ? OR species_support_ids LIKE ? OR species_support_ids LIKE ?',
|
||||||
|
species.id,
|
||||||
|
"#{species.id},%",
|
||||||
|
"%,#{species.id},%",
|
||||||
|
"%,#{species.id}"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
scope.where('name LIKE :matcher OR description LIKE :matcher', :matcher => "%#{self}%")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect
|
||||||
|
@property ? "#{@property}:#{super}" : super
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
20
app/models/species.rb
Normal file
20
app/models/species.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
class Species
|
||||||
|
attr_accessor :id, :name
|
||||||
|
|
||||||
|
@objects = []
|
||||||
|
@objects_by_name = {}
|
||||||
|
File.open(Rails.root.join('config', 'species.txt')).each do |line|
|
||||||
|
name = line.chomp.downcase
|
||||||
|
@objects << @objects_by_name[name] = species = Species.new
|
||||||
|
species.id = @objects.size
|
||||||
|
species.name = name
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find(id)
|
||||||
|
@objects[id-1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.find_by_name(name)
|
||||||
|
@objects_by_name[name.downcase]
|
||||||
|
end
|
||||||
|
end
|
54
config/species.txt
Normal file
54
config/species.txt
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
Acara
|
||||||
|
Aisha
|
||||||
|
Blumaroo
|
||||||
|
Bori
|
||||||
|
Bruce
|
||||||
|
Buzz
|
||||||
|
Chia
|
||||||
|
Chomby
|
||||||
|
Cybunny
|
||||||
|
Draik
|
||||||
|
Elephante
|
||||||
|
Eyrie
|
||||||
|
Flotsam
|
||||||
|
Gelert
|
||||||
|
Gnorbu
|
||||||
|
Grarrl
|
||||||
|
Grundo
|
||||||
|
Hissi
|
||||||
|
Ixi
|
||||||
|
Jetsam
|
||||||
|
Jubjub
|
||||||
|
Kacheek
|
||||||
|
Kau
|
||||||
|
Kiko
|
||||||
|
Koi
|
||||||
|
Korbat
|
||||||
|
Kougra
|
||||||
|
Krawk
|
||||||
|
Kyrii
|
||||||
|
Lenny
|
||||||
|
Lupe
|
||||||
|
Lutari
|
||||||
|
Meerca
|
||||||
|
Moehog
|
||||||
|
Mynci
|
||||||
|
Nimmo
|
||||||
|
Ogrin
|
||||||
|
Peophin
|
||||||
|
Poogle
|
||||||
|
Pteri
|
||||||
|
Quiggle
|
||||||
|
Ruki
|
||||||
|
Scorchio
|
||||||
|
Shoyru
|
||||||
|
Skeith
|
||||||
|
Techo
|
||||||
|
Tonu
|
||||||
|
Tuskaninny
|
||||||
|
Uni
|
||||||
|
Usul
|
||||||
|
Wocky
|
||||||
|
Xweetok
|
||||||
|
Yurble
|
||||||
|
Zafara
|
|
@ -1,6 +1,6 @@
|
||||||
class CreateItems < ActiveRecord::Migration
|
class CreateObjects < ActiveRecord::Migration
|
||||||
def self.up
|
def self.up
|
||||||
create_table :items do |t|
|
create_table :objects do |t|
|
||||||
t.string :name
|
t.string :name
|
||||||
t.string :species_support_ids
|
t.string :species_support_ids
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ class CreateItems < ActiveRecord::Migration
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.down
|
def self.down
|
||||||
drop_table :items
|
drop_table :objects
|
||||||
end
|
end
|
||||||
end
|
end
|
9
db/migrate/20100515020945_add_description_to_objects.rb
Normal file
9
db/migrate/20100515020945_add_description_to_objects.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class AddDescriptionToObjects < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
add_column :objects, :description, :text
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_column :objects, :description
|
||||||
|
end
|
||||||
|
end
|
22
db/schema.rb
Normal file
22
db/schema.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# This file is auto-generated from the current state of the database. Instead of editing this file,
|
||||||
|
# please use the migrations feature of Active Record to incrementally modify your database, and
|
||||||
|
# then regenerate this schema definition.
|
||||||
|
#
|
||||||
|
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
|
||||||
|
# to create the application database on another system, you should be using db:schema:load, not running
|
||||||
|
# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
||||||
|
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
||||||
|
#
|
||||||
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
|
ActiveRecord::Schema.define(:version => 20100515020945) do
|
||||||
|
|
||||||
|
create_table "objects", :force => true do |t|
|
||||||
|
t.string "name"
|
||||||
|
t.string "species_support_ids"
|
||||||
|
t.datetime "created_at"
|
||||||
|
t.datetime "updated_at"
|
||||||
|
t.text "description"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
105
spec/models/item_spec.rb
Normal file
105
spec/models/item_spec.rb
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Item do
|
||||||
|
specify "should accept string or array for species_support_ids" do
|
||||||
|
items = [
|
||||||
|
Factory.build(:item, :species_support_ids => '1,2,3'),
|
||||||
|
Factory.build(:item, :species_support_ids => [1,2,3])
|
||||||
|
]
|
||||||
|
items.each { |i| i.species_support_ids.should == [1,2,3] }
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search name for word" do
|
||||||
|
query_should 'blue',
|
||||||
|
:return => [
|
||||||
|
'A Hat That is Blue',
|
||||||
|
'Blue Hat',
|
||||||
|
'Blueish Hat',
|
||||||
|
'Very Blue Hat'
|
||||||
|
],
|
||||||
|
:not_return => [
|
||||||
|
'Green Hat',
|
||||||
|
'Red Hat'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search name for phrase" do
|
||||||
|
query_should '"one two"',
|
||||||
|
:return => [
|
||||||
|
'Zero one two three',
|
||||||
|
'Zero one two',
|
||||||
|
'One two three'
|
||||||
|
],
|
||||||
|
:not_return => [
|
||||||
|
'Zero one three',
|
||||||
|
'Zero two three',
|
||||||
|
'Zero one and two',
|
||||||
|
'Three two one'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search name for multiple words" do
|
||||||
|
query_should 'one two',
|
||||||
|
:return => [
|
||||||
|
'Zero one two three',
|
||||||
|
'Zero one two',
|
||||||
|
'One two three',
|
||||||
|
'Zero one and two',
|
||||||
|
'Three two one'
|
||||||
|
],
|
||||||
|
:not_return => [
|
||||||
|
'Zero one three',
|
||||||
|
'Zero two three'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search name for words and phrases" do
|
||||||
|
query_should 'zero "one two" three',
|
||||||
|
:return => [
|
||||||
|
'zero one two three',
|
||||||
|
'zero four one two three',
|
||||||
|
'one two zero three',
|
||||||
|
'three zero one two'
|
||||||
|
],
|
||||||
|
:not_return => [
|
||||||
|
'one two three',
|
||||||
|
'zero one two',
|
||||||
|
'three one zero two',
|
||||||
|
'two one three zero'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search name and description" do
|
||||||
|
query_should 'zero "one two"',
|
||||||
|
:return => [
|
||||||
|
'zero one two',
|
||||||
|
['zero one', 'one two three'],
|
||||||
|
['one two four', 'one three zero'],
|
||||||
|
['three', 'one two four zero']
|
||||||
|
],
|
||||||
|
:not_return => [
|
||||||
|
['zero one', 'two'],
|
||||||
|
['one two four', 'three'],
|
||||||
|
['five two', 'zero one three two']
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search by species" do
|
||||||
|
[[2],[1,2,3],[2,3],[3],[1,3]].each do |ids|
|
||||||
|
Factory.create :item, :species_support_ids => ids
|
||||||
|
end
|
||||||
|
Item.search('species:acara').count.should == 2
|
||||||
|
Item.search('species:aisha').count.should == 3
|
||||||
|
Item.search('species:blumaroo').count.should == 4
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "class should search by species and words" do
|
||||||
|
Factory.create :item, :name => 'Blue Hat', :species_support_ids => [1]
|
||||||
|
Factory.create :item, :name => 'Very Blue Hat', :species_support_ids => [1,2]
|
||||||
|
Factory.create :item, :name => 'Red Hat', :species_support_ids => [2]
|
||||||
|
Item.search('blue species:acara').count.should == 2
|
||||||
|
Item.search('blue species:aisha').count.should == 1
|
||||||
|
Item.search('red species:acara').count.should == 0
|
||||||
|
Item.search('red species:aisha').count.should == 1
|
||||||
|
end
|
||||||
|
end
|
18
spec/models/species_spec.rb
Normal file
18
spec/models/species_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Species do
|
||||||
|
specify "should find by id, report name" do
|
||||||
|
Species.find(1).name.should == 'acara'
|
||||||
|
Species.find(2).name.should == 'aisha'
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "should find by name, report id" do
|
||||||
|
Species.find_by_name('acara').id.should == 1
|
||||||
|
Species.find_by_name('aisha').id.should == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
specify "name should be case-insensitive" do
|
||||||
|
Species.find_by_name('Acara').id.should == 1
|
||||||
|
Species.find_by_name('acara').id.should == 1
|
||||||
|
end
|
||||||
|
end
|
|
@ -21,4 +21,12 @@ Rspec.configure do |config|
|
||||||
# If you'd prefer not to run each of your examples within a transaction,
|
# If you'd prefer not to run each of your examples within a transaction,
|
||||||
# uncomment the following line.
|
# uncomment the following line.
|
||||||
# config.use_transactional_examples = false
|
# config.use_transactional_examples = false
|
||||||
|
|
||||||
|
def query_should(query, sets)
|
||||||
|
sets = sets.each { |k,v| sets[k] = v.map { |x| x.is_a?(Array) ? x : [x, ''] } }
|
||||||
|
all_sets = sets[:return] + sets[:not_return]
|
||||||
|
all_sets.each { |s| Factory.create :item, :name => s[0], :description => s[1]}
|
||||||
|
returned_sets = Item.search(query).all.map { |i| [i.name, i.description] }.sort
|
||||||
|
returned_sets.should == sets[:return].sort
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue