railroader 4.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +1091 -0
  3. data/FEATURES +16 -0
  4. data/README.md +174 -0
  5. data/bin/railroader +8 -0
  6. data/lib/railroader/app_tree.rb +191 -0
  7. data/lib/railroader/call_index.rb +219 -0
  8. data/lib/railroader/checks/base_check.rb +505 -0
  9. data/lib/railroader/checks/check_basic_auth.rb +88 -0
  10. data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
  11. data/lib/railroader/checks/check_content_tag.rb +200 -0
  12. data/lib/railroader/checks/check_create_with.rb +74 -0
  13. data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
  14. data/lib/railroader/checks/check_default_routes.rb +86 -0
  15. data/lib/railroader/checks/check_deserialize.rb +56 -0
  16. data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
  17. data/lib/railroader/checks/check_digest_dos.rb +38 -0
  18. data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
  19. data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
  20. data/lib/railroader/checks/check_escape_function.rb +21 -0
  21. data/lib/railroader/checks/check_evaluation.rb +35 -0
  22. data/lib/railroader/checks/check_execute.rb +189 -0
  23. data/lib/railroader/checks/check_file_access.rb +71 -0
  24. data/lib/railroader/checks/check_file_disclosure.rb +35 -0
  25. data/lib/railroader/checks/check_filter_skipping.rb +31 -0
  26. data/lib/railroader/checks/check_forgery_setting.rb +81 -0
  27. data/lib/railroader/checks/check_header_dos.rb +31 -0
  28. data/lib/railroader/checks/check_i18n_xss.rb +48 -0
  29. data/lib/railroader/checks/check_jruby_xml.rb +36 -0
  30. data/lib/railroader/checks/check_json_encoding.rb +47 -0
  31. data/lib/railroader/checks/check_json_parsing.rb +107 -0
  32. data/lib/railroader/checks/check_link_to.rb +132 -0
  33. data/lib/railroader/checks/check_link_to_href.rb +146 -0
  34. data/lib/railroader/checks/check_mail_to.rb +49 -0
  35. data/lib/railroader/checks/check_mass_assignment.rb +196 -0
  36. data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
  37. data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
  38. data/lib/railroader/checks/check_model_attributes.rb +119 -0
  39. data/lib/railroader/checks/check_model_serialize.rb +67 -0
  40. data/lib/railroader/checks/check_nested_attributes.rb +38 -0
  41. data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
  42. data/lib/railroader/checks/check_number_to_currency.rb +74 -0
  43. data/lib/railroader/checks/check_permit_attributes.rb +43 -0
  44. data/lib/railroader/checks/check_quote_table_name.rb +40 -0
  45. data/lib/railroader/checks/check_redirect.rb +256 -0
  46. data/lib/railroader/checks/check_regex_dos.rb +68 -0
  47. data/lib/railroader/checks/check_render.rb +97 -0
  48. data/lib/railroader/checks/check_render_dos.rb +37 -0
  49. data/lib/railroader/checks/check_render_inline.rb +53 -0
  50. data/lib/railroader/checks/check_response_splitting.rb +21 -0
  51. data/lib/railroader/checks/check_route_dos.rb +42 -0
  52. data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
  53. data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
  54. data/lib/railroader/checks/check_secrets.rb +40 -0
  55. data/lib/railroader/checks/check_select_tag.rb +59 -0
  56. data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
  57. data/lib/railroader/checks/check_send.rb +47 -0
  58. data/lib/railroader/checks/check_send_file.rb +19 -0
  59. data/lib/railroader/checks/check_session_manipulation.rb +35 -0
  60. data/lib/railroader/checks/check_session_settings.rb +176 -0
  61. data/lib/railroader/checks/check_simple_format.rb +58 -0
  62. data/lib/railroader/checks/check_single_quotes.rb +101 -0
  63. data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
  64. data/lib/railroader/checks/check_sql.rb +700 -0
  65. data/lib/railroader/checks/check_sql_cves.rb +106 -0
  66. data/lib/railroader/checks/check_ssl_verify.rb +48 -0
  67. data/lib/railroader/checks/check_strip_tags.rb +89 -0
  68. data/lib/railroader/checks/check_symbol_dos.rb +71 -0
  69. data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
  70. data/lib/railroader/checks/check_translate_bug.rb +45 -0
  71. data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
  72. data/lib/railroader/checks/check_unscoped_find.rb +57 -0
  73. data/lib/railroader/checks/check_validation_regex.rb +116 -0
  74. data/lib/railroader/checks/check_weak_hash.rb +148 -0
  75. data/lib/railroader/checks/check_without_protection.rb +80 -0
  76. data/lib/railroader/checks/check_xml_dos.rb +45 -0
  77. data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
  78. data/lib/railroader/checks.rb +209 -0
  79. data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
  80. data/lib/railroader/commandline.rb +179 -0
  81. data/lib/railroader/differ.rb +66 -0
  82. data/lib/railroader/file_parser.rb +54 -0
  83. data/lib/railroader/format/style.css +133 -0
  84. data/lib/railroader/options.rb +339 -0
  85. data/lib/railroader/parsers/rails2_erubis.rb +6 -0
  86. data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
  87. data/lib/railroader/parsers/rails3_erubis.rb +81 -0
  88. data/lib/railroader/parsers/template_parser.rb +108 -0
  89. data/lib/railroader/processor.rb +102 -0
  90. data/lib/railroader/processors/alias_processor.rb +1229 -0
  91. data/lib/railroader/processors/base_processor.rb +295 -0
  92. data/lib/railroader/processors/config_processor.rb +14 -0
  93. data/lib/railroader/processors/controller_alias_processor.rb +278 -0
  94. data/lib/railroader/processors/controller_processor.rb +249 -0
  95. data/lib/railroader/processors/erb_template_processor.rb +77 -0
  96. data/lib/railroader/processors/erubis_template_processor.rb +92 -0
  97. data/lib/railroader/processors/gem_processor.rb +64 -0
  98. data/lib/railroader/processors/haml_template_processor.rb +191 -0
  99. data/lib/railroader/processors/lib/basic_processor.rb +37 -0
  100. data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
  101. data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
  102. data/lib/railroader/processors/lib/find_call.rb +183 -0
  103. data/lib/railroader/processors/lib/find_return_value.rb +166 -0
  104. data/lib/railroader/processors/lib/module_helper.rb +111 -0
  105. data/lib/railroader/processors/lib/processor_helper.rb +88 -0
  106. data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
  107. data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
  108. data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
  109. data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
  110. data/lib/railroader/processors/lib/render_helper.rb +181 -0
  111. data/lib/railroader/processors/lib/render_path.rb +107 -0
  112. data/lib/railroader/processors/lib/route_helper.rb +68 -0
  113. data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
  114. data/lib/railroader/processors/library_processor.rb +74 -0
  115. data/lib/railroader/processors/model_processor.rb +91 -0
  116. data/lib/railroader/processors/output_processor.rb +144 -0
  117. data/lib/railroader/processors/route_processor.rb +17 -0
  118. data/lib/railroader/processors/slim_template_processor.rb +111 -0
  119. data/lib/railroader/processors/template_alias_processor.rb +118 -0
  120. data/lib/railroader/processors/template_processor.rb +85 -0
  121. data/lib/railroader/report/config/remediation.yml +71 -0
  122. data/lib/railroader/report/ignore/config.rb +153 -0
  123. data/lib/railroader/report/ignore/interactive.rb +362 -0
  124. data/lib/railroader/report/pager.rb +112 -0
  125. data/lib/railroader/report/renderer.rb +24 -0
  126. data/lib/railroader/report/report_base.rb +292 -0
  127. data/lib/railroader/report/report_codeclimate.rb +79 -0
  128. data/lib/railroader/report/report_csv.rb +55 -0
  129. data/lib/railroader/report/report_hash.rb +23 -0
  130. data/lib/railroader/report/report_html.rb +216 -0
  131. data/lib/railroader/report/report_json.rb +45 -0
  132. data/lib/railroader/report/report_markdown.rb +107 -0
  133. data/lib/railroader/report/report_table.rb +117 -0
  134. data/lib/railroader/report/report_tabs.rb +17 -0
  135. data/lib/railroader/report/report_text.rb +198 -0
  136. data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
  137. data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
  138. data/lib/railroader/report/templates/error_overview.html.erb +29 -0
  139. data/lib/railroader/report/templates/header.html.erb +58 -0
  140. data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
  141. data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
  142. data/lib/railroader/report/templates/overview.html.erb +38 -0
  143. data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
  144. data/lib/railroader/report/templates/template_overview.html.erb +21 -0
  145. data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
  146. data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
  147. data/lib/railroader/report.rb +88 -0
  148. data/lib/railroader/rescanner.rb +483 -0
  149. data/lib/railroader/scanner.rb +321 -0
  150. data/lib/railroader/tracker/collection.rb +93 -0
  151. data/lib/railroader/tracker/config.rb +154 -0
  152. data/lib/railroader/tracker/constants.rb +171 -0
  153. data/lib/railroader/tracker/controller.rb +161 -0
  154. data/lib/railroader/tracker/library.rb +17 -0
  155. data/lib/railroader/tracker/model.rb +90 -0
  156. data/lib/railroader/tracker/template.rb +33 -0
  157. data/lib/railroader/tracker.rb +362 -0
  158. data/lib/railroader/util.rb +503 -0
  159. data/lib/railroader/version.rb +3 -0
  160. data/lib/railroader/warning.rb +294 -0
  161. data/lib/railroader/warning_codes.rb +117 -0
  162. data/lib/railroader.rb +544 -0
  163. data/lib/ruby_parser/bm_sexp.rb +626 -0
  164. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  165. metadata +386 -0
@@ -0,0 +1,132 @@
1
+
2
+ require 'railroader/processors/lib/basic_processor'
3
+
4
+ #Processes configuration. Results are put in tracker.config.
5
+ #
6
+ #Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.
7
+ #For example:
8
+ #
9
+ # MyApp::Application.configure do
10
+ # config.active_record.whitelist_attributes = true
11
+ # end
12
+ #
13
+ #will be stored in
14
+ #
15
+ # tracker.config.rails[:active_record][:whitelist_attributes]
16
+ #
17
+ #Values for tracker.config.rails will still be Sexps.
18
+ class Railroader::Rails3ConfigProcessor < Railroader::BasicProcessor
19
+ RAILS_CONFIG = Sexp.new(:call, nil, :config)
20
+
21
+ def initialize *args
22
+ super
23
+ @inside_config = false
24
+ end
25
+
26
+ #Use this method to process configuration file
27
+ def process_config src, file_name
28
+ @file_name = file_name
29
+ res = Railroader::AliasProcessor.new(@tracker).process_safely(src, nil, @file_name)
30
+ process res
31
+ end
32
+
33
+ #Look for MyApp::Application.configure do ... end
34
+ def process_iter exp
35
+ call = exp.block_call
36
+
37
+ if node_type?(call.target, :colon2) and
38
+ call.target.rhs == :Application and
39
+ call.method == :configure
40
+
41
+ @inside_config = true
42
+ process exp.block if sexp? exp.block
43
+ @inside_config = false
44
+ end
45
+
46
+ exp
47
+ end
48
+
49
+ #Look for class Application < Rails::Application
50
+ def process_class exp
51
+ if exp.class_name == :Application
52
+ @inside_config = true
53
+ process_all exp.body if sexp? exp.body
54
+ @inside_config = false
55
+ end
56
+
57
+ exp
58
+ end
59
+
60
+ #Look for configuration settings
61
+ def process_attrasgn exp
62
+ return exp unless @inside_config
63
+
64
+ if exp.target == RAILS_CONFIG
65
+ #Get rid of '=' at end
66
+ attribute = exp.method.to_s[0..-2].to_sym
67
+ if exp.args.length > 1
68
+ #Multiple arguments?...not sure if this will ever happen
69
+ @tracker.config.rails[attribute] = exp.args
70
+ else
71
+ @tracker.config.rails[attribute] = exp.first_arg
72
+ end
73
+ elsif include_rails_config? exp
74
+ options = get_rails_config exp
75
+ level = @tracker.config.rails
76
+ options[0..-2].each do |o|
77
+ level[o] ||= {}
78
+
79
+ option = level[o]
80
+
81
+ if not option.is_a? Hash
82
+ Railroader.debug "[Notice] Skipping config setting: #{options.map(&:to_s).join(".")}"
83
+ return exp
84
+ end
85
+
86
+ level = level[o]
87
+ end
88
+
89
+ level[options.last] = exp.first_arg
90
+ end
91
+
92
+ exp
93
+ end
94
+
95
+ #Check if an expression includes a call to set Rails config
96
+ def include_rails_config? exp
97
+ target = exp.target
98
+ if call? target
99
+ if target.target == RAILS_CONFIG
100
+ true
101
+ else
102
+ include_rails_config? target
103
+ end
104
+ elsif target == RAILS_CONFIG
105
+ true
106
+ else
107
+ false
108
+ end
109
+ end
110
+
111
+ #Returns an array of symbols for each 'level' in the config
112
+ #
113
+ # config.action_controller.session_store = :cookie
114
+ #
115
+ #becomes
116
+ #
117
+ # [:action_controller, :session_store]
118
+ def get_rails_config exp
119
+ if node_type? exp, :attrasgn
120
+ attribute = exp.method.to_s[0..-2].to_sym
121
+ get_rails_config(exp.target) << attribute
122
+ elsif call? exp
123
+ if exp.target == RAILS_CONFIG
124
+ [exp.method]
125
+ else
126
+ get_rails_config(exp.target) << exp.method
127
+ end
128
+ else
129
+ raise "WHAT"
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,308 @@
1
+ require 'railroader/processors/lib/basic_processor'
2
+
3
+ #Processes the Sexp from routes.rb. Stores results in tracker.routes.
4
+ #
5
+ #Note that it is only interested in determining what methods on which
6
+ #controllers are used as routes, not the generated URLs for routes.
7
+ class Railroader::Rails3RoutesProcessor < Railroader::BasicProcessor
8
+ include Railroader::RouteHelper
9
+
10
+ attr_reader :map, :nested, :current_controller
11
+
12
+ def initialize tracker
13
+ super
14
+ @map = Sexp.new(:lvar, :map)
15
+ @nested = nil #used for identifying nested targets
16
+ @prefix = [] #Controller name prefix (a module name, usually)
17
+ @current_controller = nil
18
+ @with_options = nil #For use inside map.with_options
19
+ @controller_block = false
20
+ @file_name = "config/routes.rb"
21
+ end
22
+
23
+ def process_routes exp
24
+ process Railroader::AliasProcessor.new.process_safely(exp, nil, @file_name)
25
+ end
26
+
27
+ def process_call exp
28
+ case exp.method
29
+ when :resources
30
+ process_resources exp
31
+ when :resource
32
+ process_resource exp
33
+ when :root
34
+ process_root exp
35
+ when :member
36
+ process_default exp
37
+ when :get, :put, :post, :delete
38
+ process_verb exp
39
+ when :match
40
+ process_match exp
41
+ else
42
+ exp
43
+ end
44
+ end
45
+
46
+ def process_iter exp
47
+ case exp.block_call.method
48
+ when :namespace
49
+ process_namespace exp
50
+ when :resource
51
+ process_resource_block exp
52
+ when :resources
53
+ process_resources_block exp
54
+ when :scope
55
+ process_scope_block exp
56
+ when :controller
57
+ process_controller_block exp
58
+ else
59
+ process_default exp
60
+ end
61
+ end
62
+
63
+ def process_namespace exp
64
+ arg = exp.block_call.first_arg
65
+ return exp unless symbol? arg or string? arg
66
+
67
+ name = arg.value
68
+ block = exp.block
69
+
70
+ @prefix << camelize(name)
71
+
72
+ process block
73
+
74
+ @prefix.pop
75
+
76
+ exp
77
+ end
78
+
79
+ #TODO: Need test for this
80
+ def process_root exp
81
+ if value = hash_access(exp.first_arg, :to)
82
+ if string? value
83
+ add_route_from_string value
84
+ end
85
+ end
86
+
87
+ exp
88
+ end
89
+
90
+ def process_match exp
91
+ first_arg = exp.first_arg
92
+ second_arg = exp.second_arg
93
+ last_arg = exp.last_arg
94
+
95
+ if string? first_arg
96
+
97
+ matcher = first_arg.value
98
+ if matcher == ':controller(/:action(/:id(.:format)))' or
99
+ matcher.include? ':controller' and action_route?(matcher) #Default routes
100
+ @tracker.routes[:allow_all_actions] = first_arg
101
+ return exp
102
+ elsif action_route?(first_arg)
103
+ if hash? second_arg and controller_name = hash_access(second_arg, :controller)
104
+ loose_action(controller_name, "matched") #TODO: Parse verbs
105
+ end
106
+ elsif second_arg.nil? and in_controller_block? and not matcher.include? ":"
107
+ add_route matcher
108
+ end
109
+ end
110
+
111
+ if hash? last_arg
112
+ hash_iterate last_arg do |k, v|
113
+ if string? k
114
+ if string? v
115
+ add_route_from_string v
116
+ elsif in_controller_block? and symbol? v
117
+ add_route v
118
+ end
119
+ elsif symbol? k
120
+ case k.value
121
+ when :action
122
+ if string? v
123
+ add_route_from_string v
124
+ else
125
+ add_route v
126
+ end
127
+
128
+ when :to
129
+ if string? v
130
+ add_route_from_string v[1]
131
+ elsif in_controller_block? and symbol? v
132
+ add_route v
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ @current_controller = nil unless in_controller_block?
140
+ exp
141
+ end
142
+
143
+ def add_route_from_string value
144
+ value = value[1] if string? value
145
+
146
+ controller, action = extract_action value
147
+
148
+ if action
149
+ add_route action, controller
150
+ elsif in_controller_block?
151
+ add_route value
152
+ end
153
+ end
154
+
155
+ def process_verb exp
156
+ first_arg = exp.first_arg
157
+ second_arg = exp.second_arg
158
+
159
+ if symbol? first_arg and not hash? second_arg
160
+ add_route first_arg
161
+ elsif hash? second_arg
162
+ hash_iterate second_arg do |k, v|
163
+ if symbol? k and k.value == :to
164
+ if string? v
165
+ add_route_from_string v
166
+ elsif in_controller_block? and symbol? v
167
+ add_route v
168
+ end
169
+ elsif action_route?(first_arg)
170
+ if hash? second_arg and controller_name = hash_access(second_arg, :controller)
171
+ loose_action(controller_name, exp.method)
172
+ end
173
+ end
174
+ end
175
+ elsif string? first_arg
176
+ if first_arg.value.include? ':controller' and action_route?(first_arg) #Default routes
177
+ @tracker.routes[:allow_all_actions] = first_arg
178
+ end
179
+
180
+ route = first_arg.value.split "/"
181
+ if route.length != 2
182
+ add_route route[0]
183
+ else
184
+ add_route route[1], route[0]
185
+ end
186
+ elsif in_controller_block? and symbol? first_arg
187
+ add_route first_arg
188
+ else hash? first_arg
189
+ hash_iterate first_arg do |k, v|
190
+ if string? k
191
+ if string? v
192
+ add_route_from_string v
193
+ elsif in_controller_block?
194
+ add_route v
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ @current_controller = nil unless in_controller_block?
201
+ exp
202
+ end
203
+
204
+ def process_resources exp
205
+ first_arg = exp.first_arg
206
+ second_arg = exp.second_arg
207
+
208
+ return exp unless symbol? first_arg or string? first_arg
209
+
210
+ if second_arg and second_arg.node_type == :hash
211
+ self.current_controller = first_arg.value
212
+ #handle hash
213
+ add_resources_routes
214
+ elsif exp.args.all? { |s| symbol? s }
215
+ exp.each_arg do |s|
216
+ self.current_controller = s.value
217
+ add_resources_routes
218
+ end
219
+ end
220
+
221
+ @current_controller = nil unless in_controller_block?
222
+ exp
223
+ end
224
+
225
+ def process_resource exp
226
+ #Does resource even take more than one controller name?
227
+ exp.each_arg do |s|
228
+ if symbol? s
229
+ self.current_controller = pluralize(s.value.to_s)
230
+ add_resource_routes
231
+ else
232
+ #handle something else, like options
233
+ #or something?
234
+ end
235
+ end
236
+
237
+ @current_controller = nil unless in_controller_block?
238
+ exp
239
+ end
240
+
241
+ def process_resources_block exp
242
+ in_controller_block do
243
+ process_resources exp.block_call
244
+ process exp.block
245
+ end
246
+
247
+ @current_controller = nil
248
+ exp
249
+ end
250
+
251
+ def process_resource_block exp
252
+ in_controller_block do
253
+ process_resource exp.block_call
254
+ process exp.block
255
+ end
256
+
257
+ @current_controller = nil
258
+ exp
259
+ end
260
+
261
+ def process_scope_block exp
262
+ #How to deal with options?
263
+ process exp.block
264
+ exp
265
+ end
266
+
267
+ def process_controller_block exp
268
+ if string? exp or symbol? exp
269
+ self.current_controller = exp.block_call.first_arg.value
270
+
271
+ in_controller_block do
272
+ process exp[-1] if exp[-1]
273
+ end
274
+
275
+ @current_controller = nil
276
+ end
277
+
278
+ exp
279
+ end
280
+
281
+ def extract_action str
282
+ str.split "#"
283
+ end
284
+
285
+ def in_controller_block?
286
+ @controller_block
287
+ end
288
+
289
+ def in_controller_block
290
+ prev_block = @controller_block
291
+ @controller_block = true
292
+ yield
293
+ @controller_block = prev_block
294
+ end
295
+
296
+ def action_route? arg
297
+ if string? arg
298
+ arg = arg.value
299
+ end
300
+
301
+ arg.is_a? String and (arg.include? ":action" or arg.include? "*action")
302
+ end
303
+
304
+ def loose_action controller_name, verb = "any"
305
+ self.current_controller = controller_name.value
306
+ @tracker.routes[@current_controller] = [:allow_all_actions, {:allow_verb => verb}]
307
+ end
308
+ end
@@ -0,0 +1,181 @@
1
+ require 'digest/sha1'
2
+
3
+ #Processes a call to render() in a controller or template
4
+ module Railroader::RenderHelper
5
+
6
+ #Process s(:render, TYPE, OPTION?, OPTIONS)
7
+ def process_render exp
8
+ process_default exp
9
+ @rendered = true
10
+ case exp.render_type
11
+ when :action, :template, :inline
12
+ process_action exp[2][1], exp[3], exp.line
13
+ when :default
14
+ begin
15
+ process_template template_name, exp[3], nil, exp.line
16
+ rescue ArgumentError
17
+ Railroader.debug "Problem processing render: #{exp}"
18
+ end
19
+ when :partial, :layout
20
+ process_partial exp[2], exp[3], exp.line
21
+ when :nothing
22
+ end
23
+ exp
24
+ end
25
+
26
+ #Processes layout
27
+ def process_layout name = nil
28
+ if name.nil? and defined? layout_name
29
+ name = layout_name
30
+ end
31
+
32
+ return unless name
33
+
34
+ process_template name, nil, nil, nil
35
+ end
36
+
37
+ #Determines file name for partial and then processes it
38
+ def process_partial name, args, line
39
+ if name == "" or !(string? name or symbol? name)
40
+ return
41
+ end
42
+
43
+ names = name.value.to_s.split("/")
44
+ names[-1] = "_" + names[-1]
45
+ process_template template_name(names.join("/")), args, nil, line
46
+ end
47
+
48
+ #Processes a given action
49
+ def process_action name, args, line
50
+ if name.is_a? String or name.is_a? Symbol
51
+ process_template template_name(name), args, nil, line
52
+ end
53
+ end
54
+
55
+ #Processes a template, adding any instance variables
56
+ #to its environment.
57
+ def process_template name, args, called_from = nil, *_
58
+
59
+ Railroader.debug "Rendering #{name} (#{called_from})"
60
+ #Get scanned source for this template
61
+ name = name.to_s.gsub(/^\//, "")
62
+ template = @tracker.templates[name.to_sym]
63
+ unless template
64
+ Railroader.debug "[Notice] No such template: #{name}"
65
+ return
66
+ end
67
+
68
+ template_env = only_ivars(:include_request_vars)
69
+
70
+ #Hash the environment and the source of the template to avoid
71
+ #pointlessly processing templates, which can become prohibitively
72
+ #expensive in terms of time and memory.
73
+ digest = Digest::SHA1.new.update(template_env.instance_variable_get(:@env).to_a.sort.to_s << name).to_s.to_sym
74
+
75
+ if @tracker.template_cache.include? digest
76
+ #Already processed this template with identical environment
77
+ return
78
+ else
79
+ @tracker.template_cache << digest
80
+
81
+ options = get_options args
82
+
83
+ #Process layout
84
+ if string? options[:layout]
85
+ process_template "layouts/#{options[:layout][1]}", nil, nil, nil
86
+ elsif node_type? options[:layout], :false
87
+ #nothing
88
+ elsif not template.name.to_s.match(/[^\/_][^\/]+$/)
89
+ #Don't do this for partials
90
+
91
+ process_layout
92
+ end
93
+
94
+ if hash? options[:locals]
95
+ hash_iterate options[:locals] do |key, value|
96
+ template_env[Sexp.new(:call, nil, key.value)] = value
97
+ end
98
+ end
99
+
100
+ if options[:collection]
101
+
102
+ #The collection name is the name of the partial without the leading
103
+ #underscore.
104
+ variable = template.name.to_s.match(/[^\/_][^\/]+$/)[0].to_sym
105
+
106
+ #Unless the :as => :variable_name option is used
107
+ if options[:as]
108
+ if string? options[:as] or symbol? options[:as]
109
+ variable = options[:as].value.to_sym
110
+ end
111
+ end
112
+
113
+ collection = get_class_target(options[:collection]) || Railroader::Tracker::UNKNOWN_MODEL
114
+
115
+ template_env[Sexp.new(:call, nil, variable)] = Sexp.new(:call, Sexp.new(:const, collection), :new)
116
+ end
117
+
118
+ #Set original_line for values so it is clear
119
+ #that values came from another file
120
+ template_env.all.each do |_var, value|
121
+ unless value.original_line
122
+ #TODO: This has been broken for a while now and no one noticed
123
+ #so maybe we can skip it
124
+ value.original_line = value.line
125
+ end
126
+ end
127
+
128
+ #Run source through AliasProcessor with instance variables from the
129
+ #current environment.
130
+ #TODO: Add in :locals => { ... } to environment
131
+ src = Railroader::TemplateAliasProcessor.new(@tracker, template, called_from).process_safely(template.src, template_env)
132
+
133
+ digest = Digest::SHA1.new.update(name + src.to_s).to_s.to_sym
134
+
135
+ if @tracker.template_cache.include? digest
136
+ return
137
+ else
138
+ @tracker.template_cache << digest
139
+ end
140
+
141
+ #Run alias-processed src through the template processor to pull out
142
+ #information and outputs.
143
+ #This information will be stored in tracker.templates, but with a name
144
+ #specifying this particular route. The original source should remain
145
+ #pristine (so it can be processed within other environments).
146
+ @tracker.processor.process_template name, src, template.type, called_from
147
+ end
148
+ end
149
+
150
+ #Override to process name, such as adding the controller name.
151
+ def template_name name
152
+ raise "RenderHelper#template_name should be overridden."
153
+ end
154
+
155
+ #Turn options Sexp into hash
156
+ def get_options args
157
+ options = {}
158
+ return options unless hash? args
159
+
160
+ hash_iterate args do |key, value|
161
+ if symbol? key
162
+ options[key.value] = value
163
+ end
164
+ end
165
+
166
+ options
167
+ end
168
+
169
+ def get_class_target sexp
170
+ if call? sexp
171
+ get_class_target sexp.target
172
+ else
173
+ klass = class_name sexp
174
+ if klass.is_a? Symbol
175
+ klass
176
+ else
177
+ nil
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,107 @@
1
+ module Railroader
2
+ class RenderPath
3
+ attr_reader :path
4
+
5
+ def initialize
6
+ @path = []
7
+ end
8
+
9
+ def add_controller_render controller_name, method_name, line, file
10
+ method_name ||= ""
11
+
12
+ @path << { :type => :controller,
13
+ :class => controller_name.to_sym,
14
+ :method => method_name.to_sym,
15
+ :line => line,
16
+ :file => file
17
+ }
18
+
19
+ self
20
+ end
21
+
22
+ def add_template_render template_name, line, file
23
+ @path << { :type => :template,
24
+ :name => template_name.to_sym,
25
+ :line => line,
26
+ :file => file
27
+ }
28
+
29
+ self
30
+ end
31
+
32
+ def include_template? name
33
+ name = name.to_sym
34
+
35
+ @path.any? do |loc|
36
+ loc[:type] == :template and loc[:name] == name
37
+ end
38
+ end
39
+
40
+ def include_controller? klass
41
+ klass = klass.to_sym
42
+
43
+ @path.any? do |loc|
44
+ loc[:type] == :controller and loc[:class] == klass
45
+ end
46
+ end
47
+
48
+ def include_any_method? method_names
49
+ names = method_names.map(&:to_sym)
50
+
51
+ @path.any? do |loc|
52
+ loc[:type] == :controller and names.include? loc[:method]
53
+ end
54
+ end
55
+
56
+ def rendered_from_controller?
57
+ @path.any? do |loc|
58
+ loc[:type] == :controller
59
+ end
60
+ end
61
+
62
+ def each &block
63
+ @path.each(&block)
64
+ end
65
+
66
+ def join *args
67
+ self.to_a.join(*args)
68
+ end
69
+
70
+ def length
71
+ @path.length
72
+ end
73
+
74
+ def to_a
75
+ @path.map do |loc|
76
+ case loc[:type]
77
+ when :template
78
+ "Template:#{loc[:name]}"
79
+ when :controller
80
+ "#{loc[:class]}##{loc[:method]}"
81
+ end
82
+ end
83
+ end
84
+
85
+ def last
86
+ self.to_a.last
87
+ end
88
+
89
+ def to_s
90
+ self.to_a.to_s
91
+ end
92
+
93
+ def to_sym
94
+ self.to_s.to_sym
95
+ end
96
+
97
+ def to_json *args
98
+ require 'json'
99
+ JSON.generate(@path)
100
+ end
101
+
102
+ def initialize_copy original
103
+ @path = original.path.dup
104
+ self
105
+ end
106
+ end
107
+ end