1
1
# frozen_string_literal: true
2
2
3
- require "action_view/ripper_ast_parser"
4
-
5
3
module ActionView
6
- class RenderParser # :nodoc:
7
- def initialize ( name , code )
8
- @name = name
9
- @code = code
10
- @parser = RipperASTParser
11
- end
12
-
13
- def render_calls
14
- render_nodes = @parser . parse_render_nodes ( @code )
15
-
16
- render_nodes . map do |method , nodes |
17
- nodes . map { |n | send ( :parse_render , n ) }
18
- end . flatten . compact
19
- end
20
-
21
- private
22
- def directory
23
- File . dirname ( @name )
24
- end
25
-
26
- def resolve_path_directory ( path )
27
- if path . include? ( "/" )
28
- path
29
- else
30
- "#{ directory } /#{ path } "
31
- end
32
- end
33
-
34
- # Convert
35
- # render("foo", ...)
36
- # into either
37
- # render(template: "foo", ...)
38
- # or
39
- # render(partial: "foo", ...)
40
- def normalize_args ( string , options_hash )
41
- if options_hash
42
- { partial : string , locals : options_hash }
43
- else
44
- { partial : string }
45
- end
46
- end
47
-
48
- def parse_render ( node )
49
- node = node . argument_nodes
50
-
51
- if ( node . length == 1 || node . length == 2 ) && !node [ 0 ] . hash?
52
- if node . length == 1
53
- options = normalize_args ( node [ 0 ] , nil )
54
- elsif node . length == 2
55
- options = normalize_args ( node [ 0 ] , node [ 1 ] )
56
- end
57
-
58
- return nil unless options
4
+ module RenderParser # :nodoc:
5
+ ALL_KNOWN_KEYS = [ :partial , :template , :layout , :formats , :locals , :object , :collection , :as , :status , :content_type , :location , :spacer_template ]
6
+ RENDER_TYPE_KEYS = [ :partial , :template , :layout ]
59
7
60
- parse_render_from_options ( options )
61
- elsif node . length == 1 && node [ 0 ] . hash?
62
- options = parse_hash_to_symbols ( node [ 0 ] )
63
-
64
- return nil unless options
65
-
66
- parse_render_from_options ( options )
67
- else
68
- nil
69
- end
70
- end
71
-
72
- def parse_hash ( node )
73
- node . hash? && node . to_hash
74
- end
75
-
76
- def parse_hash_to_symbols ( node )
77
- hash = parse_hash ( node )
78
-
79
- return unless hash
80
-
81
- hash . transform_keys do |key_node |
82
- key = parse_sym ( key_node )
83
-
84
- return unless key
85
-
86
- key
87
- end
88
- end
89
-
90
- ALL_KNOWN_KEYS = [ :partial , :template , :layout , :formats , :locals , :object , :collection , :as , :status , :content_type , :location , :spacer_template ]
91
-
92
- RENDER_TYPE_KEYS =
93
- [ :partial , :template , :layout ]
94
-
95
- def parse_render_from_options ( options_hash )
96
- renders = [ ]
97
- keys = options_hash . keys
98
-
99
- if ( keys & RENDER_TYPE_KEYS ) . size < 1
100
- # Must have at least one of render keys
101
- return nil
102
- end
103
-
104
- if ( keys - ALL_KNOWN_KEYS ) . any?
105
- # de-opt in case of unknown option
106
- return nil
107
- end
108
-
109
- render_type = ( keys & RENDER_TYPE_KEYS ) [ 0 ]
110
-
111
- node = options_hash [ render_type ]
112
-
113
- if node . string?
114
- template = resolve_path_directory ( node . to_string )
115
- else
116
- if node . variable_reference?
117
- dependency = node . variable_name . sub ( /\A (?:\$ |@{1,2})/ , "" )
118
- elsif node . vcall?
119
- dependency = node . variable_name
120
- elsif node . call?
121
- dependency = node . call_method_name
122
- else
123
- return
124
- end
125
-
126
- object_template = true
127
- template = "#{ dependency . pluralize } /#{ dependency . singularize } "
128
- end
129
-
130
- return unless template
131
-
132
- if spacer_template = render_template_with_spacer? ( options_hash )
133
- virtual_path = partial_to_virtual_path ( :partial , spacer_template )
134
- renders << virtual_path
135
- end
136
-
137
- if options_hash . key? ( :object ) || options_hash . key? ( :collection ) || object_template
138
- return nil if options_hash . key? ( :object ) && options_hash . key? ( :collection )
139
- return nil unless options_hash . key? ( :partial )
140
- end
141
-
142
- virtual_path = partial_to_virtual_path ( render_type , template )
143
- renders << virtual_path
144
-
145
- # Support for rendering multiple templates (i.e. a partial with a layout)
146
- if layout_template = render_template_with_layout? ( render_type , options_hash )
147
- virtual_path = partial_to_virtual_path ( :layout , layout_template )
148
-
149
- renders << virtual_path
150
- end
151
-
152
- renders
153
- end
154
-
155
- def parse_str ( node )
156
- node . string? && node . to_string
157
- end
158
-
159
- def parse_sym ( node )
160
- node . symbol? && node . to_symbol
8
+ class Base # :nodoc:
9
+ def initialize ( name , code )
10
+ @name = name
11
+ @code = code
161
12
end
162
13
163
14
private
164
- def render_template_with_layout? ( render_type , options_hash )
165
- if render_type != :layout && options_hash . key? ( :layout )
166
- parse_str ( options_hash [ :layout ] )
167
- end
168
- end
169
-
170
- def render_template_with_spacer? ( options_hash )
171
- if options_hash . key? ( :spacer_template )
172
- parse_str ( options_hash [ :spacer_template ] )
173
- end
15
+ def directory
16
+ File . dirname ( @name )
174
17
end
175
18
176
19
def partial_to_virtual_path ( render_type , partial_path )
@@ -180,9 +23,22 @@ def partial_to_virtual_path(render_type, partial_path)
180
23
partial_path
181
24
end
182
25
end
26
+ end
183
27
184
- def layout_to_virtual_path ( layout_path )
185
- "layouts/#{ layout_path } "
186
- end
28
+ # Check if prism is available. If it is, use it. Otherwise, use ripper.
29
+ begin
30
+ require "prism"
31
+ rescue LoadError
32
+ require "ripper"
33
+ require_relative "render_parser/ripper_render_parser"
34
+ Parser = RipperRenderParser
35
+ else
36
+ require_relative "render_parser/prism_render_parser"
37
+ Parser = PrismRenderParser
38
+ end
39
+
40
+ def self . new ( name , code )
41
+ Parser . new ( name , code )
42
+ end
187
43
end
188
44
end
0 commit comments