analyst 0.14.2 → 0.15.0
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.
- checksums.yaml +4 -4
- data/analyst.gemspec +3 -2
- data/lib/analyst/entities/array.rb +15 -0
- data/lib/analyst/entities/class.rb +6 -59
- data/lib/analyst/entities/code_block.rb +18 -0
- data/lib/analyst/entities/conditional.rb +19 -0
- data/lib/analyst/entities/constant.rb +41 -0
- data/lib/analyst/entities/entity.rb +68 -12
- data/lib/analyst/entities/file.rb +42 -0
- data/lib/analyst/entities/hash.rb +16 -3
- data/lib/analyst/entities/interpolated_string.rb +3 -0
- data/lib/analyst/entities/method.rb +15 -1
- data/lib/analyst/entities/method_call.rb +20 -4
- data/lib/analyst/entities/module.rb +11 -14
- data/lib/analyst/entities/pair.rb +3 -0
- data/lib/analyst/entities/root.rb +28 -4
- data/lib/analyst/entities/singleton_class.rb +2 -0
- data/lib/analyst/entities/source.rb +42 -0
- data/lib/analyst/entities/string.rb +15 -2
- data/lib/analyst/entities/symbol.rb +15 -2
- data/lib/analyst/entities/{empty.rb → unhandled.rb} +3 -1
- data/lib/analyst/parser.rb +33 -30
- data/lib/analyst/processor.rb +25 -0
- data/lib/analyst/version.rb +1 -1
- data/lib/analyst.rb +18 -29
- data/spec/class_spec.rb +1 -1
- data/spec/constant_spec.rb +26 -0
- data/spec/entity_spec.rb +118 -0
- data/spec/fixtures/music.rb +19 -3
- data/spec/parser_spec.rb +3 -2
- data/spec/spec_helper.rb +3 -0
- metadata +39 -30
- data/lib/analyst/analyzer.rb +0 -162
- data/lib/analyst/entities/begin.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b73349858dc8cc8e23a7bcee486149fa81fa14b
|
4
|
+
data.tar.gz: e8d736fd3c97802f6a8bf7db970ae730385938e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 121c7e0d71706490dd637a48d718002a217b4fa2c190708ed9cd8c8d2fef02758cb118f0704afbd3fa397922571f09fa9270ee2dd53eb5b68266678309d9b0cb
|
7
|
+
data.tar.gz: 6306167886e6d7f9e4d1b6ff3bff588525805d18a6aec93b03f0c85164def809851bcfa0c54e617002813a52f43ab5a354f9e8da3f8300dadd7c71ff00539d0f
|
data/analyst.gemspec
CHANGED
@@ -18,8 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "ephemeral", ">= 2.4.0"
|
22
|
-
spec.add_dependency "poro_plus"
|
23
21
|
spec.add_dependency "haml"
|
24
22
|
spec.add_dependency "parser"
|
25
23
|
spec.add_dependency "rainbow"
|
@@ -32,5 +30,8 @@ Gem::Specification.new do |spec|
|
|
32
30
|
spec.add_development_dependency "rspec"
|
33
31
|
spec.add_development_dependency "simplecov"
|
34
32
|
spec.add_development_dependency "pry"
|
33
|
+
spec.add_development_dependency "byebug"
|
34
|
+
spec.add_development_dependency "awesome_print"
|
35
35
|
|
36
36
|
end
|
37
|
+
|
@@ -8,8 +8,14 @@ module Analyst
|
|
8
8
|
module Entities
|
9
9
|
class Class < Analyst::Entities::Module
|
10
10
|
|
11
|
+
handles_node :class
|
12
|
+
|
11
13
|
alias :macros :method_calls
|
12
14
|
|
15
|
+
def kind
|
16
|
+
"Class"
|
17
|
+
end
|
18
|
+
|
13
19
|
def imethods
|
14
20
|
@imethods ||= contents.select { |entity| entity.is_a? Analyst::Entities::InstanceMethod }
|
15
21
|
end
|
@@ -36,65 +42,6 @@ module Analyst
|
|
36
42
|
end
|
37
43
|
end
|
38
44
|
|
39
|
-
# ASSOCIATIONS = [:belongs_to, :has_one, :has_many, :has_and_belongs_to_many]
|
40
|
-
|
41
|
-
# attr_reader :associations
|
42
|
-
|
43
|
-
# def initialize(node, parent)
|
44
|
-
# @associations = []
|
45
|
-
# super
|
46
|
-
# end
|
47
|
-
|
48
|
-
# def handle_send(node)
|
49
|
-
# target, method_name, *args = node.children
|
50
|
-
# if ASSOCIATIONS.include?(method_name)
|
51
|
-
# add_association(method_name, args)
|
52
|
-
# end
|
53
|
-
# end
|
54
|
-
|
55
|
-
# # When a class is reopened, merge its associations
|
56
|
-
# def merge_associations_from(klass)
|
57
|
-
# klass.associations.each do |association|
|
58
|
-
# associations << Association.new(
|
59
|
-
# type: association.type,
|
60
|
-
# source: self,
|
61
|
-
# target_class: association.target_class
|
62
|
-
# )
|
63
|
-
# end
|
64
|
-
# associations.uniq!
|
65
|
-
# end
|
66
|
-
|
67
|
-
# private
|
68
|
-
|
69
|
-
# def add_association(method_name, args)
|
70
|
-
# target_class = value_from_hash_node(args.last, :class_name)
|
71
|
-
# target_class ||= begin
|
72
|
-
# symbol_node = args.first
|
73
|
-
# symbol_name = symbol_node.children.first
|
74
|
-
# symbol_name.pluralize.classify
|
75
|
-
# end
|
76
|
-
# association = Association.new(type: method_name, source: self, target_class: target_class)
|
77
|
-
# associations << association
|
78
|
-
# end
|
79
|
-
|
80
|
-
# private
|
81
|
-
|
82
|
-
# # Fetches value from hash node iff key is symbol and value is str
|
83
|
-
# # Raises an exception if value is not str
|
84
|
-
# # Returns nil if key is not found
|
85
|
-
# def value_from_hash_node(node, key)
|
86
|
-
# return unless node.type == :hash
|
87
|
-
# pair = node.children.detect do |pair_node|
|
88
|
-
# key_symbol_node = pair_node.children.first
|
89
|
-
# key == key_symbol_node.children.first
|
90
|
-
# end
|
91
|
-
# if pair
|
92
|
-
# value_node = pair.children.last
|
93
|
-
# throw "Bad type. Expected (str), got (#{value_node.type})" unless value_node.type == :str
|
94
|
-
# value_node.children.first
|
95
|
-
# end
|
96
|
-
# end
|
97
|
-
|
98
45
|
end
|
99
46
|
|
100
47
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Analyst
|
2
|
+
|
3
|
+
module Entities
|
4
|
+
class CodeBlock < Entity
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
handles_node :begin
|
8
|
+
|
9
|
+
def_delegators :parent, :name, :full_name
|
10
|
+
|
11
|
+
def contents
|
12
|
+
@contents ||= ast.children.map { |child| process_node(child) }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Analyst
|
2
|
+
|
3
|
+
module Entities
|
4
|
+
class Conditional < Entity
|
5
|
+
|
6
|
+
handles_node :if
|
7
|
+
handles_node :or
|
8
|
+
handles_node :and
|
9
|
+
handles_node :or_asgn
|
10
|
+
handles_node :and_asgn
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def contents
|
15
|
+
@contents ||= ast.children.map { |child| process_node(child) }.compact
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Analyst
|
2
|
+
|
3
|
+
module Entities
|
4
|
+
class Constant < Entity
|
5
|
+
|
6
|
+
handles_node :const
|
7
|
+
|
8
|
+
def name
|
9
|
+
const_node_array(ast).join("::")
|
10
|
+
end
|
11
|
+
|
12
|
+
def full_name
|
13
|
+
name
|
14
|
+
end
|
15
|
+
|
16
|
+
def constants
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# takes a (const) node and returns an array specifying the fully-qualified
|
23
|
+
# constant name that it represents. ya know, so CoolModule::SubMod::SweetClass
|
24
|
+
# would be parsed to:
|
25
|
+
# (const
|
26
|
+
# (const
|
27
|
+
# (const nil :CoolModule) :SubMod) :SweetClass)
|
28
|
+
# and passing that node here would return [:CoolModule, :SubMod, :SweetClass]
|
29
|
+
# TODO: should really be nested Entities::Constants all the way down.
|
30
|
+
# ((cbase) can probably use the same Entity, or maybe it's a subclass of Constant)
|
31
|
+
def const_node_array(node)
|
32
|
+
return [] if node.nil?
|
33
|
+
return [''] if node.type == :cbase
|
34
|
+
raise "expected (const) or (cbase) node or nil, got (#{node.type})" unless node.type == :const
|
35
|
+
const_node_array(node.children.first) << node.children[1]
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -1,21 +1,21 @@
|
|
1
|
+
require_relative '../processor'
|
2
|
+
|
1
3
|
# An entity is a named node of a given type which may have additional properties
|
2
4
|
module Analyst
|
3
5
|
module Entities
|
4
6
|
class Entity
|
5
7
|
|
6
|
-
attr_reader :parent
|
8
|
+
attr_reader :parent, :ast
|
9
|
+
|
10
|
+
def self.handles_node(type)
|
11
|
+
Analyst::Processor.register_processor(type, self)
|
12
|
+
end
|
7
13
|
|
8
14
|
def initialize(ast, parent)
|
9
15
|
@parent = parent
|
10
16
|
@ast = ast
|
11
17
|
end
|
12
18
|
|
13
|
-
def handle_send_node(node)
|
14
|
-
# raise "Subclass must implement handle_send_node"
|
15
|
-
# abstract method. btw, this feels wrong -- send should be an entity too. but for now, whatevs.
|
16
|
-
end
|
17
|
-
|
18
|
-
# TODO: should every Entity have these accessors? maybe they're mixins... but would that provide any benefit?
|
19
19
|
def classes
|
20
20
|
@classes ||= begin
|
21
21
|
nested_classes = top_level_classes.map(&:classes).flatten
|
@@ -24,6 +24,21 @@ module Analyst
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
def modules
|
28
|
+
@modules ||= begin
|
29
|
+
nested_modules = top_level_modules.map(&:modules).flatten
|
30
|
+
top_level_modules + nested_modules
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def constants
|
35
|
+
@constants ||= top_level_constants + contents.map(&:constants).flatten
|
36
|
+
end
|
37
|
+
|
38
|
+
def top_level_constants
|
39
|
+
@top_level_constants ||= contents_of_type(Entities::Constant)
|
40
|
+
end
|
41
|
+
|
27
42
|
def top_level_modules
|
28
43
|
@top_level_modules ||= contents_of_type(Entities::Module)
|
29
44
|
end
|
@@ -36,26 +51,67 @@ module Analyst
|
|
36
51
|
@method_calls ||= contents_of_type(Entities::MethodCall)
|
37
52
|
end
|
38
53
|
|
54
|
+
# TODO: rethink the different kinds of Methods. there's really only one
|
55
|
+
# kind of Method, right??
|
56
|
+
# ref: http://www.devalot.com/articles/2008/09/ruby-singleton
|
57
|
+
def methods
|
58
|
+
@methods ||= contents_of_type(Entities::InstanceMethod)
|
59
|
+
end
|
60
|
+
|
61
|
+
def conditionals
|
62
|
+
@conditionals ||= contents_of_type(Entities::Conditional)
|
63
|
+
end
|
64
|
+
|
65
|
+
def location
|
66
|
+
"#{file_path}:#{line_number}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def file_path
|
70
|
+
parent.file_path
|
71
|
+
end
|
72
|
+
|
73
|
+
def line_number
|
74
|
+
ast.loc.line
|
75
|
+
end
|
76
|
+
|
77
|
+
def source
|
78
|
+
origin_source[source_range]
|
79
|
+
end
|
80
|
+
|
81
|
+
def origin_source
|
82
|
+
parent.origin_source
|
83
|
+
end
|
84
|
+
|
39
85
|
def full_name
|
40
86
|
throw "Subclass #{self.class.name} must implement #full_name"
|
41
87
|
end
|
42
88
|
|
43
89
|
def inspect
|
44
|
-
"\#<#{self.class}
|
90
|
+
"\#<#{self.class} location=#{location} full_name=#{full_name}>"
|
45
91
|
rescue
|
46
|
-
"\#<#{self.class}
|
92
|
+
"\#<#{self.class} location=#{location}>"
|
47
93
|
end
|
48
94
|
|
49
95
|
private
|
50
96
|
|
51
|
-
|
97
|
+
def source_range
|
98
|
+
Range.new(ast.loc.expression.begin_pos, ast.loc.expression.end_pos)
|
99
|
+
end
|
52
100
|
|
53
101
|
def contents_of_type(klass)
|
54
102
|
contents.select { |entity| entity.is_a? klass }
|
55
103
|
end
|
56
104
|
|
57
105
|
def contents
|
58
|
-
@contents ||=
|
106
|
+
@contents ||= if actual_contents.is_a? Entities::CodeBlock
|
107
|
+
actual_contents.contents
|
108
|
+
else
|
109
|
+
Array(actual_contents)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def actual_contents
|
114
|
+
@actual_contents ||= process_node(content_node)
|
59
115
|
end
|
60
116
|
|
61
117
|
def content_node
|
@@ -63,7 +119,7 @@ module Analyst
|
|
63
119
|
end
|
64
120
|
|
65
121
|
def process_node(node, parent=self)
|
66
|
-
Analyst::
|
122
|
+
Analyst::Processor.process_node(node, parent)
|
67
123
|
end
|
68
124
|
|
69
125
|
def process_nodes(nodes, parent=self)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Analyst
|
2
|
+
|
3
|
+
module Entities
|
4
|
+
|
5
|
+
class File < Entity
|
6
|
+
|
7
|
+
handles_node :analyst_file
|
8
|
+
|
9
|
+
def full_name
|
10
|
+
""
|
11
|
+
end
|
12
|
+
|
13
|
+
def file_path
|
14
|
+
parent.source_data_for(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def origin_source
|
18
|
+
::File.open(file_path, 'r').read
|
19
|
+
end
|
20
|
+
|
21
|
+
def contents
|
22
|
+
@contents ||= actual_contents.map do |child|
|
23
|
+
# skip top-level CodeBlocks
|
24
|
+
child.is_a?(Entities::CodeBlock) ? child.contents : child
|
25
|
+
end.flatten
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def source_range
|
31
|
+
0..-1
|
32
|
+
end
|
33
|
+
|
34
|
+
def actual_contents
|
35
|
+
@actual_contents ||= ast.children.map { |child| process_node(child) }
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -2,13 +2,26 @@ module Analyst
|
|
2
2
|
|
3
3
|
module Entities
|
4
4
|
class Hash < Entity
|
5
|
+
|
6
|
+
handles_node :hash
|
7
|
+
|
5
8
|
def pairs
|
6
9
|
@pairs ||= process_nodes(ast.children)
|
7
10
|
end
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
# Convenience method to turn this Entity into an actual ::Hash.
|
13
|
+
# If `extract_values` is true, then all keys and values that respond
|
14
|
+
# to `#value` will be replaced by the value they return from that call.
|
15
|
+
# Otherwise they'll be left as Analyst::Entities::Entity objects.
|
16
|
+
def to_hash(extract_values:true)
|
17
|
+
pairs.inject({}) do |hash, pair|
|
18
|
+
key = pair.key
|
19
|
+
val = pair.value
|
20
|
+
if extract_values
|
21
|
+
key = key.value if key.respond_to?(:value)
|
22
|
+
val = val.value if val.respond_to?(:value)
|
23
|
+
end
|
24
|
+
hash[key] = val
|
12
25
|
hash
|
13
26
|
end
|
14
27
|
end
|
@@ -2,19 +2,31 @@ module Analyst
|
|
2
2
|
module Entities
|
3
3
|
|
4
4
|
class InstanceMethod < Entity
|
5
|
+
|
6
|
+
handles_node :def
|
7
|
+
|
8
|
+
def kind
|
9
|
+
"Instance Method"
|
10
|
+
end
|
11
|
+
|
5
12
|
def name
|
6
13
|
ast.children.first.to_s
|
7
14
|
end
|
15
|
+
|
8
16
|
def full_name
|
9
17
|
parent.full_name + '#' + name
|
10
18
|
end
|
11
19
|
end
|
12
20
|
|
13
|
-
# TODO HERE
|
14
21
|
class ClassMethod < Entity
|
22
|
+
def kind
|
23
|
+
"Class Method"
|
24
|
+
end
|
25
|
+
|
15
26
|
def name
|
16
27
|
ast.children.first.to_s
|
17
28
|
end
|
29
|
+
|
18
30
|
def full_name
|
19
31
|
parent.full_name + '::' + name
|
20
32
|
end
|
@@ -22,6 +34,8 @@ module Analyst
|
|
22
34
|
|
23
35
|
class SingletonMethod < Entity
|
24
36
|
|
37
|
+
handles_node :defs
|
38
|
+
|
25
39
|
# NOTE: not a public API -- used by Entities::Class
|
26
40
|
def target
|
27
41
|
target, name, params, content = ast.children
|
@@ -2,6 +2,8 @@ module Analyst
|
|
2
2
|
module Entities
|
3
3
|
class MethodCall < Entity
|
4
4
|
|
5
|
+
handles_node :send
|
6
|
+
|
5
7
|
def name
|
6
8
|
name_node.to_s
|
7
9
|
end
|
@@ -13,17 +15,31 @@ module Analyst
|
|
13
15
|
def arguments
|
14
16
|
@arguments ||= begin
|
15
17
|
args = ast.children[2..-1]
|
16
|
-
args.map { |arg|
|
18
|
+
args.map { |arg| process_node(arg) }
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
def constants
|
23
|
+
if target.is_a? Analyst::Entities::Constant
|
24
|
+
super << target
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def contents
|
33
|
+
arguments
|
34
|
+
end
|
35
|
+
|
22
36
|
def target_node
|
23
37
|
ast.children.first
|
24
38
|
end
|
25
39
|
|
26
|
-
|
40
|
+
def target
|
41
|
+
process_node(target_node)
|
42
|
+
end
|
27
43
|
|
28
44
|
def name_node
|
29
45
|
ast.children[1]
|
@@ -2,8 +2,14 @@ module Analyst
|
|
2
2
|
module Entities
|
3
3
|
class Module < Entity
|
4
4
|
|
5
|
+
handles_node :module
|
6
|
+
|
7
|
+
def kind
|
8
|
+
"Module"
|
9
|
+
end
|
10
|
+
|
5
11
|
def name
|
6
|
-
|
12
|
+
name_entity.name
|
7
13
|
end
|
8
14
|
|
9
15
|
def full_name
|
@@ -12,21 +18,12 @@ module Analyst
|
|
12
18
|
|
13
19
|
private
|
14
20
|
|
15
|
-
def
|
16
|
-
|
21
|
+
def name_entity
|
22
|
+
@name_entity ||= process_node(name_node)
|
17
23
|
end
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
# would be parsed to:
|
22
|
-
# (const
|
23
|
-
# (const
|
24
|
-
# (const nil :CoolModule) :SubMod) :SweetClass)
|
25
|
-
# and passing that node here would return [:CoolModule, :SubMod, :SweetClass]
|
26
|
-
def const_node_array(node)
|
27
|
-
return [] if node.nil?
|
28
|
-
raise "expected (const) node or nil, got (#{node.type})" unless node.type == :const
|
29
|
-
const_node_array(node.children.first) << node.children[1]
|
25
|
+
def name_node
|
26
|
+
ast.children.first
|
30
27
|
end
|
31
28
|
end
|
32
29
|
end
|
@@ -1,19 +1,43 @@
|
|
1
1
|
module Analyst
|
2
2
|
|
3
3
|
module Entities
|
4
|
+
|
4
5
|
class Root < Entity
|
5
6
|
|
7
|
+
handles_node :analyst_root
|
8
|
+
|
9
|
+
def initialize(ast, source_data)
|
10
|
+
@source_data = source_data
|
11
|
+
super(ast, nil)
|
12
|
+
end
|
13
|
+
|
6
14
|
def full_name
|
7
15
|
""
|
8
16
|
end
|
9
17
|
|
18
|
+
def source_data_for(entity)
|
19
|
+
source_data[actual_contents.index(entity)]
|
20
|
+
end
|
21
|
+
|
22
|
+
def file_path
|
23
|
+
throw "Entity tree malformed - Source or File should have caught this call"
|
24
|
+
end
|
25
|
+
|
26
|
+
def origin_source
|
27
|
+
throw "Entity tree malformed - Source or File hsould have caught this call"
|
28
|
+
end
|
29
|
+
|
10
30
|
private
|
11
31
|
|
32
|
+
attr_reader :source_data
|
33
|
+
|
12
34
|
def contents
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
35
|
+
# skip all top-level entities, cuz they're all Files and Sources
|
36
|
+
@contents ||= actual_contents.map(&:contents).flatten
|
37
|
+
end
|
38
|
+
|
39
|
+
def actual_contents
|
40
|
+
@actual_contents ||= ast.children.map { |child| process_node(child) }
|
17
41
|
end
|
18
42
|
|
19
43
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Analyst
|
2
|
+
|
3
|
+
module Entities
|
4
|
+
|
5
|
+
class Source < Entity
|
6
|
+
|
7
|
+
handles_node :analyst_source
|
8
|
+
|
9
|
+
def full_name
|
10
|
+
""
|
11
|
+
end
|
12
|
+
|
13
|
+
def file_path
|
14
|
+
"$PARSED FROM SOURCE$"
|
15
|
+
end
|
16
|
+
|
17
|
+
def origin_source
|
18
|
+
parent.source_data_for(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def contents
|
22
|
+
@contents ||= actual_contents.map do |child|
|
23
|
+
# skip top-level CodeBlocks
|
24
|
+
child.is_a?(Entities::CodeBlock) ? child.contents : child
|
25
|
+
end.flatten
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def source_range
|
31
|
+
0..-1
|
32
|
+
end
|
33
|
+
|
34
|
+
def actual_contents
|
35
|
+
@actual_contents ||= ast.children.map { |child| process_node(child) }
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -1,12 +1,25 @@
|
|
1
1
|
module Analyst
|
2
2
|
|
3
3
|
module Entities
|
4
|
-
|
4
|
+
class String < Entity
|
5
5
|
|
6
|
-
|
6
|
+
handles_node :str
|
7
|
+
|
8
|
+
def value
|
7
9
|
ast.children.first
|
8
10
|
end
|
9
11
|
|
12
|
+
def name
|
13
|
+
end
|
14
|
+
|
15
|
+
def full_name
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def content_node
|
21
|
+
end
|
22
|
+
|
10
23
|
end
|
11
24
|
end
|
12
25
|
end
|