nogo 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
19
+ .rspec
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nogo.gemspec
4
+ gemspec
@@ -0,0 +1,75 @@
1
+ ### NoGo is a library to find the places in your code which touch your database
2
+ NoGo's main objective is to help find and prevent code which accesses the database during testing. Disconnecting the database completely while running your test suite may be impractical, so NoGo take a different approach by proxying an estabished database connection. It is possible to enable and disable NoGo as well as change between ```:raise```, ```:warn``` and ```:pass_through``` strategies on the fly.
3
+
4
+ ## Installation
5
+ Add nogo to your Gemfile
6
+
7
+ ```gem 'nogo'```
8
+
9
+ Or install from the command line
10
+
11
+ ```gem install nogo```
12
+
13
+ ## Usage
14
+
15
+ NoGo can be used as a standalone library, but it does integrate with the RSpec 2 test framework.
16
+
17
+ ### RSpec
18
+ Wrap any spec examples that should not use the database adapter inside a "nogo do" block.
19
+
20
+ <pre>
21
+ # In your specs
22
+ require 'nogo/rspec' # Automatically connects, so this should be included after the environment is loaded.
23
+
24
+ describe Klass do
25
+ it 'works without error' do
26
+ Klass.create
27
+ end
28
+
29
+ nogo do # Any access to the database inside this block will raise an exception
30
+ it 'raises error' do
31
+ expect{ Klass.create }.to raise_error
32
+ end
33
+ end
34
+
35
+ it 'works without error' do
36
+ Klass.create
37
+ end
38
+ end
39
+ </pre>
40
+
41
+ ### Globally enabling NoGo
42
+ By default NoGo will not check requests to the database, even after being connected. Here is an example of enabling NoGo.
43
+
44
+ <pre>
45
+ require 'nogo'
46
+
47
+ # Database adapter should already be connected
48
+ NoGo::Connection.connect!
49
+
50
+ ModelKlass.create # Works as expected
51
+
52
+ NoGo::Connection.enabled = true
53
+ ModelKlass.create # Raises exception
54
+
55
+ NoGo::Connection.enabled = false
56
+ ModelKlass.create # Works as expected
57
+ </pre>
58
+
59
+ ## Platforms
60
+ Tested with:
61
+
62
+ RSpec 2.7.0, 2.8.0
63
+ ActiveRecord 3.1.3, 3.2.1.
64
+
65
+ ##LICENSE
66
+
67
+ (The MIT License)
68
+
69
+ Copyright © 2012 Andy Ogzewalla
70
+
71
+ 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:
72
+
73
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
74
+
75
+ 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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ # Adds a connection method so that ActiveRecord recognizes the proxy adapter
2
+ module ActiveRecord
3
+ class Base
4
+ # returns an instance of <tt>NoGo::ProxyAdapter</tt> which proxies the adapter instance specified by <tt>config[:target_adapter]</tt>. This method will
5
+ # also attempt to reconnect the proxied adapter.
6
+ def self.nogo_connection(config)
7
+ proxy_adapter = NoGo::ProxyAdapter.new(config[:target_adapter])
8
+ proxy_adapter.proxied_adapter.send :connect # TODO: It would be nice if we could detect whether the adapter was connected before, but for now just attempt to reconnect it. ActiveRecord disconnects the adapter before calling <tt>::nogo_connection</tt> *
9
+ # TODO: This is probably where connection pool config should be set rather than in NoGo::Connection::connect! *
10
+ proxy_adapter
11
+ end
12
+ end
13
+ end
14
+
15
+ require 'active_record/import'
@@ -0,0 +1,13 @@
1
+ if defined?(ActiveRecord::Import)
2
+ ActiveRecord::Import.module_eval do
3
+ class << self # Lulz, I tried `module << self` which didn't work.
4
+ # Catch case where adapter is set to <tt>:nogo</tt>. Activerecord-import hijacks
5
+ # AR::Base#establish_connection, so there was no clean way to avoid having it attempt
6
+ # to require its own nogo adatper, which fails.
7
+ def require_adapter_with_nogo(adapter)
8
+ require_adapter_without_nogo(adapter) unless adapter==:nogo
9
+ end
10
+ alias_method_chain :require_adapter, :nogo
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ require 'nogo/version'
2
+ require 'nogo/abstract_method_overrides'
3
+ require 'nogo/proxy_adapter'
4
+ require 'nogo/connection'
@@ -0,0 +1,78 @@
1
+ module NoGo
2
+ # Contains overrides for methods which are defined in ActiveRecord::ConnectionAdapters::AbstractAdapter and are
3
+ # ovenrridden here as a way to hook into ActiveRecord before requests access the database connection.
4
+ module AbstractMethodOverrides
5
+
6
+ def execute(sql, name = nil)
7
+ if sql.match /BEGIN|ROLLBACK|SAVEPOINT/ # TODO: There should be a better way to avoid transactions *
8
+ pass_through(:execute, sql, name)
9
+ else
10
+ raise_or_pass_through(:execute, sql, name)
11
+ end
12
+ end
13
+
14
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
15
+ raise_or_pass_through(:insert, sql, name, pk, id_value, sequence_name, binds)
16
+ end
17
+
18
+ def update(sql, name = nil, binds = [])
19
+ raise_or_pass_through(:update, sql, name, binds)
20
+ end
21
+
22
+ def delete(sql, name = nil, binds = [])
23
+ raise_or_pass_through(:delete, sql, name, binds)
24
+ end
25
+
26
+ def select_rows(sql, name = nil)
27
+ raise_or_pass_through(:select_rows, sql, name)
28
+ end
29
+
30
+ def select(sql, name = nil)
31
+ raise_or_pass_through(:select, sql, name)
32
+ end
33
+
34
+ def select_all(sql, name = nil, binds = [])
35
+ raise_or_pass_through(:select_all, sql, name, binds)
36
+ end
37
+
38
+ def select_one(sql, name = nil)
39
+ raise_or_pass_through(:select_one, sql, name)
40
+ end
41
+
42
+ def select_value(sql, name = nil)
43
+ raise_or_pass_through(:select_value, sql, name)
44
+ end
45
+
46
+ def select_values(sql, name = nil)
47
+ raise_or_pass_through(:select_values, sql, name)
48
+ end
49
+
50
+ def exec_query(sql, name = nil, binds = [])
51
+ raise_or_pass_through(:exec_query, sql, name, binds)
52
+ end
53
+
54
+ def exec_insert(sql, name = nil, binds = [])
55
+ raise_or_pass_through(:exec_insert, sql, name, binds)
56
+ end
57
+
58
+ def exec_delete(sql, name = nil, binds = [])
59
+ raise_or_pass_through(:exec_delete, sql, name, binds)
60
+ end
61
+
62
+ def exec_update(sql, name = nil, binds = [])
63
+ raise_or_pass_through(:exec_update, sql, name, binds)
64
+ end
65
+
66
+ def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
67
+ raise_or_pass_through(:insert_sql, sql, name, pk, id_value, sequence_name, binds)
68
+ end
69
+
70
+ def update_sql(sql, name = nil)
71
+ raise_or_pass_through(:update_sql, sql, name)
72
+ end
73
+
74
+ def delete_sql(sql, name = nil)
75
+ raise_or_pass_through(:delete_sql, sql, name)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,47 @@
1
+ module NoGo
2
+ class Connection
3
+ @@proxy_adapter = nil
4
+
5
+ # Returns proxy adapter if connected, otherwise raises an error
6
+ def self.proxy_adapter
7
+ @@proxy_adapter || raise('Proxy adapter is not connected. Please run NoGo::Connection.connect! to first establish a connection.')
8
+ end
9
+
10
+ # Proxy an existing connection. Raises an exception if no database
11
+ # connection has been established.
12
+ def self.connect!
13
+ # TODO: Abstract away ORM-specific code *
14
+ original_adapter = ActiveRecord::Base.connection_pool.spec.config[:adapter]
15
+ ActiveRecord::Base.establish_connection :adapter => :nogo, :target_adapter => ActiveRecord::Base.connection
16
+
17
+ # After establishing connection the connection pool config adapter will be set to <tt>:nogo</tt> which
18
+ # may cause problems with gems such as activerecord-import which rely on that value to be set to a
19
+ # standard ActiveRecord database adapter. Fortunately we can easily change the config setting back to
20
+ # its original value.
21
+ ActiveRecord::Base.connection_pool.spec.config[:adapter] = original_adapter
22
+
23
+ @@proxy_adapter = ActiveRecord::Base.connection
24
+ end
25
+
26
+ # Returns true or false to indicate whether a proxy adapter has been connected.
27
+ def self.connected?
28
+ !!@@proxy_adapter
29
+ end
30
+
31
+ def self.enabled=(value)
32
+ proxy_adapter.enabled = value
33
+ end
34
+
35
+ def self.pop_enabled_state
36
+ proxy_adapter.pop_enabled_state
37
+ end
38
+
39
+ def self.push_enabled_state
40
+ proxy_adapter.push_enabled_state
41
+ end
42
+
43
+ def self.strategy=(strategy)
44
+ proxy_adapter.strategy = strategy
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,90 @@
1
+ module NoGo
2
+ class ProxyAdapter
3
+ # Valid values for <tt>@strategy</tt> and arguments when setting <tt>strategy</tt>
4
+ StrategyOptions = [:raise, :warn, :pass_through].freeze
5
+
6
+ ErrorMessageForRaiseStrategy = <<-EOM.freeze
7
+ Access to the database adapter is currently restricted (strategy set to :raise).
8
+ Set strategy to :pass_through or :warn to allow database access. See NoGo::Connection#strategy=.
9
+ EOM
10
+
11
+ # Most methods calls should simply be passed on to the proxied adapter. By undefining most methods we can use
12
+ # <tt>method_missing</tt> to accomplish this.
13
+ instance_methods.each do |method_name|
14
+ undef_method method_name unless method_name =~ /^__|^send$|^object_id$|^extend|^tap|^instance_variable_set|^instance_variable_get/
15
+ end
16
+
17
+ attr_accessor :enabled
18
+
19
+ # Include overriden AbstractAdapter methods
20
+ include NoGo::AbstractMethodOverrides
21
+
22
+ # Initializes an instance and sets the proxied adapter and default strategy. An exception is raised if the argument to <tt>adapter</tt> is
23
+ # not an instance of <tt>ActiveRecord::ConnectionAdapters::AbstractAdapter</tt>.
24
+ def initialize(adapter)
25
+ raise ArgumentError.new(
26
+ "Expected an instance of ActiveRecord::ConnectionAdapters::AbstractAdapter, but received #{adapter.class.name}"
27
+ ) unless adapter.is_a?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
28
+
29
+ @adapter = adapter
30
+ @strategy = :raise
31
+ self.enabled = false
32
+ @enabled_state_stack = []
33
+ end
34
+
35
+ # Pops the last value from <tt>enabled_state_stack</tt> and assigns it to <tt>enabled</tt>.
36
+ def pop_enabled_state
37
+ self.enabled = @enabled_state_stack.pop || false
38
+ end
39
+
40
+ # Pushes the current value of <tt>enabled</tt> onto <tt>enabled_state_stack</tt>.
41
+ def push_enabled_state
42
+ @enabled_state_stack.push(enabled)
43
+ end
44
+
45
+ # Returns the adapter which is being proxied by the current object.
46
+ def proxied_adapter
47
+ @adapter
48
+ end
49
+
50
+ # Returns the current strategy assigned to this adapter, which can any of the <tt>StrategyOptions</tt>.
51
+ def strategy
52
+ @strategy
53
+ end
54
+
55
+ # Sets the current strategy for this adapter. Raises an <tt>ArgumentErrer</tt> if <tt>strategy_option</tt> is not
56
+ # a value from <tt>StrategyOptions</tt>
57
+ def strategy=(strategy_option)
58
+ raise ArgumentError.new(
59
+ "Expected strategy to be set to one of [#{StrategyOptions.map{|opt| ":#{opt}"}.join(', ')}], but received #{strategy_option}"
60
+ ) unless StrategyOptions.include?(strategy_option.to_sym)
61
+
62
+ @strategy = strategy_option
63
+ end
64
+
65
+ private
66
+
67
+ # Pass through any undefined instance method calls.
68
+ def method_missing(method_name, *args, &block) # :doc:
69
+ pass_through(method_name, *args, &block)
70
+ end
71
+
72
+ # Passes a method call to the proxied adapter.
73
+ # If the strategy is set to <tt>:warn</tt> then <tt>#warn</tt> will be invoked.
74
+ def pass_through(method_name, *args, &block) # :doc:
75
+ warn(method_name, *args, &block) if enabled && @strategy == :warn
76
+ proxied_adapter.send(method_name, *args, &block)
77
+ end
78
+
79
+ # Raises an error if the current strategy is <tt>:raise</tt> and otherwise defers the method call to <tt>#pass_through</tt>.
80
+ def raise_or_pass_through(method_name, *args, &block) # :doc:
81
+ raise ErrorMessageForRaiseStrategy if enabled && @strategy == :raise
82
+ pass_through(method_name, *args, &block)
83
+ end
84
+
85
+ # Placeholder for <tt>#warn</tt> method. This is probably a place to trigger hooks once they are supported
86
+ def warn(method_name, *args, &block) # :doc:
87
+ puts "\nDatabase adapter accessed from: " + Kernel.caller[0..12].join("\n")
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,4 @@
1
+ require_relative '../nogo'
2
+ require_relative 'rspec/dsl'
3
+
4
+ NoGo::Connection::connect!
@@ -0,0 +1,31 @@
1
+ require 'rspec'
2
+
3
+ module NoGo
4
+ module RSpec
5
+ class DSL
6
+ NogoBeforeAllBlock = Proc.new do
7
+ NoGo::Connection.push_enabled_state
8
+ NoGo::Connection.enabled = true
9
+ end.freeze
10
+ NogoAfterAllBlock = Proc.new do
11
+ NoGo::Connection.pop_enabled_state
12
+ end.freeze
13
+
14
+ def self.nogo_example_group(*args, &example_group_block)
15
+ ::RSpec::Core::ExampleGroup.describe(*args) do
16
+ before(:all, &NoGo::RSpec::DSL::NogoBeforeAllBlock)
17
+ after(:all, &NoGo::RSpec::DSL::NogoAfterAllBlock)
18
+ instance_eval(&example_group_block)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ # Adds DSL method to RSpec ExampleGroup instances
26
+ RSpec::Core::DSL.module_eval do
27
+ def nogo(*args, &example_group_block)
28
+ NoGo::RSpec::DSL.nogo_example_group(*args, &example_group_block).register
29
+ end
30
+ alias :database_restricted :nogo
31
+ end
@@ -0,0 +1,3 @@
1
+ module NoGo
2
+ VERSION = "1.0.0.beta"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/nogo/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Andy Ogzewalla"]
6
+ gem.email = ["andyogzewalla@gmail.com"]
7
+ gem.description = %q{A library that lets you know when your code touches your database}
8
+ gem.summary = %q{A library that lets you know when your code touches your database}
9
+ gem.homepage = ""
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "nogo"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = NoGo::VERSION
17
+
18
+ gem.add_dependency 'rspec', '~> 2.7.0'
19
+ gem.add_dependency 'activerecord', '~> 3.0'
20
+ gem.add_development_dependency 'rspec', '~> 2.7.0'
21
+ gem.add_development_dependency 'activerecord', '~> 3.0'
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'active_record/connection_adapters/nogo_adapter'
3
+
4
+ describe ActiveRecord::Base do
5
+ describe '::nogo_connection' do
6
+ let(:adapter) { mock }
7
+ let(:nogo_adapter) { mock(:proxied_adapter => adapter) }
8
+
9
+ before :each do
10
+ NoGo::ProxyAdapter.stub(:new).with(adapter).and_return(nogo_adapter)
11
+ end
12
+
13
+ it 'returns new NoGo::ProxyAdapter instance' do
14
+ adapter.stub(:connect)
15
+ ActiveRecord::Base.nogo_connection({:target_adapter => adapter}).should == nogo_adapter
16
+ end
17
+
18
+ it 'reconnects the adapter' do
19
+ adapter.should_receive(:connect)
20
+ ActiveRecord::Base.nogo_connection({:target_adapter => adapter})
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe NoGo::Connection do
4
+ subject { NoGo::Connection }
5
+ let(:proxy_adapter) { mock.as_null_object }
6
+ before :each do
7
+ subject.class_variable_set(:@@proxy_adapter, proxy_adapter)
8
+ end
9
+
10
+ describe '::proxy_adapter' do
11
+ let(:proxy_adapter) {mock}
12
+
13
+ before :each do
14
+ subject.class_variable_set(:@@proxy_adapter, proxy_adapter)
15
+ end
16
+
17
+ it 'raises error if proxy adapter is not connected' do
18
+ subject.class_variable_set(:@@proxy_adapter, nil)
19
+ expect{subject.proxy_adapter}.to raise_error
20
+ end
21
+
22
+ it 'does not raise an error if proxy adapter is connected' do
23
+ subject.class_variable_set(:@@proxy_adapter, proxy_adapter)
24
+ expect{subject.proxy_adapter}.to_not raise_error
25
+ end
26
+
27
+ it 'returns proxy adapter if connected' do
28
+ subject.class_variable_set(:@@proxy_adapter, proxy_adapter)
29
+ subject.proxy_adapter.should == proxy_adapter
30
+ end
31
+ end
32
+
33
+ describe '::connect!' do
34
+ it 'raise an error if ActiveRecord is not connected to a database' do
35
+ ActiveRecord::Base.stub(:connection).and_raise(ActiveRecord::ConnectionNotEstablished.new)
36
+ expect{subject.connect!}.to raise_error(ActiveRecord::ConnectionNotEstablished)
37
+ end
38
+
39
+ context do
40
+ let(:adapter) { ActiveRecord::ConnectionAdapters::AbstractAdapter.new(nil)}
41
+ let(:config) {{:adapter => 'original_adapter'}}
42
+
43
+ before :each do
44
+ adapter.stub(:connect)
45
+ ActiveRecord::Base.stub(:connection) { adapter }
46
+ ActiveRecord::Base.stub(:connection_pool) { mock(:spec => mock(:config => config)) }
47
+ end
48
+
49
+ it 'proxies the current connection adapter' do
50
+ ActiveRecord::Base.should_receive(:establish_connection).
51
+ with(:adapter => :nogo, :target_adapter => adapter)
52
+ subject.connect!
53
+ end
54
+
55
+ it 'sets @@proxy_adapter with instance of NoGo::ProxyAdapter generated when establishing connection' do
56
+ subject.connect!
57
+ subject.class_variable_get(:@@proxy_adapter).should == adapter
58
+ end
59
+
60
+ it 'sets connection_pool spec config adapter to original (non-proxied) value' do
61
+ config.should_receive(:[]=).with(:adapter, 'original_adapter')
62
+ subject.connect!
63
+ end
64
+ end
65
+ end
66
+
67
+ describe '::connected?' do
68
+ it 'returns false if proxy adapter has not been connected' do
69
+ subject.class_variable_set(:@@proxy_adapter, nil)
70
+ subject.connected?.should == false
71
+ end
72
+
73
+ it 'returns true if proxy adapter has been connected' do
74
+ subject.connected?.should == true
75
+ end
76
+ end
77
+
78
+ describe '::enabled=' do
79
+ it 'invokes #enabled= with argument on proxy adapter' do
80
+ argument = mock
81
+ proxy_adapter.should_receive(:enabled=).with(argument)
82
+ subject.enabled = argument
83
+ end
84
+ end
85
+
86
+ describe '::strategy=' do
87
+ it 'calls #strategy= on the proxy adapter' do
88
+ proxy_adapter.should_receive(:strategy=).with(:pass_through)
89
+ subject.strategy = :pass_through
90
+ end
91
+ end
92
+
93
+ describe '::pop_enabled_state' do
94
+ it 'invokes #pop_enabled_state on the proxy adapter' do
95
+ proxy_adapter.should_receive(:pop_enabled_state)
96
+ subject.pop_enabled_state
97
+ end
98
+ end
99
+
100
+ describe '::push_enabled_state' do
101
+ it 'invokes #push_enabled_state on the proxy adapter' do
102
+ proxy_adapter.should_receive(:push_enabled_state)
103
+ subject.push_enabled_state
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,352 @@
1
+ require 'spec_helper'
2
+
3
+ describe NoGo::ProxyAdapter do
4
+ let(:adapter) { ActiveRecord::ConnectionAdapters::AbstractAdapter.new(nil)}
5
+ let(:proxy_adapter) { NoGo::ProxyAdapter.new(adapter) }
6
+
7
+ before :all do
8
+ NoGo::ProxyAdapter.class_eval do
9
+
10
+ # Ugh, rspec mocks freak out if we try to pass calls to
11
+ def is_a?(klass)
12
+ proxied_adapter.is_a?(klass)
13
+ end
14
+
15
+ private
16
+ def method_missing_with_test_filter(method_name, *args, &block)
17
+ # RSpec sends messages directly to method_missing when checking an object with mocked methods. Why? I don't know, but it
18
+ # causes an infinite loop for some of ProxyAdapter methods. This filter prevents that from happening.
19
+ unless %W[warn pass_through method_missing].include?(method_name.to_s)
20
+ method_missing_without_test_filter(method_name, *args, &block)
21
+ end
22
+ end
23
+ alias_method_chain :method_missing, :test_filter
24
+ end
25
+ end
26
+
27
+ describe '::new' do
28
+ it 'initializes with adapter argument' do
29
+ expect{ NoGo::ProxyAdapter.new(adapter) }.to_not raise_error
30
+ end
31
+
32
+ it 'raises error without adapter argument' do
33
+ expect{ NoGo::ProxyAdapter.new }.to raise_error(ArgumentError)
34
+ end
35
+
36
+ it 'raises error when adapter argument is not an instance of AbstractAdapter' do
37
+ expect{ NoGo::ProxyAdapter.new(Object.new) }.to raise_error(ArgumentError)
38
+ end
39
+
40
+ it 'initializes @strategy to :raise' do
41
+ NoGo::ProxyAdapter.new(adapter).instance_variable_get(:@strategy).should == :raise
42
+ end
43
+ end
44
+
45
+ it 'passes undefined instance method calls through to the adapter' do
46
+ adapter.should_receive(:method_name).with(:arg)
47
+ proxy_adapter.method_name :arg
48
+ end
49
+
50
+ it 'initializes enabled to false' do
51
+ NoGo::ProxyAdapter.new(adapter).enabled.should == false
52
+ end
53
+
54
+ it 'initializes enabled_state_stack to []' do
55
+ NoGo::ProxyAdapter.new(adapter).instance_variable_get(:@enabled_state_stack).should == []
56
+ end
57
+
58
+ describe '#proxied_adapter' do
59
+ it 'returns adapter' do
60
+ proxy_adapter.proxied_adapter.should == adapter
61
+ end
62
+ end
63
+
64
+ describe '#is_a?' do
65
+ it 'invokes method on proxied adapter' do
66
+ adapter.stub(:is_a?).with(ActiveRecord::ConnectionAdapters::AbstractAdapter) { true }
67
+ adapter.should_receive(:is_a?).with(Class)
68
+ proxy_adapter.is_a?(Class)
69
+ end
70
+ end
71
+
72
+ describe '#pop_enabled_state' do
73
+ it 'assigns true to enabled when last value on the stack is true' do
74
+ proxy_adapter.instance_variable_set(:@enabled_state_stack, [true])
75
+ proxy_adapter.enabled = nil
76
+ proxy_adapter.pop_enabled_state
77
+ proxy_adapter.enabled.should == true
78
+ end
79
+
80
+ it 'assigns false to enabled when last value on the stack is false' do
81
+ proxy_adapter.instance_variable_set(:@enabled_state_stack, [false])
82
+ proxy_adapter.enabled = nil
83
+ proxy_adapter.pop_enabled_state
84
+ proxy_adapter.enabled.should == false
85
+ end
86
+
87
+ it 'assigns false to enabled when stack is empty' do
88
+ proxy_adapter.instance_variable_set(:@enabled_state_stack, [])
89
+ proxy_adapter.enabled = nil
90
+ proxy_adapter.pop_enabled_state
91
+ proxy_adapter.enabled.should == false
92
+ end
93
+ end
94
+
95
+ describe '#push_enabled_state' do
96
+ it 'pushes true to the stack when enabled is true' do
97
+ proxy_adapter.instance_variable_set(:@enabled_state_stack, [])
98
+ proxy_adapter.enabled = true
99
+ proxy_adapter.push_enabled_state
100
+ proxy_adapter.instance_variable_get(:@enabled_state_stack).should == [true]
101
+ end
102
+
103
+ it 'pushes false to the stack when enabled is false' do
104
+ proxy_adapter.instance_variable_set(:@enabled_state_stack, [])
105
+ proxy_adapter.enabled = false
106
+ proxy_adapter.push_enabled_state
107
+ proxy_adapter.instance_variable_get(:@enabled_state_stack).should == [false]
108
+ end
109
+ end
110
+
111
+ describe '#strategy' do
112
+ NoGo::ProxyAdapter::StrategyOptions.each do |strategy_option|
113
+ it "returns :#{strategy_option}" do
114
+ proxy_adapter.instance_variable_set(:@strategy, strategy_option)
115
+ proxy_adapter.strategy.should == strategy_option
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '#strategy=' do
121
+ NoGo::ProxyAdapter::StrategyOptions.each do |strategy_option|
122
+ it "sets :#{strategy_option} strategy" do
123
+ proxy_adapter.strategy = strategy_option
124
+ proxy_adapter.strategy.should == strategy_option
125
+ end
126
+ end
127
+
128
+ it 'raises an exception if the strategy option is not valid' do
129
+ expect{ proxy_adapter.strategy = :invalid_option }.to raise_error(ArgumentError)
130
+ end
131
+ end
132
+
133
+ describe '#pass_through' do
134
+ it 'uoea' do
135
+ proxy_adapter.should_receive(:bleh).with(:m, :a)
136
+ proxy_adapter.bleh :m, :a
137
+ end
138
+
139
+ it 'sends method call and arguments to @adapter' do
140
+ adapter.should_receive(:method_name).with(:arg)
141
+ proxy_adapter.send :pass_through, :method_name, :arg
142
+ end
143
+
144
+ context 'when strategy is :warn' do
145
+ before :each do
146
+ proxy_adapter.strategy = :warn
147
+ end
148
+
149
+ it 'invokes #warn when enabled' do
150
+ proxy_adapter.enabled = true
151
+ adapter.stub(:method_name)
152
+ proxy_adapter.should_receive(:warn).with(:method_name, :arg)
153
+ proxy_adapter.send :pass_through, :method_name, :arg
154
+ end
155
+
156
+ it 'does not invoke #warn when disabled' do
157
+ proxy_adapter.enabled = false
158
+ adapter.stub(:method_name)
159
+ proxy_adapter.should_not_receive(:warn).with(:method_name, :arg)
160
+ proxy_adapter.send :pass_through, :method_name, :arg
161
+ end
162
+ end
163
+
164
+ it 'does not invoke #warn when strategy is :pass_through' do
165
+ proxy_adapter.strategy = :pass_through
166
+ adapter.stub(:method_name)
167
+ proxy_adapter.should_not_receive(:warn).with(:method_name, :arg)
168
+ proxy_adapter.send :pass_through, :method_name, :arg
169
+ end
170
+ end
171
+
172
+ describe '#raise_or_pass_through' do
173
+ context 'when strategy is set to :raise' do
174
+ before :each do
175
+ proxy_adapter.strategy = :raise
176
+ end
177
+
178
+ it 'raises an exception when enabled' do
179
+ proxy_adapter.enabled = true
180
+ expect{ proxy_adapter.send :raise_or_pass_through, :method_name, :arg }.to raise_error
181
+ end
182
+
183
+ it 'does not raise an exception when disabled' do
184
+ proxy_adapter.enabled = false
185
+ adapter.stub(:method_name)
186
+ expect{ proxy_adapter.send :raise_or_pass_through, :method_name, :arg }.to_not raise_error
187
+ end
188
+ end
189
+
190
+ context 'when strategy is set to :warn' do
191
+ before :each do
192
+ proxy_adapter.strategy = :warn
193
+ end
194
+
195
+ it 'passes method call through to the adapter' do
196
+ proxy_adapter.should_receive(:pass_through).with(:method_name, :arg)
197
+ proxy_adapter.send :raise_or_pass_through, :method_name, :arg
198
+ end
199
+ end
200
+
201
+ context 'when strategy is set to :pass_through' do
202
+ before :each do
203
+ proxy_adapter.strategy = :pass_through
204
+ end
205
+
206
+ it 'passes method call through to the adapter' do
207
+ proxy_adapter.should_receive(:pass_through).with(:method_name, :arg)
208
+ proxy_adapter.send :raise_or_pass_through, :method_name, :arg
209
+ end
210
+ end
211
+ end
212
+
213
+ # Overridden methods -------------------------------------------------------
214
+
215
+ describe '#execute' do
216
+ it 'invokes #raise_or_pass_through' do
217
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:execute, 'SELECT COUNT(*) FROM tables;', 'name')
218
+ proxy_adapter.execute('SELECT COUNT(*) FROM tables;', 'name')
219
+ end
220
+
221
+ it 'invokes #pass_through if SQL is BEGIN' do
222
+ proxy_adapter.should_receive(:pass_through).with(:execute, 'BEGIN', 'name')
223
+ proxy_adapter.execute('BEGIN', 'name')
224
+ end
225
+
226
+ it 'invokes #pass_through if SQL is ROLLBACK' do
227
+ proxy_adapter.should_receive(:pass_through).with(:execute, 'ROLLBACK', 'name')
228
+ proxy_adapter.execute('ROLLBACK', 'name')
229
+ end
230
+
231
+ it 'invokes #pass_through if SQL is SAVEPOINT' do
232
+ proxy_adapter.should_receive(:pass_through).with(:execute, 'SAVEPOINT', 'name')
233
+ proxy_adapter.execute('SAVEPOINT', 'name')
234
+ end
235
+ end
236
+
237
+ describe '#insert' do
238
+ it 'invokes #raise_or_pass_through' do
239
+ proxy_adapter.should_receive(:raise_or_pass_through).with(
240
+ :insert, 'SQL', 'name', 'pk', 'id_value', 'sequence_name', ['bind']
241
+ )
242
+ proxy_adapter.insert('SQL', 'name', 'pk', 'id_value', 'sequence_name', ['bind'])
243
+ end
244
+ end
245
+
246
+ describe '#update' do
247
+ it 'invokes #raise_or_pass_through' do
248
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:update, 'SQL', 'name', [])
249
+ proxy_adapter.send(:update, 'SQL', 'name', [])
250
+ end
251
+ end
252
+
253
+ describe '#delete' do
254
+ it 'invokes #raise_or_pass_through' do
255
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:delete, 'SQL', 'name', [])
256
+ proxy_adapter.send(:delete, 'SQL', 'name', [])
257
+ end
258
+ end
259
+
260
+ describe '#select_rows' do
261
+ it 'invokes #raise_or_pass_through' do
262
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:select_rows, 'SQL', 'name')
263
+ proxy_adapter.select_rows('SQL', 'name')
264
+ end
265
+ end
266
+
267
+ describe '#select' do
268
+ it 'invokes #raise_or_pass_through' do
269
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:select, 'SQL', 'name')
270
+ proxy_adapter.send(:select, 'SQL', 'name')
271
+ end
272
+ end
273
+
274
+ describe '#select_all' do
275
+ it 'invokes #raise_or_pass_through' do
276
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:select_all, 'SQL', 'name', [])
277
+ proxy_adapter.send(:select_all, 'SQL', 'name', [])
278
+ end
279
+ end
280
+
281
+ describe '#select_one' do
282
+ it 'invokes #raise_or_pass_through' do
283
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:select_one, 'SQL', 'name')
284
+ proxy_adapter.send(:select_one, 'SQL', 'name')
285
+ end
286
+ end
287
+
288
+ describe '#select_value' do
289
+ it 'invokes #raise_or_pass_through' do
290
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:select_value, 'SQL', 'name')
291
+ proxy_adapter.send(:select_value, 'SQL', 'name')
292
+ end
293
+ end
294
+
295
+ describe '#select_values' do
296
+ it 'invokes #raise_or_pass_through' do
297
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:select_values, 'SQL', 'name')
298
+ proxy_adapter.send(:select_values, 'SQL', 'name')
299
+ end
300
+ end
301
+
302
+ describe '#exec_query' do
303
+ it 'invokes #raise_or_pass_through' do
304
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:exec_query, 'SQL', 'name', [])
305
+ proxy_adapter.send(:exec_query, 'SQL', 'name', [])
306
+ end
307
+ end
308
+
309
+ describe '#exec_insert' do
310
+ it 'invokes #raise_or_pass_through' do
311
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:exec_insert, 'SQL', 'name', [])
312
+ proxy_adapter.send(:exec_insert, 'SQL', 'name', [])
313
+ end
314
+ end
315
+
316
+ describe '#exec_delete' do
317
+ it 'invokes #raise_or_pass_through' do
318
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:exec_delete, 'SQL', 'name', [])
319
+ proxy_adapter.send(:exec_delete, 'SQL', 'name', [])
320
+ end
321
+ end
322
+
323
+ describe '#exec_update' do
324
+ it 'invokes #raise_or_pass_through' do
325
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:exec_update, 'SQL', 'name', [])
326
+ proxy_adapter.send(:exec_update, 'SQL', 'name', [])
327
+ end
328
+ end
329
+
330
+ describe '#insert_sql' do
331
+ it 'invokes #raise_or_pass_through' do
332
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:'insert_sql', 'SQL', 'name', 'pk', 'id_value', 'sequence_name', ['bind'])
333
+ proxy_adapter.send(:'insert_sql', 'SQL', 'name', 'pk', 'id_value', 'sequence_name', ['bind'])
334
+ end
335
+ end
336
+
337
+ describe '#delete_sql' do
338
+ it 'invokes #raise_or_pass_through' do
339
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:'delete_sql', 'SQL', 'name')
340
+ proxy_adapter.send(:'delete_sql', 'SQL', 'name')
341
+ end
342
+ end
343
+
344
+ describe '#update_sql' do
345
+ it 'invokes #raise_or_pass_through' do
346
+ proxy_adapter.should_receive(:raise_or_pass_through).with(:'update_sql', 'SQL', 'name')
347
+ proxy_adapter.send(:'update_sql', 'SQL', 'name')
348
+ end
349
+ end
350
+
351
+ # --------------------------------------------------------------------------
352
+ end
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+ require_relative '../../lib/nogo/rspec/dsl'
3
+
4
+ describe NoGo::RSpec::DSL do
5
+ describe '::nogo_example_group' do
6
+ it 'returns an rspec example group' do
7
+ dummy_example_group = mock
8
+ block = Proc.new {}
9
+ RSpec::Core::ExampleGroup.stub(:subclass).and_return(dummy_example_group)
10
+ NoGo::RSpec::DSL.nogo_example_group(&block).should == dummy_example_group
11
+ end
12
+
13
+ context 'executed' do
14
+ it 'invokes nogo before all block' do
15
+ dummy = mock
16
+ dummy.should_receive(:execute)
17
+ block = Proc.new { dummy.execute }
18
+ NoGo::RSpec::DSL.stub(:NogoBeforeAllBlock).and_return(block)
19
+ NoGo::RSpec::DSL.nogo_example_group{ it{ :example_to_trigger_callbacks }}.run mock.as_null_object
20
+ end
21
+
22
+ it 'invokes nogo after all block' do
23
+ dummy = mock
24
+ dummy.should_receive(:execute)
25
+ block = Proc.new { dummy.execute }
26
+ NoGo::RSpec::DSL.stub(:NogoAfterAllBlock).and_return(block)
27
+ NoGo::RSpec::DSL.nogo_example_group{ it{ :example_to_trigger_callbacks}}.run mock.as_null_object
28
+ end
29
+
30
+ it 'invokes instance eval with example group block' do
31
+ dummy = mock
32
+ dummy.should_receive(:execute)
33
+ block = Proc.new { dummy.execute }
34
+ NoGo::RSpec::DSL.nogo_example_group(&block).run mock.as_null_object
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'NogoBeforeAllBlock' do
40
+ it 'invokes NoGo::Connection::push_enabled_state' do
41
+ NoGo::Connection.should_receive(:push_enabled_state)
42
+ NoGo::Connection.stub(:enabled=)
43
+ NoGo::RSpec::DSL::NogoBeforeAllBlock.call
44
+ end
45
+
46
+ it 'enables proxy adapter' do
47
+ NoGo::Connection.stub(:push_enabled_state)
48
+ NoGo::Connection.should_receive(:enabled=).with(true)
49
+ NoGo::RSpec::DSL::NogoBeforeAllBlock.call
50
+ end
51
+ end
52
+
53
+ describe 'NogoAfterAllBlock' do
54
+ it 'invokes NoGo::Connection::pop_enabled_state' do
55
+ NoGo::Connection.should_receive(:pop_enabled_state)
56
+ NoGo::RSpec::DSL::NogoAfterAllBlock.call
57
+ end
58
+ end
59
+ end
60
+
61
+ describe 'nogo' do
62
+ it 'registers a nogo example group' do
63
+ dummy_example_group = mock
64
+ dummy_example_group.should_receive(:register)
65
+ NoGo::RSpec::DSL.stub(:nogo_example_group).and_return(dummy_example_group)
66
+ nogo{}
67
+ end
68
+ end
69
+
70
+ describe 'database_restricted' do
71
+ it 'registers a nogo example group' do
72
+ dummy_example_group = mock
73
+ dummy_example_group.should_receive(:register)
74
+ NoGo::RSpec::DSL.stub(:nogo_example_group).and_return(dummy_example_group)
75
+ database_restricted{}
76
+ end
77
+ end
@@ -0,0 +1,4 @@
1
+ require 'active_record'
2
+ require 'rspec'
3
+
4
+ require_relative '../lib/nogo'
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nogo
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.beta
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Andy Ogzewalla
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-05 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70138985281760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.7.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70138985281760
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ requirement: &70138985281220 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70138985281220
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70138985280720 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.7.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70138985280720
47
+ - !ruby/object:Gem::Dependency
48
+ name: activerecord
49
+ requirement: &70138985280140 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70138985280140
58
+ description: A library that lets you know when your code touches your database
59
+ email:
60
+ - andyogzewalla@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - Gemfile
67
+ - README.md
68
+ - Rakefile
69
+ - lib/active_record/connection_adapters/nogo_adapter.rb
70
+ - lib/active_record/import.rb
71
+ - lib/nogo.rb
72
+ - lib/nogo/abstract_method_overrides.rb
73
+ - lib/nogo/connection.rb
74
+ - lib/nogo/proxy_adapter.rb
75
+ - lib/nogo/rspec.rb
76
+ - lib/nogo/rspec/dsl.rb
77
+ - lib/nogo/version.rb
78
+ - nogo.gemspec
79
+ - spec/active_record/connection_adapters/nogo_adapter_spec.rb
80
+ - spec/nogo/connection_spec.rb
81
+ - spec/nogo/proxy_adapter_spec.rb
82
+ - spec/rspec/dsl_spec.rb
83
+ - spec/spec_helper.rb
84
+ homepage: ''
85
+ licenses: []
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>'
100
+ - !ruby/object:Gem::Version
101
+ version: 1.3.1
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.11
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: A library that lets you know when your code touches your database
108
+ test_files:
109
+ - spec/active_record/connection_adapters/nogo_adapter_spec.rb
110
+ - spec/nogo/connection_spec.rb
111
+ - spec/nogo/proxy_adapter_spec.rb
112
+ - spec/rspec/dsl_spec.rb
113
+ - spec/spec_helper.rb