ts-datetime-delta 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Pat Allan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,50 @@
1
+ h1. Datetime Deltas for Thinking Sphinx
2
+
3
+ h2. Installation
4
+
5
+ You'll need Thinking Sphinx 1.3.0 or later.
6
+
7
+ <pre><code>gem install ts-datetime-delta --source http://gemcutter.org</code></pre>
8
+
9
+ In your @environment.rb@ file, with the rest of your gem dependencies:
10
+
11
+ <pre><code>config.gem 'ts-datetime-delta',
12
+ :lib => 'thinking_sphinx/deltas/datetime_delta'
13
+ :version => '>= 1.0.0',
14
+ :source => 'http://gemcutter.org'</code></pre>
15
+
16
+ And add the following line to the bottom of your @Rakefile@:
17
+
18
+ <pre><code>require 'thinking_sphinx/deltas/datetime_delta/tasks'</code></pre>
19
+
20
+ h2. Usage
21
+
22
+ For the indexes you want to use this delta approach, make sure you set that up in their @define_index@ blocks.
23
+
24
+ <pre><code>define_index do
25
+ # ...
26
+
27
+ set_property :delta => :datetime
28
+ end</code></pre>
29
+
30
+ If you want to use a column other than @updated_at@, you can specify it using the @:delta_column@ option. The same goes for the threshold, which defaults to one day.
31
+
32
+ <pre><code>set_property :delta => :datetime,
33
+ :threshold => 1.hour,
34
+ :delta_column => :changed_at</code></pre>
35
+
36
+ Then, while your Rails application is running, you'll need to run the delta indexing rake task regularly - as often as your threshold, allowing for some time for the indexing to actually happen.
37
+
38
+ For example, if you're going to run the delta indexing task every hour, I would recommend setting your threshold to 70 minutes.
39
+
40
+ To ensure this rake task is called regularly, it's best to set it up as a recurring task via cron or similar tools.
41
+
42
+ <pre><code>rake thinking_sphinx:index:delta</code></pre>
43
+
44
+ The shorthand version is:
45
+
46
+ <pre><code>rake ts:in:delta</code></pre>
47
+
48
+ h2. Copyright
49
+
50
+ Copyright (c) 2009 Pat Allan, and released under an MIT Licence.
@@ -0,0 +1,66 @@
1
+ Feature: Datetime Delta Indexing
2
+ In order to have delta indexing on frequently-updated sites
3
+ Developers
4
+ Should be able to use an existing datetime column to track changes
5
+
6
+ Scenario: Delta Index should not fire automatically
7
+ Given Sphinx is running
8
+ And I am searching on thetas
9
+ When I search for one
10
+ Then I should get 1 result
11
+
12
+ When I change the name of theta one to eleven
13
+ And I wait for Sphinx to catch up
14
+ And I search for one
15
+ Then I should get 1 result
16
+
17
+ When I search for eleven
18
+ Then I should get 0 results
19
+
20
+ Scenario: Delta Index should fire when jobs are run
21
+ Given Sphinx is running
22
+ And I am searching on thetas
23
+ When I search for two
24
+ Then I should get 1 result
25
+
26
+ When I change the name of theta two to twelve
27
+ And I wait for Sphinx to catch up
28
+ And I search for twelve
29
+ Then I should get 0 results
30
+
31
+ When I index the theta datetime delta
32
+ And I wait for Sphinx to catch up
33
+ And I search for twelve
34
+ Then I should get 1 result
35
+
36
+ When I search for two
37
+ Then I should get 0 results
38
+
39
+ Scenario: New records should be merged into the core index
40
+ Given Sphinx is running
41
+ And I am searching on thetas
42
+ When I search for thirteen
43
+ Then I should get 0 results
44
+
45
+ When I create a new theta named thirteen
46
+ And I search for thirteen
47
+ Then I should get 0 results
48
+
49
+ When I index the theta datetime delta
50
+ And I wait for Sphinx to catch up
51
+ And I search for thirteen
52
+ Then I should get 1 result
53
+
54
+ When I search for the document id of theta thirteen in the theta_core index
55
+ Then it should exist
56
+
57
+ Scenario: Deleting records
58
+ Given Sphinx is running
59
+ And I am searching on thetas
60
+ When I search for three
61
+ Then I should get 1 result
62
+
63
+ When I delete the theta named three
64
+ And I wait for Sphinx to catch up
65
+ And I search for three
66
+ Then I should get 0 results
@@ -0,0 +1,61 @@
1
+ Before do
2
+ $queries_executed = []
3
+
4
+ @model = nil
5
+ @method = :search
6
+ @query = ""
7
+ @conditions = {}
8
+ @with = {}
9
+ @without = {}
10
+ @with_all = {}
11
+ @options = {}
12
+ @results = nil
13
+ end
14
+
15
+ Given "Sphinx is running" do
16
+ ThinkingSphinx::Configuration.instance.controller.should be_running
17
+ end
18
+
19
+ Given /^I am searching on (.+)$/ do |model|
20
+ @model = model.gsub(/\s/, '_').singularize.camelize.constantize
21
+ end
22
+
23
+ When "I wait for Sphinx to catch up" do
24
+ sleep(0.25)
25
+ end
26
+
27
+ When /^I search for (\w+)$/ do |query|
28
+ @results = nil
29
+ @query = query
30
+ end
31
+
32
+ When /^I search for the document id of (\w+) (\w+) in the (\w+) index$/ do |model, name, index|
33
+ model = model.gsub(/\s/, '_').camelize.constantize
34
+ @id = model.find_by_name(name).sphinx_document_id
35
+ @index = index
36
+ end
37
+
38
+ Then /^I should get (\d+) results?$/ do |count|
39
+ results.length.should == count.to_i
40
+ end
41
+
42
+ Then "it should exist" do
43
+ ThinkingSphinx::Search.search_for_id(@id, @index).should == true
44
+ end
45
+
46
+ Then "it should not exist" do
47
+ ThinkingSphinx::Search.search_for_id(@id, @index).should == false
48
+ end
49
+
50
+ def results
51
+ @results ||= (@model || ThinkingSphinx).send(
52
+ @method,
53
+ @query,
54
+ @options.merge(
55
+ :conditions => @conditions,
56
+ :with => @with,
57
+ :without => @without,
58
+ :with_all => @with_all
59
+ )
60
+ )
61
+ end
@@ -0,0 +1,15 @@
1
+ When /^I index the theta datetime delta$/ do
2
+ Theta.sphinx_indexes.first.delta_object.delayed_index(Theta)
3
+ end
4
+
5
+ When /^I change the name of theta (\w+) to (\w+)$/ do |current, replacement|
6
+ Theta.find_by_name(current).update_attributes(:name => replacement)
7
+ end
8
+
9
+ When /^I create a new theta named (\w+)$/ do |name|
10
+ Theta.create(:name => name)
11
+ end
12
+
13
+ When /^I delete the theta named (\w+)$/ do |name|
14
+ Theta.find_by_name(name).destroy
15
+ end
@@ -0,0 +1,3 @@
1
+ username: root
2
+ host: localhost
3
+ password:
@@ -0,0 +1,5 @@
1
+ username: thinking_sphinx
2
+ host: localhost
3
+ password: thinking_sphinx
4
+ pool: 1
5
+ min_messages: warning
@@ -0,0 +1,10 @@
1
+ Theta.create :name => "one"
2
+ Theta.create :name => "two"
3
+ Theta.create :name => "three"
4
+ Theta.create :name => "four"
5
+ Theta.create :name => "five"
6
+ Theta.create :name => "six"
7
+ Theta.create :name => "seven"
8
+ Theta.create :name => "eight"
9
+ Theta.create :name => "nine"
10
+ Theta.create :name => "ten"
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Base.connection.create_table :thetas, :force => true do |t|
2
+ t.column :name, :string, :null => false
3
+ t.column :created_at, :datetime, :null => false
4
+ t.column :updated_at, :datetime, :null => false
5
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'cucumber'
3
+ require 'spec/expectations'
4
+ require 'fileutils'
5
+ require 'active_record'
6
+
7
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
8
+
9
+ require 'cucumber/thinking_sphinx/internal_world'
10
+
11
+ world = Cucumber::ThinkingSphinx::InternalWorld.new
12
+ world.configure_database
13
+
14
+ require 'thinking_sphinx'
15
+ require 'thinking_sphinx/deltas/datetime_delta'
16
+
17
+ world.setup
@@ -0,0 +1,7 @@
1
+ class Theta < ActiveRecord::Base
2
+ define_index do
3
+ indexes :name, :sortable => true
4
+
5
+ set_property :delta => :datetime, :threshold => 1.hour
6
+ end
7
+ end
@@ -0,0 +1,124 @@
1
+ # Datetime Deltas for Thinking Sphinx
2
+ #
3
+ # This documentation is aimed at those reading the code. If you're looking for
4
+ # a guide to Thinking Sphinx and/or deltas, I recommend you start with the
5
+ # Thinking Sphinx site instead - or the README for this library at the very
6
+ # least.
7
+ #
8
+ # @author Patrick Allan
9
+ # @see http://ts.freelancing-gods.com Thinking Sphinx
10
+ #
11
+ class ThinkingSphinx::Deltas::DatetimeDelta < ThinkingSphinx::Deltas::DefaultDelta
12
+ attr_accessor :column, :threshold
13
+
14
+ # Initialises the Delta object for the given index and settings. All handled
15
+ # by Thinking Sphinx, so you shouldn't need to call this method yourself in
16
+ # general day-to-day situations.
17
+ #
18
+ # @example
19
+ # ThinkingSphinx::Deltas::DatetimeDelta.new index,
20
+ # :delta_column => :updated_at,
21
+ # :threshold => 1.day
22
+ #
23
+ # @param [ThinkingSphinx::Index] index the index using this delta object
24
+ # @param [Hash] options a hash of options for the index
25
+ # @option options [Symbol] :delta_column (:updated_at) The column to use for
26
+ # tracking when a record has changed. Default to :updated_at.
27
+ # @option options [Integer] :threshold (1.day) The window of time to store
28
+ # changes for, in seconds. Defaults to one day.
29
+ #
30
+ def initialize(index, options = {})
31
+ @index = index
32
+ @column = options.delete(:delta_column) || :updated_at
33
+ @threshold = options.delete(:threshold) || 1.day
34
+ end
35
+
36
+ # Does absolutely nothing, beyond returning true. Thinking Sphinx expects
37
+ # this method, though, and we don't want to use the inherited behaviour from
38
+ # DefaultDelta.
39
+ #
40
+ # All the real indexing logic is done by the delayed_index method.
41
+ #
42
+ # @param [Class] model the ActiveRecord model to index.
43
+ # @param [ActiveRecord::Base] instance the instance of the given model that
44
+ # has changed. Optional.
45
+ # @return [Boolean] true
46
+ # @see #delayed_index
47
+ #
48
+ def index(model, instance = nil)
49
+ # do nothing
50
+ true
51
+ end
52
+
53
+ # Processes the delta index for the given model, and then merges the relevant
54
+ # core and delta indexes together. By default, the output of these indexer
55
+ # commands are printed to stdout. If you'd rather it didn't, set
56
+ # ThinkingSphinx.suppress_delta_output to true.
57
+ #
58
+ # @param [Class] model the ActiveRecord model to index
59
+ # @return [Boolean] true
60
+ #
61
+ def delayed_index(model)
62
+ config = ThinkingSphinx::Configuration.instance
63
+ rotate = ThinkingSphinx.sphinx_running? ? " --rotate" : ""
64
+
65
+ output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file}#{rotate} #{delta_index_name model}`
66
+ output += `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file}#{rotate} --merge #{core_index_name model} #{delta_index_name model} --merge-dst-range sphinx_deleted 0 0`
67
+ puts output unless ThinkingSphinx.suppress_delta_output?
68
+
69
+ true
70
+ end
71
+
72
+ # Toggles the given instance to be flagged as part of the next delta indexing.
73
+ # For datetime deltas, this means do nothing at all.
74
+ #
75
+ # @param [ActiveRecord::Base] instance the instance to be toggled
76
+ #
77
+ def toggle(instance)
78
+ # do nothing
79
+ end
80
+
81
+ # Report whether a given instance is considered toggled (part of the next
82
+ # delta process). For datetime deltas, this is true if the delta column
83
+ # (updated_at by default) has a value within the threshold. Otherwise, false
84
+ # is returned.
85
+ #
86
+ # @param [ActiveRecord::Base] instance the instance to check
87
+ # @return [Boolean] True if within the threshold window, otherwise false.
88
+ #
89
+ def toggled(instance)
90
+ instance.send(@column) > @threshold.ago
91
+ end
92
+
93
+ # Returns the SQL query that resets the model data after a normal index. For
94
+ # datetime deltas, nothing needs to be done, so this method returns nil.
95
+ #
96
+ # @param [Class] model The ActiveRecord model that is requesting the query
97
+ # @return [NilClass] Always nil
98
+ #
99
+ def reset_query(model)
100
+ nil
101
+ end
102
+
103
+ # A SQL condition (as part of the WHERE clause) that limits the result set to
104
+ # just the delta data, or all data, depending on whether the toggled argument
105
+ # is true or not. For datetime deltas, the former value is a check on the
106
+ # delta column being within the threshold. In the latter's case, no condition
107
+ # is needed, so nil is returned.
108
+ #
109
+ # @param [Class] model The ActiveRecord model to generate the SQL condition
110
+ # for.
111
+ # @param [Boolean] toggled Whether the query should request delta documents or
112
+ # all documents.
113
+ # @return [String, NilClass] The SQL condition if the toggled version is
114
+ # requested, otherwise nil.
115
+ #
116
+ def clause(model, toggled)
117
+ if toggled
118
+ "#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
119
+ " > #{adapter.time_difference(@threshold)}"
120
+ else
121
+ nil
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,23 @@
1
+ namespace :thinking_sphinx do
2
+ namespace :index do
3
+ desc "Index Thinking Sphinx datetime delta indexes"
4
+ task :delta => :app_env do
5
+ ThinkingSphinx.indexed_models.select { |model|
6
+ model.constantize.sphinx_indexes.any? { |index| index.delta? }
7
+ }.each do |model|
8
+ model.constantize.sphinx_indexes.select { |index|
9
+ index.delta? && index.delta_object.respond_to?(:delayed_index)
10
+ }.each { |index|
11
+ index.delta_object.delayed_index(index.model)
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ namespace :ts do
19
+ namespace :in do
20
+ desc "Index Thinking Sphinx datetime delta indexes"
21
+ task :delta => "thinking_sphinx:index:delta"
22
+ end
23
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'rubygems'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ require 'thinking_sphinx'
8
+ require 'thinking_sphinx/deltas/delayed_delta'
9
+
10
+ Spec::Runner.configure do |config|
11
+ #
12
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe ThinkingSphinx::Deltas::DatetimeDelta do
4
+ before :each do
5
+ @datetime_delta = ThinkingSphinx::Deltas::DatetimeDelta.new(
6
+ stub('index'), {}
7
+ )
8
+ end
9
+
10
+ describe '#index' do
11
+ it "should do nothing to the model" do
12
+ @datetime_delta.index(stub('model'))
13
+ end
14
+
15
+ it "should do nothing to the instance, if provided" do
16
+ @datetime_delta.index(stub('model'), stub('instance'))
17
+ end
18
+
19
+ it "should make no system calls" do
20
+ @datetime_delta.stub! :` => true
21
+ @datetime_delta.stub! :system => true
22
+
23
+ @datetime_delta.should_not_receive(:`)
24
+ @datetime_delta.should_not_receive(:system)
25
+
26
+ @datetime_delta.index(stub('model'), stub('instance'))
27
+ end
28
+
29
+ it "should return true" do
30
+ @datetime_delta.index(stub('model')).should be_true
31
+ end
32
+ end
33
+
34
+ describe '#delayed_index' do
35
+ before :each do
36
+ @model = stub('foo')
37
+ @model.stub!(:name => 'foo')
38
+ @model.stub!(:source_of_sphinx_index => @model)
39
+
40
+ ThinkingSphinx.suppress_delta_output = false
41
+
42
+ @datetime_delta.stub! :` => ""
43
+ @datetime_delta.stub! :puts => nil
44
+ end
45
+
46
+ it "should process the delta index for the given model" do
47
+ @datetime_delta.should_receive(:`).
48
+ with('indexer --config /config/development.sphinx.conf foo_delta')
49
+
50
+ @datetime_delta.delayed_index(@model)
51
+ end
52
+
53
+ it "should merge the core and delta indexes for the given model" do
54
+ @datetime_delta.should_receive(:`).with('indexer --config /config/development.sphinx.conf --merge foo_core foo_delta --merge-dst-range sphinx_deleted 0 0')
55
+
56
+ @datetime_delta.delayed_index(@model)
57
+ end
58
+
59
+ it "should include --rotate if Sphinx is running" do
60
+ ThinkingSphinx.stub!(:sphinx_running? => true)
61
+ @datetime_delta.should_receive(:`) do |command|
62
+ command.should match(/\s--rotate\s/)
63
+ end
64
+
65
+ @datetime_delta.delayed_index(@model)
66
+ end
67
+
68
+ it "should output the details by default" do
69
+ @datetime_delta.should_receive(:puts)
70
+
71
+ @datetime_delta.delayed_index(@model)
72
+ end
73
+
74
+ it "should hide the details if suppressing delta output" do
75
+ ThinkingSphinx.suppress_delta_output = true
76
+ @datetime_delta.should_not_receive(:puts)
77
+
78
+ @datetime_delta.delayed_index(@model)
79
+ end
80
+ end
81
+
82
+ describe '#toggle' do
83
+ it "should do nothing to the instance" do
84
+ @datetime_delta.toggle(stub('instance'))
85
+ end
86
+ end
87
+
88
+ describe '#toggled' do
89
+ it "should return true if the column value is more recent than the threshold" do
90
+ instance = stub('instance', :updated_at => 20.minutes.ago)
91
+ @datetime_delta.threshold = 30.minutes
92
+
93
+ @datetime_delta.toggled(instance).should be_true
94
+ end
95
+
96
+ it "should return false if the column value is older than the threshold" do
97
+ instance = stub('instance', :updated_at => 30.minutes.ago)
98
+ @datetime_delta.threshold = 20.minutes
99
+
100
+ @datetime_delta.toggled(instance).should be_false
101
+ end
102
+ end
103
+
104
+ describe '#reset_query' do
105
+ it "should be nil" do
106
+ @datetime_delta.reset_query(@model).should be_nil
107
+ end
108
+ end
109
+
110
+ describe '#clause' do
111
+ before :each do
112
+ @model = stub('model', :connection => stub('connection'))
113
+ @model.stub!(:quoted_table_name => '`foo`')
114
+ @model.connection.stub!(:quote_column_name => '`updated_at`')
115
+
116
+ @datetime_delta.stub!(
117
+ :adapter => stub('adapter', :time_difference => 'time_difference')
118
+ )
119
+ end
120
+
121
+ it "should return nil if not for the toggled results" do
122
+ @datetime_delta.clause(@model, false).should be_nil
123
+ end
124
+
125
+ it "should return only records within the threshold" do
126
+ @datetime_delta.clause(@model, true).
127
+ should == '`foo`.`updated_at` > time_difference'
128
+ end
129
+ end
130
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ts-datetime-delta
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Pat Allan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-03 00:00:00 +11:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: cucumber
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ description: Manage delta indexes via datetime columns for Thinking Sphinx
46
+ email: pat@freelancing-gods.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.textile
54
+ files:
55
+ - LICENSE
56
+ - README.textile
57
+ - lib/thinking_sphinx/deltas/datetime_delta.rb
58
+ - lib/thinking_sphinx/deltas/datetime_delta/tasks.rb
59
+ has_rdoc: true
60
+ homepage: http://github.com/freelancing-god/ts-datetime-delta
61
+ licenses: []
62
+
63
+ post_install_message:
64
+ rdoc_options:
65
+ - --charset=UTF-8
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.5
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Thinking Sphinx - Datetime Deltas
87
+ test_files:
88
+ - features/datetime_deltas.feature
89
+ - features/step_definitions/common_steps.rb
90
+ - features/step_definitions/datetime_delta_steps.rb
91
+ - features/support/database.example.yml
92
+ - features/support/database.yml
93
+ - features/support/db/fixtures/thetas.rb
94
+ - features/support/db/migrations/create_thetas.rb
95
+ - features/support/env.rb
96
+ - features/support/models/theta.rb
97
+ - spec/spec.opts
98
+ - spec/spec_helper.rb
99
+ - spec/thinking_sphinx/deltas/datetime_delta_spec.rb