From 4bd16f053847f2efe347ebda9136ef2233ee0d2c Mon Sep 17 00:00:00 2001 From: hukl Date: Tue, 28 Apr 2009 00:15:53 +0200 Subject: added thinking_sphinx plugin for fulltext search on nodes and heads --- .../plugins/thinking-sphinx/spec/fixtures/data.sql | 32 +++ .../spec/fixtures/database.yml.default | 3 + .../thinking-sphinx/spec/fixtures/models.rb | 94 ++++++ .../thinking-sphinx/spec/fixtures/structure.sql | 95 +++++++ vendor/plugins/thinking-sphinx/spec/spec_helper.rb | 56 ++++ .../plugins/thinking-sphinx/spec/sphinx_helper.rb | 114 ++++++++ .../thinking_sphinx/active_record/delta_spec.rb | 136 +++++++++ .../active_record/has_many_association_spec.rb | 53 ++++ .../thinking_sphinx/active_record/search_spec.rb | 107 +++++++ .../unit/thinking_sphinx/active_record_spec.rb | 316 +++++++++++++++++++++ .../spec/unit/thinking_sphinx/association_spec.rb | 247 ++++++++++++++++ .../spec/unit/thinking_sphinx/attribute_spec.rb | 227 +++++++++++++++ .../spec/unit/thinking_sphinx/collection_spec.rb | 14 + .../unit/thinking_sphinx/configuration_spec.rb | 136 +++++++++ .../spec/unit/thinking_sphinx/core/string_spec.rb | 9 + .../spec/unit/thinking_sphinx/field_spec.rb | 145 ++++++++++ .../unit/thinking_sphinx/index/builder_spec.rb | 5 + .../unit/thinking_sphinx/index/faux_column_spec.rb | 30 ++ .../spec/unit/thinking_sphinx/index_spec.rb | 144 ++++++++++ .../unit/thinking_sphinx/rails_additions_spec.rb | 183 ++++++++++++ .../spec/unit/thinking_sphinx/search_spec.rb | 59 ++++ .../spec/unit/thinking_sphinx_spec.rb | 133 +++++++++ 22 files changed, 2338 insertions(+) create mode 100644 vendor/plugins/thinking-sphinx/spec/fixtures/data.sql create mode 100644 vendor/plugins/thinking-sphinx/spec/fixtures/database.yml.default create mode 100644 vendor/plugins/thinking-sphinx/spec/fixtures/models.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/fixtures/structure.sql create mode 100644 vendor/plugins/thinking-sphinx/spec/spec_helper.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/sphinx_helper.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/delta_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/search_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/association_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/attribute_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/collection_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/configuration_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/core/string_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/field_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/builder_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/faux_column_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/rails_additions_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/search_spec.rb create mode 100644 vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx_spec.rb (limited to 'vendor/plugins/thinking-sphinx/spec') diff --git a/vendor/plugins/thinking-sphinx/spec/fixtures/data.sql b/vendor/plugins/thinking-sphinx/spec/fixtures/data.sql new file mode 100644 index 0000000..d5ec579 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/fixtures/data.sql @@ -0,0 +1,32 @@ +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('female','Ellie','K','Ford','38 Mills Street','Eagle Farm Bc','QLD','4009','Ellie.K.Ford@mailinator.com','1970/1/23 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('female','Aaliyah','E','Allen','71 Murphy Street','Wyola West','WA','6407','Aaliyah.E.Allen@dodgit.com','1980/3/23 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('male','Callum','C','Miah','89 Dalgarno Street','Bullawa Creek','NSW','2390','Callum.C.Miah@trashymail.com','1973/3/25 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('male','Finley','L','Buckley','18 Queen Street','Manly Vale','NSW','2093','Finley.L.Buckley@spambob.com','1962/11/20 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('female','Poppy','A','Hilton','36 Nerrigundah Drive','Nyora','VIC','3987','Poppy.A.Hilton@dodgit.com','1972/10/30 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('female','Eloise','Z','Kennedy','18 Mt Berryman Road','Lilydale','QLD','4344','Eloise.Z.Kennedy@spambob.com','1973/9/28 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('female','Shannon','L','Manning','60 Ocean Pde','Greenvale','QLD','4816','Shannon.L.Manning@dodgit.com','1956/6/13 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('male','Oscar','C','Lawson','43 Feather Street','Battery Hill','QLD','4551','Oscar.C.Lawson@spambob.com','1979/10/17 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('female','Sofia','K','Bray','26 Clifton Street','Pental Island','VIC','3586','Sofia.K.Bray@mailinator.com','1970/5/10 00:00:00', 3, 'CricketTeam'); +insert into `people` (gender, first_name, middle_initial, last_name, street_address, city, state, postcode, email, birthday, team_id, team_type) values('male','Andrew','N','Byrne','35 Cecil Street','Monash Park','NSW','2111','Andrew.N.Byrne@spambob.com','1983/2/16 00:00:00', 3, 'CricketTeam'); + +insert into `alphas` (name) values ('one'); +insert into `alphas` (name) values ('two'); +insert into `alphas` (name) values ('three'); +insert into `alphas` (name) values ('four'); +insert into `alphas` (name) values ('five'); +insert into `alphas` (name) values ('six'); +insert into `alphas` (name) values ('seven'); +insert into `alphas` (name) values ('eight'); +insert into `alphas` (name) values ('nine'); +insert into `alphas` (name) values ('ten'); + +insert into `betas` (name) values ('one'); +insert into `betas` (name) values ('two'); +insert into `betas` (name) values ('three'); +insert into `betas` (name) values ('four'); +insert into `betas` (name) values ('five'); +insert into `betas` (name) values ('six'); +insert into `betas` (name) values ('seven'); +insert into `betas` (name) values ('eight'); +insert into `betas` (name) values ('nine'); +insert into `betas` (name) values ('ten'); diff --git a/vendor/plugins/thinking-sphinx/spec/fixtures/database.yml.default b/vendor/plugins/thinking-sphinx/spec/fixtures/database.yml.default new file mode 100644 index 0000000..dfad2a6 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/fixtures/database.yml.default @@ -0,0 +1,3 @@ +username: root +password: +host: localhost \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/fixtures/models.rb b/vendor/plugins/thinking-sphinx/spec/fixtures/models.rb new file mode 100644 index 0000000..0e62906 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/fixtures/models.rb @@ -0,0 +1,94 @@ +class Person < ActiveRecord::Base + belongs_to :team, :polymorphic => :true + has_many :contacts + + has_many :friendships + has_many :friends, :through => :friendships + + has_many :tags + + has_many :football_teams, :through => :tags + + define_index do + indexes [first_name, middle_initial, last_name], :as => :name + indexes team.name, :as => :team_name + indexes contacts.phone_number, :as => :phone_numbers + indexes city, :prefixes => true + indexes state, :infixes => true + + has [first_name, middle_initial, last_name], :as => :name_sort + has team.name, :as => :team_name_sort + + has [:id, :team_id], :as => :ids + has team(:id), :as => :team_id + + has contacts.phone_number, :as => :phone_number_sort + has contacts(:id), :as => :contact_ids + + has birthday + + has friendships.person_id, :as => :friendly_ids + + set_property :delta => true + end +end + +class Parent < Person +end + +class Child < Person + belongs_to :parent + define_index do + indexes [parent.first_name, parent.middle_initial, parent.last_name], :as => :parent_name + end +end + +class Contact < ActiveRecord::Base + belongs_to :person +end + +class Tag < ActiveRecord::Base + belongs_to :person + belongs_to :football_team + belongs_to :cricket_team +end + +class FootballTeam < ActiveRecord::Base + has_many :tags +end + +class CricketTeam < ActiveRecord::Base + define_index do + indexes :name + has "SELECT cricket_team_id, id FROM tags", :source => :query, :as => :tags + end +end + +class Friendship < ActiveRecord::Base + belongs_to :person + belongs_to :friend, :class_name => "Person", :foreign_key => :friend_id + + define_index do + has person_id, friend_id + end +end + +class Alpha < ActiveRecord::Base + define_index do + indexes :name, :sortable => true + + set_property :field_weights => {"name" => 10} + end +end + +class Beta < ActiveRecord::Base + define_index do + indexes :name, :sortable => true + + set_property :delta => true + end +end + +class Search < ActiveRecord::Base + # +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/fixtures/structure.sql b/vendor/plugins/thinking-sphinx/spec/fixtures/structure.sql new file mode 100644 index 0000000..4ab390d --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/fixtures/structure.sql @@ -0,0 +1,95 @@ +DROP TABLE IF EXISTS `people`; + +CREATE TABLE `people` ( + `id` int(11) NOT NULL auto_increment, + `first_name` varchar(50) NULL, + `middle_initial` varchar(10) NULL, + `last_name` varchar(50) NULL, + `gender` varchar(10) NULL, + `street_address` varchar(200) NULL, + `city` varchar(100) NULL, + `state` varchar(100) NULL, + `postcode` varchar(10) NULL, + `email` varchar(100) NULL, + `birthday` datetime NULL, + `team_id` int(11) NULL, + `team_type` varchar(50) NULL, + `type` varchar(50) NULL, + `parent_id` varchar(50) NULL, + `delta` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `friendships`; + +CREATE TABLE `friendships` ( + `id` int(11) NOT NULL auto_increment, + `person_id` int(11) NOT NULL, + `friend_id` int(11) NOT NULL, + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `football_teams`; + +CREATE TABLE `football_teams` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(50) NOT NULL, + `state` varchar(50) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `cricket_teams`; + +CREATE TABLE `cricket_teams` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(50) NOT NULL, + `state` varchar(50) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `contacts`; + +CREATE TABLE `contacts` ( + `id` int(11) NOT NULL auto_increment, + `phone_number` varchar(50) NOT NULL, + `person_id` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `alphas`; + +CREATE TABLE `alphas` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(50) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `betas`; + +CREATE TABLE `betas` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(50) NOT NULL, + `delta` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `searches`; + +CREATE TABLE `searches` ( + `id` int(11) NOT NULL auto_increment, + `name` varchar(50) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `tags`; + +CREATE TABLE `tags` ( + `id` int(11) NOT NULL auto_increment, + `person_id` int(11) NOT NULL, + `football_team_id` int(11) NOT NULL, + `cricket_team_id` int(11) NOT NULL, + `name` varchar(50) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/vendor/plugins/thinking-sphinx/spec/spec_helper.rb b/vendor/plugins/thinking-sphinx/spec/spec_helper.rb new file mode 100644 index 0000000..6ebce6d --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/spec_helper.rb @@ -0,0 +1,56 @@ +$:.unshift File.dirname(__FILE__) + '/../lib' + +require 'rubygems' +require 'fileutils' +require 'ginger' + +require 'lib/thinking_sphinx' + +require 'not_a_mock' +require 'will_paginate' + +require 'spec/sphinx_helper' + +ActiveRecord::Base.logger = Logger.new(StringIO.new) + +Spec::Runner.configure do |config| + %w( tmp tmp/config tmp/log tmp/db ).each do |path| + FileUtils.mkdir_p "#{Dir.pwd}/#{path}" + end + + Kernel.const_set :RAILS_ROOT, "#{Dir.pwd}/tmp" unless defined?(RAILS_ROOT) + + sphinx = SphinxHelper.new + sphinx.setup_mysql + + require 'spec/fixtures/models' + + config.before :all do + %w( tmp tmp/config tmp/log tmp/db ).each do |path| + FileUtils.mkdir_p "#{Dir.pwd}/#{path}" + end + + ThinkingSphinx.updates_enabled = true + ThinkingSphinx.deltas_enabled = true + ThinkingSphinx.suppress_delta_output = true + + ThinkingSphinx::Configuration.instance.reset + ThinkingSphinx::Configuration.instance.database_yml_file = "spec/fixtures/sphinx/database.yml" + + # Ensure after_commit plugin is loaded correctly + Object.subclasses_of(ActiveRecord::ConnectionAdapters::AbstractAdapter).each { |klass| + unless klass.ancestors.include?(AfterCommit::ConnectionAdapters) + klass.send(:include, AfterCommit::ConnectionAdapters) + end + } + end + + config.after :each do + NotAMock::CallRecorder.instance.reset + NotAMock::Stubber.instance.reset + end + + config.after :all do + FileUtils.rm_r "#{Dir.pwd}/tmp" rescue nil + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/sphinx_helper.rb b/vendor/plugins/thinking-sphinx/spec/sphinx_helper.rb new file mode 100644 index 0000000..4e67523 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/sphinx_helper.rb @@ -0,0 +1,114 @@ +require 'active_record' +prefix = defined?(JRUBY_VERSION) ? "jdbc" : "" +require "active_record/connection_adapters/#{prefix}mysql_adapter" +begin + require "active_record/connection_adapters/#{prefix}postgresql_adapter" +rescue LoadError + # No postgres? no prob... +end +require 'yaml' + +class SphinxHelper + attr_accessor :host, :username, :password + attr_reader :path + + def initialize + @host = "localhost" + @username = "thinking_sphinx" + @password = "" + + if File.exist?("spec/fixtures/database.yml") + config = YAML.load(File.open("spec/fixtures/database.yml")) + @host = config["host"] + @username = config["username"] + @password = config["password"] + end + + @path = File.expand_path(File.dirname(__FILE__)) + end + + def mysql_adapter + defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql' + end + + def setup_mysql + ActiveRecord::Base.establish_connection( + :adapter => mysql_adapter, + :database => 'thinking_sphinx', + :username => @username, + :password => @password, + :host => @host + ) + ActiveRecord::Base.logger = Logger.new(File.open("tmp/activerecord.log", "a")) + + structure = File.open("spec/fixtures/structure.sql") { |f| f.read.chomp } + structure.split(';').each { |table| + ActiveRecord::Base.connection.execute table + } + + File.open("spec/fixtures/data.sql") { |f| + while line = f.gets + ActiveRecord::Base.connection.execute line unless line.blank? + end + } + end + + def setup_sphinx + @configuration = ThinkingSphinx::Configuration.instance.reset + File.open("spec/fixtures/sphinx/database.yml", "w") do |file| + YAML.dump({@configuration.environment => { + :adapter => mysql_adapter, + :host => @host, + :database => "thinking_sphinx", + :username => @username, + :password => @password + }}, file) + end + FileUtils.mkdir_p(@configuration.searchd_file_path) + + @configuration.database_yml_file = "spec/fixtures/sphinx/database.yml" + @configuration.build + + index + end + + def reset + setup_mysql + end + + def index + cmd = "indexer --config #{@configuration.config_file} --all" + cmd << " --rotate" if running? + `#{cmd}` + end + + def start + return if running? + + cmd = "searchd --config #{@configuration.config_file}" + `#{cmd}` + + sleep(1) + + unless running? + puts "Failed to start searchd daemon. Check #{@configuration.searchd_log_file}." + end + end + + def stop + return unless running? + `kill #{pid}` + end + + def pid + if File.exists?("#{@configuration.pid_file}") + `cat #{@configuration.pid_file}`[/\d+/] + else + nil + end + end + + def running? + pid && `ps #{pid} | wc -l`.to_i > 1 + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/delta_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/delta_spec.rb new file mode 100644 index 0000000..b29065f --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/delta_spec.rb @@ -0,0 +1,136 @@ +require 'spec/spec_helper' + +describe "ThinkingSphinx::ActiveRecord::Delta" do + it "should call the toggle_delta method after a save" do + @beta = Beta.new(:name => 'beta') + @beta.stub_method(:toggle_delta => true) + + @beta.save + + @beta.should have_received(:toggle_delta) + end + + it "should call the toggle_delta method after a save!" do + @beta = Beta.new(:name => 'beta') + @beta.stub_method(:toggle_delta => true) + + @beta.save! + + @beta.should have_received(:toggle_delta) + end + + describe "suspended_delta method" do + before :each do + ThinkingSphinx.stub_method(:deltas_enabled? => true) + Person.sphinx_indexes.first.delta_object.stub_method(:` => "") + end + + it "should execute the argument block with deltas disabled" do + ThinkingSphinx.should_receive(:deltas_enabled=).once.with(false) + ThinkingSphinx.should_receive(:deltas_enabled=).once.with(true) + lambda { Person.suspended_delta { raise 'i was called' } }.should( + raise_error(Exception) + ) + end + + it "should restore deltas_enabled to its original setting" do + ThinkingSphinx.stub_method(:deltas_enabled? => false) + ThinkingSphinx.should_receive(:deltas_enabled=).twice.with(false) + Person.suspended_delta { 'no-op' } + end + + it "should restore deltas_enabled to its original setting even if there was an exception" do + ThinkingSphinx.stub_method(:deltas_enabled? => false) + ThinkingSphinx.should_receive(:deltas_enabled=).twice.with(false) + lambda { Person.suspended_delta { raise 'bad error' } }.should( + raise_error(Exception) + ) + end + + it "should reindex by default after the code block is run" do + Person.should_receive(:index_delta) + Person.suspended_delta { 'no-op' } + end + + it "should not reindex after the code block if false is passed in" do + Person.should_not_receive(:index_delta) + Person.suspended_delta(false) { 'no-op' } + end + end + + describe "toggle_delta method" do + it "should set the delta value to true" do + @person = Person.new + + @person.delta.should be_false + @person.send(:toggle_delta) + @person.delta.should be_true + end + end + + describe "index_delta method" do + before :each do + ThinkingSphinx::Configuration.stub_method(:environment => "spec") + ThinkingSphinx.stub_method(:deltas_enabled? => true, :sphinx_running? => true) + Person.delta_object.stub_methods(:` => "", :toggled => true) + + @person = Person.new + @person.stub_method( + :in_both_indexes? => false, + :sphinx_document_id => 1 + ) + + @client = Riddle::Client.stub_instance(:update => true) + Riddle::Client.stub_method(:new => @client) + end + + it "shouldn't index if delta indexing is disabled" do + ThinkingSphinx.stub_method(:deltas_enabled? => false) + + @person.send(:index_delta) + + Person.sphinx_indexes.first.delta_object.should_not have_received(:`) + @client.should_not have_received(:update) + end + + it "shouldn't index if index updating is disabled" do + ThinkingSphinx.stub_method(:updates_enabled? => false) + + @person.send(:index_delta) + + Person.sphinx_indexes.first.delta_object.should_not have_received(:`) + end + + it "shouldn't index if the environment is 'test'" do + ThinkingSphinx.unstub_method(:deltas_enabled?) + ThinkingSphinx.deltas_enabled = nil + ThinkingSphinx::Configuration.stub_method(:environment => "test") + + @person.send(:index_delta) + + Person.sphinx_indexes.first.delta_object.should_not have_received(:`) + end + + it "should call indexer for the delta index" do + @person.send(:index_delta) + + Person.sphinx_indexes.first.delta_object.should have_received(:`).with( + "#{ThinkingSphinx::Configuration.instance.bin_path}indexer --config #{ThinkingSphinx::Configuration.instance.config_file} --rotate person_delta" + ) + end + + it "shouldn't update the deleted attribute if not in the index" do + @person.send(:index_delta) + + @client.should_not have_received(:update) + end + + it "should update the deleted attribute if in the core index" do + @person.stub_method(:in_both_indexes? => true) + + @person.send(:index_delta) + + @client.should have_received(:update) + end + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb new file mode 100644 index 0000000..b37ac9f --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb @@ -0,0 +1,53 @@ +require 'spec/spec_helper' + +describe 'ThinkingSphinx::ActiveRecord::HasManyAssociation' do + describe "search method" do + before :each do + Friendship.stub_method(:search => true) + + @person = Person.find(:first) + @index = Friendship.sphinx_indexes.first + end + + it "should raise an error if the required attribute doesn't exist" do + @index.stub_method(:attributes => []) + + lambda { @person.friendships.search "test" }.should raise_error(RuntimeError) + + @index.unstub_method(:attributes) + end + + it "should add a filter for the attribute into a normal search call" do + @person.friendships.search "test" + + Friendship.should have_received(:search).with( + "test", :with => {:person_id => @person.id} + ) + end + end + + describe "search method for has_many :through" do + before :each do + Person.stub_method(:search => true) + + @person = Person.find(:first) + @index = Person.sphinx_indexes.first + end + + it "should raise an error if the required attribute doesn't exist" do + @index.stub_method(:attributes => []) + + lambda { @person.friends.search "test" }.should raise_error(RuntimeError) + + @index.unstub_method(:attributes) + end + + it "should add a filter for the attribute into a normal search call" do + @person.friends.search "test" + + Person.should have_received(:search).with( + "test", :with => {:friendly_ids => @person.id} + ) + end + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/search_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/search_spec.rb new file mode 100644 index 0000000..49c59e1 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record/search_spec.rb @@ -0,0 +1,107 @@ +require 'spec/spec_helper' + +describe "ThinkingSphinx::ActiveRecord::Search" do + it "should add search_for_ids to ActiveRecord::Base" do + ActiveRecord::Base.methods.should include("search_for_ids") + end + + it "should add search_for_ids to ActiveRecord::Base" do + ActiveRecord::Base.methods.should include("search") + end + + it "should add search_count to ActiveRecord::Base" do + ActiveRecord::Base.methods.should include("search_count") + end + + it "should add search_for_id to ActiveRecord::Base" do + ActiveRecord::Base.methods.should include("search_for_id") + end + + describe "search_for_ids method" do + before :each do + ThinkingSphinx::Search.stub_method(:search_for_ids => true) + end + + it "should call ThinkingSphinx::Search#search_for_ids with the class option set" do + Person.search_for_ids("search") + + ThinkingSphinx::Search.should have_received(:search_for_ids).with( + "search", :class => Person + ) + end + + it "should override the class option" do + Person.search_for_ids("search", :class => Friendship) + + ThinkingSphinx::Search.should have_received(:search_for_ids).with( + "search", :class => Person + ) + end + end + + describe "search method" do + before :each do + ThinkingSphinx::Search.stub_method(:search => true) + end + + it "should call ThinkingSphinx::Search#search with the class option set" do + Person.search("search") + + ThinkingSphinx::Search.should have_received(:search).with( + "search", :class => Person + ) + end + + it "should override the class option" do + Person.search("search", :class => Friendship) + + ThinkingSphinx::Search.should have_received(:search).with( + "search", :class => Person + ) + end + end + + describe "search_for_id method" do + before :each do + ThinkingSphinx::Search.stub_method(:search_for_id => true) + end + + it "should call ThinkingSphinx::Search#search with the class option set" do + Person.search_for_id(10) + + ThinkingSphinx::Search.should have_received(:search_for_id).with( + 10, :class => Person + ) + end + + it "should override the class option" do + Person.search_for_id(10, :class => Friendship) + + ThinkingSphinx::Search.should have_received(:search_for_id).with( + 10, :class => Person + ) + end + end + + describe "search_count method" do + before :each do + ThinkingSphinx::Search.stub_method(:count => true) + end + + it "should call ThinkingSphinx::Search#search with the class option set" do + Person.search_count("search") + + ThinkingSphinx::Search.should have_received(:count).with( + "search", :class => Person + ) + end + + it "should override the class option" do + Person.search_count("search", :class => Friendship) + + ThinkingSphinx::Search.should have_received(:count).with( + "search", :class => Person + ) + end + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record_spec.rb new file mode 100644 index 0000000..053d8c1 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/active_record_spec.rb @@ -0,0 +1,316 @@ +require 'spec/spec_helper' + +describe "ThinkingSphinx::ActiveRecord" do + describe "define_index method" do + before :each do + module TestModule + class TestModel < ActiveRecord::Base; end + end + + TestModule::TestModel.stub_methods( + :before_save => true, + :after_commit => true, + :after_destroy => true + ) + + @index = ThinkingSphinx::Index.stub_instance(:delta? => false) + ThinkingSphinx::Index.stub_method(:new => @index) + end + + after :each do + # Remove the class so we can redefine it + TestModule.send(:remove_const, :TestModel) + + ThinkingSphinx.indexed_models.delete "TestModule::TestModel" + end + + it "should return nil and do nothing if indexes are disabled" do + ThinkingSphinx.stub_method(:define_indexes? => false) + + TestModule::TestModel.define_index {}.should be_nil + ThinkingSphinx::Index.should_not have_received(:new) + + ThinkingSphinx.unstub_method(:define_indexes?) + end + + it "should add a new index to the model" do + TestModule::TestModel.define_index do; end + + TestModule::TestModel.sphinx_indexes.length.should == 1 + end + + it "should add to ThinkingSphinx.indexed_models if the model doesn't already exist in the array" do + TestModule::TestModel.define_index do; end + + ThinkingSphinx.indexed_models.should include("TestModule::TestModel") + end + + it "shouldn't add to ThinkingSphinx.indexed_models if the model already exists in the array" do + TestModule::TestModel.define_index do; end + + ThinkingSphinx.indexed_models.select { |model| + model == "TestModule::TestModel" + }.length.should == 1 + + TestModule::TestModel.define_index do; end + + ThinkingSphinx.indexed_models.select { |model| + model == "TestModule::TestModel" + }.length.should == 1 + end + + it "should add before_save and after_commit hooks to the model if delta indexing is enabled" do + @index.stub_method(:delta? => true) + + TestModule::TestModel.define_index do; end + + TestModule::TestModel.should have_received(:before_save) + TestModule::TestModel.should have_received(:after_commit) + end + + it "should not add before_save and after_commit hooks to the model if delta indexing is disabled" do + TestModule::TestModel.define_index do; end + + TestModule::TestModel.should_not have_received(:before_save) + TestModule::TestModel.should_not have_received(:after_commit) + end + + it "should add an after_destroy hook with delta indexing enabled" do + @index.stub_method(:delta? => true) + + TestModule::TestModel.define_index do; end + + TestModule::TestModel.should have_received(:after_destroy).with(:toggle_deleted) + end + + it "should add an after_destroy hook with delta indexing disabled" do + TestModule::TestModel.define_index do; end + + TestModule::TestModel.should have_received(:after_destroy).with(:toggle_deleted) + end + + it "should return the new index" do + TestModule::TestModel.define_index.should == @index + end + end + + describe "index methods" do + before(:all) do + @person = Person.find(:first) + end + + describe "in_both_indexes?" do + it "should return true if in core and delta indexes" do + @person.should_receive(:in_core_index?).and_return(true) + @person.should_receive(:in_delta_index?).and_return(true) + @person.in_both_indexes?.should be_true + end + + it "should return false if in one index and not the other" do + @person.should_receive(:in_core_index?).and_return(true) + @person.should_receive(:in_delta_index?).and_return(false) + @person.in_both_indexes?.should be_false + end + end + + describe "in_core_index?" do + it "should call in_index? with core" do + @person.should_receive(:in_index?).with('core') + @person.in_core_index? + end + end + + describe "in_delta_index?" do + it "should call in_index? with delta" do + @person.should_receive(:in_index?).with('delta') + @person.in_delta_index? + end + end + + describe "in_index?" do + it "should return true if in the specified index" do + @person.should_receive(:sphinx_document_id).and_return(1) + @person.should_receive(:sphinx_index_name).and_return('person_core') + Person.should_receive(:search_for_id).with(1, 'person_core').and_return(true) + + @person.in_index?('core').should be_true + end + end + end + + describe "source_of_sphinx_index method" do + it "should return self if model defines an index" do + Person.source_of_sphinx_index.should == Person + end + + it "should return the parent if model inherits an index" do + Parent.source_of_sphinx_index.should == Person + end + end + + describe "to_crc32 method" do + it "should return an integer" do + Person.to_crc32.should be_a_kind_of(Integer) + end + end + + describe "to_crc32s method" do + it "should return an array" do + Person.to_crc32s.should be_a_kind_of(Array) + end + end + + describe "toggle_deleted method" do + before :each do + ThinkingSphinx.stub_method(:sphinx_running? => true) + + @configuration = ThinkingSphinx::Configuration.instance + @configuration.stub_methods( + :address => "an address", + :port => 123 + ) + @client = Riddle::Client.stub_instance(:update => true) + @person = Person.find(:first) + + Riddle::Client.stub_method(:new => @client) + Person.sphinx_indexes.each { |index| index.stub_method(:delta? => false) } + @person.stub_method(:in_core_index? => true) + end + + it "should create a client using the Configuration's address and port" do + @person.toggle_deleted + + Riddle::Client.should have_received(:new).with( + @configuration.address, @configuration.port + ) + end + + it "should update the core index's deleted flag if in core index" do + @person.toggle_deleted + + @client.should have_received(:update).with( + "person_core", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "shouldn't update the core index's deleted flag if the record isn't in it" do + @person.stub_method(:in_core_index? => false) + + @person.toggle_deleted + + @client.should_not have_received(:update).with( + "person_core", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "shouldn't attempt to update the deleted flag if sphinx isn't running" do + ThinkingSphinx.stub_method(:sphinx_running? => false) + + @person.toggle_deleted + + @person.should_not have_received(:in_core_index?) + @client.should_not have_received(:update) + end + + it "should update the delta index's deleted flag if delta indexes are enabled and the instance's delta is true" do + ThinkingSphinx.stub_method(:deltas_enabled? => true) + Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) } + @person.delta = true + + @person.toggle_deleted + + @client.should have_received(:update).with( + "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "should not update the delta index's deleted flag if delta indexes are enabled and the instance's delta is false" do + ThinkingSphinx.stub_method(:deltas_enabled? => true) + Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) } + @person.delta = false + + @person.toggle_deleted + + @client.should_not have_received(:update).with( + "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "should not update the delta index's deleted flag if delta indexes are enabled and the instance's delta is equivalent to false" do + ThinkingSphinx.stub_method(:deltas_enabled? => true) + Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) } + @person.delta = 0 + + @person.toggle_deleted + + @client.should_not have_received(:update).with( + "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "shouldn't update the delta index if delta indexes are disabled" do + ThinkingSphinx.stub_method(:deltas_enabled? => true) + @person.toggle_deleted + + @client.should_not have_received(:update).with( + "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "should not update the delta index if delta indexing is disabled" do + ThinkingSphinx.stub_method(:deltas_enabled? => false) + Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) } + @person.delta = true + + @person.toggle_deleted + + @client.should_not have_received(:update).with( + "person_delta", ["sphinx_deleted"], {@person.sphinx_document_id => 1} + ) + end + + it "should not update either index if updates are disabled" do + ThinkingSphinx.stub_methods( + :updates_enabled? => false, + :deltas_enabled => true + ) + Person.sphinx_indexes.each { |index| index.stub_method(:delta? => true) } + @person.delta = true + + @person.toggle_deleted + + @client.should_not have_received(:update) + end + end + + describe "sphinx_indexes in the inheritance chain (STI)" do + it "should hand defined indexes on a class down to its child classes" do + Child.sphinx_indexes.should include(*Person.sphinx_indexes) + end + + it "should allow associations to other STI models" do + Child.sphinx_indexes.last.link! + sql = Child.sphinx_indexes.last.to_riddle_for_core(0, 0).sql_query + sql.gsub!('$start', '0').gsub!('$end', '100') + lambda { Child.connection.execute(sql) }.should_not raise_error(ActiveRecord::StatementInvalid) + end + end + + it "should return the sphinx document id as expected" do + person = Person.find(:first) + model_count = ThinkingSphinx.indexed_models.length + offset = ThinkingSphinx.indexed_models.index("Person") + + (person.id * model_count + offset).should == person.sphinx_document_id + + alpha = Alpha.find(:first) + offset = ThinkingSphinx.indexed_models.index("Alpha") + + (alpha.id * model_count + offset).should == alpha.sphinx_document_id + + beta = Beta.find(:first) + offset = ThinkingSphinx.indexed_models.index("Beta") + + (beta.id * model_count + offset).should == beta.sphinx_document_id + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/association_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/association_spec.rb new file mode 100644 index 0000000..4b92a8b --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/association_spec.rb @@ -0,0 +1,247 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Association do + describe "class-level children method" do + before :each do + @normal_reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance( + :options => {:polymorphic => false} + ) + @normal_association = ThinkingSphinx::Association.stub_instance + @poly_reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance( + :options => {:polymorphic => true}, + :macro => :has_many, + :name => "polly", + :active_record => "AR" + ) + @non_poly_reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance + + Person.stub_method(:reflect_on_association => @normal_reflection) + ThinkingSphinx::Association.stub_methods( + :new => @normal_association, + :polymorphic_classes => [Person, Person], + :casted_options => {:casted => :options} + ) + ::ActiveRecord::Reflection::AssociationReflection.stub_method( + :new => @non_poly_reflection + ) + end + + it "should return an empty array if no association exists" do + Person.stub_method(:reflect_on_association => nil) + + ThinkingSphinx::Association.children(Person, :assoc).should == [] + end + + it "should return a single association instance in an array if assocation isn't polymorphic" do + ThinkingSphinx::Association.children(Person, :assoc).should == [@normal_association] + end + + it "should return multiple association instances for polymorphic associations" do + Person.stub_method(:reflect_on_association => @poly_reflection) + + ThinkingSphinx::Association.children(Person, :assoc).should == + [@normal_association, @normal_association] + end + + it "should generate non-polymorphic 'casted' associations for each polymorphic possibility" do + Person.stub_method(:reflect_on_association => @poly_reflection) + + ThinkingSphinx::Association.children(Person, :assoc) + + ThinkingSphinx::Association.should have_received(:casted_options).with( + Person, @poly_reflection + ).twice + + ::ActiveRecord::Reflection::AssociationReflection.should have_received(:new).with( + :has_many, :polly_Person, {:casted => :options}, "AR" + ).twice + + ThinkingSphinx::Association.should have_received(:new).with( + nil, @non_poly_reflection + ).twice + end + end + + describe "instance-level children method" do + it "should return the children associations for the given association" do + @reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance( + :klass => :klass + ) + @association = ThinkingSphinx::Association.new(nil, @reflection) + ThinkingSphinx::Association.stub_method(:children => :result) + + @association.children(:assoc).should == :result + ThinkingSphinx::Association.should have_received(:children).with(:klass, :assoc, @association) + end + end + + describe "join_to method" do + before :each do + @parent_join = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.stub_instance + @join = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.stub_instance + @parent = ThinkingSphinx::Association.stub_instance(:join_to => true, :join => nil) + @base_join = Object.stub_instance(:joins => [:a, :b, :c]) + + ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.stub_method(:new => @join) + end + + it "should call the parent's join_to if parent has no join" do + @assoc = ThinkingSphinx::Association.new(@parent, :ref) + + @assoc.join_to(@base_join) + + @parent.should have_received(:join_to).with(@base_join) + end + + it "should not call the parent's join_to if it already has a join" do + @assoc = ThinkingSphinx::Association.new(@parent, :ref) + @parent.stub_method(:join => @parent_join) + + @assoc.join_to(@base_join) + + @parent.should_not have_received(:join_to) + end + + it "should define the join association with a JoinAssociation instance" do + @assoc = ThinkingSphinx::Association.new(@parent, :ref) + + @assoc.join_to(@base_join).should == @join + @assoc.join.should == @join + end + end + + describe "to_sql method" do + before :each do + @reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance( + :klass => Person + ) + @association = ThinkingSphinx::Association.new(nil, @reflection) + @parent = Object.stub_instance(:aliased_table_name => "ALIAS TABLE NAME") + @join = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation.stub_instance( + :association_join => "full association join SQL", + :parent => @parent + ) + @association.join = @join + end + + it "should return the join's association join value" do + @association.to_sql.should == "full association join SQL" + end + + it "should replace ::ts_join_alias:: with the aliased table name" do + @join.stub_method(:association_join => "text with ::ts_join_alias:: gone") + + @association.to_sql.should == "text with `ALIAS TABLE NAME` gone" + end + end + + describe "is_many? method" do + before :each do + @parent = ThinkingSphinx::Association.stub_instance( + :is_many? => :parent_is_many + ) + @reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance( + :macro => :has_many + ) + end + + it "should return true if association is either a has_many or a habtm" do + association = ThinkingSphinx::Association.new(@parent, @reflection) + association.is_many?.should be_true + + @reflection.stub_method(:macro => :has_and_belongs_to_many) + association.is_many?.should be_true + end + + it "should return the parent value if not a has many or habtm and there is a parent" do + association = ThinkingSphinx::Association.new(@parent, @reflection) + @reflection.stub_method(:macro => :belongs_to) + association.is_many?.should == :parent_is_many + end + + it "should return false if no parent and not a has many or habtm" do + association = ThinkingSphinx::Association.new(nil, @reflection) + @reflection.stub_method(:macro => :belongs_to) + association.is_many?.should be_false + end + end + + describe "ancestors method" do + it "should return an array of associations - including all parents" do + parent = ThinkingSphinx::Association.stub_instance(:ancestors => [:all, :ancestors]) + association = ThinkingSphinx::Association.new(parent, @reflection) + association.ancestors.should == [:all, :ancestors, association] + end + end + + describe "polymorphic_classes method" do + it "should return all the polymorphic result types as classes" do + Person.connection.stub_method(:select_all => [ + {"person_type" => "Person"}, + {"person_type" => "Friendship"} + ]) + ref = Object.stub_instance( + :active_record => Person, + :options => {:foreign_type => "person_type"} + ) + + ThinkingSphinx::Association.send(:polymorphic_classes, ref).should == [Person, Friendship] + end + end + + describe "casted_options method" do + before :each do + @options = { + :foreign_key => "thing_id", + :foreign_type => "thing_type", + :polymorphic => true + } + @reflection = ::ActiveRecord::Reflection::AssociationReflection.stub_instance( + :options => @options + ) + end + + it "should return a new options set for a specific class" do + ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == { + :polymorphic => nil, + :class_name => "Person", + :foreign_key => "thing_id", + :foreign_type => "thing_type", + :conditions => "::ts_join_alias::.`thing_type` = 'Person'" + } + end + + it "should append to existing Array of conditions" do + @options[:conditions] = ["first condition"] + ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == { + :polymorphic => nil, + :class_name => "Person", + :foreign_key => "thing_id", + :foreign_type => "thing_type", + :conditions => ["first condition", "::ts_join_alias::.`thing_type` = 'Person'"] + } + end + + it "should merge to an existing Hash of conditions" do + @options[:conditions] = {"field" => "value"} + ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == { + :polymorphic => nil, + :class_name => "Person", + :foreign_key => "thing_id", + :foreign_type => "thing_type", + :conditions => {"field" => "value", "thing_type" => "Person"} + } + end + + it "should append to an existing String of conditions" do + @options[:conditions] = "first condition" + ThinkingSphinx::Association.send(:casted_options, Person, @reflection).should == { + :polymorphic => nil, + :class_name => "Person", + :foreign_key => "thing_id", + :foreign_type => "thing_type", + :conditions => "first condition AND ::ts_join_alias::.`thing_type` = 'Person'" + } + end + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/attribute_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/attribute_spec.rb new file mode 100644 index 0000000..bbe1ae9 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/attribute_spec.rb @@ -0,0 +1,227 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Attribute do + describe '#initialize' do + it 'raises if no columns are provided so that configuration errors are easier to track down' do + lambda { + ThinkingSphinx::Attribute.new([]) + }.should raise_error(RuntimeError) + end + + it 'raises if an element of the columns param is an integer - as happens when you use id instead of :id - so that configuration errors are easier to track down' do + lambda { + ThinkingSphinx::Attribute.new([1234]) + }.should raise_error(RuntimeError) + end + end + + describe "unique_name method" do + before :each do + @attribute = ThinkingSphinx::Attribute.new [ + Object.stub_instance(:__stack => [], :__name => "col_name") + ] + end + + it "should use the alias if there is one" do + @attribute.alias = "alias" + @attribute.unique_name.should == "alias" + end + + it "should use the alias if there's multiple columns" do + @attribute.columns << Object.stub_instance(:__stack => [], :__name => "col_name") + @attribute.unique_name.should be_nil + + @attribute.alias = "alias" + @attribute.unique_name.should == "alias" + end + + it "should use the column name if there's no alias and just one column" do + @attribute.unique_name.should == "col_name" + end + end + + describe "column_with_prefix method" do + before :each do + @attribute = ThinkingSphinx::Attribute.new [ + ThinkingSphinx::Index::FauxColumn.new(:col_name) + ] + @attribute.columns.each { |col| @attribute.associations[col] = [] } + @attribute.model = Person + + @first_join = Object.stub_instance(:aliased_table_name => "tabular") + @second_join = Object.stub_instance(:aliased_table_name => "data") + + @first_assoc = ThinkingSphinx::Association.stub_instance( + :join => @first_join, :has_column? => true + ) + @second_assoc = ThinkingSphinx::Association.stub_instance( + :join => @second_join, :has_column? => true + ) + end + + it "should return the column name if the column is a string" do + @attribute.columns = [ThinkingSphinx::Index::FauxColumn.new("string")] + @attribute.send(:column_with_prefix, @attribute.columns.first).should == "string" + end + + it "should return the column with model's table prefix if there's no associations for the column" do + @attribute.send(:column_with_prefix, @attribute.columns.first).should == "`people`.`col_name`" + end + + it "should return the column with its join table prefix if an association exists" do + column = @attribute.columns.first + @attribute.associations[column] = [@first_assoc] + @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`" + end + + it "should return multiple columns concatenated if more than one association exists" do + column = @attribute.columns.first + @attribute.associations[column] = [@first_assoc, @second_assoc] + @attribute.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`" + end + end + + describe "is_many? method" do + before :each do + @assoc_a = Object.stub_instance(:is_many? => true) + @assoc_b = Object.stub_instance(:is_many? => true) + @assoc_c = Object.stub_instance(:is_many? => true) + + @attribute = ThinkingSphinx::Attribute.new( + [ThinkingSphinx::Index::FauxColumn.new(:col_name)] + ) + @attribute.associations = { + :a => @assoc_a, :b => @assoc_b, :c => @assoc_c + } + end + + it "should return true if all associations return true to is_many?" do + @attribute.send(:is_many?).should be_true + end + + it "should return true if one association returns true to is_many?" do + @assoc_b.stub_method(:is_many? => false) + @assoc_c.stub_method(:is_many? => false) + + @attribute.send(:is_many?).should be_true + end + + it "should return false if all associations return false to is_many?" do + @assoc_a.stub_method(:is_many? => false) + @assoc_b.stub_method(:is_many? => false) + @assoc_c.stub_method(:is_many? => false) + + @attribute.send(:is_many?).should be_false + end + end + + describe "is_string? method" do + before :each do + @col_a = ThinkingSphinx::Index::FauxColumn.new("a") + @col_b = ThinkingSphinx::Index::FauxColumn.new("b") + @col_c = ThinkingSphinx::Index::FauxColumn.new("c") + + @attribute = ThinkingSphinx::Attribute.new( + [@col_a, @col_b, @col_c] + ) + end + + it "should return true if all columns return true to is_string?" do + @attribute.send(:is_string?).should be_true + end + + it "should return false if one column returns true to is_string?" do + @col_a.send(:instance_variable_set, :@name, :a) + @attribute.send(:is_string?).should be_false + end + + it "should return false if all columns return false to is_string?" do + @col_a.send(:instance_variable_set, :@name, :a) + @col_b.send(:instance_variable_set, :@name, :b) + @col_c.send(:instance_variable_set, :@name, :c) + @attribute.send(:is_string?).should be_false + end + end + + describe "type method" do + before :each do + @column = ThinkingSphinx::Index::FauxColumn.new(:col_name) + @attribute = ThinkingSphinx::Attribute.new([@column]) + @attribute.model = Person + @attribute.stub_method(:is_many? => false) + end + + it "should return :multi if is_many? is true" do + @attribute.stub_method(:is_many? => true) + @attribute.send(:type).should == :multi + end + + it "should return :string if there's more than one association" do + @attribute.associations = {:a => [:assoc], :b => [:assoc]} + @attribute.send(:type).should == :string + end + + it "should return the column type from the database if not :multi or more than one association" do + @column.send(:instance_variable_set, :@name, "birthday") + @attribute.send(:type).should == :datetime + + @attribute.send(:instance_variable_set, :@type, nil) + @column.send(:instance_variable_set, :@name, "first_name") + @attribute.send(:type).should == :string + + @attribute.send(:instance_variable_set, :@type, nil) + @column.send(:instance_variable_set, :@name, "id") + @attribute.send(:type).should == :integer + end + end + + describe "all_ints? method" do + it "should return true if all columns are integers" do + attribute = ThinkingSphinx::Attribute.new( + [ ThinkingSphinx::Index::FauxColumn.new(:id), + ThinkingSphinx::Index::FauxColumn.new(:team_id) ] + ) + attribute.model = Person + attribute.columns.each { |col| attribute.associations[col] = [] } + + attribute.send(:all_ints?).should be_true + end + + it "should return false if only some columns are integers" do + attribute = ThinkingSphinx::Attribute.new( + [ ThinkingSphinx::Index::FauxColumn.new(:id), + ThinkingSphinx::Index::FauxColumn.new(:first_name) ] + ) + attribute.model = Person + attribute.columns.each { |col| attribute.associations[col] = [] } + + attribute.send(:all_ints?).should be_false + end + + it "should return false if no columns are integers" do + attribute = ThinkingSphinx::Attribute.new( + [ ThinkingSphinx::Index::FauxColumn.new(:first_name), + ThinkingSphinx::Index::FauxColumn.new(:last_name) ] + ) + attribute.model = Person + attribute.columns.each { |col| attribute.associations[col] = [] } + + attribute.send(:all_ints?).should be_false + end + end + + describe "with custom queries" do + before :each do + index = CricketTeam.sphinx_indexes.first + @statement = index.to_riddle_for_core(0, 0).sql_attr_multi.first + end + + it "should track the query type accordingly" do + @statement.should match(/uint tags from query/) + end + + it "should include the SQL statement" do + @statement.should match(/SELECT cricket_team_id, id FROM tags/) + end + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/collection_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/collection_spec.rb new file mode 100644 index 0000000..b13c769 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/collection_spec.rb @@ -0,0 +1,14 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Collection do + it "should behave like WillPaginate::Collection" do + ThinkingSphinx::Collection.instance_methods.should include("previous_page") + ThinkingSphinx::Collection.instance_methods.should include("next_page") + ThinkingSphinx::Collection.instance_methods.should include("current_page") + ThinkingSphinx::Collection.instance_methods.should include("total_pages") + ThinkingSphinx::Collection.instance_methods.should include("total_entries") + ThinkingSphinx::Collection.instance_methods.should include("offset") + + ThinkingSphinx::Collection.ancestors.should include(Array) + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/configuration_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/configuration_spec.rb new file mode 100644 index 0000000..12e2775 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/configuration_spec.rb @@ -0,0 +1,136 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Configuration do + describe "environment class method" do + before :each do + ThinkingSphinx::Configuration.send(:class_variable_set, :@@environment, nil) + + ENV["RAILS_ENV"] = nil + end + + it "should use the Merb environment value if set" do + unless defined?(Merb) + module Merb; end + end + + ThinkingSphinx::Configuration.stub_method(:defined? => true) + Merb.stub_method(:environment => "merb_production") + ThinkingSphinx::Configuration.environment.should == "merb_production" + + Object.send(:remove_const, :Merb) + end + + it "should use the Rails environment value if set" do + ENV["RAILS_ENV"] = "rails_production" + ThinkingSphinx::Configuration.environment.should == "rails_production" + end + + it "should default to development" do + ThinkingSphinx::Configuration.environment.should == "development" + end + end + + describe "parse_config method" do + before :each do + @settings = { + "development" => { + "config_file" => "tmp/config/development.sphinx.conf", + "searchd_log_file" => "searchd_log_file.log", + "query_log_file" => "query_log_file.log", + "pid_file" => "pid_file.pid", + "searchd_file_path" => "searchd/file/path", + "address" => "127.0.0.1", + "port" => 3333, + "min_prefix_len" => 2, + "min_infix_len" => 3, + "mem_limit" => "128M", + "max_matches" => 1001, + "morphology" => "stem_ru", + "charset_type" => "latin1", + "charset_table" => "table", + "ignore_chars" => "e" + } + } + + open("#{RAILS_ROOT}/config/sphinx.yml", "w") do |f| + f.write YAML.dump(@settings) + end + end + + it "should use the accessors to set the configuration values" do + config = ThinkingSphinx::Configuration.instance + config.send(:parse_config) + + %w(config_file searchd_log_file query_log_file pid_file searchd_file_path + address port).each do |key| + config.send(key).should == @settings["development"][key] + end + end + + after :each do + FileUtils.rm "#{RAILS_ROOT}/config/sphinx.yml" + end + end + + describe "initialisation" do + it "should have a default bin_path of nothing" do + ThinkingSphinx::Configuration.instance.bin_path.should == "" + end + + it "should append a / to bin_path if one is supplied" do + @settings = { + "development" => { + "bin_path" => "path/to/somewhere" + } + } + + open("#{RAILS_ROOT}/config/sphinx.yml", "w") do |f| + f.write YAML.dump(@settings) + end + + ThinkingSphinx::Configuration.instance.send(:parse_config) + ThinkingSphinx::Configuration.instance.bin_path.should match(/\/$/) + end + end + + it "should insert set index options into the configuration file" do + config = ThinkingSphinx::Configuration.instance + ThinkingSphinx::Configuration::IndexOptions.each do |option| + config.index_options[option.to_sym] = "something" + config.build + + file = open(config.config_file) { |f| f.read } + file.should match(/#{option}\s+= something/) + + config.index_options[option.to_sym] = nil + end + end + + it "should insert set source options into the configuration file" do + config = ThinkingSphinx::Configuration.instance + ThinkingSphinx::Configuration::SourceOptions.each do |option| + config.source_options[option.to_sym] = "something" + config.build + + file = open(config.config_file) { |f| f.read } + file.should match(/#{option}\s+= something/) + + config.source_options[option.to_sym] = nil + end + end + + it "should set any explicit prefixed or infixed fields" do + file = open(ThinkingSphinx::Configuration.instance.config_file) { |f| + f.read + } + file.should match(/prefix_fields\s+= city/) + file.should match(/infix_fields\s+= state/) + end + + it "should not have prefix fields in indexes where nothing is set" do + file = open(ThinkingSphinx::Configuration.instance.config_file) { |f| + f.read + } + file.should_not match(/index alpha_core\s+\{\s+[^\}]*prefix_fields\s+=[^\}]*\}/m) + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/core/string_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/core/string_spec.rb new file mode 100644 index 0000000..26f813c --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/core/string_spec.rb @@ -0,0 +1,9 @@ +require 'spec/spec_helper' + +describe String do + describe "to_crc32 instance method" do + it "should return an integer" do + 'to_crc32'.to_crc32.should be_a_kind_of(Integer) + end + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/field_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/field_spec.rb new file mode 100644 index 0000000..770f749 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/field_spec.rb @@ -0,0 +1,145 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Field do + describe '#initialize' do + it 'raises if no columns are provided so that configuration errors are easier to track down' do + lambda { + ThinkingSphinx::Field.new([]) + }.should raise_error(RuntimeError) + end + + it 'raises if an element of the columns param is an integer - as happens when you use id instead of :id - so that configuration errors are easier to track down' do + lambda { + ThinkingSphinx::Field.new([1234]) + }.should raise_error(RuntimeError) + end + end + + describe "unique_name method" do + before :each do + @field = ThinkingSphinx::Field.new [ + Object.stub_instance(:__stack => [], :__name => "col_name") + ] + end + + it "should use the alias if there is one" do + @field.alias = "alias" + @field.unique_name.should == "alias" + end + + it "should use the alias if there's multiple columns" do + @field.columns << Object.stub_instance(:__stack => [], :__name => "col_name") + @field.unique_name.should be_nil + + @field.alias = "alias" + @field.unique_name.should == "alias" + end + + it "should use the column name if there's no alias and just one column" do + @field.unique_name.should == "col_name" + end + end + + describe "prefixes method" do + it "should default to false" do + @field = ThinkingSphinx::Field.new([Object.stub_instance(:__stack => [])]) + @field.prefixes.should be_false + end + + it "should be true if the corresponding option is set" do + @field = ThinkingSphinx::Field.new( + [Object.stub_instance(:__stack => [])], :prefixes => true + ) + @field.prefixes.should be_true + end + end + + describe "infixes method" do + it "should default to false" do + @field = ThinkingSphinx::Field.new([Object.stub_instance(:__stack => [])]) + @field.infixes.should be_false + end + + it "should be true if the corresponding option is set" do + @field = ThinkingSphinx::Field.new( + [Object.stub_instance(:__stack => [])], :infixes => true + ) + @field.infixes.should be_true + end + end + + describe "column_with_prefix method" do + before :each do + @field = ThinkingSphinx::Field.new [ + ThinkingSphinx::Index::FauxColumn.new(:col_name) + ] + @field.columns.each { |col| @field.associations[col] = [] } + @field.model = Person + + @first_join = Object.stub_instance(:aliased_table_name => "tabular") + @second_join = Object.stub_instance(:aliased_table_name => "data") + + @first_assoc = ThinkingSphinx::Association.stub_instance( + :join => @first_join, :has_column? => true + ) + @second_assoc = ThinkingSphinx::Association.stub_instance( + :join => @second_join, :has_column? => true + ) + end + + it "should return the column name if the column is a string" do + @field.columns = [ThinkingSphinx::Index::FauxColumn.new("string")] + @field.send(:column_with_prefix, @field.columns.first).should == "string" + end + + it "should return the column with model's table prefix if there's no associations for the column" do + @field.send(:column_with_prefix, @field.columns.first).should == "`people`.`col_name`" + end + + it "should return the column with its join table prefix if an association exists" do + column = @field.columns.first + @field.associations[column] = [@first_assoc] + @field.send(:column_with_prefix, column).should == "`tabular`.`col_name`" + end + + it "should return multiple columns concatenated if more than one association exists" do + column = @field.columns.first + @field.associations[column] = [@first_assoc, @second_assoc] + @field.send(:column_with_prefix, column).should == "`tabular`.`col_name`, `data`.`col_name`" + end + end + + describe "is_many? method" do + before :each do + @assoc_a = Object.stub_instance(:is_many? => true) + @assoc_b = Object.stub_instance(:is_many? => true) + @assoc_c = Object.stub_instance(:is_many? => true) + + @field = ThinkingSphinx::Field.new( + [ThinkingSphinx::Index::FauxColumn.new(:col_name)] + ) + @field.associations = { + :a => @assoc_a, :b => @assoc_b, :c => @assoc_c + } + end + + it "should return true if all associations return true to is_many?" do + @field.send(:is_many?).should be_true + end + + it "should return true if one association returns true to is_many?" do + @assoc_b.stub_method(:is_many? => false) + @assoc_c.stub_method(:is_many? => false) + + @field.send(:is_many?).should be_true + end + + it "should return false if all associations return false to is_many?" do + @assoc_a.stub_method(:is_many? => false) + @assoc_b.stub_method(:is_many? => false) + @assoc_c.stub_method(:is_many? => false) + + @field.send(:is_many?).should be_false + end + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/builder_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/builder_spec.rb new file mode 100644 index 0000000..6d1d25d --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/builder_spec.rb @@ -0,0 +1,5 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Index::Builder do + # +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/faux_column_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/faux_column_spec.rb new file mode 100644 index 0000000..ca76a34 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index/faux_column_spec.rb @@ -0,0 +1,30 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Index::FauxColumn do + describe "coerce class method" do + before :each do + @column = ThinkingSphinx::Index::FauxColumn.stub_instance + ThinkingSphinx::Index::FauxColumn.stub_method(:new => @column) + end + + it "should return a single faux column if passed a string" do + ThinkingSphinx::Index::FauxColumn.coerce("string").should == @column + end + + it "should return a single faux column if passed a symbol" do + ThinkingSphinx::Index::FauxColumn.coerce(:string).should == @column + end + + it "should return an array of faux columns if passed an array of strings" do + ThinkingSphinx::Index::FauxColumn.coerce(["one", "two"]).should == [ + @column, @column + ] + end + + it "should return an array of faux columns if passed an array of symbols" do + ThinkingSphinx::Index::FauxColumn.coerce([:one, :two]).should == [ + @column, @column + ] + end + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index_spec.rb new file mode 100644 index 0000000..fe1cba0 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/index_spec.rb @@ -0,0 +1,144 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::Index do + describe "generated sql_query" do + it "should include explicit groupings if requested" do + @index = ThinkingSphinx::Index.new(Person) + + @index.groupings << "custom_sql" + @index.to_riddle_for_core(0, 0).sql_query.should match(/GROUP BY.+custom_sql/) + end + end + + describe "prefix_fields method" do + before :each do + @index = ThinkingSphinx::Index.new(Person) + + @field_a = ThinkingSphinx::Field.stub_instance(:prefixes => true) + @field_b = ThinkingSphinx::Field.stub_instance(:prefixes => false) + @field_c = ThinkingSphinx::Field.stub_instance(:prefixes => true) + + @index.fields = [@field_a, @field_b, @field_c] + end + + it "should return fields that are flagged as prefixed" do + @index.prefix_fields.should include(@field_a) + @index.prefix_fields.should include(@field_c) + end + + it "should not return fields that aren't flagged as prefixed" do + @index.prefix_fields.should_not include(@field_b) + end + end + + describe "infix_fields method" do + before :each do + @index = ThinkingSphinx::Index.new(Person) + + @field_a = ThinkingSphinx::Field.stub_instance(:infixes => true) + @field_b = ThinkingSphinx::Field.stub_instance(:infixes => false) + @field_c = ThinkingSphinx::Field.stub_instance(:infixes => true) + + @index.fields = [@field_a, @field_b, @field_c] + end + + it "should return fields that are flagged as infixed" do + @index.infix_fields.should include(@field_a) + @index.infix_fields.should include(@field_c) + end + + it "should not return fields that aren't flagged as infixed" do + @index.infix_fields.should_not include(@field_b) + end + end + + describe "multi-value attribute as ranged-query with has-many association" do + before :each do + @index = ThinkingSphinx::Index.new(Person) do + has tags(:id), :as => :tag_ids, :source => :ranged_query + end + @index.link! + + @sql = @index.to_riddle_for_core(0, 0).sql_query + end + + it "should not include attribute in select-clause sql_query" do + @sql.should_not match(/tag_ids/) + @sql.should_not match(/GROUP_CONCAT\(`tags`.`id`/) + end + + it "should not join with association table" do + @sql.should_not match(/LEFT OUTER JOIN `tags`/) + end + + it "should include sql_attr_multi as ranged-query" do + attribute = @index.send(:attributes).first + attribute.send(:type_to_config).to_s.should == "sql_attr_multi" + + declaration, query, range_query = attribute.send(:config_value).split('; ') + declaration.should == "uint tag_ids from ranged-query" + query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`id` AS `tag_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end" + range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`" + end + end + + describe "multi-value attribute as ranged-query with has-many-through association" do + before :each do + @index = ThinkingSphinx::Index.new(Person) do + has football_teams(:id), :as => :football_teams_ids, :source => :ranged_query + end + @index.link! + + @sql = @index.to_riddle_for_core(0, 0).sql_query + end + + it "should not include attribute in select-clause sql_query" do + @sql.should_not match(/football_teams_ids/) + @sql.should_not match(/GROUP_CONCAT\(`tags`.`football_team_id`/) + end + + it "should not join with association table" do + @sql.should_not match(/LEFT OUTER JOIN `tags`/) + end + + it "should include sql_attr_multi as ranged-query" do + attribute = @index.send(:attributes).first + attribute.send(:type_to_config).to_s.should == "sql_attr_multi" + + declaration, query, range_query = attribute.send(:config_value).split('; ') + declaration.should == "uint football_teams_ids from ranged-query" + query.should == "SELECT `tags`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `tags`.`football_team_id` AS `football_teams_ids` FROM `tags` WHERE `tags`.`person_id` >= $start AND `tags`.`person_id` <= $end" + range_query.should == "SELECT MIN(`tags`.`person_id`), MAX(`tags`.`person_id`) FROM `tags`" + end + end + + describe "multi-value attribute as ranged-query with has-many-through association and foreign_key" do + before :each do + @index = ThinkingSphinx::Index.new(Person) do + has friends(:id), :as => :friend_ids, :source => :ranged_query + end + @index.link! + + @sql = @index.to_riddle_for_core(0, 0).sql_query + end + + it "should not include attribute in select-clause sql_query" do + @sql.should_not match(/friend_ids/) + @sql.should_not match(/GROUP_CONCAT\(`friendships`.`friend_id`/) + end + + it "should not join with association table" do + @sql.should_not match(/LEFT OUTER JOIN `friendships`/) + end + + it "should include sql_attr_multi as ranged-query" do + attribute = @index.send(:attributes).first + attribute.send(:type_to_config).to_s.should == "sql_attr_multi" + + declaration, query, range_query = attribute.send(:config_value).split('; ') + declaration.should == "uint friend_ids from ranged-query" + query.should == "SELECT `friendships`.`person_id` #{ThinkingSphinx.unique_id_expression} AS `id`, `friendships`.`friend_id` AS `friend_ids` FROM `friendships` WHERE `friendships`.`person_id` >= $start AND `friendships`.`person_id` <= $end" + range_query.should == "SELECT MIN(`friendships`.`person_id`), MAX(`friendships`.`person_id`) FROM `friendships`" + end + end +end \ No newline at end of file diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/rails_additions_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/rails_additions_spec.rb new file mode 100644 index 0000000..6453812 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/rails_additions_spec.rb @@ -0,0 +1,183 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx::HashExcept do + before(:each) do + @hash = { :number => 20, :letter => 'b', :shape => 'rectangle' } + end + + describe "except method" do + it "returns a hash without the specified keys" do + new_hash = @hash.except(:number) + new_hash.should_not have_key(:number) + end + end + + describe "except! method" do + it "modifies hash removing specified keys" do + @hash.except!(:number) + @hash.should_not have_key(:number) + end + end + + describe "extends Hash" do + it 'with except' do + Hash.instance_methods.include?('except').should be_true + end + + it 'with except!' do + Hash.instance_methods.include?('except!').should be_true + end + end +end + +describe ThinkingSphinx::ArrayExtractOptions do + describe 'extract_options! method' do + it 'returns a hash' do + array = [] + array.extract_options!.should be_kind_of(Hash) + end + + it 'returns the last option if it is a hash' do + array = ['a', 'b', {:c => 'd'}] + array.extract_options!.should == {:c => 'd'} + end + end + + describe "extends Array" do + it 'with extract_options!' do + Array.instance_methods.include?('extract_options!').should be_true + end + end +end + +describe ThinkingSphinx::AbstractQuotedTableName do + describe 'quote_table_name method' do + it 'calls quote_column_name' do + adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql') + adapter.should_receive(:quote_column_name).with('messages') + adapter.quote_table_name('messages') + end + end + + describe "extends ActiveRecord::ConnectionAdapters::AbstractAdapter" do + it 'with quote_table_name' do + ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_methods.include?('quote_table_name').should be_true + end + end +end + +describe ThinkingSphinx::MysqlQuotedTableName do + describe "quote_table_name method" do + it 'correctly quotes' do + adapter = ActiveRecord::Base.connection + adapter.quote_table_name('thinking_sphinx.messages').should == "`thinking_sphinx`.`messages`" + end + end + + describe "extends ActiveRecord::ConnectionAdapters::MysqlAdapter" do + it 'with quote_table_name' do + adapter = defined?(JRUBY_VERSION) ? :JdbcAdapter : :MysqlAdapter + ActiveRecord::ConnectionAdapters.const_get(adapter).instance_methods.include?("quote_table_name").should be_true + end + end +end + +describe ThinkingSphinx::ActiveRecordQuotedName do + describe "quoted_table_name method" do + it 'returns table name wrappd in quotes' do + Person.quoted_table_name.should == '`people`' + end + end + + describe "extends ActiveRecord::Base" do + it 'with quoted_table_name' do + ActiveRecord::Base.respond_to?("quoted_table_name").should be_true + end + end +end + +describe ThinkingSphinx::ActiveRecordStoreFullSTIClass do + describe "store_full_sti_class method" do + it 'returns false' do + Person.store_full_sti_class.should be_false + end + end + + describe "extends ActiveRecord::Base" do + it 'with store_full_sti_class' do + ActiveRecord::Base.respond_to?(:store_full_sti_class).should be_true + end + end +end + +class TestModel + @@squares = 89 + @@circles = 43 + + def number_of_polygons + @@polygons + end +end + +describe ThinkingSphinx::ClassAttributeMethods do + describe "cattr_reader method" do + it 'creates getters' do + TestModel.cattr_reader :herbivores + test_model = TestModel.new + test_model.respond_to?(:herbivores).should be_true + end + + it 'sets the initial value to nil' do + TestModel.cattr_reader :carnivores + test_model = TestModel.new + test_model.carnivores.should be_nil + end + + it 'does not override an existing definition' do + TestModel.cattr_reader :squares + test_model = TestModel.new + test_model.squares.should == 89 + end + end + + describe "cattr_writer method" do + it 'creates setters' do + TestModel.cattr_writer :herbivores + test_model = TestModel.new + test_model.respond_to?(:herbivores=).should be_true + end + + it 'does not override an existing definition' do + TestModel.cattr_writer :polygons + test_model = TestModel.new + test_model.polygons = 100 + test_model.number_of_polygons.should == 100 + end + end + + describe "cattr_accessor method" do + it 'calls cattr_reader' do + Class.should_receive(:cattr_reader).with('polygons') + Class.cattr_accessor('polygons') + end + + it 'calls cattr_writer' do + Class.should_receive(:cattr_writer).with('polygons') + Class.cattr_accessor('polygons') + end + end + + describe "extends Class" do + it 'with cattr_reader' do + Class.respond_to?('cattr_reader').should be_true + end + + it 'with cattr_writer' do + Class.respond_to?('cattr_writer').should be_true + end + + it 'with cattr_accessor' do + Class.respond_to?('cattr_accessor').should be_true + end + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/search_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/search_spec.rb new file mode 100644 index 0000000..dd85138 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx/search_spec.rb @@ -0,0 +1,59 @@ +require 'spec/spec_helper' +require 'will_paginate/collection' + +describe ThinkingSphinx::Search do + describe "search method" do + before :each do + @client = Riddle::Client.stub_instance( + :filters => [], + :filters= => true, + :id_range= => true, + :sort_mode => :asc, + :limit => 5, + :offset= => 0, + :sort_mode= => true, + :query => { + :matches => [], + :total => 50 + } + ) + + ThinkingSphinx::Search.stub_methods( + :client_from_options => @client, + :search_conditions => ["", []] + ) + end + + describe ":star option" do + + it "should not apply by default" do + ThinkingSphinx::Search.search "foo bar" + @client.should have_received(:query).with("foo bar") + end + + it "should apply when passed, and handle full extended syntax" do + input = %{a b* c (d | e) 123 5&6 (f_f g) !h "i j" "k l"~10 "m n"/3 @o p -(q|r)} + expected = %{*a* b* *c* (*d* | *e*) *123* *5*&*6* (*f_f* *g*) !*h* "i j" "k l"~10 "m n"/3 @o *p* -(*q*|*r*)} + ThinkingSphinx::Search.search input, :star => true + @client.should have_received(:query).with(expected) + end + + it "should default to /\w+/ as token" do + ThinkingSphinx::Search.search "foo@bar.com", :star => true + @client.should have_received(:query).with("*foo*@*bar*.*com*") + end + + it "should honour custom token" do + ThinkingSphinx::Search.search "foo@bar.com -foo-bar", :star => /[\w@.-]+/u + @client.should have_received(:query).with("*foo@bar.com* -*foo-bar*") + end + + end + end +end + +describe ThinkingSphinx::Search, "playing nice with Search model" do + it "should not conflict with models called Search" do + lambda { Search.find(:all) }.should_not raise_error + end +end diff --git a/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx_spec.rb b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx_spec.rb new file mode 100644 index 0000000..375e158 --- /dev/null +++ b/vendor/plugins/thinking-sphinx/spec/unit/thinking_sphinx_spec.rb @@ -0,0 +1,133 @@ +require 'spec/spec_helper' + +describe ThinkingSphinx do + it "should define indexes by default" do + ThinkingSphinx.define_indexes?.should be_true + end + + it "should disable index definition" do + ThinkingSphinx.define_indexes = false + ThinkingSphinx.define_indexes?.should be_false + end + + it "should enable index definition" do + ThinkingSphinx.define_indexes = false + ThinkingSphinx.define_indexes?.should be_false + ThinkingSphinx.define_indexes = true + ThinkingSphinx.define_indexes?.should be_true + end + + it "should index deltas by default" do + ThinkingSphinx.deltas_enabled = nil + ThinkingSphinx.deltas_enabled?.should be_true + end + + it "should disable delta indexing" do + ThinkingSphinx.deltas_enabled = false + ThinkingSphinx.deltas_enabled?.should be_false + end + + it "should enable delta indexing" do + ThinkingSphinx.deltas_enabled = false + ThinkingSphinx.deltas_enabled?.should be_false + ThinkingSphinx.deltas_enabled = true + ThinkingSphinx.deltas_enabled?.should be_true + end + + it "should update indexes by default" do + ThinkingSphinx.updates_enabled = nil + ThinkingSphinx.updates_enabled?.should be_true + end + + it "should disable index updating" do + ThinkingSphinx.updates_enabled = false + ThinkingSphinx.updates_enabled?.should be_false + end + + it "should enable index updating" do + ThinkingSphinx.updates_enabled = false + ThinkingSphinx.updates_enabled?.should be_false + ThinkingSphinx.updates_enabled = true + ThinkingSphinx.updates_enabled?.should be_true + end + + describe "use_group_by_shortcut? method" do + before :each do + adapter = defined?(JRUBY_VERSION) ? :JdbcAdapter : :MysqlAdapter + unless ::ActiveRecord::ConnectionAdapters.const_defined?(adapter) + pending "No MySQL" + return + end + + @connection = ::ActiveRecord::ConnectionAdapters.const_get(adapter).stub_instance( + :select_all => true, + :config => {:adapter => defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql'} + ) + ::ActiveRecord::Base.stub_method( + :connection => @connection + ) + end + + it "should return true if no ONLY_FULL_GROUP_BY" do + @connection.stub_method( + :select_all => {:a => "OTHER SETTINGS"} + ) + + ThinkingSphinx.use_group_by_shortcut?.should be_true + end + + it "should return true if NULL value" do + @connection.stub_method( + :select_all => {:a => nil} + ) + + ThinkingSphinx.use_group_by_shortcut?.should be_true + end + + it "should return false if ONLY_FULL_GROUP_BY is set" do + @connection.stub_method( + :select_all => {:a => "OTHER SETTINGS,ONLY_FULL_GROUP_BY,blah"} + ) + + ThinkingSphinx.use_group_by_shortcut?.should be_false + end + + it "should return false if ONLY_FULL_GROUP_BY is set in any of the values" do + @connection.stub_method( + :select_all => { + :a => "OTHER SETTINGS", + :b => "ONLY_FULL_GROUP_BY" + } + ) + + ThinkingSphinx.use_group_by_shortcut?.should be_false + end + + describe "if not using MySQL" do + before :each do + adapter = defined?(JRUBY_VERSION) ? :JdbcAdapter : :PostgreSQLAdapter + unless ::ActiveRecord::ConnectionAdapters.const_defined?(adapter) + pending "No PostgreSQL" + return + end + @connection = ::ActiveRecord::ConnectionAdapters.const_get(adapter).stub_instance( + :select_all => true, + :config => {:adapter => defined?(JRUBY_VERSION) ? 'jdbcpostgresql' : 'postgresql'} + ) + ::ActiveRecord::Base.stub_method( + :connection => @connection + ) + end + + it "should return false" do + ThinkingSphinx.use_group_by_shortcut?.should be_false + end + + it "should not call select_all" do + ThinkingSphinx.use_group_by_shortcut? + + @connection.should_not have_received(:select_all) + end + end + end +end -- cgit v1.3