activerecord-nulldb-adapter 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2008 Avdi Grimm
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,143 @@
1
+ = The NullDB Connection Adapter Plugin
2
+
3
+ == What
4
+
5
+ NullDB is the Null Object pattern as applied to ActiveRecord database
6
+ adapters. It is a database backend that translates database
7
+ interactions into no-ops. Using NullDB enables you to test your model
8
+ business logic - including +after_save+ hooks - without ever touching
9
+ a real database.
10
+
11
+ == How
12
+
13
+ Once installed, NullDB can be used much like any other ActiveRecord
14
+ database adapter:
15
+
16
+ ActiveRecord::Base.establish_connection :adapter => :nulldb
17
+
18
+ NullDB needs to know where you keep your schema file in order to
19
+ reflect table metadata. By default it looks in
20
+ RAILS_ROOT/db/schema.rb. You can override that by setting the
21
+ +schema+ option:
22
+
23
+ ActiveRecord::Base.establish_connection :adapter => :nulldb,
24
+ :schema => foo/myschema.rb
25
+
26
+ NullDB comes with RSpec integration. To replace the database with
27
+ NullDB in all of your specs, put the following in your
28
+ spec/spec_helper:
29
+
30
+ require 'nulldb_rspec'
31
+ include NullDB::RSpec::NullifiedDatabase
32
+
33
+ Or if you just want to use NullDB in a specific spec context, you can
34
+ include the same module inside a context:
35
+
36
+ require 'nulldb_rspec'
37
+
38
+ describe Employee, "with access to the database" do
39
+ fixtures :employees
40
+ # ...
41
+ end
42
+
43
+ describe Employee, "with NullDB" do
44
+ include NullDB::RSpec::NullifiedDatabase
45
+ # ...
46
+ end
47
+
48
+ NullDB::Rspec provides some custom matcher support for verifying
49
+ expectations about interactions with the database:
50
+
51
+ describe Employee do
52
+ include NullDB::RSpec::NullifiedDatabase
53
+
54
+ it "should cause an insert statement to be executed" do
55
+ Employee.create!
56
+ Employee.connection.should have_executed(:insert)
57
+ end
58
+ end
59
+
60
+ UnitRecord-style verification that no database calls have been made at
61
+ all can be achieved by using the special +:anything+ symbol:
62
+
63
+ describe "stuff that shouldn't touch the database" do
64
+ after :each do
65
+ Employee.connection.should_not have_executed(:anything)
66
+ end
67
+ # ...
68
+ end
69
+
70
+ You can also experiment with putting NullDB in your database.yml:
71
+
72
+ unit_test:
73
+ adapter: nulldb
74
+
75
+ However, due to the way Rails hard-codes specific database adapters
76
+ into its standard Rake tasks, you may find that this generates
77
+ unexpected and difficult-to-debug behavior. Workarounds for this are
78
+ under development.
79
+
80
+ == Why
81
+
82
+ There are a number of advantages to writing unit tests that never
83
+ touch the database. The biggest is probably speed of execution - unit
84
+ tests must be fast for test-driven development to be practical.
85
+ Another is separation of concerns: unit tests should be exercising
86
+ only the business logic contained in your models, not ActiveRecord.
87
+ For more on why testing-sans-database is a god idea, see:
88
+ http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database.
89
+
90
+ NullDB is one way to separate your unit tests from the database. It
91
+ was inspired by the ARBS[http://arbs.rubyforge.org/] and
92
+ UnitRecord[http://unit-test-ar.rubyforge.org/] libraries. It differs
93
+ from them in a couple of ways:
94
+
95
+ 1. It works. At the time of writing both ARBS and UnitRecord were
96
+ not working for me out of the box with Rails 2.0.
97
+
98
+ 2. It avoids monkey-patching as much as possible. Rather than
99
+ re-wiring the secret inner workings of ActiveRecord (and thus being
100
+ tightly coupled to those inner workings), NullDB implements the
101
+ same [semi-]well-documented public interface that the other standard
102
+ database adapters, like MySQL and SQLServer, implement.
103
+
104
+ 3. UnitRecord takes the approach of eliminating database interaction
105
+ in tests by turning almost every database interaction into an
106
+ exception. NullDB recognizes that ActiveRecord objects typically
107
+ can't take two steps without consulting the database, so instead it
108
+ turns database interactions into no-ops.
109
+
110
+ One concrete advantage of this null-object pattern design is that it
111
+ is possible with NullDB to test +after_save+ hooks. With NullDB, you
112
+ can call +#save+ and all of the usual callbacks will be called - but
113
+ nothing will be saved.
114
+
115
+ == Limitations
116
+
117
+ * It is *not* an in-memory database. Finds will not work. Neither
118
+ will +reload+, currently. Test fixtures won't work either, for
119
+ obvious reasons.
120
+ * It has only the most rudimentery schema/migration support. Complex
121
+ migrations will probably break it.
122
+ * Lots of other things probably don't work. Patches welcome!
123
+
124
+ == Who
125
+
126
+ NullDB was written by Avdi Grimm <mailto:avdi@avdi.org>
127
+
128
+ == Where
129
+
130
+ * Homepage: http://nulldb.rubyforge.org
131
+ * Project Info: http://rubyforge.org/projects/nulldb/
132
+ * SCM: http://rubyforge.org/scm/?group_id=7512
133
+
134
+ == Changes
135
+
136
+ * Version 0.0.1 (2007-02-18)
137
+ - Initial Release
138
+ * Version 0.0.2 (2007-05-31)
139
+ - Moved to Rubyforge
140
+
141
+ == License
142
+
143
+ See the LICENSE file for licensing information.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ # active_record connection_adapters abstract connection_specification
6
+ GEM_NAME = 'activerecord-nulldb-adapter'
7
+
8
+ desc "Run all examples"
9
+ Spec::Rake::SpecTask.new('spec') do |t|
10
+ t.spec_files = FileList['spec/**/*_spec.rb']
11
+ end
12
+
13
+ Rake::RDocTask.new do |rd|
14
+ rd.main = "README"
15
+ rd.rdoc_files.include("README", "LICENSE", "lib/**/*.rb")
16
+ end
17
+
18
+ desc "Publish project home page"
19
+ task :publish => ["rdoc"] do
20
+ sh "scp -r html/* avdi@rubyforge.org:/var/www/gforge-projects/nulldb"
21
+ end
22
+
23
+ desc "Tag release"
24
+ task :tag do
25
+ warn "This needs to be updated for git"
26
+ exit 1
27
+ repos = "http://svn.avdi.org/nulldb"
28
+ version = ENV["VERSION"]
29
+ raise "No version specified" unless version
30
+ sh "svn cp #{repos}/trunk #{repos}/tags/nulldb-#{version}"
31
+ end
32
+
33
+ desc "Build gem"
34
+ task :gem do
35
+ system 'rake gemspec'
36
+ system "gem build #{GEM_NAME}.gemspec"
37
+ end
38
+
39
+ begin
40
+ require 'jeweler'
41
+ Jeweler::Tasks.new do |gem|
42
+ gem.name = GEM_NAME
43
+ gem.summary = %Q{NullDB lets you to test your models without ever touching a real database.}
44
+ gem.email = "avdi@avdi.org"
45
+ gem.homepage = 'http://nulldb.rubyforge.org'
46
+ gem.description = "An ActiveRecord null database adapter for greater speed and isolation in unit tests"
47
+ gem.rubyforge_project = 'nulldb'
48
+ gem.authors = ['Avdi Grimm']
49
+ end
50
+
51
+ rescue LoadError
52
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,51 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{activerecord-nulldb-adapter}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Avdi Grimm"]
12
+ s.date = %q{2009-12-30}
13
+ s.description = %q{An ActiveRecord null database adapter for greater speed and isolation in unit tests}
14
+ s.email = %q{avdi@avdi.org}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README"
18
+ ]
19
+ s.files = [
20
+ "LICENSE",
21
+ "README",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "activerecord-nulldb-adapter.gemspec",
25
+ "init.rb",
26
+ "lib/active_record/connection_adapters/nulldb_adapter.rb",
27
+ "lib/nulldb_rspec.rb",
28
+ "spec/nulldb_spec.rb",
29
+ "tasks/database.rake"
30
+ ]
31
+ s.homepage = %q{http://nulldb.rubyforge.org}
32
+ s.rdoc_options = ["--charset=UTF-8"]
33
+ s.require_paths = ["lib"]
34
+ s.rubyforge_project = %q{nulldb}
35
+ s.rubygems_version = %q{1.3.5}
36
+ s.summary = %q{NullDB lets you to test your models without ever touching a real database.}
37
+ s.test_files = [
38
+ "spec/nulldb_spec.rb"
39
+ ]
40
+
41
+ if s.respond_to? :specification_version then
42
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
46
+ else
47
+ end
48
+ else
49
+ end
50
+ end
51
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ #require 'active_record/connection_adapters/nulldb_adapter'
@@ -0,0 +1,231 @@
1
+ require 'logger'
2
+ require 'stringio'
3
+ require 'singleton'
4
+ require 'active_record/connection_adapters/abstract_adapter'
5
+
6
+ class ActiveRecord::Base
7
+ # Instantiate a new NullDB connection. Used by ActiveRecord internally.
8
+ def self.nulldb_connection(config)
9
+ ActiveRecord::ConnectionAdapters::NullDBAdapter.new(config)
10
+ end
11
+ end
12
+
13
+ module ActiveRecord
14
+ # Just make sure you have the latest version of your schema
15
+ class Schema < Migration
16
+ def self.define(info={}, &block)
17
+ instance_eval(&block)
18
+ end
19
+ end
20
+ end
21
+
22
+ class ActiveRecord::ConnectionAdapters::NullDBAdapter <
23
+ ActiveRecord::ConnectionAdapters::AbstractAdapter
24
+
25
+ class Statement
26
+ attr_reader :entry_point, :content
27
+
28
+ def initialize(entry_point, content = "")
29
+ @entry_point, @content = entry_point, content
30
+ end
31
+
32
+ def ==(other)
33
+ self.entry_point == other.entry_point
34
+ end
35
+ end
36
+
37
+ class Checkpoint < Statement
38
+ def initialize
39
+ super(:checkpoint, "")
40
+ end
41
+
42
+ def ==(other)
43
+ self.class == other.class
44
+ end
45
+ end
46
+
47
+ TableDefinition = ActiveRecord::ConnectionAdapters::TableDefinition
48
+
49
+ class NullObject
50
+ def method_missing(*args, &block)
51
+ nil
52
+ end
53
+ end
54
+
55
+ # A convenience method for integratinginto RSpec. See README for example of
56
+ # use.
57
+ def self.insinuate_into_spec(config)
58
+ config.before :all do
59
+ ActiveRecord::Base.establish_connection(:adapter => :nulldb)
60
+ end
61
+
62
+ config.after :all do
63
+ ActiveRecord::Base.establish_connection(:test)
64
+ end
65
+ end
66
+
67
+ # Recognized options:
68
+ #
69
+ # [+:schema+] path to the schema file, relative to RAILS_ROOT
70
+ def initialize(config={})
71
+ @log = StringIO.new
72
+ @logger = Logger.new(@log)
73
+ @last_unique_id = 0
74
+ @tables = {'schema_info' => TableDefinition.new(nil)}
75
+ @schema_path = config.fetch(:schema){ "db/schema.rb" }
76
+ super(nil, @logger)
77
+ end
78
+
79
+ # A log of every statement that has been "executed" by this connection adapter
80
+ # instance.
81
+ def execution_log
82
+ (@execution_log ||= [])
83
+ end
84
+
85
+ # A log of every statement that has been "executed" since the last time
86
+ # #checkpoint! was called, or since the connection was created.
87
+ def execution_log_since_checkpoint
88
+ checkpoint_index = @execution_log.rindex(Checkpoint.new)
89
+ checkpoint_index = checkpoint_index ? checkpoint_index + 1 : 0
90
+ @execution_log[(checkpoint_index..-1)]
91
+ end
92
+
93
+ # Inserts a checkpoint in the log. See also #execution_log_since_checkpoint.
94
+ def checkpoint!
95
+ self.execution_log << Checkpoint.new
96
+ end
97
+
98
+ def adapter_name
99
+ "NullDB"
100
+ end
101
+
102
+ def supports_migrations?
103
+ true
104
+ end
105
+
106
+ def create_table(table_name, options = {})
107
+ table_definition = ActiveRecord::ConnectionAdapters::TableDefinition.new(self)
108
+ unless options[:id] == false
109
+ table_definition.primary_key(options[:primary_key] || "id")
110
+ end
111
+
112
+ yield table_definition
113
+
114
+ @tables[table_name] = table_definition
115
+ end
116
+
117
+ def add_fk_constraint(*args)
118
+ # NOOP
119
+ end
120
+
121
+ def add_pk_constraint(*args)
122
+ # NOOP
123
+ end
124
+
125
+ # Retrieve the table names defined by the schema
126
+ def tables
127
+ @tables.keys.map(&:to_s)
128
+ end
129
+
130
+ # Retrieve table columns as defined by the schema
131
+ def columns(table_name, name = nil)
132
+ if @tables.size <= 1
133
+ ActiveRecord::Migration.verbose = false
134
+ Kernel.load(File.join(RAILS_ROOT, @schema_path))
135
+ end
136
+ table = @tables[table_name]
137
+ table.columns.map do |col_def|
138
+ ActiveRecord::ConnectionAdapters::Column.new(col_def.name.to_s,
139
+ col_def.default,
140
+ col_def.type,
141
+ col_def.null)
142
+ end
143
+ end
144
+
145
+ def execute(statement, name = nil)
146
+ self.execution_log << Statement.new(entry_point, statement)
147
+ NullObject.new
148
+ end
149
+
150
+ def select_rows(statement, name = nil)
151
+ returning([]) do
152
+ self.execution_log << Statement.new(entry_point, statement)
153
+ end
154
+ end
155
+
156
+ def insert(statement, name, primary_key, object_id, sequence_name)
157
+ returning(object_id || next_unique_id) do
158
+ with_entry_point(:insert) do
159
+ super(statement, name, primary_key, object_id, sequence_name)
160
+ end
161
+ end
162
+ end
163
+
164
+ def update(statement, name=nil)
165
+ with_entry_point(:update) do
166
+ super(statement, name)
167
+ end
168
+ end
169
+
170
+ def delete(statement, name=nil)
171
+ with_entry_point(:delete) do
172
+ super(statement, name)
173
+ end
174
+ end
175
+
176
+ def select_all(statement, name=nil)
177
+ with_entry_point(:select_all) do
178
+ super(statement, name)
179
+ end
180
+ end
181
+
182
+ def select_one(statement, name=nil)
183
+ with_entry_point(:select_one) do
184
+ super(statement, name)
185
+ end
186
+ end
187
+
188
+ def select_value(statement, name=nil)
189
+ with_entry_point(:select_value) do
190
+ super(statement, name)
191
+ end
192
+ end
193
+
194
+ protected
195
+
196
+ def select(statement, name)
197
+ returning([]) do
198
+ self.execution_log << Statement.new(entry_point, statement)
199
+ end
200
+ end
201
+
202
+ private
203
+
204
+ def next_unique_id
205
+ @last_unique_id += 1
206
+ end
207
+
208
+ def with_entry_point(method)
209
+ if entry_point.nil?
210
+ with_thread_local_variable(:entry_point, method) do
211
+ yield
212
+ end
213
+ else
214
+ yield
215
+ end
216
+ end
217
+
218
+ def entry_point
219
+ Thread.current[:entry_point]
220
+ end
221
+
222
+ def with_thread_local_variable(name, value)
223
+ old_value = Thread.current[name]
224
+ Thread.current[name] = value
225
+ begin
226
+ yield
227
+ ensure
228
+ Thread.current[name] = old_value
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,93 @@
1
+ require 'active_record/connection_adapters/nulldb_adapter'
2
+
3
+ module NullDB
4
+ module RSpec
5
+ end
6
+ end
7
+
8
+ module NullDB::RSpec::NullifiedDatabase
9
+ NullDBAdapter = ActiveRecord::ConnectionAdapters::NullDBAdapter
10
+
11
+ class HaveExecuted
12
+
13
+ def initialize(entry_point)
14
+ @entry_point = entry_point
15
+ end
16
+
17
+ def matches?(connection)
18
+ log = connection.execution_log_since_checkpoint
19
+ if entry_point == :anything
20
+ not log.empty?
21
+ else
22
+ log.include?(NullDBAdapter::Statement.new(@entry_point))
23
+ end
24
+ end
25
+
26
+ def description
27
+ "connection should execute #{@entry_point} statement"
28
+ end
29
+
30
+ def failure_message
31
+ " did not execute #{@entry_point} statement when it should have"
32
+ end
33
+
34
+ def negative_failure_message
35
+ " executed #{@entry_point} statement when it should not have"
36
+ end
37
+ end
38
+
39
+ def self.globally_nullify_database
40
+ Spec::Runner.configure do |config|
41
+ nullify_database(config)
42
+ end
43
+ end
44
+
45
+ def self.contextually_nullify_database(context)
46
+ nullify_database(context)
47
+ end
48
+
49
+ # A matcher for asserting that database statements have (or have not) been
50
+ # executed. Usage:
51
+ #
52
+ # ActiveRecord::Base.connection.should have_executed(:insert)
53
+ #
54
+ # The types of statement that can be matched mostly mirror the public
55
+ # operations available in
56
+ # ActiveRecord::ConnectionAdapters::DatabaseStatements:
57
+ # - :select_one
58
+ # - :select_all
59
+ # - :select_value
60
+ # - :insert
61
+ # - :update
62
+ # - :delete
63
+ # - :execute
64
+ #
65
+ # There is also a special :anything symbol that will match any operation.
66
+ def have_executed(entry_point)
67
+ HaveExecuted.new(entry_point)
68
+ end
69
+
70
+ private
71
+
72
+ def self.included(other)
73
+ if other.ancestors.include?(ActiveSupport::TestCase)
74
+ contextually_nullify_database(other)
75
+ else
76
+ globally_nullify_database
77
+ end
78
+ end
79
+
80
+ def self.nullify_database(receiver)
81
+ receiver.before :all do
82
+ ActiveRecord::Base.establish_connection(:adapter => :nulldb)
83
+ end
84
+
85
+ receiver.before :each do
86
+ ActiveRecord::Base.connection.checkpoint!
87
+ end
88
+
89
+ receiver.after :all do
90
+ ActiveRecord::Base.establish_connection(:test)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,189 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'active_record'
4
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
5
+
6
+ class Employee < ActiveRecord::Base
7
+ after_save :on_save_finished
8
+
9
+ def on_save_finished
10
+ end
11
+ end
12
+
13
+ RAILS_ROOT = "RAILS_ROOT"
14
+
15
+ describe "NullDB with no schema pre-loaded" do
16
+ before :each do
17
+ Kernel.stub!(:load)
18
+ ActiveRecord::Migration.stub!(:verbose=)
19
+ end
20
+
21
+ it "should load RAILS_ROOT/db/schema.rb if no alternate is specified" do
22
+ ActiveRecord::Base.establish_connection :adapter => :nulldb
23
+ Kernel.should_receive(:load).with("RAILS_ROOT/db/schema.rb")
24
+ ActiveRecord::Base.connection.columns('schema_info')
25
+ end
26
+
27
+ it "should load the specified schema relative to RAILS_ROOT" do
28
+ Kernel.should_receive(:load).with("RAILS_ROOT/foo/myschema.rb")
29
+ ActiveRecord::Base.establish_connection :adapter => :nulldb,
30
+ :schema => "foo/myschema.rb"
31
+ ActiveRecord::Base.connection.columns('schema_info')
32
+ end
33
+
34
+ it "should suppress migration output" do
35
+ ActiveRecord::Migration.should_receive(:verbose=).with(false)
36
+ ActiveRecord::Base.establish_connection :adapter => :nulldb,
37
+ :schema => "foo/myschema.rb"
38
+ ActiveRecord::Base.connection.columns('schema_info')
39
+ end
40
+ end
41
+
42
+ describe "NullDB" do
43
+ before :all do
44
+ ActiveRecord::Base.establish_connection :adapter => :nulldb
45
+ ActiveRecord::Migration.verbose = false
46
+ ActiveRecord::Schema.define do
47
+ create_table(:employees) do |t|
48
+ t.string :name
49
+ t.date :hire_date
50
+ t.integer :employee_number
51
+ t.decimal :salary
52
+ end
53
+
54
+ add_fk_constraint "foo", "bar", "baz", "buz", "bungle"
55
+ add_pk_constraint "foo", "bar", {}, "baz", "buz"
56
+ end
57
+ end
58
+
59
+ before :each do
60
+ @employee = Employee.new(:name => "John Smith",
61
+ :hire_date => Date.civil(2000, 1, 1),
62
+ :employee_number => 42,
63
+ :salary => 56000.00)
64
+ end
65
+
66
+ it "should enable instantiation of AR objects without a database" do
67
+ @employee.should_not be_nil
68
+ @employee.should be_a_kind_of(ActiveRecord::Base)
69
+ end
70
+
71
+ it "should remember columns defined in migrations" do
72
+ should_have_column(Employee, :name, :string)
73
+ should_have_column(Employee, :hire_date, :date)
74
+ should_have_column(Employee, :employee_number, :integer)
75
+ should_have_column(Employee, :salary, :decimal)
76
+ end
77
+
78
+ it "should enable simulated saving of AR objects" do
79
+ lambda { @employee.save! }.should_not raise_error
80
+ end
81
+
82
+ it "should enable AR callbacks during simulated save" do
83
+ @employee.should_receive(:on_save_finished)
84
+ @employee.save
85
+ end
86
+
87
+ it "should enable simulated deletes of AR objects" do
88
+ lambda { @employee.destroy }.should_not raise_error
89
+ end
90
+
91
+ it "should enable simulated creates of AR objects" do
92
+ emp = Employee.create(:name => "Bob Jones")
93
+ emp.name.should == "Bob Jones"
94
+ end
95
+
96
+ it "should generate new IDs when inserting unsaved objects" do
97
+ cxn = Employee.connection
98
+ id1 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil)
99
+ id2 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil)
100
+ id2.should == (id1 + 1)
101
+ end
102
+
103
+ it "should re-use object ID when inserting saved objects" do
104
+ cxn = Employee.connection
105
+ id1 = cxn.insert("some sql", "SomeClass Create", "id", 23, nil)
106
+ id1.should == 23
107
+ end
108
+
109
+ it "should log executed SQL statements" do
110
+ cxn = @employee.connection
111
+ exec_count = cxn.execution_log.size
112
+ @employee.save!
113
+ cxn.execution_log.size.should == (exec_count + 1)
114
+ end
115
+
116
+ it "should have the adapter name 'NullDB'" do
117
+ @employee.connection.adapter_name.should == "NullDB"
118
+ end
119
+
120
+ it "should support migrations" do
121
+ @employee.connection.supports_migrations?.should be_true
122
+ end
123
+
124
+ it "should always have a schema_info table definition" do
125
+ @employee.connection.tables.should include("schema_info")
126
+ end
127
+
128
+ it "should return an empty array from #select" do
129
+ @employee.connection.select_all("who cares", "blah").should == []
130
+ end
131
+
132
+ it "should provide a way to set log checkpoints" do
133
+ cxn = @employee.connection
134
+ @employee.save!
135
+ cxn.execution_log_since_checkpoint.size.should > 0
136
+ cxn.checkpoint!
137
+ cxn.execution_log_since_checkpoint.size.should == 0
138
+ @employee.save!
139
+ cxn.execution_log_since_checkpoint.size.should == 1
140
+ end
141
+
142
+ def should_contain_statement(cxn, entry_point)
143
+ cxn.execution_log_since_checkpoint.should \
144
+ include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point))
145
+ end
146
+
147
+ def should_not_contain_statement(cxn, entry_point)
148
+ cxn.execution_log_since_checkpoint.should_not \
149
+ include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point))
150
+ end
151
+
152
+ it "should tag logged statements with their entry point" do
153
+ cxn = @employee.connection
154
+
155
+ should_not_contain_statement(cxn, :insert)
156
+ @employee.save
157
+ should_contain_statement(cxn, :insert)
158
+
159
+ cxn.checkpoint!
160
+ should_not_contain_statement(cxn, :update)
161
+ @employee.save
162
+ should_contain_statement(cxn, :update)
163
+
164
+ cxn.checkpoint!
165
+ should_not_contain_statement(cxn, :delete)
166
+ @employee.destroy
167
+ should_contain_statement(cxn, :delete)
168
+
169
+ cxn.checkpoint!
170
+ should_not_contain_statement(cxn, :select_all)
171
+ Employee.find(:all)
172
+ should_contain_statement(cxn, :select_all)
173
+
174
+ cxn.checkpoint!
175
+ should_not_contain_statement(cxn, :select_value)
176
+ Employee.count_by_sql("frobozz")
177
+ should_contain_statement(cxn, :select_value)
178
+ end
179
+
180
+ it "should allow #finish to be called on the result of #execute" do
181
+ @employee.connection.execute("blah").finish
182
+ end
183
+
184
+ def should_have_column(klass, col_name, col_type)
185
+ col = klass.columns_hash[col_name.to_s]
186
+ col.should_not be_nil
187
+ col.type.should == col_type
188
+ end
189
+ end
@@ -0,0 +1,33 @@
1
+ # Sadly, we have to monkeypatch Rake because all of the Rails database tasks are
2
+ # hardcoded for specific adapters, with no extension points (!)
3
+ Rake::TaskManager.class_eval do
4
+ def remove_task(task_name)
5
+ @tasks.delete(task_name.to_s)
6
+ end
7
+ end
8
+
9
+ def remove_task(task_name)
10
+ Rake.application.remove_task(task_name)
11
+ end
12
+
13
+ def wrap_task(task_name, &wrapper)
14
+ wrapped_task = Rake::Task[task_name]
15
+ remove_task(Rake::Task.scope_name(Rake.application.current_scope,
16
+ task_name))
17
+ task(task_name) do
18
+ wrapper.call(wrapped_task)
19
+ end
20
+ end
21
+
22
+ # For later exploration...
23
+ # namespace :db do
24
+ # namespace :test do
25
+ # wrap_task :purge do |wrapped_task|
26
+ # if ActiveRecord::Base.configurations["test"]["adapter"] == "nulldb"
27
+ # # NO-OP
28
+ # else
29
+ # wrapped_task.invoke
30
+ # end
31
+ # end
32
+ # end
33
+ # end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-nulldb-adapter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Avdi Grimm
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-30 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: An ActiveRecord null database adapter for greater speed and isolation in unit tests
17
+ email: avdi@avdi.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README
25
+ files:
26
+ - LICENSE
27
+ - README
28
+ - Rakefile
29
+ - VERSION
30
+ - activerecord-nulldb-adapter.gemspec
31
+ - init.rb
32
+ - lib/active_record/connection_adapters/nulldb_adapter.rb
33
+ - lib/nulldb_rspec.rb
34
+ - spec/nulldb_spec.rb
35
+ - tasks/database.rake
36
+ has_rdoc: true
37
+ homepage: http://nulldb.rubyforge.org
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project: nulldb
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: NullDB lets you to test your models without ever touching a real database.
64
+ test_files:
65
+ - spec/nulldb_spec.rb