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.
- data/Manifest +99 -0
- data/Rakefile +17 -0
- data/bin/stratagem +10 -0
- data/init.rb +2 -0
- data/lib/bootstrap.rb +31 -0
- data/lib/stratagem/authentication.rb +64 -0
- data/lib/stratagem/auto_mock/aquifer.rb +86 -0
- data/lib/stratagem/auto_mock/factory.rb +213 -0
- data/lib/stratagem/auto_mock/value_generator.rb +174 -0
- data/lib/stratagem/auto_mock.rb +6 -0
- data/lib/stratagem/blocker.rb +16 -0
- data/lib/stratagem/client.rb +32 -0
- data/lib/stratagem/command.rb +13 -0
- data/lib/stratagem/commands/analyze.rb +22 -0
- data/lib/stratagem/commands/base.rb +11 -0
- data/lib/stratagem/commands/devel_crawl.rb +27 -0
- data/lib/stratagem/commands/devel_mock.rb +10 -0
- data/lib/stratagem/commands.rb +7 -0
- data/lib/stratagem/crawler/authentication.rb +109 -0
- data/lib/stratagem/crawler/form.rb +101 -0
- data/lib/stratagem/crawler/html_utils.rb +92 -0
- data/lib/stratagem/crawler/session.rb +296 -0
- data/lib/stratagem/crawler/site_model.rb +138 -0
- data/lib/stratagem/crawler/trace_utils.rb +10 -0
- data/lib/stratagem/crawler.rb +9 -0
- data/lib/stratagem/extensions/class.rb +9 -0
- data/lib/stratagem/extensions/hash.rb +16 -0
- data/lib/stratagem/extensions/module.rb +11 -0
- data/lib/stratagem/extensions/object.rb +15 -0
- data/lib/stratagem/extensions/red_parse.rb +86 -0
- data/lib/stratagem/extensions/string.rb +20 -0
- data/lib/stratagem/extensions.rb +6 -0
- data/lib/stratagem/framework_extensions/controllers/action_controller.rb +10 -0
- data/lib/stratagem/framework_extensions/controllers/action_mailer.rb +12 -0
- data/lib/stratagem/framework_extensions/controllers.rb +5 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb +35 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +103 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +50 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/detect.rb +11 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/extensions.rb +10 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/metadata.rb +30 -0
- data/lib/stratagem/framework_extensions/models/adapters/authlogic/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/adapters/common/authentication_metadata.rb +21 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/detect.rb +13 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/extensions.rb +19 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/metadata.rb +30 -0
- data/lib/stratagem/framework_extensions/models/adapters/restful_authentication/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/annotations.rb +79 -0
- data/lib/stratagem/framework_extensions/models/detect.rb +7 -0
- data/lib/stratagem/framework_extensions/models/metadata.rb +85 -0
- data/lib/stratagem/framework_extensions/models/mocking.rb +23 -0
- data/lib/stratagem/framework_extensions/models/tracing.rb +71 -0
- data/lib/stratagem/framework_extensions/models.rb +21 -0
- data/lib/stratagem/framework_extensions/rails.rb +8 -0
- data/lib/stratagem/framework_extensions.rb +6 -0
- data/lib/stratagem/interface/browser.rb +37 -0
- data/lib/stratagem/interface/public/images/backgrounds/content.png +0 -0
- data/lib/stratagem/interface/public/images/backgrounds/shadow.png +0 -0
- data/lib/stratagem/interface/public/javascripts/jquery-1.4.2.min.js +154 -0
- data/lib/stratagem/interface/public/javascripts/stratagem.js +27 -0
- data/lib/stratagem/interface/public/javascripts/stratagem_debug.js +53 -0
- data/lib/stratagem/interface/public/stylesheets/960.css +1 -0
- data/lib/stratagem/interface/public/stylesheets/reset.css +10 -0
- data/lib/stratagem/interface/public/stylesheets/stratagem.css +20 -0
- data/lib/stratagem/interface/public/stylesheets/stratagem_debug.css +20 -0
- data/lib/stratagem/interface/views/debug.haml +43 -0
- data/lib/stratagem/interface/views/index.haml +35 -0
- data/lib/stratagem/labs/auto_mock.rb +7 -0
- data/lib/stratagem/labs/crawler.rb +0 -0
- data/lib/stratagem/logger.rb +46 -0
- data/lib/stratagem/model/application.rb +157 -0
- data/lib/stratagem/model/components/base.rb +55 -0
- data/lib/stratagem/model/components/controller.rb +118 -0
- data/lib/stratagem/model/components/model.rb +170 -0
- data/lib/stratagem/model/components/reference.rb +30 -0
- data/lib/stratagem/model/components/route.rb +53 -0
- data/lib/stratagem/model/components/static_file.rb +18 -0
- data/lib/stratagem/model/components/view.rb +186 -0
- data/lib/stratagem/model/parse_util.rb +61 -0
- data/lib/stratagem/model.rb +12 -0
- data/lib/stratagem/model_builder.rb +146 -0
- data/lib/stratagem/recipes/deploy.rb +30 -0
- data/lib/stratagem/scan/checks/capistrano/secure_deploy.rb +43 -0
- data/lib/stratagem/scan/checks/email_address.rb +15 -0
- data/lib/stratagem/scan/checks/error_pages.rb +25 -0
- data/lib/stratagem/scan/checks/filter_parameter_logging.rb +6 -0
- data/lib/stratagem/scan/checks/mongo_mapper/base.rb +19 -0
- data/lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb +32 -0
- data/lib/stratagem/scan/checks/routes.rb +16 -0
- data/lib/stratagem/scan/checks/ssl/secure_login_page.rb +19 -0
- data/lib/stratagem/scan/checks/ssl/secure_login_submit.rb +18 -0
- data/lib/stratagem/scan/result.rb +45 -0
- data/lib/stratagem/scan.rb +19 -0
- data/lib/stratagem/scanner.rb +32 -0
- data/lib/stratagem/site_crawler.rb +47 -0
- data/lib/stratagem/snapshot.rb +33 -0
- data/lib/stratagem.rb +77 -0
- data/lib/tasks/_old_stratagem.rake +99 -0
- data/stratagem.gemspec +56 -0
- metadata +380 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::ActiveModel
|
|
2
|
+
|
|
3
|
+
# prefix method names with to avoid collision
|
|
4
|
+
class Metadata
|
|
5
|
+
|
|
6
|
+
attr_reader :model, :instance
|
|
7
|
+
|
|
8
|
+
def initialize(model)
|
|
9
|
+
@model = model
|
|
10
|
+
@instance = @model.new unless (@model == ActiveRecord::Base)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def relations(relation_type=nil) # :belongs_to, :has_many
|
|
14
|
+
@relations ||= {}
|
|
15
|
+
@relations[relation_type || :all] ||= model.reflect_on_all_associations(relation_type).map {|a|
|
|
16
|
+
Stratagem::ApplicationExtensions::Models::Metadata::StratagemAssociation.new(a.name.to_sym, a.association_foreign_key.to_sym, a.klass, a.macro)
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def unaccessible_attributes
|
|
21
|
+
attrs = []
|
|
22
|
+
if (model.accessible_attributes)
|
|
23
|
+
attrs = model.stratagem.attribute_names - model.accessible_attributes.map {|a| a.to_sym }
|
|
24
|
+
end
|
|
25
|
+
attrs += model.protected_attributes.map {|a| a.to_sym } if model.protected_attributes
|
|
26
|
+
attrs
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# parses a database error and returns the columns that had problems
|
|
30
|
+
# this is typically a not null enforced by the database but not
|
|
31
|
+
# by the model
|
|
32
|
+
def column_from_error(database_error)
|
|
33
|
+
if (database_error.kind_of?(Mysql::Error) || database_error.kind_of?(::ActiveRecord::StatementInvalid))
|
|
34
|
+
database_error.message =~ /Column '(.*)?' cannot/
|
|
35
|
+
$1 ? $1.to_sym : nil
|
|
36
|
+
else
|
|
37
|
+
puts database_error.class.name
|
|
38
|
+
nil
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def valid?
|
|
44
|
+
model.valid?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def whitelists_attributes?
|
|
48
|
+
!model.accessible_attributes.nil?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def blacklists_attributes?
|
|
52
|
+
!model.protected_attributes.nil?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def attribute_names
|
|
56
|
+
instance.attribute_names.map {|a| a.to_sym} - ignore_attributes
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# junk attributes
|
|
60
|
+
def ignore_attributes
|
|
61
|
+
["!".to_sym, :[]]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Attributes generally used by the persistence mechanism that should not be human writable
|
|
65
|
+
# accessible from the class
|
|
66
|
+
def internal_attributes
|
|
67
|
+
attrs = [:id, :created_at, :updated_at]
|
|
68
|
+
attrs += attribute_names.select {|a|
|
|
69
|
+
(a.to_s =~ /_count$/) ||
|
|
70
|
+
(a.to_s =~ /_salt$/) ||
|
|
71
|
+
(a.to_s =~ /_token$/) ||
|
|
72
|
+
(a.to_s == 'type')
|
|
73
|
+
}.map {|a| a.to_sym }
|
|
74
|
+
attrs
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def attribute_type(name)
|
|
78
|
+
column = instance.column_for_attribute(name.to_s)
|
|
79
|
+
if (!column.nil?)
|
|
80
|
+
if (model.stratagem.foreign_keys.include?(name.to_sym))
|
|
81
|
+
:integer
|
|
82
|
+
else
|
|
83
|
+
column.type
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
if (name =~ /password/)
|
|
87
|
+
:string
|
|
88
|
+
else
|
|
89
|
+
types = [:string, :boolean, :integer]
|
|
90
|
+
types[rand(3)]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def invalid_columns(instance)
|
|
96
|
+
instance.valid?
|
|
97
|
+
errors = []
|
|
98
|
+
instance.errors.each {|error,i| errors << error.to_s.to_sym }
|
|
99
|
+
errors & attribute_names
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::ActiveModel
|
|
2
|
+
module Tracing
|
|
3
|
+
def self.included(model)
|
|
4
|
+
model.class_eval do
|
|
5
|
+
# inject read tracing
|
|
6
|
+
@@removed_finder_methods = [:first, :last]+methods.select {|m| m =~ /^find/ }.map {|m| m.to_sym }.uniq
|
|
7
|
+
@@removed_methods = [:method_missing]+@@removed_finder_methods
|
|
8
|
+
class << self
|
|
9
|
+
@@removed_methods.each do |m|
|
|
10
|
+
alias_method "old_#{m}", m
|
|
11
|
+
undef_method m
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
model.removed_methods = @@removed_methods
|
|
15
|
+
|
|
16
|
+
# inject write tracing
|
|
17
|
+
[:create_or_update].each do |m|
|
|
18
|
+
alias_method "old_#{m}", m
|
|
19
|
+
undef_method m
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# enhance method missing
|
|
23
|
+
class << self
|
|
24
|
+
def method_missing(method, *args, &block)
|
|
25
|
+
if (self.removed_methods.include?(method.to_sym))
|
|
26
|
+
# puts "read invocation: #{self.name} -> #{method} -> #{args.inspect}"
|
|
27
|
+
stratagem.read_invocation(method, args) if (@@removed_finder_methods.include?(method))
|
|
28
|
+
send("old_"+method.to_s, *args, &block)
|
|
29
|
+
elsif (self.removed_validators.include?(method.to_sym))
|
|
30
|
+
stratagem.validator_called(method, args)
|
|
31
|
+
puts "calling validator #{method.to_s} with #{args.inspect}"
|
|
32
|
+
send("old_"+method.to_s, *args, &block)
|
|
33
|
+
else
|
|
34
|
+
old_method_missing(method, *args, &block)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# add logging of save methods
|
|
40
|
+
|
|
41
|
+
def create_or_update(*args)
|
|
42
|
+
path,action,line = stratagem.controller_trace(/active_record\/base\.rb/)
|
|
43
|
+
stratagem.write_invocation(self, action.to_sym, args)
|
|
44
|
+
old_create_or_update(*args)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::Authlogic
|
|
2
|
+
class Detect < Stratagem::ApplicationExtensions::Models::Detect
|
|
3
|
+
def self.supports?(model)
|
|
4
|
+
begin
|
|
5
|
+
model.ancestors.include?(::Authlogic::ActsAsAuthentic::MagicColumns::Methods)
|
|
6
|
+
rescue
|
|
7
|
+
false
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
if defined?(Authlogic)
|
|
2
|
+
module Authlogic::ActsAsAuthentic::MagicColumns::Methods
|
|
3
|
+
def self.included(model)
|
|
4
|
+
# automatically activate the account
|
|
5
|
+
model.class_eval do
|
|
6
|
+
before_save { |record| record.active = true if record.methods_include?(:active) }
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::Authlogic
|
|
2
|
+
|
|
3
|
+
# prefix method names with to avoid collision
|
|
4
|
+
class Metadata
|
|
5
|
+
include Stratagem::ApplicationExtensions::Models::Adapters::Common::AuthenticationMetadata
|
|
6
|
+
|
|
7
|
+
VIRTUAL_COLUMNS = [:password, :password_confirmation]
|
|
8
|
+
|
|
9
|
+
def authenticates?
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(model)
|
|
14
|
+
@model = model
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def attribute_names
|
|
18
|
+
VIRTUAL_COLUMNS
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def internal_attributes
|
|
22
|
+
[:crypted_password, :password_salt]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def attribute_type(name)
|
|
26
|
+
:string
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::Common
|
|
2
|
+
|
|
3
|
+
# prefix method names with to avoid collision
|
|
4
|
+
module AuthenticationMetadata
|
|
5
|
+
def exclude_attributes_for_mocking
|
|
6
|
+
# open id
|
|
7
|
+
attrs = @model.stratagem.attribute_names.select {|a|
|
|
8
|
+
(a =~ /open_id/ || a =~ /openid/ || a =~ /identity_url/)
|
|
9
|
+
}
|
|
10
|
+
attrs << :identity_url
|
|
11
|
+
attrs << :openid_identifier
|
|
12
|
+
|
|
13
|
+
# aasm
|
|
14
|
+
if (@model.methods_include?(:aasm_column))
|
|
15
|
+
attrs << @model.aasm_column
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
attrs.uniq
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::RestfulAuthentication
|
|
4
|
+
class Detect < Stratagem::ApplicationExtensions::Models::Detect
|
|
5
|
+
def self.supports?(model)
|
|
6
|
+
begin
|
|
7
|
+
model.ancestors.include?(::Authentication::ByPassword)
|
|
8
|
+
rescue
|
|
9
|
+
false
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
if (defined?(Authentication))
|
|
2
|
+
Authentication.subclasses.each do |model|
|
|
3
|
+
model.class_eval do
|
|
4
|
+
# add support for aasm models
|
|
5
|
+
before_save { |record|
|
|
6
|
+
begin
|
|
7
|
+
if (record.methods_include?(:active?) && !record.send(:active?))
|
|
8
|
+
Stratagem.logger.debug "Activating Restful Authentication model #{name}";
|
|
9
|
+
record.send("#{record.class.aasm_column}=", record.class.aasm_initial_state.values.first.to_s)
|
|
10
|
+
record.activate! unless record.active?
|
|
11
|
+
end
|
|
12
|
+
rescue
|
|
13
|
+
puts $!.message
|
|
14
|
+
puts $!.backtrace
|
|
15
|
+
end
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models::Adapters::RestfulAuthentication
|
|
2
|
+
|
|
3
|
+
# prefix method names with to avoid collision
|
|
4
|
+
class Metadata
|
|
5
|
+
include Stratagem::ApplicationExtensions::Models::Adapters::Common::AuthenticationMetadata
|
|
6
|
+
|
|
7
|
+
VIRTUAL_COLUMNS = [:password, :password_confirmation]
|
|
8
|
+
|
|
9
|
+
def authenticates?
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(model)
|
|
14
|
+
@model = model
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def attribute_names
|
|
18
|
+
VIRTUAL_COLUMNS
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def internal_attributes
|
|
22
|
+
[:crypted_password]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def attribute_type(name)
|
|
26
|
+
:string
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Defines the stratagem namespace attached to the model
|
|
2
|
+
module Stratagem::ApplicationExtensions::Models
|
|
3
|
+
MethodInvocation = Struct.new(:method, :controller_path, :controller_action, :line_number, :model_instance, :model_class, :stack_trace, :args)
|
|
4
|
+
ValidatorDefinition = Struct.new(:validation, :field, :args, :model_class)
|
|
5
|
+
|
|
6
|
+
class InstanceAnnotations
|
|
7
|
+
include Mocking
|
|
8
|
+
|
|
9
|
+
def initialize(object)
|
|
10
|
+
@object = object
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def method_missing(method, *args, &block)
|
|
14
|
+
@object.class.stratagem.send(method, *args, &block)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Annotations
|
|
19
|
+
include Metadata
|
|
20
|
+
include Tracing
|
|
21
|
+
|
|
22
|
+
attr_reader :model
|
|
23
|
+
|
|
24
|
+
AdapterDescriptor = Struct.new(:tracing, :metadata, :detector)
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def configure(model)
|
|
28
|
+
puts "configuring #{model.name}"
|
|
29
|
+
|
|
30
|
+
# add the stratagem namespace
|
|
31
|
+
model.class_eval do
|
|
32
|
+
def self.stratagem
|
|
33
|
+
# one stratagem instance per subclass
|
|
34
|
+
@@stratagem ||= {}
|
|
35
|
+
@@stratagem[self] ||= Stratagem::ApplicationExtensions::Models::Annotations.new(self)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def stratagem
|
|
40
|
+
@stratagem ||= Stratagem::ApplicationExtensions::Models::InstanceAnnotations.new(self)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# connect the adapters
|
|
45
|
+
detect_adapters(model).each {|adapter|
|
|
46
|
+
if adapter.detector.supports?(model)
|
|
47
|
+
model.send(:include, adapter.tracing)
|
|
48
|
+
end
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def detect_adapters(model)
|
|
53
|
+
Detect.subclasses.map do |detector|
|
|
54
|
+
namespace = detector.name.split('::')
|
|
55
|
+
namespace.pop
|
|
56
|
+
namespace = namespace.join('::')
|
|
57
|
+
|
|
58
|
+
tracing = module_eval(namespace+"::Tracing")
|
|
59
|
+
metadata = module_eval(namespace+"::Metadata").new(model)
|
|
60
|
+
|
|
61
|
+
AdapterDescriptor.new(tracing, metadata, detector)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize(model)
|
|
67
|
+
puts "initializing stratagem for #{model.name}"
|
|
68
|
+
@model = model
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def adapters
|
|
72
|
+
# supported adapters may change throughout the lifecycle of a class / object
|
|
73
|
+
@adapters ||= self.class.detect_adapters(model)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
|
|
2
|
+
module Stratagem::ApplicationExtensions::Models
|
|
3
|
+
module Metadata
|
|
4
|
+
StratagemAssociation = Struct.new(:name, :foreign_key, :klass, :macro)
|
|
5
|
+
|
|
6
|
+
INSTANCE_ENUMERATION_METHODS = [:relations, :attribute_names, :ignore_attributes, :internal_attributes, :unaccessible_attributes, :invalid_columns, :exclude_attributes_for_mocking]
|
|
7
|
+
INSTANCE_ENTITY_METHODS = [:attribute_type, :column_from_error, :authenticates?, :whitelists_attributes?, :blacklists_attributes?]
|
|
8
|
+
|
|
9
|
+
def Metadata.included(mod)
|
|
10
|
+
mod.class_eval do
|
|
11
|
+
INSTANCE_ENUMERATION_METHODS.each do |name|
|
|
12
|
+
define_method(name) do |*args|
|
|
13
|
+
run_callbacks(name, *args)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
INSTANCE_ENTITY_METHODS.each do |name|
|
|
18
|
+
define_method(name) do |*args|
|
|
19
|
+
val = run_callbacks(name, *args).first
|
|
20
|
+
val ||= false if (name.to_s.include?('?'))
|
|
21
|
+
val
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Convenience methods
|
|
29
|
+
|
|
30
|
+
def foreign_keys
|
|
31
|
+
relations(:belongs_to).map {|relation| relation.foreign_key }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def relation(name)
|
|
35
|
+
relations.find {|relation| relation.name == name }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def relation_names(relation_type=nil)
|
|
39
|
+
@relation_names ||= {}
|
|
40
|
+
@relation_names[relation_type || :all] ||= relations(relation_type).map {|relation| relation.name }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Tracking of validates_xyz methods. Population of this data can be found in the tracing library
|
|
45
|
+
|
|
46
|
+
def validations(field=nil, validation=nil)
|
|
47
|
+
validators = self.validators
|
|
48
|
+
|
|
49
|
+
if (field || validation)
|
|
50
|
+
validators.select {|v|
|
|
51
|
+
conditions = []
|
|
52
|
+
conditions << (v.field == field) if field
|
|
53
|
+
conditions << (v.validation == validation) if validation
|
|
54
|
+
!conditions.include?(false)
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
validators
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def callbacks
|
|
62
|
+
adapters.select {|a| a.detector.supports?(model) }.map {|a| a.metadata }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def validators
|
|
66
|
+
@validators ||= []
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def run_callbacks(method, *args)
|
|
72
|
+
results = callbacks.inject([]) {|memory,callback|
|
|
73
|
+
begin
|
|
74
|
+
memory << callback.send(method, *args) if callback.methods_include?(method) || callback.methods_include?(method.to_s)
|
|
75
|
+
memory
|
|
76
|
+
rescue
|
|
77
|
+
puts $!.message
|
|
78
|
+
puts $!.backtrace
|
|
79
|
+
end
|
|
80
|
+
}
|
|
81
|
+
(results || []).flatten.compact
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models
|
|
2
|
+
module Mocking
|
|
3
|
+
def mock_attributes
|
|
4
|
+
@attributes ||= {}
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def write_mock_attribute(name, value)
|
|
8
|
+
mock_attributes[name] = value
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def read_mock_attribute(name)
|
|
12
|
+
mock_attributes[name]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def used_in_mock_relation
|
|
16
|
+
@available_for_mock_relation = false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def used_in_mock_relation?
|
|
20
|
+
@available_for_mock_relation ||= true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models
|
|
2
|
+
module Tracing
|
|
3
|
+
|
|
4
|
+
@@invocations_audit = []
|
|
5
|
+
|
|
6
|
+
def invocations_audit
|
|
7
|
+
@@invocations_audit
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def read_invocations
|
|
11
|
+
@read_invocations ||= []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def write_invocations
|
|
15
|
+
@write_invocations ||= []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def validator_called(validation, args)
|
|
19
|
+
params = args.find {|a| a.kind_of?(Hash) } || {}
|
|
20
|
+
(args-[params]).each do |field|
|
|
21
|
+
self.validators << ValidatorDefinition.new(validation, field, params, model)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def clear_invocations
|
|
26
|
+
read_invocations.clear
|
|
27
|
+
write_invocations.clear
|
|
28
|
+
invocations_audit.clear
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def write_invocation(model_instance, method, args)
|
|
32
|
+
invocation(method, args, write_invocations, model_instance)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def read_invocation(method, *args)
|
|
36
|
+
# ensure that the read did not stem from a write operation
|
|
37
|
+
path,action,line = controller_trace(/active_record\/base\.rb/)
|
|
38
|
+
invocation(method, args, read_invocations) unless (action =~ /create/) || (action =~ /update/) || (action =~ /save/)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def invocation(method, args, enumeration, model_instance=nil)
|
|
42
|
+
path,action,line = controller_trace
|
|
43
|
+
args = args.first if args && (args.size == 1) && (args.first.kind_of?(Array))
|
|
44
|
+
add_invocation enumeration, MethodInvocation.new(method, path, action, line, model_instance, model, caller, args) if (path)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def controller_trace(regex = /_controller\.rb/)
|
|
48
|
+
controller_trace = caller.select {|c| c =~ regex }.last
|
|
49
|
+
if controller_trace
|
|
50
|
+
path,line,action = controller_trace.split(':')
|
|
51
|
+
action.gsub!(/[`']/, '').gsub!('in ', '')
|
|
52
|
+
line = line.to_i
|
|
53
|
+
[path,action,line]
|
|
54
|
+
else
|
|
55
|
+
[]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def add_invocation(enumeration, invocation)
|
|
60
|
+
# puts "adding invocation: #{invocation.inspect}"
|
|
61
|
+
invocations = (enumeration ||= [])
|
|
62
|
+
existing = invocations.find {|i|
|
|
63
|
+
i.controller_path == invocation.controller_path &&
|
|
64
|
+
i.controller_action == invocation.controller_action &&
|
|
65
|
+
i.line_number == invocation.line_number
|
|
66
|
+
}
|
|
67
|
+
invocations << invocation unless existing
|
|
68
|
+
invocations_audit << invocation
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Stratagem::ApplicationExtensions::Models; end
|
|
2
|
+
module Stratagem::ApplicationExtensions::Models::Adapters; end
|
|
3
|
+
|
|
4
|
+
require 'stratagem/framework_extensions/models/mocking'
|
|
5
|
+
require 'stratagem/framework_extensions/models/metadata'
|
|
6
|
+
require 'stratagem/framework_extensions/models/tracing'
|
|
7
|
+
require 'stratagem/framework_extensions/models/annotations'
|
|
8
|
+
require 'stratagem/framework_extensions/models/detect'
|
|
9
|
+
|
|
10
|
+
base = File.join(File.dirname(__FILE__), 'models', 'adapters', 'common')
|
|
11
|
+
Dir.entries(base).select {|s| s =~ /\.rb$/}.each {|helper|
|
|
12
|
+
require File.join(base, helper.gsub(/\.rb/, ''))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
base = File.join(File.dirname(__FILE__), 'models', 'adapters')
|
|
16
|
+
Dir.entries(base).select {|s| s !~ /^\./ && s != 'common' }.each {|adapter_dir|
|
|
17
|
+
require File.join(base, adapter_dir, 'detect')
|
|
18
|
+
require File.join(base, adapter_dir, 'tracing')
|
|
19
|
+
require File.join(base, adapter_dir, 'metadata')
|
|
20
|
+
require File.join(base, adapter_dir, 'extensions')
|
|
21
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'sinatra'
|
|
3
|
+
require 'erb'
|
|
4
|
+
|
|
5
|
+
set :run, false
|
|
6
|
+
enable :sessions
|
|
7
|
+
disable :logging
|
|
8
|
+
# log = File.new("log/sinatra.log", "a")
|
|
9
|
+
# STDOUT.reopen(log)
|
|
10
|
+
# STDERR.reopen(log)
|
|
11
|
+
|
|
12
|
+
get '/' do
|
|
13
|
+
session[:instance_id] = Stratagem.session_id
|
|
14
|
+
Stratagem.analyze
|
|
15
|
+
haml :index
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
get '/credentials' do
|
|
19
|
+
Stratagem::Authentication.instance.store_credentials(params[:account], params[:api_key], params[:project])
|
|
20
|
+
redirect '/'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
get '/logs' do
|
|
24
|
+
if (session[:instance_id] == Stratagem.session_id)
|
|
25
|
+
logger = Stratagem.logger
|
|
26
|
+
logs = [logger.pop]
|
|
27
|
+
logs << logger.pop until logger.empty?
|
|
28
|
+
logs.to_json
|
|
29
|
+
else
|
|
30
|
+
""
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
Thread.new do
|
|
36
|
+
Sinatra::Application.run!
|
|
37
|
+
end
|
|
Binary file
|
|
Binary file
|