modelist 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ Modelist
2
+ =====
3
+
4
+ CLI and API to test and analyze ActiveRecord models.
5
+
6
+ ### Setup
7
+
8
+ In your Rails 3+ project, add this to your Gemfile:
9
+
10
+ gem 'modelist'
11
+
12
+ Then run:
13
+
14
+ bundle install
15
+
16
+ ### API Configuration
17
+
18
+ Not needed for CLI. Just used if you are using as an API:
19
+
20
+ Modelist::quiet = true
21
+
22
+ ### Usage
23
+
24
+ #### CLI
25
+
26
+ Modelist has a command-line interface with options to test for required circular dependencies, models that the specified models require due to nullable or validation constraints, or to test attributes and associations.
27
+
28
+ ##### Circular
29
+
30
+ Check ActiveRecord circular dependencies. Find circular chains of dependencies where foreign keys that are not primary keys of the models are all not nullable in the schema or not nullable because of ActiveRecord presence validation with:
31
+
32
+ bundle exec modelist circular
33
+
34
+ or:
35
+
36
+ bundle exec modelist circular my_model_1 my_model_2 --output-file=/path/to/errors.log
37
+
38
+ Example output:
39
+
40
+ The following non-nullable foreign keys used in ActiveRecord model associations are involved in circular dependencies:
41
+
42
+ beers.waitress_id -> waitresses.bartender_id -> bartenders.beer_id -> beers.waitress_id
43
+
44
+ beers.waitress_id -> waitresses.bartender_id -> bartenders.order_id -> order.beer_id -> beers.waitress_id
45
+
46
+
47
+ Distinct foreign keys involved in a circular dependency:
48
+
49
+ beers.waitress_id
50
+ order.beer_id
51
+ bartenders.beer_id
52
+ bartenders.order_id
53
+ waitresses.bartender_id
54
+
55
+
56
+ Foreign keys by number of circular dependency chains involved with:
57
+
58
+ 2 (out of 2): beers.waitress_id -> waitresses
59
+ 2 (out of 2): waitresses.bartender_id -> bartenders
60
+ 1 (out of 2): order.beer_id -> beers
61
+ 1 (out of 2): bartenders.order_id -> order
62
+ 1 (out of 2): bartenders.beer_id -> beers
63
+
64
+ Specify '--output-file' to provide an pathname of an errors file.
65
+
66
+ ##### Required
67
+
68
+ Find the models that the specified models have non-nullable or presence validations for directly and indirectly. You can use this to determine which models are really required with:
69
+
70
+ bundle exec modelist required my_model_1 my_model_2
71
+
72
+ Example output:
73
+
74
+ Required models:
75
+
76
+ Bartender
77
+ Beer
78
+ Order
79
+ Waitress
80
+
81
+ ##### Test
82
+
83
+ Test ActiveRecord models, their attributes, and associations with:
84
+
85
+ bundle exec modelist test
86
+
87
+ or:
88
+
89
+ bundle exec modelist test my_model_1 my_model_2 --output-file=/path/to/errors.log
90
+
91
+ Example output:
92
+
93
+ Specify '--output-file' to provide an pathname of an errors file.
94
+
95
+ ### API
96
+
97
+ Modelist::Analyst.find_required_models(:model1, :model2)
98
+ Modelist::CircularRefChecker.test_models(:model1, :model2, output_file: true)
99
+ Modelist::Tester.test_models(:model1, :model2, output_file: true)
100
+
101
+ ### License
102
+
103
+ Copyright (c) 2012 Gary S. Weaver, released under the [MIT license][lic].
104
+
105
+ [lic]: http://github.com/garysweaver/modelist/blob/master/LICENSE
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake/testtask'
2
+
3
+ #http://nicksda.apotomo.de/2010/10/testing-your-rails-3-engine-sitting-in-a-gem/
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/**/*_test.rb']
7
+ t.verbose = true
8
+ end
9
+
10
+ desc "Run tests"
11
+ task :default => :test
data/bin/modelist ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'modelist'
3
+ require 'modelist/cli'
@@ -0,0 +1,97 @@
1
+ module Modelist
2
+ class Analyst
3
+
4
+ # Check refs on all models or models specified, e.g.
5
+ # Modelist::Analyst.find_model_requirements
6
+ # or
7
+ # Modelist::Analyst.find_model_requirements(:my_model, :some_other_model)
8
+ def self.find_required_models(*args)
9
+ # less-dependent extract_options!
10
+ #options = args.last.is_a?(Hash) ? args.pop : {}
11
+ raise ArgumentError.new("Please supply one or more models") unless args.size > 0
12
+ results = {}
13
+ models = []
14
+ included_models = args.compact.collect{|m|m.to_sym}
15
+ puts "Checking models: #{included_models.collect{|m|m.inspect}.join(', ')}" if !Modelist.quiet? && included_models.size > 0
16
+ Dir[File.join('app','models','*.rb').to_s].each do |filename|
17
+ model_name = File.basename(filename).sub(/.rb$/, '')
18
+ next if included_models.size > 0 && !included_models.include?(model_name.to_sym)
19
+ load File.join('app','models',"#{model_name}.rb")
20
+
21
+ begin
22
+ model_class = model_name.camelize.constantize
23
+ rescue => e
24
+ puts "Problem in #{model_name.camelize}" unless Modelist.quiet?
25
+ raise e
26
+ end
27
+
28
+ next unless model_class.ancestors.include?(ActiveRecord::Base)
29
+ models << model_class
30
+ end
31
+
32
+ models.each do |model_class|
33
+ test_model(model_class, results)
34
+ end
35
+
36
+ unless Modelist.quiet? || !results[:required_models]
37
+ puts "Required models:"
38
+ puts
39
+ results[:required_models].collect{|c|c.name}.sort.each do |c|
40
+ puts "#{c}"
41
+ end
42
+ puts
43
+ end
44
+
45
+ return results[:required_models] ? results[:required_models] : []
46
+ end
47
+
48
+ # Get hash of required model information including non-nullable or validation presence associations throughout associations tree for model specified, e.g.
49
+ # Modelist::Analyst.test_model(:my_model)
50
+ # Also can take model class:
51
+ # Modelist::Analyst.test_model(MyModel)
52
+ def self.test_model(model_class, results = nil, model_and_association_names = [])
53
+ model_class = model_class.to_s.camelize.constantize unless model_class.is_a?(Class)
54
+
55
+ results ||= {}
56
+ results[:required_models] ||= []
57
+ results[:required_models] << model_class unless results[:required_models].include?(model_class)
58
+
59
+ model_class.reflections.collect {|association_name, reflection|
60
+ puts "warning: #{model_class}'s association #{reflection.name}'s foreign_key was nil. can't check." unless reflection.foreign_key || Modelist.quiet?
61
+ assc_sym = reflection.name.to_sym
62
+
63
+ begin
64
+ next_class = reflection.class_name.constantize
65
+ rescue => e
66
+ puts "Problem in #{model_class.name} with association: #{reflection.macro} #{assc_sym.inspect} which refers to class #{reflection.class_name}" unless Modelist.quiet?
67
+ raise e
68
+ end
69
+
70
+ has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator)
71
+ required = false
72
+ if reflection.macro == :belongs_to
73
+ # note: supports composite_primary_keys gem which stores primary_key as an array
74
+ foreign_key_is_also_primary_key = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}.include?(reflection.foreign_key.to_sym)
75
+ is_not_null_fkey_that_is_not_primary_key = model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym && !foreign_key_is_also_primary_key}
76
+ required = is_not_null_fkey_that_is_not_primary_key || has_presence_validator
77
+ else
78
+ # no nullable metadata on column if no foreign key in this table. we'd figure out the null requirement on the column if inspecting the child model
79
+ required = has_presence_validator
80
+ end
81
+
82
+ puts "#{model_class.name}.#{association_name} #{required ? 'required' : 'not required'}"
83
+
84
+ if required
85
+ key = [model_class.table_name.to_sym, reflection.foreign_key.to_sym, next_class.table_name.to_sym]
86
+ unless model_and_association_names.include?(key)
87
+ model_and_association_names << key
88
+ test_model(next_class, results, model_and_association_names)
89
+ end
90
+ end
91
+ }
92
+
93
+ model_and_association_names.pop
94
+ results
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,169 @@
1
+ module Modelist
2
+ class CircularRefChecker
3
+
4
+ # Check refs on all models or models specified, e.g.
5
+ # Modelist::CircularRefChecker.test_models
6
+ # or
7
+ # Modelist::CircularRefChecker.test_models(:my_model, :some_other_model)
8
+ def self.test_models(*args)
9
+ # less-dependent extract_options!
10
+ options = args.last.is_a?(Hash) ? args.pop : {}
11
+ results = {}
12
+ models = []
13
+ included_models = args.compact.collect{|m|m.to_sym}
14
+ puts "Checking models: #{included_models.collect{|m|m.inspect}.join(', ')}" if !Modelist.quiet? && included_models.size > 0
15
+ Dir[File.join('app','models','*.rb').to_s].each do |filename|
16
+ model_name = File.basename(filename).sub(/.rb$/, '')
17
+ next if included_models.size > 0 && !included_models.include?(model_name.to_sym)
18
+ load File.join('app','models',"#{model_name}.rb")
19
+
20
+ begin
21
+ model_class = model_name.camelize.constantize
22
+ rescue => e
23
+ puts "Problem in #{model_name.camelize}" unless Modelist.quiet?
24
+ raise e
25
+ end
26
+
27
+ next unless model_class.ancestors.include?(ActiveRecord::Base)
28
+ models << model_class
29
+ end
30
+
31
+ models.each do |model_class|
32
+ test_model(model_class, results)
33
+ end
34
+
35
+ if results[:circles].nil? || results[:circles].size == 0
36
+ unless Modelist.quiet?
37
+ puts
38
+ puts "No circular dependencies."
39
+ puts
40
+ end
41
+ return true
42
+ end
43
+
44
+ if !Modelist.quiet? || options[:output_file]
45
+ totals = {}
46
+ results[:circles_sorted].each do |arr|
47
+ arr.each do |key|
48
+ totals[key] = 0 unless totals[key]
49
+ totals[key] = totals[key] + 1
50
+ end
51
+ end
52
+
53
+ unless Modelist.quiet?
54
+ puts "The following non-nullable foreign keys used in ActiveRecord model associations are involved in circular dependencies:"
55
+ results[:circles].sort.each do |c|
56
+ puts
57
+ puts "#{c}"
58
+ end
59
+ puts
60
+ puts
61
+ puts "Distinct foreign keys involved in a circular dependency:"
62
+ puts
63
+ results[:offenders].sort.each do |c|
64
+ puts "#{c[0]}.#{c[1]}"
65
+ end
66
+ puts
67
+ puts
68
+ puts "Foreign keys by number of circular dependency chains involved with:"
69
+ puts
70
+ totals.sort_by {|k,v| v}.reverse.each do |arr|
71
+ c = arr[0]
72
+ t = arr[1]
73
+ puts "#{t} (out of #{results[:circles_sorted].size}): #{c[0]}.#{c[1]} -> #{c[2]}"
74
+ end
75
+ puts
76
+ end
77
+
78
+ if options[:output_file]
79
+ File.open(options[:output_file], "w") do |f|
80
+ f.puts "The following non-nullable foreign keys used in ActiveRecord model associations are involved in circular dependencies:"
81
+ results[:circles].sort.each do |c|
82
+ f.puts
83
+ f.puts "#{c}"
84
+ end
85
+ f.puts
86
+ f.puts
87
+ f.puts "Distinct foreign keys involved in a circular dependency:"
88
+ f.puts
89
+ results[:offenders].sort.each do |c|
90
+ f.puts "#{c[0]}.#{c[1]}"
91
+ end
92
+ f.puts
93
+ f.puts
94
+ f.puts "Foreign keys by number of circular dependency chains involved with:"
95
+ f.puts
96
+ totals.sort_by {|k,v| v}.reverse.each do |arr|
97
+ c = arr[0]
98
+ t = arr[1]
99
+ f.puts "#{t} (out of #{results[:circles_sorted].size}): #{c[0]}.#{c[1]} -> #{c[2]}"
100
+ end
101
+ f.puts
102
+ end
103
+ end
104
+ end
105
+
106
+ return false
107
+ end
108
+
109
+ # Get hash of circular reference information about associations tree on model specified, e.g.
110
+ # Modelist::CircularRefChecker.test_model(:my_model)
111
+ # Also can take model class:
112
+ # Modelist::CircularRefChecker.test_model(MyModel)
113
+ def self.test_model(model_class, results = nil, model_and_association_names = [])
114
+ model_class = model_class.to_s.camelize.constantize unless model_class.is_a?(Class)
115
+
116
+ results ||= {}
117
+ results[:offenders] ||= []
118
+ results[:circles_sorted] ||= []
119
+ results[:circles] ||= []
120
+ results[:selected_offenders] ||= []
121
+
122
+ model_class.reflections.collect {|association_name, reflection|
123
+ puts "warning: #{model_class}'s association #{reflection.name}'s foreign_key was nil. can't check." unless reflection.foreign_key || Modelist.quiet?
124
+ assc_sym = reflection.name.to_sym
125
+
126
+ begin
127
+ next_class = reflection.class_name.constantize
128
+ rescue => e
129
+ puts "Problem in #{model_class.name} with association: #{reflection.macro} #{assc_sym.inspect} which refers to class #{reflection.class_name}" unless Modelist.quiet?
130
+ raise e
131
+ end
132
+
133
+ has_presence_validator = model_class.validators_on(assc_sym).collect{|v|v.class}.include?(ActiveModel::Validations::PresenceValidator)
134
+ required = false
135
+ if reflection.macro == :belongs_to
136
+ # note: supports composite_primary_keys gem which stores primary_key as an array
137
+ foreign_key_is_also_primary_key = Array.wrap(model_class.primary_key).collect{|pk|pk.to_sym}.include?(reflection.foreign_key.to_sym)
138
+ is_not_null_fkey_that_is_not_primary_key = model_class.columns.any?{|c| !c.null && c.name.to_sym == reflection.foreign_key.to_sym && !foreign_key_is_also_primary_key}
139
+ required = is_not_null_fkey_that_is_not_primary_key || has_presence_validator
140
+ else
141
+ # no nullable metadata on column if no foreign key in this table. we'd figure out the null requirement on the column if inspecting the child model
142
+ required = has_presence_validator
143
+ end
144
+
145
+ if required
146
+ key = [model_class.table_name.to_sym, reflection.foreign_key.to_sym, next_class.table_name.to_sym]
147
+ if model_and_association_names.include?(key)
148
+ results[:offenders] << model_and_association_names.last unless results[:offenders].include?(model_and_association_names.last)
149
+ short = model_and_association_names.dup
150
+ # drop all preceding keys that have nothing to do with the circle
151
+ (short.index(key)).times {short.delete_at(0)}
152
+ sorted = short.sort
153
+ unless results[:circles_sorted].include?(sorted)
154
+ results[:circles_sorted] << sorted
155
+ results[:circles] << "#{(short + [key]).collect{|b|"#{b[0]}.#{b[1]}"}.join(' -> ')}".to_sym
156
+ end
157
+ else
158
+ model_and_association_names << key
159
+ test_model(next_class, results, model_and_association_names)
160
+ end
161
+ end
162
+ }
163
+
164
+ model_and_association_names.pop
165
+
166
+ results
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,44 @@
1
+ require 'thor'
2
+
3
+ module Modelist
4
+ class CLI < Thor
5
+ desc "test", "Tests specified models, their attributes, and their associations. Will test all models if no models are specified."
6
+ method_option :output_file, :desc => "Pathname of file to output to"
7
+ def test(*args)
8
+ # load Rails environment
9
+ require './config/environment'
10
+ require 'modelist/tester'
11
+ #args = options[:models] ? options.delete(:models).split(',').collect{|s|s.strip} << options : []
12
+ puts "args=#{args.inspect}"
13
+ args.each {|a| puts "Unsupported option: #{args.delete(a)}" if a.to_s.starts_with?('-')}
14
+ exit ::Modelist::Tester.test_models(*args) ? 0 : 1
15
+ end
16
+
17
+ desc "required", "Finds model dependencies in specified models."
18
+ def required(*args)
19
+ # load Rails environment
20
+ require './config/environment'
21
+ require 'modelist/analyst'
22
+ # args are [*options[:models], options hash (minus the models)]
23
+ #args = options[:models] ? options.delete(:models).split(',').collect{|s|s.strip} << options : []
24
+ puts "args=#{args.inspect}"
25
+ args.each {|a| puts "Unsupported option: #{args.delete(a)}" if a.to_s.starts_with?('-')}
26
+ Modelist::Analyst.find_required_models(*args)
27
+ exit 0
28
+ end
29
+
30
+ desc "circular", "Checks for required circular references in specified models. Will test all models if no models are specified."
31
+ method_option :output_file, :desc => "Pathname of file to output to"
32
+ def circular(*args)
33
+ # load Rails environment
34
+ require './config/environment'
35
+ require 'modelist/circular_ref_checker'
36
+ #args = options[:models] ? options.delete(:models).split(',').collect{|s|s.strip} << options : []
37
+ puts "args=#{args.inspect}"
38
+ args.each {|a| puts "Unsupported option: #{args.delete(a)}" if a.to_s.starts_with?('-')}
39
+ exit ::Modelist::CircularRefChecker.test_models(*args) ? 0 : 1
40
+ end
41
+ end
42
+ end
43
+
44
+ ::Modelist::CLI.start
@@ -0,0 +1,11 @@
1
+ # If you use Modelist in an application, you can specify:
2
+ # Modelist.quiet = true
3
+ # to limit output.
4
+ module Modelist
5
+ OPTIONS = [:quiet]
6
+
7
+ class << self
8
+ OPTIONS.each{|o|attr_accessor o; define_method("#{o}?".to_sym){!!send("#{o}")}}
9
+ def configure(&blk); class_eval(&blk); end
10
+ end
11
+ end
@@ -0,0 +1,184 @@
1
+ module Modelist
2
+ class Tester
3
+
4
+ # Check refs on all models or models specified, e.g.
5
+ # Modelist::Tester.test_models
6
+ # or
7
+ # Modelist::Tester.test_models(:my_model, :some_other_model)
8
+ def self.test_models(*args)
9
+ # less-dependent extract_options!
10
+ options = args.last.is_a?(Hash) ? args.pop : {}
11
+ results = {}
12
+ models = []
13
+ included_models = args.compact.collect{|m|m.to_sym}
14
+ puts "Checking models: #{included_models.collect{|m|m.inspect}.join(', ')}" if !Modelist.quiet? && included_models.size > 0
15
+ Dir[File.join('app','models','*.rb').to_s].each do |filename|
16
+ model_name = File.basename(filename).sub(/.rb$/, '')
17
+ next if included_models.size > 0 && !included_models.include?(model_name.to_sym)
18
+ load File.join('app','models',"#{model_name}.rb")
19
+
20
+ begin
21
+ model_class = model_name.camelize.constantize
22
+ rescue => e
23
+ puts "Problem in #{model_name.camelize}" unless Modelist.quiet?
24
+ results[:failures] << "FAILED: #{model_name}\n\n---\n\n#{filename}\n---\n#{formatted_errors.join("\n")}\n\n"
25
+ results[:failed] << model_class_name
26
+ raise e
27
+ end
28
+
29
+ next unless model_class.ancestors.include?(ActiveRecord::Base)
30
+ models << model_class
31
+ end
32
+
33
+ models.each do |model_class|
34
+ test_model(model_class, results)
35
+ end
36
+
37
+ unless Modelist.quiet?
38
+ puts "Done testing"
39
+ puts ""
40
+ puts ""
41
+
42
+ # Write warnings to file and console
43
+ if results[:failures] && results[:failures].size > 0
44
+ unless Modelist.quiet?
45
+ puts "Failures:"
46
+ puts
47
+ results[:failures].sort.each do |failure|
48
+ puts *(failure.split('\n'))
49
+ end
50
+ end
51
+
52
+ if options[:output_file]
53
+ File.open(options[:output_file], "w") do |f|
54
+ f.puts "Failures:"
55
+ f.puts
56
+ results[:failures].sort.each do |failure|
57
+ f.puts *(failure.split('\n'))
58
+ end
59
+ end
60
+
61
+ unless Modelist.quiet?
62
+ puts ""
63
+ puts "Errors in #{ERRORS_FILE}"
64
+ end
65
+ end
66
+ end
67
+
68
+ unless Modelist.quiet? || !results[:passed]
69
+ puts
70
+ puts "Passed (#{results[:passed].size}):"
71
+ puts "---"
72
+ results[:passed].each do |s|
73
+ puts s
74
+ end
75
+ puts
76
+ puts "Warnings (#{results[:warnings].size}):"
77
+ puts "---"
78
+ results[:warnings].each do |s|
79
+ puts s
80
+ end
81
+ puts
82
+ puts "Failed (#{results[:failed].size}):"
83
+ puts "---"
84
+ results[:failed].each do |s|
85
+ puts s
86
+ end
87
+ end
88
+ end
89
+
90
+ return results[:failed] ? results[:failed].size > 0 : true
91
+ end
92
+
93
+ # Test and get hash of information about model specified, e.g.
94
+ # Modelist::Tester.test_associations(:my_model)
95
+ # Also can take model class:
96
+ # Modelist::Tester.test_associations(MyModel)
97
+ def self.test_model(model_class, results = nil)
98
+ model_class = model_class.to_s.camelize.constantize unless model_class.is_a?(Class)
99
+
100
+ results ||= {}
101
+ results[:passed] ||= []
102
+ results[:warnings] ||= []
103
+ results[:failed] ||= []
104
+ results[:failures] ||= []
105
+
106
+ puts "Testing #{model_class}"
107
+ method_name_to_exception = {}
108
+ if model_class
109
+
110
+ model = nil
111
+
112
+ begin
113
+ model = model_class.first
114
+ puts "#{model_class}.first = #{model.inspect}"
115
+ if model.nil?
116
+ results[:warnings] << "#{model_class.name}.first was nil. Assuming there is no data in the associated table, but please verify."
117
+ end
118
+ rescue Exception => e
119
+ method_name_to_exception["#{model_class.name}.first"] = e
120
+ end
121
+
122
+ if model
123
+ begin
124
+ model = model_class.last
125
+ puts "#{model_class}.last = #{model.inspect}"
126
+ if model.nil?
127
+ results[:warnings] << "#{model_class.name}.last was nil."
128
+ end
129
+ rescue Exception => e
130
+ method_name_to_exception["#{model_class.name}.last"] = e
131
+ end
132
+ end
133
+
134
+ if model
135
+ attrs = model.attributes.keys
136
+ attrs.each do |attr|
137
+ begin
138
+ result = model.read_attribute(attr)
139
+ if result.is_a? Array
140
+ size = result.size
141
+ puts "#{model_class.name.underscore}.#{attr}.size = #{size}"
142
+ else
143
+ puts "#{model_class.name.underscore}.#{attr} = #{result}"
144
+ end
145
+ rescue Exception => e
146
+ method_name_to_exception["#{model_class.name}.#{attr}"] = e
147
+ end
148
+ end
149
+ end
150
+
151
+ model_class.reflections.collect do |association_name, reflection|
152
+ begin
153
+ reflection.class_name
154
+ rescue Exception => e
155
+ method_name_to_exception["#{model_class.name}'.{association_name}'s reflection class_name method"] = e
156
+ next
157
+ end
158
+
159
+ model = model_class.new unless model
160
+ begin
161
+ value = model.send(association_name.to_sym)
162
+ if value.nil?
163
+ #results[:warnings] << "(ignore) #{model_class_name}.#{association_name} was nil"
164
+ elsif (value.is_a?(Array) && value.size == 0)
165
+ #results[:warnings] << "(ignore) #{model_class_name}.#{association_name} was empty"
166
+ end
167
+ rescue Exception => e
168
+ method_name_to_exception["#{model_class.name}.last.#{association_name}"] = e
169
+ end
170
+ end
171
+ end
172
+
173
+ if method_name_to_exception.size > 0
174
+ formatted_errors = method_name_to_exception.keys.collect{|method_name|"#{method_name}: #{method_name_to_exception[method_name].message}\n#{method_name_to_exception[method_name].backtrace.join("\n")}\n---\n"}
175
+ results[:failures] << "FAILED: #{model_class.name}\n\n---\n#{formatted_errors.join("\n")}\n\n"
176
+ results[:failed] << model_class.name
177
+ else
178
+ results[:passed] << model_class.name
179
+ end
180
+
181
+ results
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,3 @@
1
+ module Modelist
2
+ VERSION = '0.0.1'
3
+ end
data/lib/modelist.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'modelist/version'
2
+ require 'modelist/config'
3
+ require 'modelist/analyst'
4
+ require 'modelist/circular_ref_checker'
5
+ require 'modelist/tester'
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: modelist
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gary S. Weaver
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: CLI and API to perform basic testing of all models, their attributes,
47
+ and their associations, and determine what models are depended on directly and indirectly
48
+ by a specific model.
49
+ email:
50
+ - garysweaver@gmail.com
51
+ executables:
52
+ - modelist
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - lib/modelist/analyst.rb
57
+ - lib/modelist/circular_ref_checker.rb
58
+ - lib/modelist/cli.rb
59
+ - lib/modelist/config.rb
60
+ - lib/modelist/tester.rb
61
+ - lib/modelist/version.rb
62
+ - lib/modelist.rb
63
+ - Rakefile
64
+ - README.md
65
+ - bin/modelist
66
+ homepage: https://github.com/garysweaver/modelist
67
+ licenses:
68
+ - MIT
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.24
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Tests and analyzes requirements of your ActiveRecord models.
91
+ test_files: []