stratagem 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +16 -18
- data/Rakefile +3 -3
- data/bin/stratagem +54 -6
- data/generators/stratagem/stratagem_generator.rb +26 -0
- data/lib/generators/stratagem/install/USAGE +0 -0
- data/lib/generators/stratagem/install/install_base.rb +35 -0
- data/lib/generators/stratagem/install/install_generator.rb +24 -0
- data/lib/stratagem.rb +87 -57
- data/lib/stratagem/authentication.rb +2 -2
- data/lib/stratagem/auto_mock/aquifer.rb +6 -1
- data/lib/stratagem/auto_mock/factory.rb +2 -2
- data/lib/stratagem/client.rb +1 -1
- data/lib/stratagem/crawler.rb +2 -0
- data/lib/stratagem/crawler/authentication.rb +10 -9
- data/lib/stratagem/crawler/parameter_resolver.rb +83 -0
- data/lib/stratagem/crawler/route_invoker.rb +187 -0
- data/lib/stratagem/crawler/session.rb +23 -251
- data/lib/stratagem/crawler/site_model.rb +18 -16
- data/lib/stratagem/framework_extensions.rb +12 -1
- data/lib/stratagem/framework_extensions/method_invocation.rb +50 -0
- data/lib/stratagem/framework_extensions/models/adapters/active_model/detect.rb +1 -1
- data/lib/stratagem/framework_extensions/models/adapters/active_model/extensions.rb +20 -11
- data/lib/stratagem/framework_extensions/models/adapters/active_model/metadata.rb +7 -3
- data/lib/stratagem/framework_extensions/models/adapters/active_model/tracing.rb +11 -9
- data/lib/stratagem/framework_extensions/models/adapters/friendly_id/detect.rb +12 -0
- data/lib/stratagem/framework_extensions/models/adapters/friendly_id/extensions.rb +0 -0
- data/lib/stratagem/framework_extensions/models/adapters/friendly_id/metadata.rb +21 -0
- data/lib/stratagem/framework_extensions/models/adapters/friendly_id/tracing.rb +4 -0
- data/lib/stratagem/framework_extensions/models/annotations.rb +1 -24
- data/lib/stratagem/framework_extensions/models/tracing.rb +9 -3
- data/lib/stratagem/framework_extensions/rails.rb +0 -6
- data/lib/stratagem/framework_extensions/{controllers → rails2}/action_controller.rb +0 -0
- data/lib/stratagem/framework_extensions/{controllers → rails2}/action_mailer.rb +0 -0
- data/lib/stratagem/framework_extensions/rails3/parameters.rb +14 -0
- data/lib/stratagem/interface/browser.rb +3 -1
- data/lib/stratagem/model/application.rb +6 -6
- data/lib/stratagem/model/components/controller.rb +17 -63
- data/lib/stratagem/model/components/model.rb +33 -33
- data/lib/stratagem/model/components/reference.rb +8 -4
- data/lib/stratagem/model/components/route.rb +40 -14
- data/lib/stratagem/model/components/view.rb +1 -1
- data/lib/stratagem/model_builder.rb +71 -42
- data/lib/stratagem/site_crawler.rb +1 -1
- data/lib/stratagem/snapshot.rb +0 -1
- data/stratagem.gemspec +10 -7
- data/templates/install/environments/stratagem.rb.erb +16 -0
- data/templates/install/tasks/stratagem.rake +18 -0
- metadata +57 -40
- data/lib/stratagem/framework_extensions/controllers.rb +0 -5
- data/lib/stratagem/scan/checks/filter_parameter_logging.rb +0 -6
- data/lib/stratagem/scan/checks/mongo_mapper/base.rb +0 -19
- data/lib/stratagem/scan/checks/mongo_mapper/foreign_keys_exposed.rb +0 -32
- data/lib/stratagem/scan/checks/routes.rb +0 -16
- data/lib/tasks/_old_stratagem.rake +0 -99
- data/spec/model/component_spec.rb +0 -43
- data/spec/model/components/view_spec.rb +0 -43
- data/spec/model/test_spec.rb +0 -10
- data/spec/samples/404.html.erb +0 -30
- data/spec/samples/_form.html.erb +0 -8
- data/spec/samples/index.html.erb +0 -77
- data/spec/samples/sample_model.rb +0 -5
- data/spec/samples/signup.html.erb +0 -14
- data/spec/scan/checks/email_address_spec.rb +0 -24
- data/spec/scan/checks/error_pages_spec.rb +0 -22
data/lib/stratagem/client.rb
CHANGED
@@ -11,7 +11,7 @@ module Stratagem
|
|
11
11
|
url = URI.parse("#{@authentication.base_url}/snapshots")
|
12
12
|
req = Net::HTTP::Post.new(url.path)
|
13
13
|
req.set_form_data({
|
14
|
-
'
|
14
|
+
'auth_token' => @authentication.credentials[:token],
|
15
15
|
'project_id' => @authentication.credentials[:project],
|
16
16
|
'timestamp' => snapshot.timestamp.to_i,
|
17
17
|
'model' => snapshot.model.export.to_json
|
data/lib/stratagem/crawler.rb
CHANGED
@@ -5,5 +5,7 @@ require 'stratagem/crawler/form'
|
|
5
5
|
require 'stratagem/crawler/trace_utils'
|
6
6
|
require 'stratagem/crawler/html_utils'
|
7
7
|
require 'stratagem/crawler/authentication'
|
8
|
+
require 'stratagem/crawler/parameter_resolver'
|
9
|
+
require 'stratagem/crawler/route_invoker'
|
8
10
|
require 'stratagem/crawler/session'
|
9
11
|
require 'stratagem/crawler/site_model'
|
@@ -41,14 +41,14 @@ module Stratagem::Crawler
|
|
41
41
|
reset_authentication
|
42
42
|
|
43
43
|
login(user)
|
44
|
-
route = application_model.routes.recognize(
|
44
|
+
route = application_model.routes.recognize(request.path, :post)
|
45
45
|
|
46
46
|
redirected_to = nil
|
47
|
-
page = site_model.add(route, response) {|redirect_url| redirected_to = redirect_url }
|
47
|
+
page = site_model.add(route, request, response) {|redirect_url| redirected_to = redirect_url }
|
48
48
|
authentication.response_page = page
|
49
49
|
|
50
50
|
begin
|
51
|
-
if (
|
51
|
+
if (request.url == (redirected_to || '')) || (![200,302].include?(response.code.to_i))
|
52
52
|
authentication.success = false
|
53
53
|
else
|
54
54
|
authentication.success = authentication.response_page.login_form.nil?
|
@@ -60,7 +60,7 @@ module Stratagem::Crawler
|
|
60
60
|
|
61
61
|
puts "authenticated? #{authentication.success}"
|
62
62
|
if (response && authentication.success)
|
63
|
-
authentication.ssl =
|
63
|
+
authentication.ssl = request.ssl?
|
64
64
|
yield
|
65
65
|
logout
|
66
66
|
else
|
@@ -90,15 +90,13 @@ module Stratagem::Crawler
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def logout
|
93
|
-
|
94
|
-
get "/logout"
|
95
|
-
delete "/user_sessions/1"
|
93
|
+
reset!
|
96
94
|
end
|
97
95
|
|
98
96
|
def login(user)
|
99
97
|
populate_login_form(user).submit {|action,params|
|
100
98
|
post(action, params)
|
101
|
-
puts response.body
|
99
|
+
# puts response.body
|
102
100
|
}
|
103
101
|
end
|
104
102
|
|
@@ -124,7 +122,10 @@ module Stratagem::Crawler
|
|
124
122
|
def populate_login_form(user)
|
125
123
|
# set up the form
|
126
124
|
page = find_login_form
|
127
|
-
page.
|
125
|
+
p page.login_form
|
126
|
+
page.reload {|url| get url; [request,response] }
|
127
|
+
p page.login_form
|
128
|
+
p page.response.body
|
128
129
|
form = page.login_form
|
129
130
|
|
130
131
|
# map the input values
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Stratagem::Crawler
|
2
|
+
module ParameterResolver
|
3
|
+
|
4
|
+
def resolve_parameter_types(route_container)
|
5
|
+
log "\tresolving parameter types"
|
6
|
+
resolved_params = {}
|
7
|
+
route_infos, params = build_url(route_container, resolved_params)
|
8
|
+
route_info = route_infos.first
|
9
|
+
unknown_params = params.keys
|
10
|
+
log "\tunknown params: #{unknown_params.inspect} - #{unknown_params.size}"
|
11
|
+
|
12
|
+
resolve_with_convention(unknown_params, resolved_params)
|
13
|
+
log "\tunknown params after convention: #{unknown_params.inspect} - #{unknown_params.size}"
|
14
|
+
|
15
|
+
resolve_with_instrumentation(route_container, resolved_params)
|
16
|
+
log "\tunknown params after instrumentation: #{unknown_params.inspect} - #{unknown_params.size}"
|
17
|
+
|
18
|
+
p resolved_params
|
19
|
+
|
20
|
+
if (resolved_params.size > 0)
|
21
|
+
resolved_params
|
22
|
+
else
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def resolve_with_instrumentation(route_container, resolved_params)
|
28
|
+
route_infos, params = build_url(route_container, resolved_params)
|
29
|
+
route_info = route_infos.first
|
30
|
+
unknown_params = params.keys
|
31
|
+
|
32
|
+
progress = nil
|
33
|
+
while ((unknown_params.size > 0) && (progress.nil? || (progress > 0)))
|
34
|
+
progress = 0
|
35
|
+
|
36
|
+
puts "\tloading model invocations for request"
|
37
|
+
delta = model_invocations_for_request do
|
38
|
+
call_route(route_info, false)
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "\tcalled route, found #{delta.size} invocations"
|
42
|
+
|
43
|
+
unknown_params.clone.each do |key|
|
44
|
+
value = params[key]
|
45
|
+
value_s = params[key].map {|v| v.to_s }
|
46
|
+
delta.each do |invocation|
|
47
|
+
# TODO inspect is a hack, refactor
|
48
|
+
if (invocation.args.include?(value.first)) || (invocation.args.inspect.include?('"'+value.first.to_s+'"'))
|
49
|
+
# found match
|
50
|
+
puts "\t\tresolved #{key} to #{invocation.model_class}"
|
51
|
+
unknown_params.delete(key)
|
52
|
+
resolved_params[key] = invocation.model_class
|
53
|
+
progress += 1
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
route_infos, params = build_url(route_container, resolved_params)
|
60
|
+
route_info = route_infos.first
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def resolve_with_convention(unknown_params, resolved_params)
|
65
|
+
unknown_params.each do |param|
|
66
|
+
if (param =~ /_id$/)
|
67
|
+
begin
|
68
|
+
model = param.gsub(/^:/,'').gsub(/_id$/, '').camelize.constantize
|
69
|
+
#success
|
70
|
+
resolved_params[param] = model
|
71
|
+
unknown_params.delete(param)
|
72
|
+
puts "\t\tresolved #{param} to #{model} using convention"
|
73
|
+
rescue NameError
|
74
|
+
puts $!
|
75
|
+
puts $!.backtrace
|
76
|
+
# not a model
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Stratagem::Crawler
|
2
|
+
module RouteInvoker
|
3
|
+
include Stratagem::Crawler::ParameterResolver
|
4
|
+
|
5
|
+
def visit(route_container)
|
6
|
+
puts "Visiting #{route_container.route}"
|
7
|
+
build_urls(route_container).each do |route_info|
|
8
|
+
call_route(route_info)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def call_route(route_info, track_invocations=true)
|
13
|
+
begin
|
14
|
+
call_route!(route_info, track_invocations)
|
15
|
+
rescue
|
16
|
+
# TODO - add exception as a response page
|
17
|
+
puts "ERROR: #{$!.message}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def call_route!(route_info, track_invocations=true)
|
22
|
+
return if route_info.nil?
|
23
|
+
|
24
|
+
puts 'CALLING: .'+route_info[:verb].downcase+". - "+route_info[:path]
|
25
|
+
verb = route_info[:verb].downcase
|
26
|
+
verb = 'get' if verb == '' || verb == 'any'
|
27
|
+
|
28
|
+
invocations = model_invocations_for_request do
|
29
|
+
case verb
|
30
|
+
when 'get'
|
31
|
+
do_get(route_info)
|
32
|
+
puts "\tresponse code: #{response.code}" if response
|
33
|
+
when 'post'
|
34
|
+
when 'put'
|
35
|
+
do_put(route_info)
|
36
|
+
when 'delete'
|
37
|
+
else
|
38
|
+
raise "Unsupported verb: #{route[:verb]}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if (response)
|
43
|
+
if (track_invocations)
|
44
|
+
changes = detect_attribute_changes_in_models(invocations)
|
45
|
+
puts "\tfound #{invocations.size} invocations"
|
46
|
+
puts "\tchanges: #{changes.values.inspect}" if changes.size > 0
|
47
|
+
site_model.add(route_info[:route_container], request, response, invocations, changes) {|redirect_url| redirect_proc.call(redirect_url) }
|
48
|
+
end
|
49
|
+
else
|
50
|
+
puts "ERROR: did not call #{route_info.inspect}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def do_get(route_info)
|
55
|
+
get route_info[:path]
|
56
|
+
end
|
57
|
+
|
58
|
+
def do_put(route_info)
|
59
|
+
|
60
|
+
# note: this should fail to generate anything meaningful, as we have not yet set up the parameters
|
61
|
+
put route_info[:path]
|
62
|
+
|
63
|
+
# let's find out what the method is looking for in the params object
|
64
|
+
params = map_models_to_attributes(infer_models_for_param_reads(route_info[:route_container],controller.params.hash_reads))
|
65
|
+
|
66
|
+
# run again with the params
|
67
|
+
puts "PUTTING: #{route_info[:path]} with #{params.inspect}"
|
68
|
+
|
69
|
+
invocation_delta = model_invocations_for_request(:write) do
|
70
|
+
put route_info[:path], params
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
|
77
|
+
def map_models_to_attributes(models)
|
78
|
+
result = {}
|
79
|
+
models.each {|param_read,model|
|
80
|
+
result[param_read] = aquifer.mock_attributes(model.klass)
|
81
|
+
}
|
82
|
+
result
|
83
|
+
end
|
84
|
+
|
85
|
+
def infer_models_for_param_reads(route,param_reads)
|
86
|
+
param_reads = param_reads.select {|read| read.to_s !~ /_id$/ }
|
87
|
+
param_reads -= [:action,'action',:controller,'controller',:id,'id',:format,'format']
|
88
|
+
|
89
|
+
result = {}
|
90
|
+
|
91
|
+
# resolve param reads to model types
|
92
|
+
# only support simple expected mappings at the moment
|
93
|
+
param_reads.each do |param_read|
|
94
|
+
class_name = param_read.to_s.camelize
|
95
|
+
if (class_name.singularize == class_name)
|
96
|
+
# this looks promising
|
97
|
+
model = application_model.models.find {|model| model.klass.name == class_name }
|
98
|
+
if (model)
|
99
|
+
puts "\t\tresolved param #{param_read} to #{model.klass.name}"
|
100
|
+
result[param_read] = model
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
result
|
106
|
+
end
|
107
|
+
|
108
|
+
def detect_attribute_changes_in_models(invocations)
|
109
|
+
changes = {}
|
110
|
+
invocations.select {|invocation| invocation.type == :write }.each do |invocation|
|
111
|
+
if (invocation.model_instance)
|
112
|
+
model = application_model.models.find {|m| m.klass == invocation.model_instance.class }
|
113
|
+
|
114
|
+
puts "\t\t#{invocation.model_class}.#{invocation.method} - #{invocation.model_instance}"
|
115
|
+
prior = aquifer.instances_of(invocation.model_instance.class).find {|m| m.id == invocation.model_instance.id }
|
116
|
+
post = invocation.model_instance
|
117
|
+
attribute_names = (prior.stratagem.attribute_names + prior.stratagem.foreign_keys)
|
118
|
+
changes[model] = attribute_names.select {|an|
|
119
|
+
begin
|
120
|
+
prior.send(an) != post.send(an)
|
121
|
+
rescue
|
122
|
+
puts "\t\t\t#{an} cannot be determined - #{$!.message}"
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
changes
|
128
|
+
end
|
129
|
+
|
130
|
+
# Builds a list of string URLs for a given route. This is done
|
131
|
+
# by replacing :xyz_id segments in the route with known values
|
132
|
+
# from the well
|
133
|
+
def build_urls(route_container)
|
134
|
+
urls = []
|
135
|
+
route = route_container.route
|
136
|
+
param_types = (self.parameter_types[route_container] ||= resolve_parameter_types(route_container))
|
137
|
+
route_infos, params = build_url(route_container, param_types)
|
138
|
+
route_infos
|
139
|
+
end
|
140
|
+
|
141
|
+
def url_permutations(route, segment_values, &block)
|
142
|
+
path = route.path.sub('(.:format)', '')
|
143
|
+
if (segment_values.size == 0)
|
144
|
+
yield path
|
145
|
+
else
|
146
|
+
combinations = segment_values[0].product(*segment_values.slice(1,segment_values.size-1))
|
147
|
+
combinations.each do |combination|
|
148
|
+
url = path.clone
|
149
|
+
route.segment_keys.each_with_index {|segment_key,i| url.gsub!(':'+segment_key.to_s, combination[i].to_s) }
|
150
|
+
yield url
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def build_url(route_container, parameter_types={})
|
156
|
+
parameter_types ||= {}
|
157
|
+
params = {}
|
158
|
+
route = route_container.route
|
159
|
+
verb = route_container.verb.to_s
|
160
|
+
|
161
|
+
|
162
|
+
i = 12345
|
163
|
+
insert_values = (route.segment_keys - [:format]).map {|segment_key|
|
164
|
+
model = parameter_types[segment_key.to_s]
|
165
|
+
value = nil
|
166
|
+
if (model)
|
167
|
+
value = (aquifer.instances_of model).map {|inst|
|
168
|
+
inst.methods_include?(segment_key) ? inst.send(segment_key) : inst.to_param
|
169
|
+
}
|
170
|
+
else
|
171
|
+
value = [i += 1]
|
172
|
+
end
|
173
|
+
params[segment_key.to_s] = value
|
174
|
+
}
|
175
|
+
|
176
|
+
routes = []
|
177
|
+
url_permutations(route_container,insert_values) do |path|
|
178
|
+
puts "yielded: #{path}"
|
179
|
+
permutation = {:verb => verb, :path => path, :route_container => route_container}
|
180
|
+
routes << permutation
|
181
|
+
end
|
182
|
+
|
183
|
+
[routes, params]
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
@@ -29,6 +29,20 @@ module Stratagem::Crawler::Session
|
|
29
29
|
include Stratagem::Crawler::HtmlUtils
|
30
30
|
include Stratagem::Crawler::TraceUtils
|
31
31
|
include Stratagem::Crawler::Authentication
|
32
|
+
include Stratagem::Crawler::RouteInvoker
|
33
|
+
|
34
|
+
attr_writer :aquifer
|
35
|
+
|
36
|
+
# def self.app
|
37
|
+
# # DEPRECATE Rails application fallback
|
38
|
+
# # This should be set by the initializer
|
39
|
+
# (defined?(Rails.application) && Rails.application) || nil
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
|
43
|
+
def app
|
44
|
+
(defined?(Rails.application) && Rails.application) || nil
|
45
|
+
end
|
32
46
|
|
33
47
|
def log(msg)
|
34
48
|
Stratagem.logger.debug msg
|
@@ -68,14 +82,18 @@ module Stratagem::Crawler::Session
|
|
68
82
|
end
|
69
83
|
|
70
84
|
def page_set(name, &block)
|
85
|
+
reset!
|
71
86
|
site_models << Stratagem::Crawler::SiteModel.new(name)
|
72
87
|
yield site_model
|
73
88
|
end
|
74
89
|
|
75
|
-
def
|
90
|
+
def print
|
76
91
|
# print out pages and inbound / outbound links
|
77
92
|
site_model.pages.each do |page|
|
78
|
-
|
93
|
+
title = page.title
|
94
|
+
title ||= page.redirected_to.url if page.redirected_to
|
95
|
+
title ||= page.response.code
|
96
|
+
log "Page: #{page.url} - #{title} - #{page.response.code}"
|
79
97
|
page.outbound_edges.each do |edge|
|
80
98
|
log "\tout: #{edge.to.url} - #{edge.to.title} - #{edge.to.route}"
|
81
99
|
end
|
@@ -102,10 +120,10 @@ module Stratagem::Crawler::Session
|
|
102
120
|
else
|
103
121
|
if (application_model.routes.invalid.include?(route_container))
|
104
122
|
log "Skipping invalid route #{route_container.route.to_s}"
|
105
|
-
elsif (verbs.include?(route_container.
|
123
|
+
elsif (verbs.include?(route_container.verb))
|
106
124
|
visit(route_container)
|
107
125
|
else
|
108
|
-
log "Skipping route with verb #{route_container.
|
126
|
+
log "Skipping route with verb #{route_container.verb} - #{route_container.route.to_s}"
|
109
127
|
end
|
110
128
|
end
|
111
129
|
}
|
@@ -120,203 +138,6 @@ module Stratagem::Crawler::Session
|
|
120
138
|
|
121
139
|
private
|
122
140
|
|
123
|
-
def visit(route_container)
|
124
|
-
puts "Visiting #{route_container.route}"
|
125
|
-
build_urls(route_container).each do |route_info|
|
126
|
-
call_route(route_container, route_info)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def call_route(route, route_info, track_invocations=true)
|
131
|
-
return if route_info.nil?
|
132
|
-
|
133
|
-
puts 'CALLING: .'+route_info[:verb].downcase+". - "+route_info[:path]
|
134
|
-
verb = route_info[:verb].downcase
|
135
|
-
verb = 'get' if verb == '' || verb == 'any'
|
136
|
-
|
137
|
-
begin
|
138
|
-
invocations = model_invocations_for_request do
|
139
|
-
case verb
|
140
|
-
when 'get'
|
141
|
-
do_get(route, route_info[:path])
|
142
|
-
puts "\tresponse code: #{response.code}" if response
|
143
|
-
when 'post'
|
144
|
-
when 'put'
|
145
|
-
do_put(route, route_info[:path])
|
146
|
-
when 'delete'
|
147
|
-
else
|
148
|
-
raise "Unsupported verb: #{route[:verb]}"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
if (response)
|
153
|
-
changes = detect_attribute_changes_in_models(invocations)
|
154
|
-
puts "\tfound #{invocations.size} invocations"
|
155
|
-
puts "\tchanges: #{changes.values.inspect}" if changes.size > 0
|
156
|
-
site_model.add(route, response, invocations, changes) {|redirect_url| redirect_proc.call(redirect_url) }
|
157
|
-
else
|
158
|
-
puts "ERROR: did not call #{route_info.inspect}"
|
159
|
-
end
|
160
|
-
rescue
|
161
|
-
puts $!.message
|
162
|
-
puts $!.backtrace
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def do_get(route,path)
|
167
|
-
get path
|
168
|
-
end
|
169
|
-
|
170
|
-
def do_put(route,path)
|
171
|
-
|
172
|
-
# note: this should fail to generate anything meaningful, as we have not yet set up the parameters
|
173
|
-
put path
|
174
|
-
|
175
|
-
# let's find out what the method is looking for in the params object
|
176
|
-
params = map_models_to_attributes(infer_models_for_param_reads(route,controller.params.hash_reads))
|
177
|
-
|
178
|
-
# run again with the params
|
179
|
-
puts "PUTTING: #{path} with #{params.inspect}"
|
180
|
-
|
181
|
-
invocation_delta = model_invocations_for_request(:write) do
|
182
|
-
put path, params
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
def map_models_to_attributes(models)
|
187
|
-
result = {}
|
188
|
-
models.each {|param_read,model|
|
189
|
-
result[param_read] = aquifer.mock_attributes(model.klass)
|
190
|
-
}
|
191
|
-
result
|
192
|
-
end
|
193
|
-
|
194
|
-
def infer_models_for_param_reads(route,param_reads)
|
195
|
-
param_reads = param_reads.select {|read| read.to_s !~ /_id$/ }
|
196
|
-
param_reads -= [:action,'action',:controller,'controller',:id,'id',:format,'format']
|
197
|
-
|
198
|
-
result = {}
|
199
|
-
|
200
|
-
# resolve param reads to model types
|
201
|
-
# only support simple expected mappings at the moment
|
202
|
-
param_reads.each do |param_read|
|
203
|
-
class_name = param_read.to_s.camelize
|
204
|
-
if (class_name.singularize == class_name)
|
205
|
-
# this looks promising
|
206
|
-
model = application_model.models.find {|model| model.klass.name == class_name }
|
207
|
-
if (model)
|
208
|
-
puts "\t\tresolved param #{param_read} to #{model.klass.name}"
|
209
|
-
result[param_read] = model
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
|
214
|
-
result
|
215
|
-
end
|
216
|
-
|
217
|
-
def detect_attribute_changes_in_models(invocations)
|
218
|
-
changes = {}
|
219
|
-
invocations.select {|invocation| invocation.type == :write }.each do |invocation|
|
220
|
-
if (invocation.model_instance)
|
221
|
-
model = application_model.models.find {|m| m.klass == invocation.model_instance.class }
|
222
|
-
|
223
|
-
puts "\t\t#{invocation.model_class}.#{invocation.method} - #{invocation.model_instance}"
|
224
|
-
prior = aquifer.instances_of(invocation.model_instance.class).find {|m| m.id == invocation.model_instance.id }
|
225
|
-
post = invocation.model_instance
|
226
|
-
attribute_names = (prior.stratagem.attribute_names + prior.stratagem.foreign_keys)
|
227
|
-
changes[model] = attribute_names.select {|an|
|
228
|
-
begin
|
229
|
-
prior.send(an) != post.send(an)
|
230
|
-
rescue
|
231
|
-
puts "\t\t\t#{an} cannot be determined - #{$!.message}"
|
232
|
-
end
|
233
|
-
}
|
234
|
-
end
|
235
|
-
end
|
236
|
-
changes
|
237
|
-
end
|
238
|
-
|
239
|
-
# Builds a list of string URLs for a given route. This is done
|
240
|
-
# by replacing :xyz_id segments in the route with known values
|
241
|
-
# from the well
|
242
|
-
def build_urls(route_container)
|
243
|
-
urls = []
|
244
|
-
route = route_container.route
|
245
|
-
param_types = (self.parameter_types[route_container] ||= resolve_parameter_types(route_container))
|
246
|
-
route_infos, params = build_url(route_container, param_types)
|
247
|
-
# puts "route: #{route_container.route.to_s} - #{parameter_types.inspect} - permutations:"
|
248
|
-
# route_infos.each do |info|
|
249
|
-
# puts "\t#{info[:path]}"
|
250
|
-
# end
|
251
|
-
route_infos
|
252
|
-
end
|
253
|
-
|
254
|
-
def url_permutations(meta_segments, segment_stack=[], &block)
|
255
|
-
if (segment_stack.size == meta_segments.size)
|
256
|
-
yield segment_stack
|
257
|
-
else
|
258
|
-
cursor = segment_stack.size
|
259
|
-
options = meta_segments[cursor]
|
260
|
-
if (options.kind_of?(Array))
|
261
|
-
options.each do |option|
|
262
|
-
url_permutations(meta_segments, segment_stack + [option], &block)
|
263
|
-
end
|
264
|
-
else
|
265
|
-
url_permutations(meta_segments, segment_stack + [options], &block)
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
def build_url(route_container, parameter_types={})
|
271
|
-
params = {}
|
272
|
-
route = route_container.route
|
273
|
-
name = route.to_s
|
274
|
-
verb = route.conditions[:method].to_s
|
275
|
-
|
276
|
-
parameter_types ||= {}
|
277
|
-
|
278
|
-
i = 12345
|
279
|
-
segs = route.segments.inject([]) {|accumulated,segment|
|
280
|
-
s = segment.to_s
|
281
|
-
if (s =~ /^:/)
|
282
|
-
model = parameter_types[s]
|
283
|
-
value = nil
|
284
|
-
if (model)
|
285
|
-
if (aquifer.instances_of(model).size == 0)
|
286
|
-
aquifer.print
|
287
|
-
end
|
288
|
-
value = (aquifer.instances_of model).map {|inst|
|
289
|
-
attr_name = s.gsub(/^:/, '').to_sym
|
290
|
-
if inst.methods_include?(attr_name)
|
291
|
-
inst.send(attr_name)
|
292
|
-
else
|
293
|
-
inst.id
|
294
|
-
end
|
295
|
-
}
|
296
|
-
else
|
297
|
-
i += 1
|
298
|
-
value = [i]
|
299
|
-
end
|
300
|
-
accumulated << value
|
301
|
-
params[s] = value
|
302
|
-
else
|
303
|
-
accumulated << s
|
304
|
-
end
|
305
|
-
accumulated
|
306
|
-
}
|
307
|
-
|
308
|
-
routes = []
|
309
|
-
reqs = route.requirements.empty? ? "" : route.requirements.inspect
|
310
|
-
url_permutations(segs) do |segments|
|
311
|
-
path = segments.join('').gsub('(.:format)', '').gsub(/\/$/, '')
|
312
|
-
puts "\t\tbuilt url: #{path}"
|
313
|
-
permutation = {:name => name, :verb => verb, :segs => segs, :reqs => reqs, :path => path}
|
314
|
-
routes << permutation
|
315
|
-
end
|
316
|
-
|
317
|
-
[routes, params]
|
318
|
-
end
|
319
|
-
|
320
141
|
def handle_redirect(redirect_url)
|
321
142
|
existing_pages = site_model.pages_for(response.redirect_url)
|
322
143
|
if (existing_pages.size > 0)
|
@@ -326,59 +147,10 @@ module Stratagem::Crawler::Session
|
|
326
147
|
get redirect_url
|
327
148
|
end
|
328
149
|
|
329
|
-
site_model.add(nil, response) {|redirect_url|
|
150
|
+
site_model.add(nil, request, response) {|redirect_url|
|
330
151
|
# TODO - record as bug!
|
331
152
|
puts "recursive redirect #{redirect_url}"
|
332
153
|
}
|
333
154
|
end
|
334
155
|
end
|
335
|
-
|
336
|
-
def resolve_parameter_types(route_container)
|
337
|
-
puts "\tresolving parameter types"
|
338
|
-
resolved_parameters = {}
|
339
|
-
route_infos, params = build_url(route_container, resolved_parameters)
|
340
|
-
route_info = route_infos.first
|
341
|
-
unknown_params = params.keys
|
342
|
-
log "\tunknown params: #{unknown_params.inspect} - #{unknown_params.size}"
|
343
|
-
progress = nil
|
344
|
-
while ((unknown_params.size > 0) && (progress.nil? || (progress > 0)))
|
345
|
-
progress = 0
|
346
|
-
|
347
|
-
puts "\tloading model invocations for request"
|
348
|
-
delta = model_invocations_for_request do
|
349
|
-
call_route(route_container, route_info, false)
|
350
|
-
end
|
351
|
-
|
352
|
-
puts "\tcalled route, found #{delta.size} invocations"
|
353
|
-
|
354
|
-
unknown_params.clone.each do |key|
|
355
|
-
value = params[key]
|
356
|
-
value_s = params[key].map {|v| v.to_s }
|
357
|
-
delta.each do |invocation|
|
358
|
-
# puts "\t#{route_info[:path]} - #{invocation.model_class.name} - #{invocation.method} - #{invocation.args.inspect} - #{value_s}"
|
359
|
-
# TODO inspect is a hack, refactor
|
360
|
-
if (invocation.args.include?(value.first)) || (invocation.args.inspect.include?('"'+value.first.to_s+'"'))
|
361
|
-
# found match
|
362
|
-
|
363
|
-
puts "\t\tresolved #{key} to #{invocation.model_class}"
|
364
|
-
unknown_params.delete(key)
|
365
|
-
resolved_parameters[key] = invocation.model_class
|
366
|
-
progress += 1
|
367
|
-
|
368
|
-
break
|
369
|
-
end
|
370
|
-
end
|
371
|
-
end
|
372
|
-
|
373
|
-
route_infos, params = build_url(route_container, resolved_parameters)
|
374
|
-
route_info = route_infos.first
|
375
|
-
end
|
376
|
-
|
377
|
-
if (resolved_parameters.size > 0)
|
378
|
-
resolved_parameters
|
379
|
-
else
|
380
|
-
nil
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
156
|
end
|