active_sanity 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +14 -0
- data/features/check_sanity.feature +3 -3
- data/features/check_sanity_with_db_storage.feature +5 -5
- data/features/step_definitions/rails_app.rb +8 -8
- data/features/support/env.rb +12 -9
- data/lib/active_sanity/checker.rb +53 -12
- data/lib/active_sanity/version.rb +1 -1
- data/test/rails_template.rb +18 -8
- metadata +4 -4
data/Rakefile
CHANGED
@@ -1,2 +1,16 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
task :default => :features
|
5
|
+
|
6
|
+
desc "Run features"
|
7
|
+
task :features do
|
8
|
+
raise "Failed!" unless system('bundle exec cucumber features')
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Clean test rails app"
|
12
|
+
task :clean do
|
13
|
+
if system('rm -r test/rails_app')
|
14
|
+
puts "test/rails_app deleted successfully"
|
15
|
+
end
|
16
|
+
end
|
@@ -9,7 +9,7 @@ Feature: Check sanity
|
|
9
9
|
|
10
10
|
Scenario: Check sanity on empty database
|
11
11
|
When I run "rake db:check_sanity"
|
12
|
-
Then I should see "Checking the following models:
|
12
|
+
Then I should see "Checking the following models: Category, InvalidRecord, Post, User"
|
13
13
|
Then I should not see any invalid records
|
14
14
|
|
15
15
|
Scenario: Check sanity on database with valid records
|
@@ -19,8 +19,8 @@ Feature: Check sanity
|
|
19
19
|
|
20
20
|
Scenario: Check sanity on database with invalid records
|
21
21
|
Given the database contains a few valid records
|
22
|
-
And the first
|
22
|
+
And the first author's username is empty and the first post category_id is nil
|
23
23
|
When I run "rake db:check_sanity"
|
24
24
|
Then I should see the following invalid records:
|
25
|
-
| User | 1 | {:username=>["can't be blank", "is too short (minimum is 3 characters)"]} |
|
25
|
+
| User | 1 | {:username=>["can't be blank", "is too short (minimum is 3 characters)", "can't be blank", "can't be blank", "is too short (minimum is 3 characters)"]} |
|
26
26
|
| Post | 1 | {:category=>["can't be blank"]} |
|
@@ -9,7 +9,7 @@ Feature: Check sanity with db storage
|
|
9
9
|
|
10
10
|
Scenario: Check sanity on empty database
|
11
11
|
When I run "rake db:check_sanity"
|
12
|
-
Then I should see "Checking the following models:
|
12
|
+
Then I should see "Checking the following models: Category, InvalidRecord, Post, User"
|
13
13
|
Then the table "invalid_records" should be empty
|
14
14
|
|
15
15
|
Scenario: Check sanity on database with valid records
|
@@ -19,7 +19,7 @@ Feature: Check sanity with db storage
|
|
19
19
|
|
20
20
|
Scenario: Check sanity on database with invalid records
|
21
21
|
Given the database contains a few valid records
|
22
|
-
And the first
|
22
|
+
And the first author's username is empty and the first post category_id is nil
|
23
23
|
When I run "rake db:check_sanity"
|
24
24
|
Then the table "invalid_records" should contain:
|
25
25
|
| User | 1 | {:username=>["is too short (minimum is 3 characters)"]} |
|
@@ -27,13 +27,13 @@ Feature: Check sanity with db storage
|
|
27
27
|
|
28
28
|
Scenario: Check sanity on database with invalid records now valid
|
29
29
|
Given the database contains a few valid records
|
30
|
-
And the first
|
30
|
+
And the first author's username is empty and the first post category_id is nil
|
31
31
|
When I run "rake db:check_sanity"
|
32
32
|
Then the table "invalid_records" should contain:
|
33
33
|
| User | 1 | {:username=>["is too short (minimum is 3 characters)"]} |
|
34
34
|
| Post | 1 | {:category=>["can't be blank"]} |
|
35
35
|
|
36
|
-
Given the first
|
36
|
+
Given the first author's username is "Greg"
|
37
37
|
When I run "rake db:check_sanity"
|
38
38
|
Then the table "invalid_records" should contain:
|
39
39
|
| Post | 1 | {:category=>["can't be blank"]} |
|
@@ -41,7 +41,7 @@ Feature: Check sanity with db storage
|
|
41
41
|
|
42
42
|
Scenario: Check sanity on database with invalid records that were invalid for different reasons earlier
|
43
43
|
Given the database contains a few valid records
|
44
|
-
And the first
|
44
|
+
And the first author's username is empty and the first post category_id is nil
|
45
45
|
When I run "rake db:check_sanity"
|
46
46
|
Then the table "invalid_records" should contain:
|
47
47
|
| User | 1 | {:username=>["is too short (minimum is 3 characters)"]} |
|
@@ -30,21 +30,21 @@ Given /^I have a rails app using 'active_sanity' with db storage$/ do
|
|
30
30
|
end
|
31
31
|
|
32
32
|
Given /^the database contains a few valid records$/ do
|
33
|
-
|
34
|
-
|
33
|
+
Author.create!(:first_name => "Greg", :last_name => "Bell", :username => "gregbell")
|
34
|
+
Publisher.create!(:first_name => "Sam", :last_name => "Vincent", :username => "samvincent")
|
35
35
|
Category.create!(:name => "Uncategorized")
|
36
|
-
Post.create!(:author =>
|
37
|
-
:title => "How ActiveAdmin changed the world", :body => "
|
36
|
+
Post.create!(:author => Author.first, :category => Category.first,
|
37
|
+
:title => "How ActiveAdmin changed the world", :body => "Lot of love.",
|
38
38
|
:published_at => 4.years.from_now)
|
39
39
|
end
|
40
40
|
|
41
|
-
Given /^the first
|
42
|
-
|
41
|
+
Given /^the first author's username is empty and the first post category_id is nil$/ do
|
42
|
+
Author.first.update_attribute(:username, "")
|
43
43
|
Post.first.update_attribute(:category_id, nil)
|
44
44
|
end
|
45
45
|
|
46
|
-
Given /^the first
|
47
|
-
|
46
|
+
Given /^the first author's username is "([^"]*)"$/ do |username|
|
47
|
+
Author.first.update_attribute('username', username)
|
48
48
|
end
|
49
49
|
|
50
50
|
Given /^the first post category is set$/ do
|
data/features/support/env.rb
CHANGED
@@ -4,17 +4,20 @@ require 'rubygems'
|
|
4
4
|
require "bundler"
|
5
5
|
Bundler.setup
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
raise unless
|
7
|
+
if File.directory?("test/rails_app")
|
8
|
+
Dir.chdir("test/rails_app") do
|
9
|
+
raise unless system("rm -f db/migrate/*create_invalid_records.rb && rake db:drop db:create db:migrate")
|
10
|
+
end
|
11
|
+
end
|
10
12
|
|
11
13
|
After do
|
12
14
|
# Reset DB!
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
tables = ['categories', 'invalid_records', 'posts', 'users']
|
16
|
+
conn = ActiveRecord::Base.connection
|
17
|
+
tables.each do |table|
|
18
|
+
if conn.table_exists?(table)
|
19
|
+
conn.execute("DELETE FROM '#{table}'")
|
20
|
+
conn.execute("DELETE FROM sqlite_sequence WHERE name='#{table}'")
|
21
|
+
end
|
19
22
|
end
|
20
23
|
end
|
@@ -8,30 +8,63 @@ module ActiveSanity
|
|
8
8
|
puts "Sanity Check"
|
9
9
|
puts "Checking the following models: #{models.join(', ')}"
|
10
10
|
|
11
|
+
# TODO: Wouldnt this list already be checked by the next all records call if those records do exist?
|
12
|
+
# This will validate and destroy the records that either dont exist currently, or are now valid. But the ones are continue to be invalid - these will
|
13
|
+
# have been run through the validation process twice
|
11
14
|
check_previously_invalid_records
|
12
15
|
check_all_records
|
13
16
|
end
|
14
17
|
|
18
|
+
# @return [Array] of [ActiveRecord::Base] direct descendants
|
15
19
|
def models
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@models ||= ActiveRecord::Base.subclasses
|
20
|
+
return @models if @models
|
21
|
+
|
22
|
+
load_all_models
|
23
|
+
|
24
|
+
@models ||= direct_active_record_base_descendants
|
23
25
|
end
|
24
26
|
|
25
27
|
protected
|
26
28
|
|
29
|
+
# Require all files under /app/models.
|
30
|
+
# All models under /lib are required when the rails app loads.
|
31
|
+
def load_all_models
|
32
|
+
Dir["#{Rails.root}/app/models/**/*.rb"].each { |file_path| require file_path rescue nil }
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array] of direct ActiveRecord::Base descendants.
|
36
|
+
# Example:
|
37
|
+
# The following tree:
|
38
|
+
# ActiveRecord::Base
|
39
|
+
# |
|
40
|
+
# |- User
|
41
|
+
# |- Account
|
42
|
+
# | |
|
43
|
+
# | |- PersonalAccount
|
44
|
+
# | |- BusinessAccount
|
45
|
+
#
|
46
|
+
# Should return: [Account, User]
|
47
|
+
def direct_active_record_base_descendants
|
48
|
+
ActiveRecord::Base.descendants.select(&:descends_from_active_record?).sort_by(&:name)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Remove records that are now valid from the list of invalid records.
|
27
52
|
def check_previously_invalid_records
|
28
53
|
return unless InvalidRecord.table_exists?
|
29
54
|
|
30
55
|
InvalidRecord.find_each do |invalid_record|
|
31
|
-
|
56
|
+
begin
|
57
|
+
invalid_record.destroy if invalid_record.record.valid?
|
58
|
+
rescue
|
59
|
+
# Record does not exists.
|
60
|
+
invalid_record.delete
|
61
|
+
end
|
32
62
|
end
|
33
63
|
end
|
34
64
|
|
65
|
+
# Go over every single record. When the record is not valid
|
66
|
+
# log it to STDOUT and into the invalid_records table if it exists.
|
67
|
+
#
|
35
68
|
def check_all_records
|
36
69
|
models.each do |model|
|
37
70
|
begin
|
@@ -54,22 +87,30 @@ module ActiveSanity
|
|
54
87
|
store_invalid_record(record)
|
55
88
|
end
|
56
89
|
|
90
|
+
# Say that the record is invalid. Example:
|
91
|
+
#
|
92
|
+
# Account | 10 | :name => "Can't be blank"
|
57
93
|
def log_invalid_record(record)
|
58
|
-
puts record
|
94
|
+
puts "#{type_of(record)} | #{record.id} | #{pretty_errors(record)}"
|
59
95
|
end
|
60
|
-
|
96
|
+
|
97
|
+
# Store invalid record in InvalidRecord table if it exists
|
61
98
|
def store_invalid_record(record)
|
62
99
|
return unless InvalidRecord.table_exists?
|
63
100
|
|
64
|
-
invalid_record = InvalidRecord.where(:record_type => record
|
101
|
+
invalid_record = InvalidRecord.where(:record_type => type_of(record), :record_id => record.id).first
|
65
102
|
invalid_record ||= InvalidRecord.new
|
66
103
|
invalid_record.record = record
|
67
104
|
invalid_record.validation_errors = record.errors
|
68
105
|
invalid_record.save!
|
69
106
|
end
|
70
107
|
|
108
|
+
def type_of(record)
|
109
|
+
record.class.base_class
|
110
|
+
end
|
111
|
+
|
71
112
|
def pretty_errors(record)
|
72
|
-
record.errors.inspect.sub(/^#<OrderedHash
|
113
|
+
record.errors.inspect.sub(/^#<OrderedHash (.*)>$/, '\1')
|
73
114
|
end
|
74
115
|
end
|
75
116
|
end
|
data/test/rails_template.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
|
2
2
|
# Generate some test models
|
3
|
+
|
4
|
+
# Post
|
3
5
|
generate :model, "post title:string body:text published_at:datetime author_id:integer category_id:integer"
|
4
6
|
post_code = <<-CODE
|
5
7
|
belongs_to :author, :class_name => 'User'
|
@@ -10,7 +12,17 @@ post_code = <<-CODE
|
|
10
12
|
CODE
|
11
13
|
inject_into_file 'app/models/post.rb', post_code, :after => "class Post < ActiveRecord::Base\n"
|
12
14
|
|
13
|
-
|
15
|
+
# Category
|
16
|
+
generate :model, 'category name:string description:text'
|
17
|
+
category_code = <<-CODE
|
18
|
+
has_many :posts
|
19
|
+
|
20
|
+
validates_presence_of :name
|
21
|
+
CODE
|
22
|
+
inject_into_file 'app/models/category.rb', category_code, :after => "class Category < ActiveRecord::Base\n"
|
23
|
+
|
24
|
+
# User
|
25
|
+
generate :model, "user first_name:string last_name:string username:string type:string"
|
14
26
|
user_code = <<-CODE
|
15
27
|
has_many :posts, :foreign_key => 'author_id'
|
16
28
|
|
@@ -19,14 +31,13 @@ user_code = <<-CODE
|
|
19
31
|
CODE
|
20
32
|
inject_into_file 'app/models/user.rb', user_code, :after => "class User < ActiveRecord::Base\n"
|
21
33
|
|
22
|
-
|
23
|
-
|
24
|
-
|
34
|
+
# Author < User
|
35
|
+
create_file 'app/models/author.rb', "class Author < User; end"
|
36
|
+
# Publisher < User
|
37
|
+
create_file 'app/models/publisher.rb', "class Publisher < User; end"
|
25
38
|
|
26
|
-
validates_presence_of :name
|
27
|
-
CODE
|
28
|
-
inject_into_file 'app/models/category.rb', category_code, :after => "class Category < ActiveRecord::Base\n"
|
29
39
|
|
40
|
+
# NotAModel
|
30
41
|
create_file 'app/models/not_a_model.rb', "class NotAModel; end"
|
31
42
|
|
32
43
|
# Add active_sanity
|
@@ -34,4 +45,3 @@ append_file 'Gemfile', "gem 'active_sanity', :path => '../../'"
|
|
34
45
|
|
35
46
|
run "bundle"
|
36
47
|
rake "db:migrate"
|
37
|
-
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_sanity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- VersaPay
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-
|
19
|
+
date: 2011-09-06 00:00:00 -07:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|