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



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

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

#on_array(args) ⇒ Object



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

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



297
298
299
# File 'lib/ripper-tags/parser.rb', line 297

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



93
94
95
# File 'lib/ripper-tags/parser.rb', line 93

def on_bodystmt(*args)
  args
end

#on_call(lhs, op, rhs) ⇒ Object



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

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
# 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])
  end
end

#on_command_call(*args) ⇒ Object

handle ‘Class.new arg` call without parens



253
254
255
256
257
258
259
260
261
# File 'lib/ripper-tags/parser.rb', line 253

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



321
322
323
324
# File 'lib/ripper-tags/parser.rb', line 321

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

#on_def_delegators(*args) ⇒ Object



326
327
328
329
330
331
332
333
334
335
# File 'lib/ripper-tags/parser.rb', line 326

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



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/ripper-tags/parser.rb', line 301

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



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

def on_do_block(*args)
  args
end

#on_dyna_symbol(*args) ⇒ Object



107
108
109
110
111
# File 'lib/ripper-tags/parser.rb', line 107

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

#on_fcall(*args) ⇒ Object



263
264
265
# File 'lib/ripper-tags/parser.rb', line 263

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



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

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



96
97
98
99
# File 'lib/ripper-tags/parser.rb', line 96

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

#on_method_add_arg(call, args) ⇒ Object



163
164
165
166
167
168
169
170
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
# File 'lib/ripper-tags/parser.rb', line 163

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



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/ripper-tags/parser.rb', line 279

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



130
131
132
# File 'lib/ripper-tags/parser.rb', line 130

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

#on_string_dvarObject



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

def on_string_dvar(*)
  :string_embexpr
end

#on_string_embexprObject



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

def on_string_embexpr(*)
  :string_embexpr
end

#on_string_literal(*args) ⇒ Object



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

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

#on_tstring_content(str) ⇒ Object



126
127
128
# File 'lib/ripper-tags/parser.rb', line 126

def on_tstring_content(str)
  str
end

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



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

def on_unless_mod(condition, success)
  nil
end

#on_var_ref(*args) ⇒ Object



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

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

#on_vcall(name) ⇒ Object



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

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



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

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