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



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

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



314
315
316
# File 'lib/ripper-tags/parser.rb', line 314

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



267
268
269
270
271
272
273
274
275
# File 'lib/ripper-tags/parser.rb', line 267

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



338
339
340
341
# File 'lib/ripper-tags/parser.rb', line 338

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

#on_def_delegators(*args) ⇒ Object



343
344
345
346
347
348
349
350
351
352
# File 'lib/ripper-tags/parser.rb', line 343

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



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/ripper-tags/parser.rb', line 318

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



289
290
291
# File 'lib/ripper-tags/parser.rb', line 289

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



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

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
259
260
261
262
263
264
# 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?(Array)
        args[1].inject([]) do |gen, def_args|
          if def_args[1].is_a?(String)
            gen << [procedure, klass, def_args[1], access, line]
          else
            gen
          end
        end
      elsif args[1][0].is_a?(String)
        [:redefine_access, klass, args[1][0], access, line]
      elsif args[1][1] == 'self'
        [procedure, klass, args[1][2], access, line]
      else
        [procedure, nil, args[1][1], access, line]
      end
    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'
      gen = []
      args[1..-1].compact.each do |arg|
        next unless arg[0].is_a?(String)
        gen << [:def, arg[0], line] if gen_reader
        gen << [:def, "#{arg[0]}=", line] if gen_writer
      end
      gen
    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



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/ripper-tags/parser.rb', line 293

def on_method_add_block(method, body)
  if method.nil?
    body ? body.last : nil
  elsif (method[2] == "class_eval" || method[2] == "module_eval") && 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
  elsif :fcall == method[0] && body
    body.last
  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] unless args[0].is_a?(Array) && args[0].include?(:string_embexpr)
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