basic specs for items, including search

This commit is contained in:
Emi Matchu 2010-05-15 11:38:45 -04:00
parent fb73d23db8
commit 851aa49091
9 changed files with 301 additions and 4 deletions

View file

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

20
app/models/species.rb Normal file
View 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
View 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

View file

@ -1,6 +1,6 @@
class CreateItems < ActiveRecord::Migration
class CreateObjects < ActiveRecord::Migration
def self.up
create_table :items do |t|
create_table :objects do |t|
t.string :name
t.string :species_support_ids
@ -9,6 +9,6 @@ class CreateItems < ActiveRecord::Migration
end
def self.down
drop_table :items
drop_table :objects
end
end

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

View 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

View file

@ -21,4 +21,12 @@ Rspec.configure do |config|
# If you'd prefer not to run each of your examples within a transaction,
# uncomment the following line.
# 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