stratagem 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/Manifest +99 -0
  2. data/Rakefile +17 -0
  3. data/bin/stratagem +10 -0
  4. data/init.rb +2 -0
  5. data/lib/bootstrap.rb +31 -0
  6. data/lib/stratagem/authentication.rb +64 -0
  7. data/lib/stratagem/auto_mock/aquifer.rb +86 -0
  8. data/lib/stratagem/auto_mock/factory.rb +213 -0
  9. data/lib/stratagem/auto_mock/value_generator.rb +174 -0
  10. data/lib/stratagem/auto_mock.rb +6 -0
  11. data/lib/stratagem/blocker.rb +16 -0
  12. data/lib/stratagem/client.rb +32 -0
  13. data/lib/stratagem/command.rb +13 -0
  14. data/lib/stratagem/commands/analyze.rb +22 -0
  15. data/lib/stratagem/commands/base.rb +11 -0
  16. data/lib/stratagem/commands/devel_crawl.rb +27 -0
  17. data/lib/stratagem/commands/devel_mock.rb +10 -0
  18. data/lib/stratagem/commands.rb +7 -0
  19. data/lib/stratagem/crawler/authentication.rb +109 -0
  20. data/lib/stratagem/crawler/form.rb +101 -0
  21. data/lib/stratagem/crawler/html_utils.rb +92 -0
  22. data/lib/stratagem/crawler/session.rb +296 -0
  23. data/lib/stratagem/crawler/site_model.rb +138 -0
  24. data/lib/stratagem/crawler/trace_utils.rb +10 -0
  25. data/lib/stratagem/crawler.rb +9 -0
  26. data/lib/stratagem/extensions/class.rb +9 -0
  27. data/lib/stratagem/extensions/hash.rb +16 -0
  28. data/lib/stratagem/extensions/module.rb +11 -0
  29. data/lib/stratagem/extensions/object.rb +15 -0
  30. data/lib/stratagem/extensions/red_parse.rb +86 -0
  31. data/lib/stratagem/extensions/string.rb +20 -0
  32. data/lib/stratagem/extensions.rb +6 -0
  33. data/lib/stratagem/framework_extensions/controllers/action_controller.rb +10 -0
  34. data/lib/stratagem/framework_extensions/controllers/action_mailer.rb +12 -0
  35. data/lib/stratagem/framework_extensions/controllers.rb +5 -0
  36. data/lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb +7 -0
  37. data/lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb +35 -0
  38. data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +103 -0
  39. data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +50 -0
  40. data/lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb +11 -0
  41. data/lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb +10 -0
  42. data/lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb +30 -0
  43. data/lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb +4 -0
  44. data/lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb +21 -0
  45. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb +13 -0
  46. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb +19 -0
  47. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb +30 -0
  48. data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb +4 -0
  49. data/lib/stratagem/framework_extensions/models/annotations.rb +79 -0
  50. data/lib/stratagem/framework_extensions/models/detect.rb +7 -0
  51. data/lib/stratagem/framework_extensions/models/metadata.rb +85 -0
  52. data/lib/stratagem/framework_extensions/models/mocking.rb +23 -0
  53. data/lib/stratagem/framework_extensions/models/tracing.rb +71 -0
  54. data/lib/stratagem/framework_extensions/models.rb +21 -0
  55. data/lib/stratagem/framework_extensions/rails.rb +8 -0
  56. data/lib/stratagem/framework_extensions.rb +6 -0
  57. data/lib/stratagem/interface/browser.rb +37 -0
  58. data/lib/stratagem/interface/public/images/backgrounds/content.png +0 -0
  59. data/lib/stratagem/interface/public/images/backgrounds/shadow.png +0 -0
  60. data/lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js +154 -0
  61. data/lib/stratagem/interface/public/javascripts/stratagem.js +27 -0
  62. data/lib/stratagem/interface/public/javascripts/stratagem_debug.js +53 -0
  63. data/lib/stratagem/interface/public/stylesheets/960.css +1 -0
  64. data/lib/stratagem/interface/public/stylesheets/reset.css +10 -0
  65. data/lib/stratagem/interface/public/stylesheets/stratagem.css +20 -0
  66. data/lib/stratagem/interface/public/stylesheets/stratagem_debug.css +20 -0
  67. data/lib/stratagem/interface/views/debug.haml +43 -0
  68. data/lib/stratagem/interface/views/index.haml +35 -0
  69. data/lib/stratagem/labs/auto_mock.rb +7 -0
  70. data/lib/stratagem/labs/crawler.rb +0 -0
  71. data/lib/stratagem/logger.rb +46 -0
  72. data/lib/stratagem/model/application.rb +157 -0
  73. data/lib/stratagem/model/components/base.rb +55 -0
  74. data/lib/stratagem/model/components/controller.rb +118 -0
  75. data/lib/stratagem/model/components/model.rb +170 -0
  76. data/lib/stratagem/model/components/reference.rb +30 -0
  77. data/lib/stratagem/model/components/route.rb +53 -0
  78. data/lib/stratagem/model/components/static_file.rb +18 -0
  79. data/lib/stratagem/model/components/view.rb +186 -0
  80. data/lib/stratagem/model/parse_util.rb +61 -0
  81. data/lib/stratagem/model.rb +12 -0
  82. data/lib/stratagem/model_builder.rb +146 -0
  83. data/lib/stratagem/recipes/deploy.rb +30 -0
  84. data/lib/stratagem/scan/checks/capistrano/secure_deploy.rb +43 -0
  85. data/lib/stratagem/scan/checks/email_address.rb +15 -0
  86. data/lib/stratagem/scan/checks/error_pages.rb +25 -0
  87. data/lib/stratagem/scan/checks/filter_parameter_logging.rb +6 -0
  88. data/lib/stratagem/scan/checks/mongo_mapper/base.rb +19 -0
  89. data/lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb +32 -0
  90. data/lib/stratagem/scan/checks/routes.rb +16 -0
  91. data/lib/stratagem/scan/checks/ssl/secure_login_page.rb +19 -0
  92. data/lib/stratagem/scan/checks/ssl/secure_login_submit.rb +18 -0
  93. data/lib/stratagem/scan/result.rb +45 -0
  94. data/lib/stratagem/scan.rb +19 -0
  95. data/lib/stratagem/scanner.rb +32 -0
  96. data/lib/stratagem/site_crawler.rb +47 -0
  97. data/lib/stratagem/snapshot.rb +33 -0
  98. data/lib/stratagem.rb +77 -0
  99. data/lib/tasks/_old_stratagem.rake +99 -0
  100. data/stratagem.gemspec +56 -0
  101. metadata +380 -0
data/Manifest ADDED
@@ -0,0 +1,99 @@
1
+ Manifest
2
+ Rakefile
3
+ bin/stratagem
4
+ init.rb
5
+ lib/bootstrap.rb
6
+ lib/stratagem.rb
7
+ lib/stratagem/authentication.rb
8
+ lib/stratagem/auto_mock.rb
9
+ lib/stratagem/auto_mock/aquifer.rb
10
+ lib/stratagem/auto_mock/factory.rb
11
+ lib/stratagem/auto_mock/value_generator.rb
12
+ lib/stratagem/blocker.rb
13
+ lib/stratagem/client.rb
14
+ lib/stratagem/command.rb
15
+ lib/stratagem/commands.rb
16
+ lib/stratagem/commands/analyze.rb
17
+ lib/stratagem/commands/base.rb
18
+ lib/stratagem/commands/devel_crawl.rb
19
+ lib/stratagem/commands/devel_mock.rb
20
+ lib/stratagem/crawler.rb
21
+ lib/stratagem/crawler/authentication.rb
22
+ lib/stratagem/crawler/form.rb
23
+ lib/stratagem/crawler/html_utils.rb
24
+ lib/stratagem/crawler/session.rb
25
+ lib/stratagem/crawler/site_model.rb
26
+ lib/stratagem/crawler/trace_utils.rb
27
+ lib/stratagem/extensions.rb
28
+ lib/stratagem/extensions/class.rb
29
+ lib/stratagem/extensions/hash.rb
30
+ lib/stratagem/extensions/module.rb
31
+ lib/stratagem/extensions/object.rb
32
+ lib/stratagem/extensions/red_parse.rb
33
+ lib/stratagem/extensions/string.rb
34
+ lib/stratagem/framework_extensions.rb
35
+ lib/stratagem/framework_extensions/controllers.rb
36
+ lib/stratagem/framework_extensions/controllers/action_controller.rb
37
+ lib/stratagem/framework_extensions/controllers/action_mailer.rb
38
+ lib/stratagem/framework_extensions/models.rb
39
+ lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb
40
+ lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb
41
+ lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb
42
+ lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb
43
+ lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb
44
+ lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb
45
+ lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb
46
+ lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb
47
+ lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb
48
+ lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb
49
+ lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb
50
+ lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb
51
+ lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb
52
+ lib/stratagem/framework_extensions/models/annotations.rb
53
+ lib/stratagem/framework_extensions/models/detect.rb
54
+ lib/stratagem/framework_extensions/models/metadata.rb
55
+ lib/stratagem/framework_extensions/models/mocking.rb
56
+ lib/stratagem/framework_extensions/models/tracing.rb
57
+ lib/stratagem/framework_extensions/rails.rb
58
+ lib/stratagem/interface/browser.rb
59
+ lib/stratagem/interface/public/images/backgrounds/content.png
60
+ lib/stratagem/interface/public/images/backgrounds/shadow.png
61
+ lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js
62
+ lib/stratagem/interface/public/javascripts/stratagem.js
63
+ lib/stratagem/interface/public/javascripts/stratagem_debug.js
64
+ lib/stratagem/interface/public/stylesheets/960.css
65
+ lib/stratagem/interface/public/stylesheets/reset.css
66
+ lib/stratagem/interface/public/stylesheets/stratagem.css
67
+ lib/stratagem/interface/public/stylesheets/stratagem_debug.css
68
+ lib/stratagem/interface/views/debug.haml
69
+ lib/stratagem/interface/views/index.haml
70
+ lib/stratagem/labs/auto_mock.rb
71
+ lib/stratagem/labs/crawler.rb
72
+ lib/stratagem/logger.rb
73
+ lib/stratagem/model.rb
74
+ lib/stratagem/model/application.rb
75
+ lib/stratagem/model/components/base.rb
76
+ lib/stratagem/model/components/controller.rb
77
+ lib/stratagem/model/components/model.rb
78
+ lib/stratagem/model/components/reference.rb
79
+ lib/stratagem/model/components/route.rb
80
+ lib/stratagem/model/components/static_file.rb
81
+ lib/stratagem/model/components/view.rb
82
+ lib/stratagem/model/parse_util.rb
83
+ lib/stratagem/model_builder.rb
84
+ lib/stratagem/recipes/deploy.rb
85
+ lib/stratagem/scan.rb
86
+ lib/stratagem/scan/checks/capistrano/secure_deploy.rb
87
+ lib/stratagem/scan/checks/email_address.rb
88
+ lib/stratagem/scan/checks/error_pages.rb
89
+ lib/stratagem/scan/checks/filter_parameter_logging.rb
90
+ lib/stratagem/scan/checks/mongo_mapper/base.rb
91
+ lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb
92
+ lib/stratagem/scan/checks/routes.rb
93
+ lib/stratagem/scan/checks/ssl/secure_login_page.rb
94
+ lib/stratagem/scan/checks/ssl/secure_login_submit.rb
95
+ lib/stratagem/scan/result.rb
96
+ lib/stratagem/scanner.rb
97
+ lib/stratagem/site_crawler.rb
98
+ lib/stratagem/snapshot.rb
99
+ lib/tasks/_old_stratagem.rake
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('stratagem', '0.1.7') do |p|
6
+ p.description = "Intuitive security analysis of your Rails applications"
7
+ p.url = "http://github.com/stratagem/stratagem"
8
+ p.author = "Charles Grimes"
9
+ p.email = "cj@stratagemapp.com"
10
+ p.executable_pattern = ['bin/*']
11
+ p.ignore_pattern = ["tmp/*", "script/*", "spec/*", "webapp/*"]
12
+ p.runtime_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "haml >=3.0.0"]
13
+ p.development_dependencies = ["launchy >=0.3.5", "redparse >=0.8.4", "sinatra =1.0", "haml >=3.0.0", "webrat >=0.4.3"]
14
+ # p.requirements ["Install the stratagem-ui gem for the web browser interface."]
15
+ end
16
+
17
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/bin/stratagem ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ RAILS_ENV='test'
4
+
5
+ require 'rubygems'
6
+ require File.join(Dir.pwd, 'config', 'boot')
7
+ require 'bootstrap'
8
+ require './config/environment'
9
+
10
+ Stratagem::Command.run(ARGV[0])
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'launchy'
2
+ require 'stratagem'
data/lib/bootstrap.rb ADDED
@@ -0,0 +1,31 @@
1
+
2
+ # inject environment helpers into Rails class to be used during bootstrapping
3
+ Rails.class_eval do
4
+ def self.stratagem_production_env
5
+ @stratagem_initial_env ||= @_env
6
+ @_env = ActiveSupport::StringInquirer.new('production')
7
+ end
8
+
9
+ def self.stratagem_restore_env
10
+ @_env = @stratagem_initial_env
11
+ end
12
+ end
13
+
14
+
15
+ # Inject env change into Rails initializer prior to loading controllers
16
+ # but after framework is initialized. This is to force SSL to work as
17
+ # if the app were running in production mode
18
+ Rails::Initializer.class_eval do
19
+ alias_method :rails_load_application_classes, :load_application_classes
20
+
21
+ def load_application_classes
22
+ puts "LOADING APPLICATION CLASSES"
23
+ # load stratagem
24
+ require 'stratagem'
25
+
26
+ Rails.stratagem_production_env
27
+ rails_load_application_classes
28
+ Rails.stratagem_restore_env
29
+ end
30
+
31
+ end
@@ -0,0 +1,64 @@
1
+ require 'open-uri'
2
+ require 'singleton'
3
+
4
+ module Stratagem
5
+ class Authentication
6
+ include Singleton
7
+
8
+ FILENAME = '.stratagem'
9
+
10
+ attr_reader :token, :project
11
+
12
+ def store_credentials(account, token, project)
13
+ File.open(FILENAME, 'w') do |f|
14
+ f.puts account
15
+ f.puts token
16
+ f.puts project
17
+ end
18
+ reload_credentials
19
+ end
20
+
21
+ def url
22
+ if (credentials.nil?)
23
+ base_url+"/project_links/new"
24
+ else
25
+ base_url+"/project_links/validate/#{credentials[:token]}/#{credentials[:project]}"
26
+ end
27
+ end
28
+
29
+ def base_url
30
+ subdomain = credentials ? credentials[:account] : 'www'
31
+ "http://#{subdomain}.#{Stratagem.domain}"
32
+ end
33
+
34
+ def project_url
35
+ "#{base_url}/projects/#{credentials[:project]}?api_key=#{credentials[:token]}"
36
+ end
37
+
38
+ def credentials
39
+ @credentials ||= load_credentials
40
+ end
41
+
42
+ private
43
+ def reload_credentials
44
+ @credentials = nil
45
+ credentials
46
+ end
47
+
48
+ def load_credentials
49
+ if File.exists?(FILENAME)
50
+ credentials = nil
51
+ File.open(FILENAME) do |f|
52
+ credentials = {
53
+ :account => f.readline.strip,
54
+ :token => f.readline.strip,
55
+ :project => f.readline.strip
56
+ }
57
+ end
58
+ return credentials
59
+ else
60
+ return nil
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,86 @@
1
+ require 'timeout'
2
+
3
+ module Stratagem::AutoMock
4
+ class Aquifer
5
+ include Singleton
6
+ include Factory
7
+
8
+ attr_accessor :application
9
+
10
+ def self.init(application)
11
+ at_exit { self.instance.destroy }
12
+ self.instance.application = application
13
+ self.instance
14
+ end
15
+
16
+ def destroy
17
+ objects = self.repo.values.inject([]) {|memo,obj| memo += obj.compact }
18
+ i = 0
19
+ while (objects.size > 0 && ((i+=1) < objects.size))
20
+ objects = objects.select do |instance|
21
+ puts "deleting #{instance.class.name}"
22
+ begin
23
+ instance.destroy
24
+ rescue
25
+ begin
26
+ instance.delete
27
+ rescue
28
+ puts "Unable to delete object: #{instance.class.name} - #{$!.message}"
29
+ puts $!.backtrace
30
+ end
31
+ end
32
+ !instance.frozen?
33
+ end
34
+ end
35
+ end
36
+
37
+ def instances_of(model_klass)
38
+ repo[model_klass].clone
39
+ end
40
+
41
+ def random_instance(model_klass)
42
+ objects = repo[model_klass]
43
+ puts "found #{objects.size} instances in well"
44
+ instance = objects[rand objects.size]
45
+ instance
46
+ end
47
+
48
+ def fill
49
+ Stratagem.logger.phase "mocking_models"
50
+ application.models.each do |meta_model|
51
+ models = mock_model(meta_model.klass) if (meta_model.stratagem?)
52
+ end
53
+ puts "aquifer full"
54
+ application.models.each do |meta_model|
55
+ puts "#{meta_model.klass.name}"
56
+ (repo[meta_model.klass] || []).each do |instance|
57
+ puts "\t#{instance.id}"
58
+ end
59
+ end
60
+ end
61
+
62
+ def mock_model(klass, count=2)
63
+ count.times do |i|
64
+ instance,valid = mock(klass)
65
+ end
66
+ end
67
+
68
+ protected
69
+
70
+ def instance_call(instance, method, *args)
71
+ begin
72
+ timeout(3) do
73
+ instance.send(method, *args)
74
+ end
75
+ rescue Timeout::Error
76
+ e = StratagemError.new("unable to call method #{method} on model #{instance.class.name}, timed out") # log it
77
+ puts e.message
78
+ end
79
+ end
80
+
81
+
82
+ def repo
83
+ mocked
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,213 @@
1
+ module Stratagem::AutoMock
2
+ @@bank = {} # key is model class, value is an array of mocked instances
3
+
4
+ # todo - move these to a generic automock file
5
+ class MockError < StratagemError; end
6
+ class UnsupportedModelError < MockError; end
7
+ class UnsupportedColumnTypeError < MockError; end
8
+ class ColumnValidationError < MockError; end
9
+ class ParentsExhaustedError < MockError; end
10
+
11
+ module Factory
12
+ include ValueGenerator
13
+
14
+ def mock(model,mock_chain=[], belongs_to=nil)
15
+ return if mock_chain.select {|m| m == model }.size > 1
16
+ mock_chain << model
17
+
18
+ object,valid = model.new, nil
19
+ begin
20
+ 10.times do |i|
21
+ log "mocking model #{model.name}"
22
+ populate_attributes(object, mock_chain)
23
+ correct_invalid_columns(object, mock_chain)
24
+
25
+ valid = populate_relations(object, mock_chain, belongs_to)
26
+
27
+ # allow relationship attributes to be invalid
28
+ valid = save_object(object) if (valid)
29
+
30
+ if valid
31
+ add_mocked(object)
32
+ break
33
+ end
34
+ end
35
+ rescue
36
+ puts $!.class.name
37
+ puts $!.message
38
+ puts $!.backtrace
39
+ e = MockError.new("Unexpected error mocking model #{model.name}")
40
+ e.target = $!
41
+ # raise e
42
+ end
43
+
44
+ return [object,valid] if valid
45
+ end
46
+
47
+ protected
48
+
49
+ def add_mocked(instance)
50
+ (mocked[instance.class] ||= []) << instance
51
+ end
52
+
53
+ def mocked(model=nil)
54
+ @mocked ||= {}
55
+ if (model)
56
+ @mocked[model] ||= []
57
+ else
58
+ @mocked
59
+ end
60
+ end
61
+
62
+ def populate_attributes(object, mock_chain)
63
+ exclude = [:created_at, :updated_at] + object.stratagem.relation_names + object.stratagem.foreign_keys + object.stratagem.internal_attributes
64
+
65
+ # exclude the confirmation attributes, as they will be set automatically
66
+ exclude += object.stratagem.validations(nil, :validates_confirmation_of).map {|v| (v.field.to_s+"_confirmation").to_sym }
67
+ exclude += object.stratagem.exclude_attributes_for_mocking
68
+ exclude += object.stratagem.internal_attributes
69
+
70
+ exclude_regex = [/^photo/, /picture/]
71
+ names = object.stratagem.attribute_names.select {|n| n !~ /_id$/ } - exclude
72
+ puts "mocking names: #{names.inspect}"
73
+ puts "excluded: #{exclude.inspect}"
74
+ puts "internal: #{object.stratagem.internal_attributes.inspect}"
75
+ names.each do |attr_name|
76
+ next if exclude_regex.find {|r| attr_name =~ r }
77
+ set_attribute_value(object, attr_name, mock_chain)
78
+ end
79
+
80
+ # check for fields that should be forced into specific values
81
+ object.stratagem.validations.select {|v| [:validates_acceptance_of, :validates_presence_of].include?(v.validation) }.each do |validator|
82
+ value = validator.args[:with] || validator.args[:accept]
83
+ set_attribute_value(object, validator.field, mock_chain, value) if (value && value.kind_of?(String))
84
+ end
85
+
86
+ (object.stratagem.invalid_columns(object)).size == 0
87
+ end
88
+
89
+ def populate_relations(object, mock_chain, belongs_to=nil)
90
+ # belongs_to relations
91
+ object.stratagem.relation_names(:belongs_to).each do |attr_name|
92
+ relation = object.stratagem.relation(attr_name)
93
+ if (belongs_to && belongs_to.class == relation.klass)
94
+ object.send(attr_name.to_s+"=", belongs_to)
95
+ else
96
+ instances = mocked(relation.klass)
97
+ if (instances.size > 1)
98
+ object.send(attr_name.to_s+"=", instances[rand instances.size])
99
+ else
100
+ set_attribute_value(object, attr_name, mock_chain)
101
+ end
102
+ end
103
+ end
104
+
105
+ # has_many relations
106
+ # object.stratagem.relation_names(:has_many).each do |attr_name|
107
+ # relation = object.stratagem.relation(attr_name)
108
+ # # select the instances that don't have an existing belongs_to for this relation type
109
+ # instances = mocked(relation.klass).select {|i|
110
+ # br = i.stratagem.relations.find {|r| r.klass == i.class }
111
+ # if (br && !i.send(br.name).nil?)
112
+ # false
113
+ # else
114
+ # true
115
+ # end
116
+ # }
117
+ # if (instances.size > 2)
118
+ # collection = object.send(attr_name.to_s)
119
+ # collection << instances[rand instances.size]
120
+ # else
121
+ # mock(relation.klass, mock_chain, object)
122
+ # end
123
+ # end
124
+
125
+ (object.stratagem.invalid_columns(object)).size == 0
126
+ end
127
+
128
+ def correct_invalid_columns(object, mock_chain)
129
+ exclude = [:created_at, :updated_at] + object.stratagem.relation_names + object.stratagem.foreign_keys
130
+
131
+ # randomize over incorrect values and try to correct
132
+ (object.stratagem.invalid_columns(object) - exclude).each do |error_column|
133
+ puts "\t\terrors for #{error_column}: #{object.errors[error_column].inspect}"
134
+
135
+ i = 0
136
+ begin
137
+ value = set_attribute_value(object, error_column, mock_chain)
138
+ print_value = value || 'nil'
139
+ #log "\tattempting value #{print_value} for column #{error_column} to correct error - invalid columns: #{object.stratagem.invalid_columns(object)}"
140
+ i += 1
141
+ end while object.stratagem.invalid_columns(object).include?(error_column) && (i < 200)
142
+
143
+ if object.stratagem.invalid_columns(object).include?(error_column)
144
+ raise Stratagem::AutoMock::ColumnValidationError.new("Column #{error_column} cannot be resolved")
145
+ end
146
+ end
147
+ object
148
+ end
149
+
150
+ def save_object(object)
151
+ valid = true
152
+ retries = 0
153
+ begin
154
+ object.save!
155
+ rescue Stratagem::AutoMock::MockError
156
+ puts "Error mocking #{klass.name}"
157
+ valid = false
158
+ rescue
159
+ puts object.stratagem.mock_attributes.inspect
160
+ puts "Error mocking #{object.class.name} - #{$!.message}"
161
+ error_column = object.stratagem.column_from_error($!)
162
+ puts "\terror column: #{error_column}"
163
+ if error_column && !object.stratagem.foreign_keys.include?(error_column)
164
+ if retries < 100
165
+ puts "\tretrying to correct field #{error_column} that was missing validation"
166
+ set_attribute_value(object, error_column, [])
167
+ retries += 1
168
+ retry
169
+ else
170
+ valid = false
171
+ end
172
+ else
173
+ valid = false
174
+ end
175
+ puts $!.backtrace unless valid
176
+ end
177
+ valid
178
+ end
179
+
180
+ def set_attribute_value(object, attr_name, mock_chain=[], value=nil)
181
+ return if attr_name.to_s =~ /_confirmation$/ # ignore confirmation fields
182
+
183
+ unless value
184
+ if (object.stratagem.relation_names.include?(attr_name))
185
+ puts "\tmocking relation #{attr_name} for #{object.class.name}"
186
+ relation = object.stratagem.relation(attr_name)
187
+ value,valid = mock(relation.klass, mock_chain+[object.class])
188
+ value = nil unless valid
189
+ else
190
+ value = generate_value(attr_name, object.stratagem.attribute_type(attr_name))
191
+ #puts "#{attr_name} -> #{value}"
192
+ end
193
+ end
194
+
195
+ object.send("#{attr_name}=", value)
196
+ object.stratagem.write_mock_attribute(attr_name, value)
197
+
198
+ confirmation_writer = "#{attr_name}_confirmation="
199
+ if object.methods_include?(confirmation_writer) || (object.stratagem.validations(attr_name, :validates_confirmation_of).size > 0)
200
+ puts "setting confirmation field for #{attr_name}"
201
+ object.send(confirmation_writer, value)
202
+ object.stratagem.write_mock_attribute("#{attr_name}_confirmation".to_sym, value)
203
+ end
204
+
205
+
206
+ value
207
+ end
208
+
209
+ def log(msg)
210
+ Stratagem.logger.debug msg
211
+ end
212
+ end
213
+ end