Class: RipperTags::Parser

Inherits:
Ripper
  • Object
show all
Defined in:
lib/ripper-tags/parser.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extract(data, file = '(eval)') ⇒ Object



6
7
8
9
# File 'lib/ripper-tags/parser.rb', line 6

def self.extract(data, file='(eval)')
  sexp = new(data, file).parse
  Visitor.new(sexp, file, data).tags
end

Instance Method Details

#on_alias(lhs, rhs) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/ripper-tags/parser.rb', line 38

def on_alias(lhs, rhs)
  if lhs && rhs
    name, other = lhs[0], rhs[0]
    name = name[0] if Array === name
    other = other[0] if Array === other
    line = rhs[1]
    [:alias, name, other, line]
  end
end

#on_aref_field(*args) ⇒ Object



60
61
62
# File 'lib/ripper-tags/parser.rb', line 60

def on_aref_field(*args)
  [:aref_field, *args]
end

#on_args_add(sub, arg) ⇒ Object



275
276
277
278
279
280
281
# File 'lib/ripper-tags/parser.rb', line 275

def on_args_add(sub, arg)
  if sub
    sub + [arg]
  else
    [:args, arg].compact
  end
end

#on_array(args) ⇒ Object



121
122
123
124
125
# File 'lib/ripper-tags/parser.rb', line 121

def on_array(args)
  if args.is_a?(Array) && args[0] == :args
    args[1..-1]
  end
end

#on_assign(lhs, rhs) ⇒ Object



47
48
49
50
51
52
53
# File 'lib/ripper-tags/parser.rb', line 47

def on_assign(lhs, rhs)
  return if lhs.nil?
  return if lhs[0] == :field
  return if lhs[0] == :aref_field
  lhs, line = lhs
  [:assign, lhs, rhs, line]
end

#on_assoc_new(key, value) ⇒ Object

Otherwise hashes and keyword arguments turn into a list of their keys only



305
306
307
# File 'lib/ripper-tags/parser.rb', line 305

def on_assoc_new(key, value)
  [:assoc, key, value]
end

#on_binary(*args) ⇒ Object



71
72
# File 'lib/ripper-tags/parser.rb', line 71

def on_binary(*args)
end

#on_bodystmt(*args) ⇒ Object



101
102
103
# File 'lib/ripper-tags/parser.rb', line 101

def on_bodystmt(*args)
  args
end

#on_call(lhs, op, rhs) ⇒ Object



165
166
167
168
169
# File 'lib/ripper-tags/parser.rb', line 165

def on_call(lhs, op, rhs)
  return unless lhs && rhs
  arg = block = nil
  [:call, lhs[0], rhs[0], arg, block]
end

#on_class(name, superclass, body) ⇒ Object



27
28
29
30
# File 'lib/ripper-tags/parser.rb', line 27

def on_class(name, superclass, body)
  superclass.flatten!(1) if superclass
  [:class, name, superclass, *body.compact]
end

#on_command(name, *args) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/ripper-tags/parser.rb', line 74

def on_command(name, *args)
  case name[0]
  when "define_method", "alias_method",
       "has_one", "has_many",
       "belongs_to", "has_and_belongs_to_many",
       "scope", "named_scope",
       "public_class_method", "private_class_method", "protected_class_method",
       "public", "protected", "private",
       "module_function",
       /^[mc]?attr_(accessor|reader|writer)$/
    on_method_add_arg([:fcall, name], args[0])
  when "delegate"
    on_delegate(*Array(args[0])[1..-1])
  when "def_delegator", "def_instance_delegator"
    on_def_delegator(*args[0][1..-1])
  when "def_delegators", "def_instance_delegators"
    on_def_delegators(*args[0][1..-1])
  else
    if args[0].respond_to?(:[])
      # Handle decorators: if what follows is a method def, pass it on
      inner = args[0][1]
      if inner.respond_to?(:[]) && inner[0] == :def
        on_def(inner[1..-1], nil, nil)
      end
    end
  end
end

#on_command_call(*args) ⇒ Object

handle ‘Class.new arg` call without parens



261
262
263
264
265
266
267
268
269
# File 'lib/ripper-tags/parser.rb', line 261

def on_command_call(*args)
  if args.last && :args == args.last[0]
    args_add = args.pop
    call = on_call(*args)
    on_method_add_arg(call, args_add)
  else
    super
  end
end

#on_const_path_ref(a, b) ⇒ Object Also known as: on_const_path_field



64
65
66
67
68
# File 'lib/ripper-tags/parser.rb', line 64

def on_const_path_ref(a, b)
  return if a.nil? || b.nil?
  a.flatten!(1)
  [[a && a[0], b[0]].join('::'), b[1]]
end

#on_def(method, args, body) ⇒ Object



31
32
33
# File 'lib/ripper-tags/parser.rb', line 31

def on_def(method, args, body)
  [:def, *method]
end

#on_def_delegator(*args) ⇒ Object



329
330
331
332
# File 'lib/ripper-tags/parser.rb', line 329

def on_def_delegator(*args)
  name, lineno = args.last
  [:def, name, lineno] if lineno
end

#on_def_delegators(*args) ⇒ Object



334
335
336
337
338
339
340
341
342
343
# File 'lib/ripper-tags/parser.rb', line 334

def on_def_delegators(*args)
  _target, *names = args
  names.map do |name, lineno, *args|
    if lineno.is_a?(Numeric)
      [:def, name, lineno] if lineno
    elsif name.is_a?(Symbol)
      [name, lineno, *args]
    end
  end
end

#on_defs(receiver, op, method, args, body) ⇒ Object



34
35
36
37
# File 'lib/ripper-tags/parser.rb', line 34

def on_defs(receiver, op, method, args, body)
  receiver.flatten!(1) if receiver
  [:defs, receiver && receiver[0], *method]
end

#on_delegate(*args) ⇒ Object



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/ripper-tags/parser.rb', line 309

def on_delegate(*args)
  method_names = args.select { |arg| arg.first.is_a? String }
  options = args.select { |arg| arg.first.is_a?(Array) && arg.first.first == :assoc }.flatten(1)
  options = Hash[options.map { |_assoc, key, val| [key[0], val[0]] if key }.compact]

  target = options["to:"] || options["to"] # When using hashrocket syntax there is no ':'
  prefix = options["prefix:"] || options["prefix"]
  method_prefix = if prefix.is_a?(Array) && prefix.first == "true"
                    "#{target}_"
                  elsif prefix.is_a? String
                    "#{prefix}_"
                  else
                    ""
                  end

  method_names.map do |name, lineno|
    [:def, "#{method_prefix}#{name}", lineno]
  end
end

#on_do_block(*args) ⇒ Object



283
284
285
# File 'lib/ripper-tags/parser.rb', line 283

def on_do_block(*args)
  args
end

#on_dyna_symbol(*args) ⇒ Object



115
116
117
118
119
# File 'lib/ripper-tags/parser.rb', line 115

def on_dyna_symbol(*args)
  if args.length == 1 && args[0]
    [args[0], lineno]
  end
end

#on_fcall(*args) ⇒ Object



271
272
273
# File 'lib/ripper-tags/parser.rb', line 271

def on_fcall(*args)
  [:fcall, *args]
end

#on_field(lhs, op, rhs) ⇒ Object



57
58
59
# File 'lib/ripper-tags/parser.rb', line 57

def on_field(lhs, op, rhs)
  [:field, lhs && lhs[0], rhs[0], rhs[1]]
end

#on_hash(args) ⇒ Object



127
128
129
130
131
# File 'lib/ripper-tags/parser.rb', line 127

def on_hash(args)
  return unless args

  args.select { |arg| arg.is_a?(Array) && arg[0] == :assoc }.map { |_assoc, k, _v| k }
end

#on_if(condition, success, failure) ⇒ Object Also known as: on_unless



104
105
106
107
# File 'lib/ripper-tags/parser.rb', line 104

def on_if(condition, success, failure)
  ret = [success, failure].flatten(1).compact
  ret.any?? ret : nil
end

#on_method_add_arg(call, args) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/ripper-tags/parser.rb', line 171

def on_method_add_arg(call, args)
  call_name = call && call[0]
  first_arg = args && :args == args[0] && args[1]

  if :call == call_name && first_arg
    if args.length == 2
      # augment call if a single argument was used
      call = call.dup
      call[3] = args[1]
    end
    call
  elsif :fcall == call_name && first_arg
    name, line = call[1]
    case name
    when "alias_method"
      if args[1] && String === args[1][0] && args[2] && String === args[2][0]
        [:alias, args[1][0], args[2][0], line]
      end
    when "define_method"
      [:def, args[1][0], line] if String === args[1][0]
    when "public_class_method", "private_class_method", "protected_class_method", "private", "public", "protected"
      access = name.sub("_class_method", "")
      klass = name == access ? nil : 'self'
      procedure = :def_with_access

      if args[1][0].is_a?(String)
        procedure = :redefine_access
        method_name = args[1][0]
      elsif args[1][1] == 'self'
        method_name = args[1][2]
      else
        klass = nil
        method_name = args[1][1]
      end

      [procedure, klass, method_name, access, line]
    when "module_function"
      access = "public"
      klass = "self"
      procedure = :def_with_access

      if args[1][0].is_a?(String)
        procedure = :redefine_access
        method_name = args[1][0]
      else
        method_name = args[1][1]
      end

      [procedure, klass, method_name, access, line]
    when "scope", "named_scope"
      scope_name = args[1][0]
      [:rails_def, :scope, scope_name, line] if scope_name
    when /^[mc]?attr_(accessor|reader|writer)$/
      gen_reader = $1 != 'writer'
      gen_writer = $1 != 'reader'
      args[1..-1].compact.inject([]) do |gen, arg|
        gen << [:def, arg[0], line] if gen_reader
        gen << [:def, "#{arg[0]}=", line] if gen_writer
        gen
      end
    when "has_many", "has_and_belongs_to_many"
      a = args[1][0]
      kind = name.to_sym
      gen = []
      unless a.is_a?(Enumerable) && !a.is_a?(String)
        a = a.to_s
        gen << [:rails_def, kind, a, line]
        gen << [:rails_def, kind, "#{a}=", line]
        if (sing = a.chomp('s')) != a
          # poor man's singularize
          gen << [:rails_def, kind, "#{sing}_ids", line]
          gen << [:rails_def, kind, "#{sing}_ids=", line]
        end
      end
      gen
    when "belongs_to", "has_one"
      a = args[1][0]
      unless a.is_a?(Enumerable) && !a.is_a?(String)
        kind = name.to_sym
        %W[ #{a} #{a}= build_#{a} create_#{a} create_#{a}! ].inject([]) do |all, ident|
          all << [:rails_def, kind, ident, line]
        end
      end
    end
  else
    super
  end
end

#on_method_add_block(method, body) ⇒ Object



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/ripper-tags/parser.rb', line 287

def on_method_add_block(method, body)
  return unless method
  if %w[class_eval module_eval].include?(method[2]) && body
    [:class_eval, [
      method[1].is_a?(Array) ? method[1][0] : method[1],
      method[3]
    ], body.last]
  elsif :call == method[0] && body
    # augment the `Class.new/Struct.new` call with associated block
    call = method.dup
    call[4] = body.last
    call
  else
    super
  end
end

#on_module(name, body) ⇒ Object



24
25
26
# File 'lib/ripper-tags/parser.rb', line 24

def on_module(name, body)
  [:module, name, *body.compact]
end

#on_sclass(name, body) ⇒ Object



54
55
56
# File 'lib/ripper-tags/parser.rb', line 54

def on_sclass(name, body)
  [:sclass, name && name.flatten(1), *body.compact]
end

#on_stmts_add(first, *rest) ⇒ Object



19
20
21
22
# File 'lib/ripper-tags/parser.rb', line 19

def on_stmts_add(first, *rest)
  return if first == :~
  Array(first) + rest.compact
end

#on_string_add(*args) ⇒ Object



138
139
140
# File 'lib/ripper-tags/parser.rb', line 138

def on_string_add(*args)
  [args[1], lineno]
end

#on_string_dvarObject



145
146
147
# File 'lib/ripper-tags/parser.rb', line 145

def on_string_dvar(*)
  :string_embexpr
end

#on_string_embexprObject



142
143
144
# File 'lib/ripper-tags/parser.rb', line 142

def on_string_embexpr(*)
  :string_embexpr
end

#on_string_literal(*args) ⇒ Object



148
149
150
151
# File 'lib/ripper-tags/parser.rb', line 148

def on_string_literal(*args)
  args = args.flatten
  args unless args.include?(:string_embexpr)
end

#on_tstring_content(str) ⇒ Object



134
135
136
# File 'lib/ripper-tags/parser.rb', line 134

def on_tstring_content(str)
  str
end

#on_unless_mod(condition, success) ⇒ Object Also known as: on_if_mod



110
111
112
# File 'lib/ripper-tags/parser.rb', line 110

def on_unless_mod(condition, success)
  nil
end

#on_var_ref(*args) ⇒ Object



157
158
159
# File 'lib/ripper-tags/parser.rb', line 157

def on_var_ref(*args)
  on_vcall(*args) || args
end

#on_vcall(name) ⇒ Object



161
162
163
# File 'lib/ripper-tags/parser.rb', line 161

def on_vcall(name)
  [name[0].to_sym] if name[0].to_s =~ /^(private|protected|public|private_class_method|public_class_method|module_function)$/
end

#on_xstring_add(first, arg) ⇒ Object



153
154
155
# File 'lib/ripper-tags/parser.rb', line 153

def on_xstring_add(first, arg)
  arg if first.nil?
end