stratagem 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +16 -6
- data/Rakefile +8 -1
- data/lib/generators/stratagem/install/install_base.rb +13 -3
- data/lib/generators/stratagem/install/install_generator.rb +1 -1
- data/lib/stratagem.rb +42 -18
- data/lib/stratagem/authentication.rb +2 -5
- data/lib/stratagem/auto_mock.rb +1 -0
- data/lib/stratagem/auto_mock/aquifer.rb +49 -26
- data/lib/stratagem/auto_mock/factory.rb +1 -6
- data/lib/stratagem/auto_mock/user_loader.rb +38 -0
- data/lib/stratagem/client.rb +15 -4
- data/lib/stratagem/configuration/auth_auth.rb +19 -0
- data/lib/stratagem/configuration/core.rb +20 -0
- data/lib/stratagem/crawler/authentication.rb +17 -12
- data/lib/stratagem/crawler/authentication/automated.rb +40 -0
- data/lib/stratagem/crawler/authentication/base.rb +140 -0
- data/lib/stratagem/crawler/authentication/configured.rb +27 -0
- data/lib/stratagem/crawler/parameter_resolver.rb +12 -8
- data/lib/stratagem/crawler/route_invoker.rb +10 -13
- data/lib/stratagem/crawler/session.rb +14 -2
- data/lib/stratagem/crawler/site_model.rb +4 -173
- data/lib/stratagem/crawler/site_model/edge.rb +20 -0
- data/lib/stratagem/crawler/site_model/page.rb +121 -0
- data/lib/stratagem/crawler/site_model/page_set.rb +58 -0
- data/lib/stratagem/instrumentation/models.rb +3 -14
- data/lib/stratagem/instrumentation/models/annotations.rb +39 -5
- data/lib/stratagem/instrumentation/models/authentication.rb +0 -1
- data/lib/stratagem/instrumentation/models/authentication/authlogic/detect.rb +1 -0
- data/lib/stratagem/instrumentation/models/authentication/devise/detect.rb +1 -1
- data/lib/stratagem/instrumentation/models/authentication/devise/instrumentation.rb +0 -4
- data/lib/stratagem/instrumentation/models/metadata.rb +23 -1
- data/lib/stratagem/instrumentation/models/persistence.rb +3 -4
- data/lib/stratagem/instrumentation/models/persistence/active_record/metadata.rb +2 -2
- data/lib/stratagem/interface/browser.rb +9 -3
- data/lib/stratagem/interface/public/javascripts/stratagem.js +14 -12
- data/lib/stratagem/interface/views/index.haml +3 -3
- data/lib/stratagem/logger.rb +28 -2
- data/lib/stratagem/model.rb +6 -0
- data/lib/stratagem/model/application.rb +21 -134
- data/lib/stratagem/model/components/base.rb +1 -4
- data/lib/stratagem/model/components/controller.rb +1 -2
- data/lib/stratagem/model/components/model.rb +15 -15
- data/lib/stratagem/model/components/route.rb +3 -2
- data/lib/stratagem/model/components/view.rb +0 -1
- data/lib/stratagem/model/containers/base.rb +60 -0
- data/lib/stratagem/model/containers/gem.rb +25 -0
- data/lib/stratagem/model/containers/plugin.rb +11 -0
- data/lib/stratagem/model/containers/route.rb +19 -0
- data/lib/stratagem/model/parse_util.rb +3 -3
- data/lib/stratagem/model_builder.rb +1 -4
- data/lib/stratagem/rack_hack.rb +15 -0
- data/lib/stratagem/site_crawler.rb +5 -4
- data/lib/stratagem/snapshot.rb +5 -7
- data/spec/stratagem/configuration_spec.rb +32 -0
- data/stratagem.gemspec +5 -8
- data/templates/install/environments/stratagem.rb.erb +31 -2
- data/templates/install/script/stratagem +16 -0
- data/templates/install/tasks/stratagem.rake +2 -2
- metadata +36 -65
- data/bin/stratagem +0 -58
- data/lib/stratagem/scan.rb +0 -19
- data/lib/stratagem/scan/checks/email_address.rb +0 -15
- data/lib/stratagem/scan/checks/error_pages.rb +0 -25
- data/lib/stratagem/scan/result.rb +0 -45
- data/lib/stratagem/scanner.rb +0 -32
@@ -15,9 +15,7 @@ module Stratagem::Crawler
|
|
15
15
|
begin
|
16
16
|
call_route!(route_info, track_invocations)
|
17
17
|
rescue Exception
|
18
|
-
|
19
|
-
puts "ERROR: #{$!.message}"
|
20
|
-
puts $!.backtrace
|
18
|
+
Stratagem.logger.error($!)
|
21
19
|
end
|
22
20
|
end
|
23
21
|
|
@@ -31,8 +29,8 @@ module Stratagem::Crawler
|
|
31
29
|
invocations = model_invocations_for_request do
|
32
30
|
case verb
|
33
31
|
when 'get'
|
32
|
+
puts "\t#{route_info[:path].to_s == '/connections'} - #{route_info[:path]}"
|
34
33
|
do_get(route_info)
|
35
|
-
puts "\tresponse code: #{response.code}" if response
|
36
34
|
when 'post'
|
37
35
|
do_post(route_info)
|
38
36
|
when 'put'
|
@@ -43,20 +41,20 @@ module Stratagem::Crawler
|
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
|
-
if (response)
|
47
|
-
|
48
|
-
|
44
|
+
if (response && track_invocations)
|
45
|
+
changes = detect_attribute_changes_in_models(invocations)
|
46
|
+
if (![500].include?(response.code) || (changes.size > 0) || (invocations.size > 0))
|
49
47
|
puts "\tfound #{invocations.size} invocations"
|
50
48
|
invocations.each do |i|
|
51
49
|
puts "\t\t#{i.controller_action} -> #{i.model_class}"
|
52
50
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
51
|
+
puts "\tchanges values: #{changes.values.inspect}"
|
52
|
+
page = site_model.add(route_info[:route_container], controller, request, response, invocations, changes) {|redirect_url| redirect_proc.call(redirect_url) }
|
53
|
+
puts "\tresponse code: #{response.code} - body size: #{page.response_body.size}"
|
56
54
|
puts "\tadded to site model"
|
57
55
|
end
|
58
56
|
else
|
59
|
-
puts "ERROR: did not call #{route_info
|
57
|
+
puts "ERROR: did not call #{route_info[:path]}"
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
@@ -169,7 +167,6 @@ module Stratagem::Crawler
|
|
169
167
|
|
170
168
|
def guess_form_for_route(route_info)
|
171
169
|
forms = []
|
172
|
-
pages = site_models.map {|sm| sm.pages }.flatten
|
173
170
|
site_models.each do |site_model|
|
174
171
|
site_model.pages.each do |page|
|
175
172
|
page.forms.each do |form|
|
@@ -178,7 +175,7 @@ module Stratagem::Crawler
|
|
178
175
|
(route_info[:path].sub(/\.\(\:format\)*/, '') == form.action) &&
|
179
176
|
((form.implied_method || form.method).to_s.downcase == route_info[:verb].to_s.downcase)
|
180
177
|
end
|
181
|
-
puts "\
|
178
|
+
puts "\tpageset: #{site_model.name} - form: #{form.action} - #{form.implied_method || form.method} - #{usable} - #{form.inputs.map{|i| i.name}}"
|
182
179
|
forms << form if (usable)
|
183
180
|
end
|
184
181
|
end
|
@@ -56,6 +56,7 @@ module Stratagem::Crawler::Session
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def crawler_session(application_model=nil)
|
59
|
+
aquifer # force fill
|
59
60
|
@application_model = application_model if application_model
|
60
61
|
open_session do |session|
|
61
62
|
@session = session
|
@@ -68,7 +69,16 @@ module Stratagem::Crawler::Session
|
|
68
69
|
end
|
69
70
|
|
70
71
|
def aquifer(initial_capacity=6)
|
71
|
-
@aquifer ||=
|
72
|
+
@aquifer ||= begin
|
73
|
+
Stratagem.logger.phase "mocking_models"
|
74
|
+
|
75
|
+
if Stratagem.configuration.use_automatic_mocking
|
76
|
+
Stratagem::AutoMock::Aquifer.init(application_model).fill_by_automock(initial_capacity)
|
77
|
+
else
|
78
|
+
# TODO - refactor out into another method
|
79
|
+
Stratagem::AutoMock::Aquifer.init(application_model).fill_by_configuration(Stratagem.configuration.credentials)
|
80
|
+
end
|
81
|
+
end
|
72
82
|
end
|
73
83
|
|
74
84
|
def site_models
|
@@ -81,11 +91,13 @@ module Stratagem::Crawler::Session
|
|
81
91
|
end
|
82
92
|
|
83
93
|
def page_set(name, &block)
|
94
|
+
Stratagem.logger.phase "traversing_site"
|
95
|
+
|
84
96
|
log "---------------------------------------"
|
85
97
|
log "Crawling page set #{name}"
|
86
98
|
log "---------------------------------------"
|
87
99
|
reset!
|
88
|
-
site_models << Stratagem::Crawler::SiteModel.new(name)
|
100
|
+
site_models << Stratagem::Crawler::SiteModel::PageSet.new(name)
|
89
101
|
yield site_model
|
90
102
|
end
|
91
103
|
|
@@ -1,174 +1,5 @@
|
|
1
|
-
module Stratagem::Crawler
|
2
|
-
class SiteModel
|
3
|
-
include Stratagem::Crawler::HtmlUtils
|
1
|
+
module Stratagem::Crawler::SiteModel; end
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(name)
|
9
|
-
@name = name
|
10
|
-
@pages = []
|
11
|
-
@edges = []
|
12
|
-
end
|
13
|
-
|
14
|
-
def export
|
15
|
-
{
|
16
|
-
:name => name,
|
17
|
-
:pages => @pages.map {|page| page.export },
|
18
|
-
:edges => @edges.map {|edge| edge.export },
|
19
|
-
:authentication => authentication.nil? ? nil : {
|
20
|
-
:authenticated_user_id => authentication.authenticated_with.object_id,
|
21
|
-
:success => authentication.success,
|
22
|
-
:login_page_external_id => authentication.login_page.object_id,
|
23
|
-
:response_page_external_id => authentication.response_page.object_id,
|
24
|
-
:ssl => authentication.ssl
|
25
|
-
},
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
29
|
-
def add_edge(from,to,type)
|
30
|
-
self.edges << Edge.new(from,to,type)
|
31
|
-
end
|
32
|
-
|
33
|
-
def add(route, controller, request, response, invocations=[], model_changes={}, &block)
|
34
|
-
page = Page.new(self, controller, request, response, invocations, model_changes, &block)
|
35
|
-
self.pages << page
|
36
|
-
page
|
37
|
-
end
|
38
|
-
|
39
|
-
def pages_for(id)
|
40
|
-
if (id.kind_of?(String))
|
41
|
-
pages.select {|page| page.url == id }
|
42
|
-
else
|
43
|
-
pages.select {|page| page.route == id }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
class Edge
|
50
|
-
# type -> :link, :redirect,
|
51
|
-
attr_accessor :from, :to, :type
|
52
|
-
|
53
|
-
def initialize(from, to, type)
|
54
|
-
@from = from
|
55
|
-
@to = to
|
56
|
-
@type = type
|
57
|
-
end
|
58
|
-
|
59
|
-
def export
|
60
|
-
{
|
61
|
-
:from => from.object_id,
|
62
|
-
:to => to.object_id,
|
63
|
-
:relation_type => type
|
64
|
-
}
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
class Page
|
69
|
-
include Stratagem::Crawler::HtmlUtils
|
70
|
-
|
71
|
-
attr_reader :response
|
72
|
-
|
73
|
-
attr_accessor :url
|
74
|
-
attr_accessor :path
|
75
|
-
attr_accessor :method
|
76
|
-
attr_accessor :redirected_to
|
77
|
-
attr_accessor :document
|
78
|
-
attr_accessor :response_body
|
79
|
-
|
80
|
-
def initialize(site_model, controller, request, response, invocations, model_changes, &block)
|
81
|
-
@site_model = site_model
|
82
|
-
@invocations = invocations
|
83
|
-
@model_changes = model_changes
|
84
|
-
@authenticity_checked = (controller && controller.methods.include?(:authenticity_checked?)) ? controller.authenticity_checked? : true
|
85
|
-
init(request, response, &block)
|
86
|
-
end
|
87
|
-
|
88
|
-
def route
|
89
|
-
@route ||= Stratagem::Model::Application.instance.routes.recognize(self)
|
90
|
-
end
|
91
|
-
|
92
|
-
def export
|
93
|
-
h = {
|
94
|
-
:external_id => self.object_id,
|
95
|
-
:url => url,
|
96
|
-
:path => path,
|
97
|
-
:request_method => method,
|
98
|
-
:redirected_to_page_external_id => redirected_to ? redirected_to.object_id : nil,
|
99
|
-
:route_external_id => route ? route.object_id : nil,
|
100
|
-
:references => @invocations.map {|i| i.to_reference.export },
|
101
|
-
:model_changes => Hash[@model_changes.map {|model,changes| [model.object_id, changes] }].to_json,
|
102
|
-
:authenticity_checked => @authenticity_checked,
|
103
|
-
:parameters => @request.parameters.to_json,
|
104
|
-
:response_body => @response_body
|
105
|
-
}
|
106
|
-
h
|
107
|
-
end
|
108
|
-
|
109
|
-
def init(request, response, &block)
|
110
|
-
@request = request.clone
|
111
|
-
@response = response.clone
|
112
|
-
@url = request.url
|
113
|
-
@path = request.path
|
114
|
-
@method = request.method
|
115
|
-
begin
|
116
|
-
@document = Nokogiri::HTML(response.body)
|
117
|
-
rescue
|
118
|
-
puts "ERROR: Could not parse html: #{$!.message} - #{response.body}"
|
119
|
-
end
|
120
|
-
@response_body = response.body
|
121
|
-
self.redirected_to = block.call(response.redirect_url) if response.redirect?
|
122
|
-
end
|
123
|
-
|
124
|
-
def reload(&block)
|
125
|
-
# TODO - should support all the verbs and params, but
|
126
|
-
# hack together for now to reload the authenticity token
|
127
|
-
request,response = yield url
|
128
|
-
init(request, response) {|redirected_to| }
|
129
|
-
end
|
130
|
-
|
131
|
-
def redirected?
|
132
|
-
!self.redirected_to.nil?
|
133
|
-
end
|
134
|
-
|
135
|
-
def forms
|
136
|
-
@forms ||= begin
|
137
|
-
if (@document)
|
138
|
-
forms = self.parse_forms(@document)
|
139
|
-
forms.each do |form|
|
140
|
-
form.page = self
|
141
|
-
end
|
142
|
-
forms
|
143
|
-
else
|
144
|
-
[]
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def login_form
|
150
|
-
self.find_login_form(@document)
|
151
|
-
end
|
152
|
-
|
153
|
-
def to_html
|
154
|
-
@document.to_html
|
155
|
-
end
|
156
|
-
|
157
|
-
def outbound_edges(type=nil)
|
158
|
-
@site_model.edges.select {|edge| edge.from == self && (type.nil? || (type == edge.type)) }
|
159
|
-
end
|
160
|
-
|
161
|
-
def inbound_edges(type=nil)
|
162
|
-
@site_model.edges.select {|edge| (edge.to == self) && (type.nil? || (type == edge.type)) }
|
163
|
-
end
|
164
|
-
|
165
|
-
def title
|
166
|
-
if ((@document) && !(@title))
|
167
|
-
title = (@document/'head title').first
|
168
|
-
@title = title.inner_html if title
|
169
|
-
end
|
170
|
-
@title
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
3
|
+
require 'stratagem/crawler/site_model/page_set'
|
4
|
+
require 'stratagem/crawler/site_model/page'
|
5
|
+
require 'stratagem/crawler/site_model/edge'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Stratagem::Crawler::SiteModel
|
2
|
+
class Edge
|
3
|
+
# type -> :link, :redirect,
|
4
|
+
attr_accessor :from, :to, :type
|
5
|
+
|
6
|
+
def initialize(from, to, type)
|
7
|
+
@from = from
|
8
|
+
@to = to
|
9
|
+
@type = type
|
10
|
+
end
|
11
|
+
|
12
|
+
def export
|
13
|
+
{
|
14
|
+
:from_page_external_id => from.object_id,
|
15
|
+
:to_page_external_id => to.object_id,
|
16
|
+
:relation_type => type
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Stratagem::Crawler::SiteModel
|
2
|
+
|
3
|
+
class Page
|
4
|
+
include Stratagem::Crawler::HtmlUtils
|
5
|
+
|
6
|
+
attr_reader :response
|
7
|
+
|
8
|
+
attr_accessor :url
|
9
|
+
attr_accessor :path
|
10
|
+
attr_accessor :method
|
11
|
+
attr_accessor :redirected_to
|
12
|
+
attr_accessor :document
|
13
|
+
attr_accessor :response_body
|
14
|
+
|
15
|
+
def initialize(site_model, controller, request, response, invocations, model_changes, &block)
|
16
|
+
@site_model = site_model
|
17
|
+
@invocations = invocations
|
18
|
+
@model_changes = model_changes
|
19
|
+
@authenticity_checked = (controller && controller.methods.include?(:authenticity_checked?)) ? controller.authenticity_checked? : true
|
20
|
+
init(request, response, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def route
|
24
|
+
@route ||= Stratagem::Model::Application.instance.routes.recognize(self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def export
|
28
|
+
h = {
|
29
|
+
:external_id => self.object_id,
|
30
|
+
:url => url,
|
31
|
+
:path => path,
|
32
|
+
:request_method => method,
|
33
|
+
:redirected_to_page_external_id => redirected_to ? redirected_to.object_id : nil,
|
34
|
+
:route_external_id => route ? route.object_id : nil,
|
35
|
+
:references_attributes => @invocations.map {|i| i.to_reference.export },
|
36
|
+
:model_changes => Hash[@model_changes.map {|model,changes| [model.object_id, changes] }].to_json,
|
37
|
+
:authenticity_checked => @authenticity_checked,
|
38
|
+
:parameters => @request.parameters.to_json,
|
39
|
+
:response_body => @response_body,
|
40
|
+
:response_code => @response.code
|
41
|
+
}
|
42
|
+
h
|
43
|
+
end
|
44
|
+
|
45
|
+
def init(request, response, &block)
|
46
|
+
@request = request.clone
|
47
|
+
@response = response.clone
|
48
|
+
@url = request.url
|
49
|
+
@path = request.path
|
50
|
+
@method = request.method
|
51
|
+
body = response.body
|
52
|
+
begin
|
53
|
+
if [500, 400, 302].include?(response.code)
|
54
|
+
body = ''
|
55
|
+
elsif
|
56
|
+
body.kind_of?(Array)
|
57
|
+
body = body.join
|
58
|
+
body = body.slice(0,20000)
|
59
|
+
end
|
60
|
+
|
61
|
+
@document = Nokogiri::HTML(body)
|
62
|
+
rescue
|
63
|
+
puts "ERROR: Could not parse html: #{$!.message} - #{body}"
|
64
|
+
end
|
65
|
+
|
66
|
+
@response_body = body
|
67
|
+
|
68
|
+
self.redirected_to = block.call(response.redirect_url) if response.redirect?
|
69
|
+
end
|
70
|
+
|
71
|
+
def reload(&block)
|
72
|
+
# TODO - should support all the verbs and params, but
|
73
|
+
# hack together for now to reload the authenticity token
|
74
|
+
request,response = yield url
|
75
|
+
init(request, response) {|redirected_to| }
|
76
|
+
end
|
77
|
+
|
78
|
+
def redirected?
|
79
|
+
!self.redirected_to.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
def forms
|
83
|
+
@forms ||= begin
|
84
|
+
if (@document)
|
85
|
+
forms = self.parse_forms(@document)
|
86
|
+
forms.each do |form|
|
87
|
+
form.page = self
|
88
|
+
end
|
89
|
+
forms
|
90
|
+
else
|
91
|
+
[]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def login_form
|
97
|
+
self.find_login_form(@document)
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_html
|
101
|
+
@document.to_html
|
102
|
+
end
|
103
|
+
|
104
|
+
def outbound_edges(type=nil)
|
105
|
+
@site_model.edges.select {|edge| edge.from == self && (type.nil? || (type == edge.type)) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def inbound_edges(type=nil)
|
109
|
+
@site_model.edges.select {|edge| (edge.to == self) && (type.nil? || (type == edge.type)) }
|
110
|
+
end
|
111
|
+
|
112
|
+
def title
|
113
|
+
if ((@document) && !(@title))
|
114
|
+
title = (@document/'head title').first
|
115
|
+
@title = title.inner_html if title
|
116
|
+
end
|
117
|
+
@title
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Stratagem::Crawler::SiteModel
|
2
|
+
|
3
|
+
class PageSet
|
4
|
+
include Stratagem::Crawler::HtmlUtils
|
5
|
+
|
6
|
+
attr_reader :pages, :edges, :name
|
7
|
+
attr_accessor :authentication
|
8
|
+
|
9
|
+
def initialize(name)
|
10
|
+
@name = name
|
11
|
+
@pages = []
|
12
|
+
@edges = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def export
|
16
|
+
puts "Exporting page set #{name} with #{@pages.size} initial pages"
|
17
|
+
pages = @pages.select {|page| page.response_body && page.response_body.size > 0 }
|
18
|
+
puts "\t#{pages.size} filtered pages"
|
19
|
+
pages = pages.map {|page| page.export }
|
20
|
+
|
21
|
+
pages << authentication.login_page.export if authentication && authentication.login_page
|
22
|
+
pages << authentication.response_page.export if authentication && authentication.response_page
|
23
|
+
|
24
|
+
puts "\t#{pages.size} exported pages"
|
25
|
+
{
|
26
|
+
:name => name,
|
27
|
+
:pages_attributes => pages,
|
28
|
+
:edges_attributes => @edges.map {|edge| edge.export },
|
29
|
+
:authentication_attributes => authentication.nil? ? nil : {
|
30
|
+
:authenticated_user_id => authentication.authenticated_with.object_id,
|
31
|
+
:success => authentication.success,
|
32
|
+
:login_page_external_id => authentication.login_page ? authentication.login_page.object_id : nil,
|
33
|
+
:response_page_external_id => authentication.response_page ? authentication.response_page.object_id : nil,
|
34
|
+
:ssl => authentication.ssl
|
35
|
+
},
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def add_edge(from,to,type)
|
40
|
+
self.edges << Edge.new(from,to,type)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add(route, controller, request, response, invocations=[], model_changes={}, &block)
|
44
|
+
page = Page.new(self, controller, request, response, invocations, model_changes, &block)
|
45
|
+
self.pages << page
|
46
|
+
page
|
47
|
+
end
|
48
|
+
|
49
|
+
def pages_for(id)
|
50
|
+
if (id.kind_of?(String))
|
51
|
+
pages.select {|page| page.url == id }
|
52
|
+
else
|
53
|
+
pages.select {|page| page.route == id }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|