activerecord-nulldb-adapter-0.4.0/0000755000004100000410000000000013516557202017117 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/.travis.yml0000644000004100000410000000361313516557202021233 0ustar www-datawww-datalanguage: ruby cache: bundler bundler_args: --without development sudo: false rvm: - 2.2 - 2.3 - 2.4 - 2.5 - 2.6 - jruby - rbx gemfile: - gemfiles/activerecord_2.3.gemfile - gemfiles/activerecord_3.2.gemfile - gemfiles/activerecord_4.0.gemfile - gemfiles/activerecord_4.1.gemfile - gemfiles/activerecord_4.2.gemfile - gemfiles/activerecord_5.0.gemfile - gemfiles/activerecord_5.1.gemfile - gemfiles/activerecord_6.0.gemfile - gemfiles/activerecord_master.gemfile matrix: fast_finish: true allow_failures: - rvm: jruby - rvm: rbx - gemfile: gemfiles/activerecord_master.gemfile exclude: - rvm: 2.2 gemfile: gemfiles/activerecord_2.3.gemfile - rvm: 2.2 gemfile: gemfiles/activerecord_6.0.gemfile - rvm: 2.2 gemfile: gemfiles/activerecord_master.gemfile - rvm: 2.3 gemfile: gemfiles/activerecord_2.3.gemfile - rvm: 2.3 gemfile: gemfiles/activerecord_6.0.gemfile - rvm: 2.3 gemfile: gemfiles/activerecord_master.gemfile - rvm: 2.4 gemfile: gemfiles/activerecord_2.3.gemfile - rvm: 2.4 gemfile: gemfiles/activerecord_3.2.gemfile - rvm: 2.4 gemfile: gemfiles/activerecord_4.0.gemfile - rvm: 2.4 gemfile: gemfiles/activerecord_4.1.gemfile - rvm: 2.4 gemfile: gemfiles/activerecord_6.0.gemfile - rvm: 2.4 gemfile: gemfiles/activerecord_master.gemfile - rvm: 2.5 gemfile: gemfiles/activerecord_2.3.gemfile - rvm: 2.5 gemfile: gemfiles/activerecord_3.2.gemfile - rvm: 2.5 gemfile: gemfiles/activerecord_4.0.gemfile - rvm: 2.5 gemfile: gemfiles/activerecord_4.1.gemfile - rvm: 2.6 gemfile: gemfiles/activerecord_2.3.gemfile - rvm: 2.6 gemfile: gemfiles/activerecord_3.2.gemfile - rvm: 2.6 gemfile: gemfiles/activerecord_4.0.gemfile - rvm: 2.6 gemfile: gemfiles/activerecord_4.1.gemfile activerecord-nulldb-adapter-0.4.0/gemfiles/0000755000004100000410000000000013516557202020712 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/gemfiles/activerecord_2.3.gemfile0000644000004100000410000000045713516557202025306 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 2.3.0" gem "iconv", :platforms => :ruby group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_4.1.gemfile0000644000004100000410000000041613516557202025301 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 4.1.0" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_4.2.gemfile0000644000004100000410000000041613516557202025302 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 4.2.0" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_master.gemfile0000644000004100000410000000055013516557202026271 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" git 'https://github.com/rails/rails.git' do gem 'activerecord' end gem 'arel', github: 'rails/arel', branch: 'master' group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_3.2.gemfile0000644000004100000410000000041613516557202025301 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 3.2.0" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_6.0.gemfile0000644000004100000410000000043413516557202025302 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 6.0.0.rc1" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" gem "rdoc" end group :development do gem "appraisal" gem "simplecov", require: false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_5.0.gemfile0000644000004100000410000000041613516557202025301 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 5.0.0" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_4.0.gemfile0000644000004100000410000000041613516557202025300 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 4.0.0" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/gemfiles/activerecord_5.1.gemfile0000644000004100000410000000041613516557202025302 0ustar www-datawww-data# This file was generated by Appraisal source "https://rubygems.org" gem "activerecord", "~> 5.1.0" group :development, :test do gem "spec" gem "rspec", ">= 1.2.9" gem "rake" end group :development do gem "appraisal" gem "simplecov", :require => false end activerecord-nulldb-adapter-0.4.0/spec/0000755000004100000410000000000013516557202020051 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/spec/spec.opts0000644000004100000410000000001713516557202021710 0ustar www-datawww-data--format nestedactiverecord-nulldb-adapter-0.4.0/spec/nulldb_spec.rb0000644000004100000410000002573313516557202022702 0ustar www-datawww-datarequire 'rubygems' # Optional simplecov loading begin require 'simplecov' SimpleCov.start rescue LoadError end require 'active_record' require 'active_record/version' $: << File.join(File.dirname(__FILE__), "..", "lib") if ActiveRecord::VERSION::MAJOR > 2 require 'rspec' # rspec 2 else require 'spec' # rspec 1 end require 'nulldb_rspec' class Employee < ActiveRecord::Base after_save :on_save_finished def on_save_finished end end class TablelessModel < ActiveRecord::Base end NullDB.configure {|ndb| ndb.project_root = 'Rails.root'} describe "NullDB with no schema pre-loaded" do before :each do allow( Kernel ).to receive :load allow( ActiveRecord::Migration ).to receive :verbose= end it "should load Rails.root/db/schema.rb if no alternate is specified" do ActiveRecord::Base.establish_connection :adapter => :nulldb expect( Kernel ).to receive(:load).with("Rails.root/db/schema.rb") ActiveRecord::Base.connection.columns('schema_info') end it "should load the specified schema relative to Rails.root" do expect( Kernel ).to receive(:load).with("Rails.root/foo/myschema.rb") ActiveRecord::Base.establish_connection :adapter => :nulldb, :schema => "foo/myschema.rb" ActiveRecord::Base.connection.columns('schema_info') end it "should suppress migration output" do expect( ActiveRecord::Migration).to receive(:verbose=).with(false) ActiveRecord::Base.establish_connection :adapter => :nulldb, :schema => "foo/myschema.rb" ActiveRecord::Base.connection.columns('schema_info') end it "should allow creating a table without passing a block" do ActiveRecord::Base.establish_connection :adapter => :nulldb ActiveRecord::Schema.define do create_table(:employees) end end end describe "NullDB" do before :all do ActiveRecord::Base.establish_connection :adapter => :nulldb ActiveRecord::Migration.verbose = false ActiveRecord::Schema.define do create_table(:employees) do |t| t.string :name, null: false, limit: 50 t.date :hire_date t.integer :employee_number t.decimal :salary end create_table(:employees_widgets, :id => false, :force => true) do |t| t.integer :employee_id t.integer :widget_id end add_index "employees", :name, :name => "index_employees_on_name" add_index "employees", ["employee_number"], :name => "index_employees_on_employee_number", :unique => true add_index "employees_widgets", ["employee_id", "widget_id"], :name => "my_index" add_fk_constraint "foo", "bar", "baz", "buz", "bungle" add_pk_constraint "foo", "bar", {}, "baz", "buz" end end before :each do @employee = Employee.new(:name => "John Smith", :hire_date => Date.civil(2000, 1, 1), :employee_number => 42, :salary => 56000.00) end it "should set the @config instance variable so plugins that assume its there can use it" do expect( Employee.connection.instance_variable_get(:@config)[:adapter]).to eq :nulldb end it "should enable instantiation of AR objects without a database" do expect( @employee ).to be_a_kind_of(ActiveRecord::Base) end it "should remember columns defined in migrations" do should_have_column(Employee, :name, :string) should_have_column(Employee, :hire_date, :date) should_have_column(Employee, :employee_number, :integer) should_have_column(Employee, :salary, :decimal) end it 'should have limit on name' do expect(Employee.columns_hash['name'].limit).to eq 50 end it "should return true on nullable field" do expect(Employee.columns_hash['salary'].null).to be true end it "should return false on non-nullable field" do expect(Employee.columns_hash['name'].null).to be false end it "should return the appropriate primary key" do expect( ActiveRecord::Base.connection.primary_key('employees') ).to eq 'id' end it "should return a nil primary key on habtm" do expect( ActiveRecord::Base.connection.primary_key('employees_widgets') ).to eq nil end it "should return an empty array of columns for a table-less model" do expect( TablelessModel.columns).to eq [] end it "should enable simulated saving of AR objects" do expect{ @employee.save! }.to_not raise_error end it "should enable AR callbacks during simulated save" do expect( @employee ).to receive :on_save_finished @employee.save end it "should enable simulated deletes of AR objects" do expect{ @employee.destroy }.to_not raise_error end it "should enable simulated creates of AR objects" do emp = Employee.create(:name => "Bob Jones") expect( emp.name ).to eq "Bob Jones" end it "should generate new IDs when inserting unsaved objects" do cxn = Employee.connection id1 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil) id2 = cxn.insert("some sql", "SomeClass Create", "id", nil, nil) expect( id2 ).to eq (id1 + 1) end it "should re-use object ID when inserting saved objects" do cxn = Employee.connection id1 = cxn.insert("some sql", "SomeClass Create", "id", 23, nil) expect( id1 ).to eq 23 end it "should log executed SQL statements" do cxn = Employee.connection exec_count = cxn.execution_log.size @employee.save! expect( cxn.execution_log.size ).to eq (exec_count + 1) end it "should have the adapter name 'NullDB'" do expect( Employee.connection.adapter_name ).to eq "NullDB" end it "should support migrations" do expect( Employee.connection.supports_migrations? ).to eq true end it "should always have a schema_info table definition" do expect( Employee.connection.tables ).to include "schema_info" end it "should return an empty array from #select" do result = Employee.connection.select_all("who cares", "blah") expect( result ).to eq [] end it "should provide a way to set log checkpoints" do cxn = Employee.connection @employee.save! expect( cxn.execution_log_since_checkpoint.size ).to be > 0 cxn.checkpoint! expect( cxn.execution_log_since_checkpoint.size ).to eq 0 @employee.salary = @employee.salary + 1 @employee.save! expect( cxn.execution_log_since_checkpoint.size ).to eq 1 end def should_contain_statement(cxn, entry_point) expect( cxn.execution_log_since_checkpoint).to \ include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point)) end def should_not_contain_statement(cxn, entry_point) expect( cxn.execution_log_since_checkpoint ).to_not \ include(ActiveRecord::ConnectionAdapters::NullDBAdapter::Statement.new(entry_point)) end it "should tag logged statements with their entry point" do cxn = Employee.connection should_not_contain_statement(cxn, :insert) @employee.save should_contain_statement(cxn, :insert) cxn.checkpoint! should_not_contain_statement(cxn, :update) @employee.salary = @employee.salary + 1 @employee.save should_contain_statement(cxn, :update) cxn.checkpoint! should_not_contain_statement(cxn, :delete) @employee.destroy should_contain_statement(cxn, :delete) cxn.checkpoint! should_not_contain_statement(cxn, :select_all) Employee.all.each do |emp|; end should_contain_statement(cxn, :select_all) cxn.checkpoint! should_not_contain_statement(cxn, :select_value) Employee.count_by_sql("frobozz") should_contain_statement(cxn, :select_value) cxn.checkpoint! should_not_contain_statement(cxn, :select_values) cxn.select_values("") should_contain_statement(cxn, :select_values) end it "should allow #finish to be called on the result of #execute" do Employee.connection.execute("blah").finish end it "should #to_a return empty array on the result of #execute" do result = Employee.connection.execute("blah") expect( result.to_a ).to be_a Array expect( result.to_a ).to be_empty end def should_have_column(klass, col_name, col_type) col = klass.columns_hash[col_name.to_s] expect(col.sql_type.to_s.gsub(/\([0-9]+\)/, "").to_sym).to eq col_type end it "should support adding indexes" do expect( Employee.connection.indexes('employees').size ).to eq 2 expect( Employee.connection.indexes('employees_widgets').size ).to eq 1 end it "should support unique indexes" do expect( Employee.connection.indexes('employees').detect{|idx| idx.columns == ["name"]}.unique ).to eq false expect( Employee.connection.indexes('employees').detect{|idx| idx.columns == ["employee_number"]}.unique ).to eq true end it "should support multi-column indexes" do expect( Employee.connection.indexes('employees_widgets').first.columns).to eq ["employee_id", "widget_id"] end it "should support custom index names" do expect( Employee.connection.indexes('employees_widgets').first.name ).to eq 'my_index' end it 'should handle ActiveRecord::ConnectionNotEstablished' do expect( ActiveRecord::Base ).to receive(:connection_pool).and_raise(ActiveRecord::ConnectionNotEstablished) expect { NullDB.nullify }.to_not raise_error end it 'should handle count queries' do expect(Employee.count).to eql(0) end end # need a fallback db for contextual nullification ActiveRecord::Base.configurations['test'] = {'adapter' => 'nulldb'} describe NullDB::RSpec::NullifiedDatabase do describe 'have_executed rspec matcher' do before(:all) do ActiveRecord::Schema.define do create_table(:employees) end end include NullDB::RSpec::NullifiedDatabase before { NullDB.checkpoint } it 'passes if an execution was made' do expect( Employee.connection ).to receive(:insert) allow( Kernel ).to receive :load Employee.create end end describe '.globally_nullify_database' do it 'nullifies the database' do expect( NullDB::RSpec::NullifiedDatabase ).to respond_to(:nullify_database) expect( NullDB::RSpec::NullifiedDatabase ).to receive(:nullify_database) NullDB::RSpec::NullifiedDatabase.globally_nullify_database end end end describe 'adapter-specific extensions' do before(:all) do ActiveRecord::Base.establish_connection :adapter => :nulldb ActiveRecord::Migration.verbose = false end it "supports 'enable_extension' in the schema definition" do expect{ ActiveRecord::Schema.define do enable_extension "plpgsql" end }.to_not raise_error end if ActiveRecord::VERSION::MAJOR > 4 it 'registers a primary_key type' do expect(ActiveRecord::Type.lookup(:primary_key, adapter: 'NullDB')) .to be_a(ActiveModel::Type::Integer) end end end describe ActiveRecord::ConnectionAdapters::NullDBAdapter::EmptyResult do it "should return an empty array from #cast_values" do result = described_class.new expect( result.cast_values ).to be_a Array expect( result.cast_values ).to be_empty end end activerecord-nulldb-adapter-0.4.0/Appraisals0000644000004100000410000000133013516557202021136 0ustar www-datawww-dataappraise "activerecord-2.3" do gem 'iconv', :platforms => :ruby gem "activerecord", "~> 2.3.0" end appraise "activerecord-3.0" do gem "activerecord", "~> 3.0.0" end appraise "activerecord-3.1" do gem "activerecord", "~> 3.1.0" end appraise "activerecord-3.2" do gem "activerecord", "~> 3.2.0" end appraise "activerecord-4.0" do gem "activerecord", "~> 4.0.0" end appraise "activerecord-4.1" do gem "activerecord", "~> 4.1.0" end appraise "activerecord-4.2" do gem "activerecord", "~> 4.2.0" end appraise "activerecord-5.0" do gem "activerecord", "~> 5.0.0" end appraise "activerecord-5.1" do gem "activerecord", "~> 5.1.0" end appraise "activerecord-6.0" do gem "activerecord", "~> 6.0.0.rc1" end activerecord-nulldb-adapter-0.4.0/.gitignore0000644000004100000410000000027413516557202021112 0ustar www-datawww-data# generated coverage coverage coverage.data # docs rdoc doc .yardoc # bundler .bundle *.lock # gem pkg # mac os specific .DS_Store # vim *.swp # rvm .rvmrc .ruby-* # ginger .ginger activerecord-nulldb-adapter-0.4.0/LICENSE0000644000004100000410000000205713516557202020130 0ustar www-datawww-dataThe MIT License Copyright (c) 2008 Avdi Grimm Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. activerecord-nulldb-adapter-0.4.0/Rakefile0000644000004100000410000000044613516557202020570 0ustar www-datawww-datarequire 'rubygems' require 'rake' require 'rspec/core/rake_task' require 'bundler/gem_tasks' RSpec::Core::RakeTask.new(:spec) task :default => :spec require 'rdoc/task' Rake::RDocTask.new do |rd| rd.main = "README.rdoc" rd.rdoc_files.include("README.rdoc", "LICENSE", "lib/**/*.rb") end activerecord-nulldb-adapter-0.4.0/lib/0000755000004100000410000000000013516557202017665 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/nulldb.rb0000644000004100000410000000005513516557202021472 0ustar www-datawww-datarequire 'nulldb/core' require 'nulldb/rails' activerecord-nulldb-adapter-0.4.0/lib/tasks/0000755000004100000410000000000013516557202021012 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/tasks/database.rake0000644000004100000410000000145413516557202023426 0ustar www-datawww-data# Sadly, we have to monkeypatch Rake because all of the Rails database tasks are # hardcoded for specific adapters, with no extension points (!) Rake::TaskManager.class_eval do def remove_task(task_name) @tasks.delete(task_name.to_s) end end def remove_task(task_name) Rake.application.remove_task(task_name) end def wrap_task(task_name, &wrapper) wrapped_task = Rake::Task[task_name] remove_task(Rake::Task.scope_name(Rake.application.current_scope, task_name)) task(task_name) do wrapper.call(wrapped_task) end end namespace :db do namespace :test do wrap_task :purge do |wrapped_task| if ActiveRecord::Base.configurations["test"]["adapter"] == "nulldb" # NO-OP else wrapped_task.invoke end end end end activerecord-nulldb-adapter-0.4.0/lib/activerecord-nulldb-adapter.rb0000644000004100000410000000002113516557202025551 0ustar www-datawww-datarequire 'nulldb' activerecord-nulldb-adapter-0.4.0/lib/nulldb/0000755000004100000410000000000013516557202021145 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/nulldb/version.rb0000644000004100000410000000004613516557202023157 0ustar www-datawww-datamodule NullDB VERSION = "0.4.0" end activerecord-nulldb-adapter-0.4.0/lib/nulldb/arel_compiler.rb0000644000004100000410000000013613516557202024307 0ustar www-datawww-datamodule Arel module SqlCompiler class NullDBCompiler < GenericCompiler end end end activerecord-nulldb-adapter-0.4.0/lib/nulldb/rails.rb0000644000004100000410000000045413516557202022607 0ustar www-datawww-datarequire 'nulldb/core' # Need to defer calling Rails.root because when bundler loads, Rails.root is nil NullDB.configure {|ndb| def ndb.project_root;Rails.root;end} ActiveRecord::Tasks::DatabaseTasks.register_task(/nulldb/, ActiveRecord::Tasks::NullDBDatabaseTasks) if defined?(ActiveRecord::Tasks) activerecord-nulldb-adapter-0.4.0/lib/nulldb/extensions.rb0000644000004100000410000000103413516557202023667 0ustar www-datawww-dataunless respond_to?(:tap) class Object def tap yield self self end end end unless respond_to?(:try) class Object def try(*a, &b) if a.empty? && block_given? yield self else __send__(*a, &b) end end end class NilClass def try(*args); nil; end end end class ActiveRecord::Base # Instantiate a new NullDB connection. Used by ActiveRecord internally. def self.nulldb_connection(config) ActiveRecord::ConnectionAdapters::NullDBAdapter.new(config) end endactiverecord-nulldb-adapter-0.4.0/lib/nulldb/core.rb0000644000004100000410000000203713516557202022424 0ustar www-datawww-datarequire 'active_support' require 'active_support/deprecation' require 'active_record/connection_adapters/nulldb_adapter' module NullDB LEGACY_ACTIVERECORD = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.2.0') class Configuration < Struct.new(:project_root); end class << self def configure @configuration = Configuration.new.tap {|c| yield c} end def configuration if @configuration.nil? raise "NullDB not configured. Require a framework, ex 'nulldb/rails'" end @configuration end def nullify(options={}) begin @prev_connection = ActiveRecord::Base.connection_pool.try(:spec) rescue ActiveRecord::ConnectionNotEstablished end ActiveRecord::Base.establish_connection(options.merge(:adapter => :nulldb)) end def restore if @prev_connection ActiveRecord::Base.establish_connection(@prev_connection.config) end end def checkpoint ActiveRecord::Base.connection.checkpoint! end end end activerecord-nulldb-adapter-0.4.0/lib/active_record/0000755000004100000410000000000013516557202022476 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/active_record/tasks/0000755000004100000410000000000013516557202023623 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/active_record/tasks/nulldb_database_tasks.rb0000644000004100000410000000064613516557202030467 0ustar www-datawww-dataclass ActiveRecord::Tasks::NullDBDatabaseTasks def initialize(configuration) @configuration = configuration end def create(master_established = false) # NO-OP end def drop # NO-OP end def purge # NO-OP end def structure_dump(filename, extra_flags) # NO-OP end def structure_load(filename, extra_flags) # NO-OP end def clear_active_connections! # NO-OP end end activerecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/0000755000004100000410000000000013516557202026520 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/0000755000004100000410000000000013516557202031500 5ustar www-datawww-dataactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/column.rb0000644000004100000410000000057513516557202033331 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class Column < ::ActiveRecord::ConnectionAdapters::Column private def simplified_type(field_type) super || simplified_type_from_sql_type end def simplified_type_from_sql_type case sql_type when :primary_key :integer when :string :string end end end end ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/index_definition.rbactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/index_definit0000644000004100000410000000023313516557202034232 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders); end end ././@LongLink0000644000000000000000000000015000000000000011577 Lustar rootrootactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/configuration.rbactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/configuration0000644000004100000410000000016313516557202034272 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class Configuration < Struct.new(:project_root); end end activerecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/statement.rb0000644000004100000410000000045513516557202034035 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class Statement attr_reader :entry_point, :content def initialize(entry_point, content = "") @entry_point, @content = entry_point, content end def ==(other) self.entry_point == other.entry_point end end end ././@LongLink0000644000000000000000000000015300000000000011602 Lustar rootrootactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/table_definition.rbactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/table_definit0000644000004100000410000000020213516557202034206 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter TableDefinition = ActiveRecord::ConnectionAdapters::TableDefinition end ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/null_object.rbactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/null_object.r0000644000004100000410000000025413516557202034164 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class NullObject def method_missing(*args, &block) nil end def to_a [] end end end ././@LongLink0000644000000000000000000000014700000000000011605 Lustar rootrootactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/empty_result.rbactiverecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/empty_result.0000644000004100000410000000124313516557202034235 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class EmptyResult < Array attr_reader :column_types def bind_column_meta(columns) @columns = columns return if columns.empty? @column_types = columns.reduce({}) do |ctypes, col| ctypes[col.name] = ActiveRecord::Type.lookup(col.type) ctypes end end def columns @columns ||= [] end def column_types @column_types ||= {} end def cast_values(type_overrides = nil) rows end def rows [] end def >(num) rows.size > num end def includes_column?(name) false end end end activerecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/core.rb0000644000004100000410000002434613516557202032766 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter < ActiveRecord::ConnectionAdapters::AbstractAdapter # A convenience method for integratinginto RSpec. See README for example of # use. def self.insinuate_into_spec(config) config.before :all do ActiveRecord::Base.establish_connection(:adapter => :nulldb) end config.after :all do ActiveRecord::Base.establish_connection(:test) end end # Recognized options: # # [+:schema+] path to the schema file, relative to Rails.root # [+:table_definition_class_name+] table definition class # (e.g. ActiveRecord::ConnectionAdapters::PostgreSQL::TableDefinition for Postgres) or nil. def initialize(config={}) @log = StringIO.new @logger = Logger.new(@log) @last_unique_id = 0 @tables = {'schema_info' => new_table_definition(nil)} @indexes = Hash.new { |hash, key| hash[key] = [] } @schema_path = config.fetch(:schema){ "db/schema.rb" } @config = config.merge(:adapter => :nulldb) super *initialize_args @visitor ||= Arel::Visitors::ToSql.new self if defined?(Arel::Visitors::ToSql) if config[:table_definition_class_name] ActiveRecord::ConnectionAdapters::NullDBAdapter.send(:remove_const, 'TableDefinition') ActiveRecord::ConnectionAdapters::NullDBAdapter.const_set('TableDefinition', self.class.const_get(config[:table_definition_class_name])) end register_types unless NullDB::LEGACY_ACTIVERECORD || \ ActiveRecord::VERSION::MAJOR < 4 end # A log of every statement that has been "executed" by this connection adapter # instance. def execution_log (@execution_log ||= []) end # A log of every statement that has been "executed" since the last time # #checkpoint! was called, or since the connection was created. def execution_log_since_checkpoint checkpoint_index = @execution_log.rindex(Checkpoint.new) checkpoint_index = checkpoint_index ? checkpoint_index + 1 : 0 @execution_log[(checkpoint_index..-1)] end # Inserts a checkpoint in the log. See also #execution_log_since_checkpoint. def checkpoint! self.execution_log << Checkpoint.new end def adapter_name "NullDB" end def supports_migrations? true end def create_table(table_name, options = {}) table_definition = new_table_definition(self, table_name, options.delete(:temporary), options) unless options[:id] == false table_definition.primary_key(options[:primary_key] || "id") end yield table_definition if block_given? @tables[table_name] = table_definition end def add_index(table_name, column_names, options = {}) column_names = Array.wrap(column_names).map(&:to_s) index_name, index_type, ignore = add_index_options(table_name, column_names, options) @indexes[table_name] << IndexDefinition.new(table_name, index_name, (index_type == 'UNIQUE'), column_names, [], []) end unless instance_methods.include? :add_index_options def add_index_options(table_name, column_name, options = {}) column_names = Array.wrap(column_name) index_name = index_name(table_name, :column => column_names) if Hash === options # legacy support, since this param was a string index_type = options[:unique] ? "UNIQUE" : "" index_name = options[:name].to_s if options.key?(:name) else index_type = options end if index_name.length > index_name_length raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters" end if index_name_exists?(table_name, index_name, false) raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists" end index_columns = quoted_columns_for_index(column_names, options).join(", ") [index_name, index_type, index_columns] end end unless instance_methods.include? :index_name_exists? def index_name_exists?(table_name, index_name, default) return default unless respond_to?(:indexes) index_name = index_name.to_s indexes(table_name).detect { |i| i.name == index_name } end end def add_fk_constraint(*args) # NOOP end def add_pk_constraint(*args) # NOOP end def enable_extension(*) # NOOP end # Retrieve the table names defined by the schema def tables @tables.keys.map(&:to_s) end def views [] # TODO: Implement properly if needed - This is new method in rails end # Retrieve table columns as defined by the schema def columns(table_name, name = nil) if @tables.size <= 1 ActiveRecord::Migration.verbose = false schema_path = if Pathname(@schema_path).absolute? @schema_path else File.join(NullDB.configuration.project_root, @schema_path) end Kernel.load(schema_path) end if table = @tables[table_name] table.columns.map do |col_def| col_args = new_column_arguments(col_def) ActiveRecord::ConnectionAdapters::NullDBAdapter::Column.new(*col_args) end else [] end end # Retrieve table indexes as defined by the schema def indexes(table_name, name = nil) @indexes[table_name] end def execute(statement, name = nil) self.execution_log << Statement.new(entry_point, statement) NullObject.new end def exec_query(statement, name = 'SQL', binds = [], options = {}) self.execution_log << Statement.new(entry_point, statement) EmptyResult.new end def select_rows(statement, name = nil, binds = []) [].tap do self.execution_log << Statement.new(entry_point, statement) end end def insert(statement, name = nil, primary_key = nil, object_id = nil, sequence_name = nil, binds = []) (object_id || next_unique_id).tap do with_entry_point(:insert) do super(statement, name, primary_key, object_id, sequence_name) end end end alias :create :insert def update(statement, name=nil, binds = []) with_entry_point(:update) do super(statement, name) end end def delete(statement, name=nil, binds = []) with_entry_point(:delete) do super(statement, name).size end end def select_all(statement, name=nil, binds = [], options = {}) with_entry_point(:select_all) do super(statement, name) end end def select_one(statement, name=nil, binds = []) with_entry_point(:select_one) do super(statement, name) end end def select_value(statement, name=nil, binds = []) with_entry_point(:select_value) do super(statement, name) end end def select_values(statement, name=nil) with_entry_point(:select_values) do super(statement, name) end end def primary_key(table_name) columns(table_name).detect { |col| col.sql_type == :primary_key }.try(:name) end protected def select(statement, name = nil, binds = []) EmptyResult.new.tap do |r| r.bind_column_meta(columns_for(name)) self.execution_log << Statement.new(entry_point, statement) end end private def columns_for(table_name) table_meta = @tables[table_name] return [] unless table_meta table_meta.columns end def next_unique_id @last_unique_id += 1 end def with_entry_point(method) if entry_point.nil? with_thread_local_variable(:entry_point, method) do yield end else yield end end def entry_point Thread.current[:entry_point] end def with_thread_local_variable(name, value) old_value = Thread.current[name] Thread.current[name] = value begin yield ensure Thread.current[name] = old_value end end def includes_column? false end def new_table_definition(adapter = nil, table_name = nil, is_temporary = nil, options = {}) case ::ActiveRecord::VERSION::MAJOR when 6 TableDefinition.new(self, table_name, temporary: is_temporary, options: options.except(:id)) when 5 TableDefinition.new(table_name, is_temporary, options.except(:id), nil) when 4 TableDefinition.new(native_database_types, table_name, is_temporary, options) when 2,3 TableDefinition.new(adapter) else raise "Unsupported ActiveRecord version #{::ActiveRecord::VERSION::STRING}" end end def new_column_arguments(col_def) args_with_optional_cast_type(col_def) end def args_with_optional_cast_type(col_def) default_column_arguments(col_def).tap do |args| if defined?(ActiveRecord::ConnectionAdapters::SqlTypeMetadata) meta = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: col_def.type) args.insert(2, meta_with_limit!(meta, col_def)) elsif initialize_column_with_cast_type? args.insert(2, meta_with_limit!(lookup_cast_type(col_def.type), col_def)) else args[2] = args[2].to_s + "(#{col_def.limit})" if col_def.limit end end end def meta_with_limit!(meta, col_def) meta.instance_variable_set('@limit', col_def.limit) meta end def default_column_arguments(col_def) if ActiveRecord::VERSION::MAJOR >= 5 [ col_def.name.to_s, col_def.default, col_def.null.nil? || col_def.null # cast [false, nil, true] => [false, true, true], other adapters default to null=true ] else [ col_def.name.to_s, col_def.default, col_def.type, col_def.null.nil? || col_def.null # cast [false, nil, true] => [false, true, true], other adapters default to null=true ] end end def initialize_column_with_cast_type? ::ActiveRecord::VERSION::MAJOR == 4 && ::ActiveRecord::VERSION::MINOR >= 2 end def initialize_args return [nil, @logger, @config] if ActiveRecord::VERSION::MAJOR > 3 [nil, @logger] end # 4.2 introduced ActiveRecord::Type # https://github.com/rails/rails/tree/4-2-stable/activerecord/lib/active_record def register_types if ActiveRecord::VERSION::MAJOR < 5 type_map.register_type(:primary_key, ActiveRecord::Type::Integer.new) else require 'active_model/type' ActiveRecord::Type.register( :primary_key, ActiveModel::Type::Integer, adapter: adapter_name, override: true ) end end end activerecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter/checkpoint.rb0000644000004100000410000000032413516557202034153 0ustar www-datawww-dataclass ActiveRecord::ConnectionAdapters::NullDBAdapter class Checkpoint < Statement def initialize super(:checkpoint, "") end def ==(other) self.class == other.class end end end activerecord-nulldb-adapter-0.4.0/lib/active_record/connection_adapters/nulldb_adapter.rb0000644000004100000410000000165113516557202032030 0ustar www-datawww-datarequire 'logger' require 'stringio' require 'singleton' require 'pathname' require 'active_support' require 'active_record/connection_adapters/abstract_adapter' require 'nulldb/core' require 'nulldb/extensions' require 'active_record/connection_adapters/nulldb_adapter/core' require 'active_record/connection_adapters/nulldb_adapter/statement' require 'active_record/connection_adapters/nulldb_adapter/checkpoint' require 'active_record/connection_adapters/nulldb_adapter/column' require 'active_record/connection_adapters/nulldb_adapter/configuration' require 'active_record/connection_adapters/nulldb_adapter/empty_result' require 'active_record/connection_adapters/nulldb_adapter/index_definition' require 'active_record/connection_adapters/nulldb_adapter/null_object' require 'active_record/connection_adapters/nulldb_adapter/table_definition' require 'active_record/tasks/nulldb_database_tasks' if defined?(ActiveRecord::Tasks) activerecord-nulldb-adapter-0.4.0/lib/nulldb_rspec.rb0000644000004100000410000000454413516557202022675 0ustar www-datawww-datarequire 'active_record/connection_adapters/nulldb_adapter' module NullDB module RSpec end end module NullDB::RSpec::NullifiedDatabase NullDBAdapter = ActiveRecord::ConnectionAdapters::NullDBAdapter class HaveExecuted def initialize(entry_point) @entry_point = entry_point end def matches?(connection) log = connection.execution_log_since_checkpoint if @entry_point == :anything not log.empty? else log.include?(NullDBAdapter::Statement.new(@entry_point)) end end def description "connection should execute #{@entry_point} statement" end def failure_message " did not execute #{@entry_point} statement when it should have" end def negative_failure_message " executed #{@entry_point} statement when it should not have" end end def self.globally_nullify_database block = lambda { |config| nullify_database(config) } if defined?(RSpec) RSpec.configure(&block) else Spec::Runner.configure(&block) end end def self.contextually_nullify_database(context) nullify_database(context) end # A matcher for asserting that database statements have (or have not) been # executed. Usage: # # ActiveRecord::Base.connection.should have_executed(:insert) # # The types of statement that can be matched mostly mirror the public # operations available in # ActiveRecord::ConnectionAdapters::DatabaseStatements: # - :select_one # - :select_all # - :select_value # - :insert # - :update # - :delete # - :execute # # There is also a special :anything symbol that will match any operation. def have_executed(entry_point) HaveExecuted.new(entry_point) end private def self.included(other) if nullify_contextually?(other) contextually_nullify_database(other) else globally_nullify_database end end def self.nullify_contextually?(other) if defined?(RSpec) other < RSpec::Core::ExampleGroup else other.is_a? Spec::ExampleGroup end end def self.nullify_database(receiver) receiver.before :all do ActiveRecord::Base.establish_connection(:adapter => :nulldb) end receiver.before :each do ActiveRecord::Base.connection.checkpoint! end receiver.after :all do ActiveRecord::Base.establish_connection(:test) end end end activerecord-nulldb-adapter-0.4.0/README.rdoc0000644000004100000410000001173113516557202020730 0ustar www-datawww-data{Gem Version}[http://badge.fury.io/rb/activerecord-nulldb-adapter] {}[https://codeclimate.com/github/nulldb/nulldb] {Build Status}[https://travis-ci.org/nulldb/nulldb] = The NullDB Connection Adapter Plugin == What NullDB is the Null Object pattern as applied to ActiveRecord database adapters. It is a database backend that translates database interactions into no-ops. Using NullDB enables you to test your model business logic - including +after_save+ hooks - without ever touching a real database. == Compatibility === Ruby Currently supported Ruby versions: MRI 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x Experimental support provided for: JRuby, Rubinius === ActiveRecord Any version of ActiveRecord since 2.0, including ActiveRecord 5.0 It is tested against AR 2.3, 3.0, 3.1, 3.2, 4.0, 4.1, 4.2 and 5.0. == Installation gem install activerecord-nulldb-adapter == How Once installed, NullDB can be used much like any other ActiveRecord database adapter: ActiveRecord::Base.establish_connection :adapter => :nulldb NullDB needs to know where you keep your schema file in order to reflect table metadata. By default it looks in RAILS_ROOT/db/schema.rb. You can override that by setting the +schema+ option: ActiveRecord::Base.establish_connection :adapter => :nulldb, :schema => 'foo/myschema.rb' NullDB comes with RSpec integration. To replace the database with NullDB in all of your specs, put the following in your spec/spec_helper: require 'nulldb_rspec' include NullDB::RSpec::NullifiedDatabase Or if you just want to use NullDB in a specific spec context, you can include the same module inside a context: require 'nulldb_rspec' describe Employee, "with access to the database" do fixtures :employees # ... end describe Employee, "with NullDB" do include NullDB::RSpec::NullifiedDatabase # ... end If you want to have NullDB enabled by default but disabled for particular contexts then (see this post)[https://web.archive.org/web/20120419204019/http://andywaite.com/2011/5/18/rspec-disable-nulldb] NullDB::Rspec provides some custom matcher support for verifying expectations about interactions with the database: describe Employee do include NullDB::RSpec::NullifiedDatabase it "should cause an insert statement to be executed" do Employee.create! Employee.connection.should have_executed(:insert) end end UnitRecord-style verification that no database calls have been made at all can be achieved by using the special +:anything+ symbol: describe "stuff that shouldn't touch the database" do after :each do Employee.connection.should_not have_executed(:anything) end # ... end You can also experiment with putting NullDB in your database.yml: unit_test: adapter: nulldb However, due to the way Rails hard-codes specific database adapters into its standard Rake tasks, you may find that this generates unexpected and difficult-to-debug behavior. Workarounds for this are under development. == Why There are a number of advantages to writing unit tests that never touch the database. The biggest is probably speed of execution - unit tests must be fast for test-driven development to be practical. Another is separation of concerns: unit tests should be exercising only the business logic contained in your models, not ActiveRecord. For more on why testing-sans-database is a good idea, see: http://www.dcmanges.com/blog/rails-unit-record-test-without-the-database. NullDB is one way to separate your unit tests from the database. It was inspired by the ARBS[http://arbs.rubyforge.org/] and UnitRecord[http://unit-test-ar.rubyforge.org/] libraries. It differs from them in that rather than modifying parts of ActiveRecord, it implements the same [semi-]well-documented public interface that the other standard database adapters, like MySQL and SQLServer, implement. This has enabled it to evolve to support new ActiveRecord versions relatively easily. One concrete advantage of this null-object pattern design is that it is possible with NullDB to test +after_save+ hooks. With NullDB, you can call +#save+ and all of the usual callbacks will be called - but nothing will be saved. == Limitations * It is *not* an in-memory database. Finds will not work. Neither will +reload+, currently. Test fixtures won't work either, for obvious reasons. * It has only the most rudimentery schema/migration support. Complex migrations will probably break it. * Lots of other things probably don't work. Patches welcome! == Who NullDB was originally written by Avdi Grimm . It is currently maintained by {Bram de Vries}[https://github.com/blaet]. == Where * Homepage: https://github.com/nulldb/nulldb == License See the LICENSE file for licensing information. activerecord-nulldb-adapter-0.4.0/activerecord-nulldb-adapter.gemspec0000644000004100000410000000215413516557202026034 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'nulldb/version' Gem::Specification.new do |s| s.name = "activerecord-nulldb-adapter" s.version = NullDB::VERSION s.require_paths = ["lib"] s.authors = ["Avdi Grimm", "Myron Marston"] s.summary = "The Null Object pattern as applied to ActiveRecord database adapters" s.description = "A database backend that translates database interactions into no-ops. Using NullDB enables you to test your model business logic - including after_save hooks - without ever touching a real database." s.email = "myron.marston@gmail.com" s.extra_rdoc_files = [ "LICENSE", "README.rdoc" ] s.files = `git ls-files`.split($/) s.homepage = "http://github.com/nulldb/nulldb" s.licenses = ["MIT"] s.add_runtime_dependency 'activerecord', '>= 2.0.0' s.add_development_dependency 'spec' s.add_development_dependency 'rdoc' s.add_development_dependency 'rspec' s.add_development_dependency 'rake' s.add_development_dependency 'appraisal' s.add_development_dependency 'simplecov' end activerecord-nulldb-adapter-0.4.0/Gemfile0000644000004100000410000000036313516557202020414 0ustar www-datawww-datasource "https://rubygems.org" gem 'activerecord', '>= 2.0.0' group :development, :test do gem 'spec' gem 'rspec', '>= 1.2.9' gem 'rake' gem 'rdoc' end group :development do gem 'appraisal' gem 'simplecov', :require => false end activerecord-nulldb-adapter-0.4.0/CHANGES.md0000644000004100000410000000345513516557202020520 0ustar www-datawww-dataUnreleased ---------- 0.4.0 (2019-07-22) ----------- - *Breaking* Drop support to Ruby 1.9 - Add support for ActiveRecord 6.0 #90 - Prevent ActiveRecord::Tasks::DatabaseNotSupported #88 0.3.9 (2018-07-07) ----------- - Fix broken count - Avoid monkey patching Schema.define - Support ruby 2.4 (drop support for ruby 2.1 and rails 3.0/3.1) - Support custom TableDefinition (useful for postgres) 0.3.8 (2018-02-06) ----------- - Adds support for ActiveRecord Edge (6.0) 0.3.7 (2017-06-04) ----------- - Adds support for ActiveRecord 5.1/5.2. - Support limit and null 0.3.6 (2016-11-23) ----------- - Adds support for ActiveRecord 5.0. 0.3.5 (2016-09-26) ----------- - Adds support for #cast_values on EmptyResult instance. 0.3.4 (2016-08-10) ----------- - Adds support for Postgres-specific 'enable_extension' 0.3.3 (2016-08-01) ----------- - Adds support for ActiveRecord 4.2. - Deprecates support for MRI 2.0.0. 0.3.2 (2016-01-25) ----------- - Deprecates support for MRI 1.9.3 and adds support for 2.3.x. - Fixes :string column type fetching for AR 4.1. 0.3.1 (2014-02-17) ----------- - Removes accidental dependency on iconv. Fixing JRuby support. 0.3.0 (2014-01-31) ----------- - Drops 1.8.7 support. - Adds support for Ruby 2.0, 2.1 and ActiveRecord 4. - Fixes ActiveRecord 2.3 support on Ruby 2 and up. - Misc small fixes 0.2.1 (2010-09-01) ----------- - Updated Rails 3 support so that nulldb works against AR 3.0.0. - Add support for RSpec 2. 0.2.0 (2010-03-20) ----------- - Rails 3 support. All specs pass against ActiveRecord 3.0.0.beta. 0.1.1 (2010-03-15) ----------- - Released as activerecord-nulldb-adapter gem. 0.1.0 (2010-03-02) ----------- - Released as nulldb gem, with some bug fixes. 0.0.2 (2007-05-31) ----------- - Moved to Rubyforge 0.0.1 (2007-02-18) ----------- - Initial Release