stratagem 0.1.8 → 0.1.9
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 +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
|