stratagem 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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