tphases 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ script: bundle exec rspec spec
6
+ notifications:
7
+ email: false
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # TPhases
2
2
 
3
+ [![Build Status](https://travis-ci.org/charleseff/tphases.png)](https://travis-ci.org/charleseff/tphases)
4
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/charleseff/tphases)
5
+
3
6
  TPhases (Transactional Phases) is a support framework that helps you build your Rails request life cycles into read-only, write-only, and no-transaction-allowed phases.
4
7
 
5
8
  The way it accomplishes this is with the methods `TPhases.read_phase`, `TPhases.write_phase` and `TPhases.no_transactions_phase` which take blocks. Here is a simple example inside of a controller action:
@@ -46,23 +49,38 @@ Somewhere in the initialization process of your app, call
46
49
 
47
50
  ## Usage
48
51
 
49
- ### In production:
52
+ ### Environments
53
+
54
+ #### In production:
50
55
  The `read_phase`, `write_phase`, and `no_transaction_phase` methods simply yield to the block given.
51
56
 
52
- ### In development:
57
+ #### In development:
53
58
 
54
- #### `read_phase`
59
+ ##### `read_phase`
55
60
  throws an exception if a database transaction is attempted within its block which is a write. This is known as a "read transactional violation".
56
61
 
57
- #### `write_phase`
62
+ ##### `write_phase`
58
63
  throws an exception if a database transaction is attempted within its block which is a read. This is a write transactional violation.
59
64
 
60
- #### `no_transactions_phase`
65
+ ##### `no_transactions_phase`
61
66
  throws an exception if any database transaction is attempted within its block.
62
67
 
63
- ### In test:
68
+ #### In test:
64
69
  If a transactional violation occurs in a TPhase, the code will continue to run, but the test will fail at the end citing the list of transactional violations that occurred.
65
70
 
71
+ ### Methods
72
+
73
+ #### `ensure_no_transactions_on`
74
+ Call `ensure_no_transactions_on` on controllers to prevent DB transactions within the views of controller actions. e.g.:
75
+
76
+ ```ruby
77
+ class BarsController < ApplicationController
78
+
79
+ ensure_no_transactions_on [:show,:update]
80
+ end
81
+ ```
82
+
83
+
66
84
  ## Contributing
67
85
 
68
86
  1. Fork it
@@ -8,10 +8,16 @@ module TPhases
8
8
  yield config
9
9
  end
10
10
 
11
- private
12
11
  # the config
12
+ # settings options are:
13
+ #
14
+ # - mode
15
+ # - collect_mode_failures_on - defaults to true, but can be turned off temporarily to disable failures on
16
+ # transaction violations
17
+ #
13
18
  # sets default value `mode` value based on presence of Rails and environment type
14
19
  # the default setting is the safest, :pass_through, which means TPhases does nothing.
20
+ #
15
21
  def config
16
22
  @config ||= begin
17
23
 
@@ -32,7 +38,7 @@ module TPhases
32
38
  end
33
39
  end
34
40
 
35
- Struct.new(:mode).new(default_mode)
41
+ Struct.new(:mode, :collect_mode_failures_on).new(default_mode, true)
36
42
  end
37
43
  end
38
44
 
@@ -5,6 +5,12 @@ module TPhases
5
5
  module ClassMethods
6
6
  # initiates TPhases. Any overrides to config mode need to be made prior to running this.
7
7
  def initiate!
8
+ add_mode_methods!
9
+ add_rails_methods! if defined? Rails
10
+ end
11
+
12
+ private
13
+ def add_mode_methods!
8
14
  case config.mode
9
15
  when :pass_through
10
16
  require 'tphases/modes/pass_through_mode'
@@ -18,7 +24,6 @@ module TPhases
18
24
  else
19
25
  raise "TPhases mode must be one of :pass_through, :exceptions, or :collect, but instead is #{config.mode}"
20
26
  end
21
-
22
27
  end
23
28
 
24
29
  end
@@ -1,5 +1,6 @@
1
1
  require 'tphases/transactional_violation'
2
2
  require 'tphases/modes/helpers/transactional_violations_helper'
3
+ require 'tphases/modes/helpers/rails_helper'
3
4
 
4
5
  # the default 'test' mode, Collect Mode collects incidents of
5
6
  # immediately inside of a TPhase block if a transactional violation occurs
@@ -8,9 +9,11 @@ module TPhases
8
9
  module CollectMode
9
10
  extend ActiveSupport::Concern
10
11
  include Helpers::TransactionalViolationsHelper
12
+ include Helpers::RailsHelper if defined? Rails
11
13
 
12
14
  included do
13
15
  add_rspec_after! if defined?(RSpec)
16
+ add_cucumber_after! if defined?(Cucumber)
14
17
  @violations = []
15
18
  end
16
19
 
@@ -34,26 +37,49 @@ module TPhases
34
37
  # adds an after block for all rspec tests that cause them to fail if any transactional violations are present
35
38
  def add_rspec_after!
36
39
  RSpec.configure do |config|
37
- config.after(:each) do
38
- begin
39
- unless TPhases.violations.empty?
40
- #fail "This spec had #{TPhases.violations.count} transactional violations: \n\t#{TPhases.violations.map(&:inspect).join("\n\t")}"
41
- fail <<-FAILURE_MESSAGE
42
- This spec had #{TPhases.violations.count} transactional violations:
43
- #{TPhases.violations.each_with_index.map do |violation,index|
44
- "#{index}: Violation Type: #{violation[:type]},\nSQL: #{violation[:sql]}\nCall Stack:\n\t#{violation[:call_stack].join("\n\t")}"
45
- end.join("\n*********************************************************\n")}
46
- end
47
- FAILURE_MESSAGE
40
+ config.after(:each, &after_test_fail_if_violations_proc)
41
+ end
42
+ end
43
+
44
+ def add_cucumber_after!
45
+ # todo: get me working
46
+ #require 'cucumber/rb_support/rb_dsl'
47
+ #Cucumber::RbSupport::RbDsl.register_rb_hook('after', [], after_test_fail_if_violations_proc)
48
+ end
49
+
50
+ # fails if there were any transactional violations
51
+ def after_test_fail_if_violations_proc
52
+ Proc.new do
53
+ begin
54
+ if TPhases.config.collect_mode_failures_on && !TPhases.violations.empty?
55
+
56
+ fail_message = "This spec had #{TPhases.violations.count} transactional violations:\n"
57
+ TPhases.violations.each_with_index.map do |violation, index|
58
+ fail_message << "*"*50 + "\n"
59
+ fail_message << "Violation ##{index+1}, type: #{violation[:type]}\n"
60
+ fail_message << "SQL: #{violation[:sql]}\n"
61
+ fail_message << "Call Stack: \n\t\t#{TPhases.cleaned_call_stack(violation[:call_stack]).join("\n\t\t")}\n"
48
62
  end
49
- ensure
50
- # reset violations list:
51
- TPhases.violations = []
63
+
64
+ fail fail_message
52
65
  end
66
+ ensure
67
+ TPhases.violations = [] # reset violations list
53
68
  end
54
69
  end
70
+
55
71
  end
56
72
 
73
+ public
74
+ # taken from https://github.com/rails/rails/blob/77977f34a5a4ea899f59e31ad869b582285fa5c1/actionpack/lib/action_dispatch/middleware/show_exceptions.rb#L148 :
75
+ # shows a cleaned stack for Rails apps by default
76
+ def cleaned_call_stack(call_stack)
77
+ defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
78
+ Rails.backtrace_cleaner.clean(call_stack, :silent) :
79
+ call_stack
80
+ end
81
+
82
+
57
83
  end
58
84
  end
59
85
  end
@@ -1,5 +1,6 @@
1
1
  require 'tphases/transactional_violation'
2
2
  require 'tphases/modes/helpers/transactional_violations_helper'
3
+ require 'tphases/modes/helpers/rails_helper'
3
4
 
4
5
  # the default 'development' mode, Exceptions Mode means that an exception will be raised
5
6
  # immediately inside of a TPhase block if a transactional violation occurs
@@ -8,6 +9,7 @@ module TPhases
8
9
  module ExceptionsMode
9
10
  extend ActiveSupport::Concern
10
11
  include Helpers::TransactionalViolationsHelper
12
+ include Helpers::RailsHelper if defined? Rails
11
13
 
12
14
  module ClassMethods
13
15
  private
@@ -0,0 +1,41 @@
1
+ module TPhases::Modes
2
+ module Helpers
3
+ module RailsHelper
4
+ extend ActiveSupport::Concern
5
+ module ClassMethods
6
+ def add_rails_methods!
7
+ add_render_alias_method_chain!
8
+
9
+ require 'tphases/rails/no_transactions_in_controller'
10
+ ActionController::Base.send :include, TPhases::Rails::NoTransactionsInController
11
+ end
12
+
13
+ private
14
+ def add_render_alias_method_chain!
15
+ unless Gem.loaded_specs.values.map { |value| value.full_gem_path }.any? { |n| n.include? "actionpack-3.0." }
16
+ raise "TPhases currently expects Rails version 3.0.* for patching ActionView template."
17
+ end
18
+
19
+ ActionView::Template.class_eval do
20
+
21
+ def render_with_tphases_no_transactions(view, locals, &block)
22
+ controller_class = view.controller.class
23
+ if controller_class.class_variable_defined?(:@@no_transaction_actions) &&
24
+ controller_class.class_variable_get(:@@no_transaction_actions).include?(view.action_name.to_sym)
25
+ TPhases.no_transactions_phase do
26
+ render_without_tphases_no_transactions(view, locals, &block)
27
+ end
28
+ else
29
+ render_without_tphases_no_transactions(view, locals, &block)
30
+ end
31
+ end
32
+
33
+ alias_method_chain :render, :tphases_no_transactions
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -13,121 +13,75 @@ module TPhases
13
13
 
14
14
  included do
15
15
  define_phase_methods!
16
+
17
+ # used to keep track of nested phases. a nested phase overrides any prior phase.
18
+ @phase_stack = []
16
19
  end
17
20
 
18
- private
19
21
  module ClassMethods
20
22
 
21
- # if version of activesupport is 3.2.1, it has the subscribed method. else, it doesn't
22
23
  def define_phase_methods!
23
- if ActiveSupport::VERSION::MAJOR > 3
24
- define_phase_methods_with_subscribed_method!
25
- elsif ActiveSupport::VERSION::MAJOR == 3
26
- if ActiveSupport::VERSION::MINOR > 2
27
- define_phase_methods_with_subscribed_method!
28
- elsif ActiveSupport::VERSION::MINOR == 2
29
- if ActiveSupport::VERSION::TINY >= 1
30
- define_phase_methods_with_subscribed_method!
31
- else
32
- define_phase_methods_without_subscribed_method!
24
+ %w{read write no_transactions}.each do |phase_type|
25
+ define_singleton_method(:"#{phase_type}_phase") do |&block|
26
+ phase = Phase.new
27
+ @phase_stack << phase
28
+ begin
29
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |name, date, date2, sha, args|
30
+ next unless @phase_stack.last == phase
31
+ if send(:"#{phase_type}_violation?", args[:sql])
32
+ send(:"#{phase_type}_violation_action", args[:sql], caller)
33
+ end
34
+
35
+ end
36
+ block.call
37
+ ensure
38
+ ActiveSupport::Notifications.unsubscribe(subscriber)
39
+ @phase_stack.pop
33
40
  end
34
- else
35
- define_phase_methods_without_subscribed_method!
36
41
  end
37
- else
38
- define_phase_methods_without_subscribed_method!
39
- end
40
- end
41
-
42
- # adds methods using the subscribed method
43
- def define_phase_methods_with_subscribed_method!
44
- define_singleton_method(:read_phase) do |&block|
45
- ActiveSupport::Notifications.subscribed(read_phase_subscription_callback, "sql.active_record", &block)
46
- end
47
-
48
- define_singleton_method(:write_phase) do |&block|
49
- ActiveSupport::Notifications.subscribed(write_phase_subscription_callback, "sql.active_record", &block)
50
- end
51
-
52
- define_singleton_method(:no_transactions_phase) do |&block|
53
- ActiveSupport::Notifications.subscribed(no_transactions_phase_subscription_callback, "sql.active_record", &block)
54
42
  end
55
43
  end
56
44
 
57
- def define_phase_methods_without_subscribed_method!
58
- define_singleton_method(:read_phase) do |&block|
59
- begin
60
- subscriber = ActiveSupport::Notifications.subscribe("sql.active_record", &read_phase_subscription_callback)
61
- block.call
62
- ensure
63
- ActiveSupport::Notifications.unsubscribe(subscriber)
64
- end
65
- end
66
-
67
- define_singleton_method(:write_phase) do |&block|
68
- begin
69
- subscriber = ActiveSupport::Notifications.subscribe("sql.active_record", &write_phase_subscription_callback)
70
- block.call
71
- ensure
72
- ActiveSupport::Notifications.unsubscribe(subscriber)
73
- end
74
- end
45
+ private
46
+ READ_QUERIES = %w{show select describe}
47
+ WRITE_QUERIES = %w{update commit insert delete}
48
+ RAILS_IGNORABLE_QUERIES = %w{describe}
75
49
 
76
- define_singleton_method(:no_transactions_phase) do |&block|
77
- begin
78
- subscriber = ActiveSupport::Notifications.subscribe("sql.active_record", &no_transactions_phase_subscription_callback)
79
- block.call
80
- ensure
81
- ActiveSupport::Notifications.unsubscribe(subscriber)
82
- end
83
- end
50
+ def rails_ignorable_read_queries
51
+ @rails_ignorable_read_queries ||= READ_QUERIES - RAILS_IGNORABLE_QUERIES
84
52
  end
85
53
 
86
- # the set of blocks that run when an ActiveSupport notification is fired on sql.active_record
87
- # each call *_violation_action methods which are defined in the implementing module
88
-
89
- def write_phase_subscription_callback
90
- Proc.new do |name, date, date2, sha, args|
91
- if write_transactional_violation?(args[:sql])
92
- write_violation_action(args[:sql], caller)
93
- end
94
- end
54
+ # determines if this query is a read transactional violation (if it is anything besides a read)
55
+ def read_violation?(sql)
56
+ WRITE_QUERIES.include?(first_word(sql))
95
57
  end
96
58
 
97
- def read_phase_subscription_callback
98
- Proc.new do |name, date, date2, sha, args|
99
- if read_transactional_violation?(args[:sql])
100
- read_violation_action(args[:sql], caller)
101
- end
102
- end
59
+ # determines if this query is a write transactional violation (if it is anything besides a write)
60
+ def write_violation?(sql)
61
+ query_types = (Object.const_defined? :Rails) ? rails_ignorable_read_queries : READ_QUERIES
62
+ query_types.include?(first_word(sql))
103
63
  end
104
64
 
105
- def no_transactions_phase_subscription_callback
106
- Proc.new do |name, date, date2, sha, args|
107
- no_transactions_violation_action(args[:sql], caller)
65
+ # violation unless it's Rails and we are running an ignorable query
66
+ def no_transactions_violation?(sql)
67
+ if Object.const_defined? :Rails
68
+ !RAILS_IGNORABLE_QUERIES.include?(first_word(sql))
69
+ else
70
+ true
108
71
  end
109
72
  end
110
73
 
111
- READ_QUERIES = %w{update commit insert delete}
112
- WRITE_QUERIES = %w{show select}
113
-
114
- # determines if this query is a read transactional violation (if it is anything besides a read)
115
- def read_transactional_violation?(sql)
116
- READ_QUERIES.include?(first_word(sql))
117
- end
118
-
119
- # determines if this query is a write transactional violation (if it is anything besides a write)
120
- def write_transactional_violation?(sql)
121
- WRITE_QUERIES.include?(first_word(sql))
122
- end
123
-
124
74
  def first_word(str)
125
75
  str.split(' ').first.downcase
126
76
  end
127
77
 
128
78
  end
129
79
 
80
+ # simple class to represent a phase on the stack of phases. Used to determine which phase is active
81
+ class Phase;
82
+ end
83
+
130
84
  end
131
85
  end
132
86
  end
133
- end
87
+ end
@@ -16,6 +16,12 @@ module TPhases
16
16
  def no_transactions_phase
17
17
  yield
18
18
  end
19
+
20
+ private
21
+ def add_rails_methods!
22
+ require 'tphases/rails/no_transactions_in_controller_pass_through'
23
+ ActionController::Base.send :include, TPhases::Rails::NoTransactionsInControllerPassThrough
24
+ end
19
25
  end
20
26
  end
21
27
  end
@@ -0,0 +1,15 @@
1
+ module TPhases
2
+ module Rails
3
+ module NoTransactionsInController
4
+ extend ActiveSupport::Concern
5
+ module ClassMethods
6
+
7
+ def ensure_no_transactions_on(actions)
8
+ class_variable_set(:@@no_transaction_actions, actions)
9
+ end
10
+
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module TPhases
2
+ module Rails
3
+ module NoTransactionsInControllerPassThrough
4
+ extend ActiveSupport::Concern
5
+ module ClassMethods
6
+ def ensure_no_transactions_on(actions)
7
+ # do nothing
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Tphases
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
Binary file
@@ -58,4 +58,45 @@ describe TPhases::Modes::ExceptionsMode do
58
58
 
59
59
  end
60
60
  end
61
+
62
+ describe "nested phases" do
63
+ context "read_phase inside of a no_transactions_phase" do
64
+ it "should allow read transactions" do
65
+ expect {
66
+ subject.no_transactions_phase do
67
+ subject.read_phase do
68
+ ActiveRecord::Base.connection.select_all(read_sql)
69
+ end
70
+ end
71
+ }.to_not raise_error
72
+
73
+ end
74
+ end
75
+
76
+ context "no_transactions_phase inside a read_phase" do
77
+ it "should disallow read transactions" do
78
+ expect {
79
+ subject.read_phase do
80
+ subject.no_transactions_phase do
81
+ ActiveRecord::Base.connection.select_all(read_sql)
82
+ end
83
+ end
84
+ }.to raise_error(ActiveRecord::StatementInvalid, "TransactionalViolation: #{read_sql} ran inside of a 'no_transactions_phase' block.: #{read_sql}")
85
+
86
+
87
+ end
88
+ end
89
+
90
+ it "should have the right phase_stack sizes" do
91
+ subject.instance_variable_get("@phase_stack").should be_empty
92
+ subject.read_phase do
93
+ subject.instance_variable_get("@phase_stack").size.should == 1
94
+ subject.no_transactions_phase do
95
+ subject.instance_variable_get("@phase_stack").size.should == 2
96
+ end
97
+ subject.instance_variable_get("@phase_stack").size.should == 1
98
+ end
99
+ subject.instance_variable_get("@phase_stack").should be_empty
100
+ end
101
+ end
61
102
  end
@@ -2,37 +2,67 @@ require 'spec_helper'
2
2
  require 'tphases/modes/helpers/transactional_violations_helper'
3
3
 
4
4
  describe TPhases::Modes::Helpers::TransactionalViolationsHelper do
5
- describe TPhases::Modes::Helpers::TransactionalViolationsHelper do
6
- subject { Module.new { include TPhases::Modes::Helpers::TransactionalViolationsHelper } }
5
+ subject { Module.new { include TPhases::Modes::Helpers::TransactionalViolationsHelper } }
7
6
 
8
- let(:write_queries) {
9
- [
10
- "UPDATE `users` SET `email` = 'woifjwe@owiejf.com' WHERE `users`.`id` = 1",
11
- "INSERT INTO tablename (col1, col2) VALUES('data1', 'data2' )",
12
- "COMMIT",
13
- "DELETE FROM example WHERE age='15'"
14
- ]
15
- }
16
- let(:read_queries) {
17
- [
18
- "select * from foobar",
19
- "show variables like 'version'"
20
- ]
21
- }
7
+ let(:write_queries) {
8
+ [
9
+ "UPDATE `users` SET `email` = 'woifjwe@owiejf.com' WHERE `users`.`id` = 1",
10
+ "INSERT INTO tablename (col1, col2) VALUES('data1', 'data2' )",
11
+ "COMMIT",
12
+ "DELETE FROM example WHERE age='15'"
13
+ ]
14
+ }
15
+ let(:read_queries) {
16
+ [
17
+ "select * from foobar",
18
+ "show variables like 'version'"
19
+ ]
20
+ }
22
21
 
23
- describe "#read_transactional_violation?" do
24
- it "should detect correctly" do
25
- read_queries.each { |read_query| expect(subject.send(:read_transactional_violation?, read_query)).to eq(false) }
26
- write_queries.each { |write_query| expect(subject.send(:read_transactional_violation?, write_query)).to eq(true) }
22
+ describe "#read_transactional_violation?" do
23
+ it "should detect correctly" do
24
+ read_queries.each { |read_query| expect(subject.send(:read_violation?, read_query)).to eq(false) }
25
+ write_queries.each { |write_query| expect(subject.send(:read_violation?, write_query)).to eq(true) }
26
+ end
27
+ end
28
+
29
+ describe "#write_violation?" do
30
+ it "should detect correctly" do
31
+ read_queries.each { |read_query| expect(subject.send(:write_violation?, read_query)).to be_true }
32
+ write_queries.each { |write_query| expect(subject.send(:write_violation?, write_query)).to be_false }
33
+ end
34
+
35
+ context "describe queries" do
36
+ context "when Rails is present" do
37
+ it "should detect correctly" do
38
+ Object.should_receive(:const_defined?).with(:Rails).and_return(true)
39
+ expect(subject.send(:write_violation?, "describe foo")).to be_false
40
+ end
41
+ end
42
+ context "when Rails is not present" do
43
+ it "should detect correctly" do
44
+ Object.should_receive(:const_defined?).with(:Rails).and_return(false)
45
+ expect(subject.send(:write_violation?, "describe foo")).to be_true
46
+ end
27
47
  end
28
48
  end
29
49
 
30
- describe " #write_transactional_violation?" do
50
+ end
51
+
52
+ describe "#no_transactions_violation?" do
53
+ context "when Rails is present" do
31
54
  it "should detect correctly" do
32
- read_queries.each { |read_query| expect(subject.send(:write_transactional_violation?, read_query)).to be_true }
33
- write_queries.each { |write_query| expect(subject.send(:write_transactional_violation?, write_query)).to be_false }
55
+ Object.stub(:const_defined?).with(:Rails).and_return(true)
56
+ expect(subject.send(:no_transactions_violation?, "describe foo")).to be_false
57
+ expect(subject.send(:no_transactions_violation?, "select * from foo")).to be_true
58
+ end
59
+ end
60
+ context "when Rails is not present" do
61
+ it "should detect correctly" do
62
+ Object.stub(:const_defined?).with(:Rails).and_return(false)
63
+ expect(subject.send(:no_transactions_violation?, "describe foo")).to be_true
64
+ expect(subject.send(:no_transactions_violation?, "select * from foo")).to be_true
34
65
  end
35
66
  end
36
-
37
67
  end
38
- end
68
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tphases
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-03 00:00:00.000000000 Z
12
+ date: 2012-11-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -117,6 +117,7 @@ extra_rdoc_files: []
117
117
  files:
118
118
  - .gitignore
119
119
  - .rspec
120
+ - .travis.yml
120
121
  - Gemfile
121
122
  - LICENSE.txt
122
123
  - README.md
@@ -126,8 +127,11 @@ files:
126
127
  - lib/tphases/initialization.rb
127
128
  - lib/tphases/modes/collect_mode.rb
128
129
  - lib/tphases/modes/exceptions_mode.rb
130
+ - lib/tphases/modes/helpers/rails_helper.rb
129
131
  - lib/tphases/modes/helpers/transactional_violations_helper.rb
130
132
  - lib/tphases/modes/pass_through_mode.rb
133
+ - lib/tphases/rails/no_transactions_in_controller.rb
134
+ - lib/tphases/rails/no_transactions_in_controller_pass_through.rb
131
135
  - lib/tphases/transactional_violation.rb
132
136
  - lib/tphases/version.rb
133
137
  - spec/fixtures/database.sqlite3