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,111 @@
1
+ module Railroader::ModuleHelper
2
+ def handle_module exp, tracker_class, parent = nil
3
+ name = class_name(exp.module_name)
4
+
5
+ if @current_module
6
+ outer_module = @current_module
7
+ name = (outer_module.name.to_s + "::" + name.to_s).to_sym
8
+ end
9
+
10
+ if @current_class
11
+ name = (@current_class.name.to_s + "::" + name.to_s).to_sym
12
+ end
13
+
14
+ if @tracker.libs[name]
15
+ @current_module = @tracker.libs[name]
16
+ @current_module.add_file @file_name, exp
17
+ else
18
+ @current_module = tracker_class.new name, parent, @file_name, exp, @tracker
19
+ @tracker.libs[name] = @current_module
20
+ end
21
+
22
+ exp.body = process_all! exp.body
23
+
24
+ if outer_module
25
+ @current_module = outer_module
26
+ else
27
+ @current_module = nil
28
+ end
29
+
30
+ exp
31
+ end
32
+
33
+ def handle_class exp, collection, tracker_class
34
+ name = class_name(exp.class_name)
35
+ parent = class_name exp.parent_name
36
+
37
+ if @current_class
38
+ outer_class = @current_class
39
+ name = (outer_class.name.to_s + "::" + name.to_s).to_sym
40
+ end
41
+
42
+ if @current_module
43
+ name = (@current_module.name.to_s + "::" + name.to_s).to_sym
44
+ end
45
+
46
+ if collection[name]
47
+ @current_class = collection[name]
48
+ @current_class.add_file @file_name, exp
49
+ else
50
+ @current_class = tracker_class.new name, parent, @file_name, exp, @tracker
51
+ collection[name] = @current_class
52
+ end
53
+
54
+ exp.body = process_all! exp.body
55
+
56
+ yield if block_given?
57
+
58
+ if outer_class
59
+ @current_class = outer_class
60
+ else
61
+ @current_class = nil
62
+ end
63
+
64
+ exp
65
+ end
66
+
67
+ def process_defs exp
68
+ name = exp.method_name
69
+
70
+ if node_type? exp[1], :self
71
+ if @current_class
72
+ target = @current_class.name
73
+ elsif @current_module
74
+ target = @current_module.name
75
+ else
76
+ target = nil
77
+ end
78
+ else
79
+ target = class_name exp[1]
80
+ end
81
+
82
+ @current_method = name
83
+ res = Sexp.new :defs, target, name, exp.formal_args, *process_all!(exp.body)
84
+ res.line(exp.line)
85
+ @current_method = nil
86
+
87
+ if @current_class
88
+ @current_class.add_method @visibility, name, res, @file_name
89
+ elsif @current_module
90
+ @current_module.add_method @visibility, name, res, @file_name
91
+ end
92
+ res
93
+ end
94
+
95
+ def process_defn exp
96
+ name = exp.method_name
97
+
98
+ @current_method = name
99
+ res = Sexp.new :defn, name, exp.formal_args, *process_all!(exp.body)
100
+ res.line(exp.line)
101
+ @current_method = nil
102
+
103
+ if @current_class
104
+ @current_class.add_method @visibility, name, res, @file_name
105
+ elsif @current_module
106
+ @current_module.add_method @visibility, name, res, @file_name
107
+ end
108
+
109
+ res
110
+ end
111
+ end
@@ -0,0 +1,88 @@
1
+ #Contains a couple shared methods for Processors.
2
+ module Railroader::ProcessorHelper
3
+ def process_all exp
4
+ exp.each_sexp do |e|
5
+ process e
6
+ end
7
+ exp
8
+ end
9
+
10
+ def process_all! exp
11
+ exp.map! do |e|
12
+ if sexp? e
13
+ process e
14
+ else
15
+ e
16
+ end
17
+ end
18
+
19
+ exp
20
+ end
21
+
22
+ #Process the arguments of a method call. Does not store results.
23
+ #
24
+ #This method is used because Sexp#args and Sexp#arglist create new objects.
25
+ def process_call_args exp
26
+ exp.each_arg do |a|
27
+ process a if sexp? a
28
+ end
29
+
30
+ exp
31
+ end
32
+
33
+ def process_class exp
34
+ current_class = @current_class
35
+ @current_class = class_name exp[1]
36
+ process_all exp.body
37
+ @current_class = current_class
38
+ exp
39
+ end
40
+
41
+ #Sets the current module.
42
+ def process_module exp
43
+ module_name = class_name(exp.class_name).to_s
44
+ prev_module = @current_module
45
+
46
+ if prev_module
47
+ @current_module = "#{prev_module}::#{module_name}"
48
+ else
49
+ @current_module = module_name
50
+ end
51
+
52
+ if block_given?
53
+ yield
54
+ else
55
+ process_all exp.body
56
+ end
57
+
58
+ @current_module = prev_module
59
+
60
+ exp
61
+ end
62
+
63
+ # e.g. private defn
64
+ def process_call_defn? exp
65
+ if call? exp and exp.target.nil? and node_type? exp.first_arg, :defn, :defs and [:private, :public, :protected].include? exp.method
66
+ prev_visibility = @visibility
67
+ @visibility = exp.method
68
+ process exp.first_arg
69
+ @visibility = prev_visibility
70
+ exp
71
+ else
72
+ false
73
+ end
74
+ end
75
+
76
+ def current_file_name
77
+ case
78
+ when @file_name
79
+ @file_name
80
+ when @current_class.is_a?(Railroader::Collection)
81
+ @current_class.file
82
+ when @current_module.is_a?(Railroader::Collection)
83
+ @current_module.file
84
+ else
85
+ nil
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,145 @@
1
+ require 'railroader/processors/lib/basic_processor'
2
+
3
+ #Processes configuration. Results are put in tracker.config.
4
+ #
5
+ #Configuration of Rails via Rails::Initializer are stored in tracker.config.rails.
6
+ #For example:
7
+ #
8
+ # Rails::Initializer.run |config|
9
+ # config.action_controller.session_store = :cookie_store
10
+ # end
11
+ #
12
+ #will be stored in
13
+ #
14
+ # tracker.config[:rails][:action_controller][:session_store]
15
+ #
16
+ #Values for tracker.config.rails will still be Sexps.
17
+ class Railroader::Rails2ConfigProcessor < Railroader::BasicProcessor
18
+ #Replace block variable in
19
+ #
20
+ # Rails::Initializer.run |config|
21
+ #
22
+ #with this value so we can keep track of it.
23
+ RAILS_CONFIG = Sexp.new(:const, :"!BRAKEMAN_RAILS_CONFIG")
24
+
25
+ def initialize *args
26
+ super
27
+ end
28
+
29
+ #Use this method to process configuration file
30
+ def process_config src, file_name
31
+ @file_name = file_name
32
+ res = Railroader::ConfigAliasProcessor.new.process_safely(src, nil, file_name)
33
+ process res
34
+ end
35
+
36
+ #Check if config is set to use Erubis
37
+ def process_call exp
38
+ target = exp.target
39
+ target = process target if sexp? target
40
+
41
+ if exp.method == :gem and exp.first_arg.value == "erubis"
42
+ Railroader.notify "[Notice] Using Erubis for ERB templates"
43
+ @tracker.config.erubis = true
44
+ end
45
+
46
+ exp
47
+ end
48
+
49
+ #Look for configuration settings
50
+ def process_attrasgn exp
51
+ if exp.target == RAILS_CONFIG
52
+ #Get rid of '=' at end
53
+ attribute = exp.method.to_s[0..-2].to_sym
54
+ if exp.args.length > 1
55
+ #Multiple arguments?...not sure if this will ever happen
56
+ @tracker.config.rails[attribute] = exp.args
57
+ else
58
+ @tracker.config.rails[attribute] = exp.first_arg
59
+ end
60
+ elsif include_rails_config? exp
61
+ options = get_rails_config exp
62
+ level = @tracker.config.rails
63
+ options[0..-2].each do |o|
64
+ level[o] ||= {}
65
+ level = level[o]
66
+ end
67
+
68
+ level[options.last] = exp.first_arg
69
+ end
70
+
71
+ exp
72
+ end
73
+
74
+ #Check for Rails version
75
+ def process_cdecl exp
76
+ #Set Rails version required
77
+ if exp.lhs == :RAILS_GEM_VERSION
78
+ @tracker.config.rails_version = exp.rhs.value
79
+ end
80
+
81
+ exp
82
+ end
83
+
84
+ #Check if an expression includes a call to set Rails config
85
+ def include_rails_config? exp
86
+ target = exp.target
87
+ if call? target
88
+ if target.target == RAILS_CONFIG
89
+ true
90
+ else
91
+ include_rails_config? target
92
+ end
93
+ elsif target == RAILS_CONFIG
94
+ true
95
+ else
96
+ false
97
+ end
98
+ end
99
+
100
+ #Returns an array of symbols for each 'level' in the config
101
+ #
102
+ # config.action_controller.session_store = :cookie
103
+ #
104
+ #becomes
105
+ #
106
+ # [:action_controller, :session_store]
107
+ def get_rails_config exp
108
+ if node_type? exp, :attrasgn
109
+ attribute = exp.method.to_s[0..-2].to_sym
110
+ get_rails_config(exp.target) << attribute
111
+ elsif call? exp
112
+ if exp.target == RAILS_CONFIG
113
+ [exp.method]
114
+ else
115
+ get_rails_config(exp.target) << exp.method
116
+ end
117
+ else
118
+ raise "WHAT"
119
+ end
120
+ end
121
+ end
122
+
123
+ #This is necessary to replace block variable so we can track config settings
124
+ class Railroader::ConfigAliasProcessor < Railroader::AliasProcessor
125
+
126
+ RAILS_INIT = Sexp.new(:colon2, Sexp.new(:const, :Rails), :Initializer)
127
+
128
+ #Look for a call to
129
+ #
130
+ # Rails::Initializer.run do |config|
131
+ # ...
132
+ # end
133
+ #
134
+ #and replace config with RAILS_CONFIG
135
+ def process_iter exp
136
+ target = exp.block_call.target
137
+ method = exp.block_call.method
138
+
139
+ if sexp? target and target == RAILS_INIT and method == :run
140
+ env[Sexp.new(:lvar, exp.block_args.value)] = Railroader::Rails2ConfigProcessor::RAILS_CONFIG
141
+ end
142
+
143
+ process_default exp
144
+ end
145
+ end
@@ -0,0 +1,313 @@
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::Rails2RoutesProcessor < 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
+ @file_name = "config/routes.rb"
20
+ end
21
+
22
+ #Call this with parsed route file information.
23
+ #
24
+ #This method first calls RouteAliasProcessor#process_safely on the +exp+,
25
+ #so it does not modify the +exp+.
26
+ def process_routes exp
27
+ process Railroader::RouteAliasProcessor.new.process_safely(exp, nil, @file_name)
28
+ end
29
+
30
+ #Looking for mapping of routes
31
+ def process_call exp
32
+ target = exp.target
33
+
34
+ if target == map or (not target.nil? and target == nested)
35
+ process_map exp
36
+ else
37
+ process_default exp
38
+ end
39
+
40
+ exp
41
+ end
42
+
43
+ #Process a map.something call
44
+ #based on the method used
45
+ def process_map exp
46
+ args = exp.args
47
+
48
+ case exp.method
49
+ when :resource
50
+ process_resource args
51
+ when :resources
52
+ process_resources args
53
+ when :connect, :root
54
+ process_connect args
55
+ else
56
+ process_named_route args
57
+ end
58
+
59
+ exp
60
+ end
61
+
62
+ #Look for map calls that take a block.
63
+ #Otherwise, just do the default processing.
64
+ def process_iter exp
65
+ target = exp.block_call.target
66
+
67
+ if target == map or target == nested
68
+ method = exp.block_call.method
69
+ case method
70
+ when :namespace
71
+ process_namespace exp
72
+ when :resources, :resource
73
+ process_resources exp.block_call.args
74
+ process_default exp.block if exp.block
75
+ when :with_options
76
+ process_with_options exp
77
+ end
78
+ exp
79
+ else
80
+ process_default exp
81
+ end
82
+ end
83
+
84
+ #Process
85
+ # map.resources :x, :controller => :y, :member => ...
86
+ #etc.
87
+ def process_resources exp
88
+ controller = check_for_controller_name exp
89
+ if controller
90
+ self.current_controller = controller
91
+ process_resource_options exp[-1]
92
+ else
93
+ exp.each do |argument|
94
+ if node_type? argument, :lit
95
+ self.current_controller = exp.first.value
96
+ add_resources_routes
97
+ process_resource_options exp.last
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ #Process all the options that might be in the hash passed to
104
+ #map.resource, et al.
105
+ def process_resource_options exp
106
+ if exp.nil? and @with_options
107
+ exp = @with_options
108
+ elsif @with_options
109
+ exp = exp.concat @with_options[1..-1]
110
+ end
111
+ return unless exp.node_type == :hash
112
+
113
+ hash_iterate(exp) do |option, value|
114
+ case option[1]
115
+ when :controller, :requirements, :singular, :path_prefix, :as,
116
+ :path_names, :shallow, :name_prefix, :member_path, :nested_member_path,
117
+ :belongs_to, :conditions, :active_scaffold
118
+ #should be able to skip
119
+ when :collection, :member, :new
120
+ process_collection value
121
+ when :has_one
122
+ save_controller = current_controller
123
+ process_resource value[1..-1] #Verify this is proper behavior
124
+ self.current_controller = save_controller
125
+ when :has_many
126
+ save_controller = current_controller
127
+ process_resources value[1..-1]
128
+ self.current_controller = save_controller
129
+ when :only
130
+ process_option_only value
131
+ when :except
132
+ process_option_except value
133
+ else
134
+ Railroader.notify "[Notice] Unhandled resource option, please report: #{option}"
135
+ end
136
+ end
137
+ end
138
+
139
+ #Process route option :only => ...
140
+ def process_option_only exp
141
+ routes = @tracker.routes[@current_controller]
142
+ [:index, :new, :create, :show, :edit, :update, :destroy].each do |r|
143
+ routes.delete r
144
+ end
145
+
146
+ if exp.node_type == :array
147
+ exp[1..-1].each do |e|
148
+ routes << e.value
149
+ end
150
+ end
151
+ end
152
+
153
+ #Process route option :except => ...
154
+ def process_option_except exp
155
+ return unless exp.node_type == :array
156
+ routes = @tracker.routes[@current_controller]
157
+
158
+ exp[1..-1].each do |e|
159
+ routes.delete e.value
160
+ end
161
+ end
162
+
163
+ # map.resource :x, ..
164
+ def process_resource exp
165
+ controller = check_for_controller_name exp
166
+ if controller
167
+ self.current_controller = controller
168
+ process_resource_options exp.last
169
+ else
170
+ exp.each do |argument|
171
+ if node_type? argument, :lit
172
+ self.current_controller = pluralize(exp.first.value.to_s)
173
+ add_resource_routes
174
+ process_resource_options exp.last
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ #Process
181
+ # map.connect '/something', :controller => 'blah', :action => 'whatever'
182
+ def process_connect exp
183
+ return if exp.empty?
184
+
185
+ controller = check_for_controller_name exp
186
+ self.current_controller = controller if controller
187
+
188
+ #Check for default route
189
+ if string? exp.first
190
+ if exp.first.value == ":controller/:action/:id"
191
+ @tracker.routes[:allow_all_actions] = exp.first
192
+ elsif exp.first.value.include? ":action"
193
+ @tracker.routes[@current_controller] = [:allow_all_actions, exp.line]
194
+ return
195
+ end
196
+ end
197
+
198
+ #This -seems- redundant, but people might connect actions
199
+ #to a controller which already allows them all
200
+ return if @tracker.routes[@current_controller].is_a? Array and @tracker.routes[@current_controller][0] == :allow_all_actions
201
+
202
+ exp.last.each_with_index do |e,i|
203
+ if symbol? e and e.value == :action
204
+ action = exp.last[i + 1]
205
+
206
+ if node_type? action, :lit
207
+ @tracker.routes[@current_controller] << action.value.to_sym
208
+ end
209
+
210
+ return
211
+ end
212
+ end
213
+ end
214
+
215
+ # map.with_options :controller => 'something' do |something|
216
+ # something.resources :blah
217
+ # end
218
+ def process_with_options exp
219
+ @with_options = exp.block_call.last_arg
220
+ @nested = Sexp.new(:lvar, exp.block_args.value)
221
+
222
+ self.current_controller = check_for_controller_name exp.block_call.args
223
+
224
+ #process block
225
+ process exp.block
226
+
227
+ @with_options = nil
228
+ @nested = nil
229
+ end
230
+
231
+ # map.namespace :something do |something|
232
+ # something.resources :blah
233
+ # end
234
+ def process_namespace exp
235
+ call = exp.block_call
236
+ formal_args = exp.block_args
237
+ block = exp.block
238
+
239
+ @prefix << camelize(call.first_arg.value)
240
+
241
+ if formal_args
242
+ @nested = Sexp.new(:lvar, formal_args.value)
243
+ end
244
+
245
+ process block
246
+
247
+ @prefix.pop
248
+ end
249
+
250
+ # map.something_abnormal '/blah', :controller => 'something', :action => 'wohoo'
251
+ def process_named_route exp
252
+ process_connect exp
253
+ end
254
+
255
+ #Process collection option
256
+ # :collection => { :some_action => :http_actions }
257
+ def process_collection exp
258
+ return unless exp.node_type == :hash
259
+ routes = @tracker.routes[@current_controller]
260
+
261
+ hash_iterate(exp) do |action, _type|
262
+ routes << action.value
263
+ end
264
+ end
265
+
266
+ private
267
+
268
+ #Checks an argument list for a hash that has a key :controller.
269
+ #If it does, returns the value.
270
+ #
271
+ #Otherwise, returns nil.
272
+ def check_for_controller_name args
273
+ args.each do |a|
274
+ if hash? a and value = hash_access(a, :controller)
275
+ return value.value if string? value or symbol? value
276
+ end
277
+ end
278
+
279
+ nil
280
+ end
281
+ end
282
+
283
+ #This is for a really specific case where a hash is used as arguments
284
+ #to one of the map methods.
285
+ class Railroader::RouteAliasProcessor < Railroader::AliasProcessor
286
+
287
+ #This replaces
288
+ # { :some => :hash }.keys
289
+ #with
290
+ # [:some]
291
+ def process_call exp
292
+ process_default exp
293
+
294
+ if hash? exp.target and exp.method == :keys
295
+ keys = get_keys exp.target
296
+ exp.clear
297
+ keys.each_with_index do |e,i|
298
+ exp[i] = e
299
+ end
300
+ end
301
+ exp
302
+ end
303
+
304
+ #Returns an array Sexp containing the keys from the hash
305
+ def get_keys hash
306
+ keys = Sexp.new(:array)
307
+ hash_iterate(hash) do |key, _value|
308
+ keys << key
309
+ end
310
+
311
+ keys
312
+ end
313
+ end