method_log 0.0.2 → 0.0.3
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/README.md +7 -5
- data/bin/method_log +9 -3
- data/lib/method_log/api.rb +19 -5
- data/lib/method_log/commit.rb +19 -5
- data/lib/method_log/method_commit.rb +14 -2
- data/lib/method_log/method_definition.rb +2 -5
- data/lib/method_log/method_diff.rb +17 -0
- data/lib/method_log/method_finder.rb +64 -25
- data/lib/method_log/scope.rb +69 -0
- data/lib/method_log/version.rb +1 -1
- data/method_log.gemspec +2 -0
- data/spec/api_spec.rb +11 -3
- data/spec/commit_spec.rb +40 -0
- data/spec/method_commit_spec.rb +11 -2
- data/spec/method_definition_spec.rb +2 -7
- data/spec/method_diff_spec.rb +13 -0
- data/spec/method_finder_spec.rb +175 -0
- data/spec/scope_spec.rb +79 -0
- metadata +47 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8416c62e1edaaecff55eca789a8480abee33ed7e
|
4
|
+
data.tar.gz: e1a5011e4329bf97b0115bdaf95cf56107042b00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d221a1bf23774b980ef1b54869e1beca1ec9eac6a98e5fa628235c63e122658ec5f0667beb2ab3536945fd52b0e0d97dd3c59e5288d2f2c0b777d38e3b310a60
|
7
|
+
data.tar.gz: 787efa546b545eb8376f515d58b98d4161a53dc57a22d17157bae60b481d74ee05c44b4014a5f1499c6685a6defa0c9855067b5f2955467abf930ebfbbedf3bf
|
data/README.md
CHANGED
@@ -21,22 +21,24 @@ This is a work-in-progress and nowhere near production-ready.
|
|
21
21
|
### Todo
|
22
22
|
|
23
23
|
* Support earlier versions of Ruby (it ought to be possible to support down to v1.9.3 fairly easily)
|
24
|
-
* Support class
|
25
|
-
* Support namespaced classes e.g. `class Foo::Bar`
|
24
|
+
* Support absolute namespaces e.g. `class ::Foo::Bar`
|
26
25
|
* Support for Rspec tests
|
27
|
-
* Display diffs in method implementation between commits
|
28
|
-
* Only display diffs when method implementation has changed
|
29
|
-
* Default to looking for git repo in current working directory
|
30
26
|
* Default to looking for commits in current git branch
|
31
27
|
* Maybe add as new git command or extension to existing command e.g. `git log`
|
32
28
|
* Optimise search for method definitions:
|
29
|
+
* Only consider commits where file that last contained method has changed
|
33
30
|
* First look in file where method was last defined
|
34
31
|
* Simple text search for files containing method name to narrow files that need to be parsed
|
32
|
+
* Find "similar" method implementations e.g. by comparing ASTs of implementations
|
35
33
|
|
36
34
|
### Credits
|
37
35
|
|
38
36
|
Written by [James Mead](http://jamesmead.org) and the other members of [Go Free Range](http://gofreerange.com).
|
39
37
|
|
38
|
+
Thanks to Michael Feathers for some ideas in [delta-flora](https://github.com/michaelfeathers/delta-flora).
|
39
|
+
|
40
|
+
Thanks to [TICOSA](http://ticosa.org/) for giving me the impetus to do something about an idea I'd been kicking around for a while.
|
41
|
+
|
40
42
|
### License
|
41
43
|
|
42
44
|
Released under the [MIT License](https://github.com/freerange/method_log/blob/master/LICENSE).
|
data/bin/method_log
CHANGED
@@ -7,7 +7,13 @@ require 'method_log/api'
|
|
7
7
|
|
8
8
|
repository = MethodLog::Repository.new(path: Dir.pwd)
|
9
9
|
api = MethodLog::API.new(repository: repository)
|
10
|
-
|
11
|
-
|
12
|
-
puts method_commit
|
10
|
+
api.diffs(ARGV[0]).each do |method_commit, method_diff|
|
11
|
+
puts "commit #{method_commit.sha}"
|
12
|
+
puts "Author: #{method_commit.author[:name]} <#{method_commit.author[:email]}>"
|
13
|
+
puts "Date: #{method_commit.author[:time].strftime('%a %b %-e %T %Y %z')}"
|
14
|
+
puts
|
15
|
+
puts method_commit.message
|
16
|
+
puts
|
17
|
+
puts method_diff.to_s(:color)
|
18
|
+
puts
|
13
19
|
end
|
data/lib/method_log/api.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'method_log/method_finder'
|
2
2
|
require 'method_log/method_commit'
|
3
|
+
require 'method_log/method_diff'
|
3
4
|
|
4
5
|
module MethodLog
|
5
6
|
class API
|
@@ -8,12 +9,25 @@ module MethodLog
|
|
8
9
|
end
|
9
10
|
|
10
11
|
def history(method_identifier)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
Enumerator.new do |yielder|
|
13
|
+
@repository.commits.each do |commit|
|
14
|
+
method_definitions = commit.source_files.inject([]) do |definitions, source_file|
|
15
|
+
method_finder = MethodFinder.new(source_file: source_file)
|
16
|
+
definitions += Array(method_finder.find(method_identifier))
|
17
|
+
end
|
18
|
+
yielder << MethodCommit.new(commit: commit, method_definition: method_definitions.first)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def diffs(method_identifier)
|
24
|
+
Enumerator.new do |yielder|
|
25
|
+
history(method_identifier).each_cons(2) do |(commit, parent)|
|
26
|
+
diff = MethodDiff.new(commit: commit, parent: parent)
|
27
|
+
unless diff.empty?
|
28
|
+
yielder << [commit, diff]
|
29
|
+
end
|
15
30
|
end
|
16
|
-
MethodCommit.new(commit: commit, method_definition: method_definitions.first)
|
17
31
|
end
|
18
32
|
end
|
19
33
|
end
|
data/lib/method_log/commit.rb
CHANGED
@@ -17,24 +17,32 @@ module MethodLog
|
|
17
17
|
@index.add(path: source_file.path, oid: oid, mode: 0100644)
|
18
18
|
end
|
19
19
|
|
20
|
-
def apply
|
20
|
+
def apply(user: { email: 'test@example.com', name: 'test', time: Time.now }, message: 'commit-message')
|
21
21
|
tree = @index.write_tree(@repository)
|
22
22
|
parents = @repository.empty? ? [] : [@repository.head.target].compact
|
23
|
-
|
24
|
-
@sha = Rugged::Commit.create(@repository, tree: tree, parents: parents, update_ref: 'HEAD', author: user, committer: user, message: 'commit-message')
|
23
|
+
@sha = Rugged::Commit.create(@repository, tree: tree, parents: parents, update_ref: 'HEAD', author: user, committer: user, message: message)
|
25
24
|
end
|
26
25
|
|
27
26
|
def source_files
|
28
|
-
commit = @repository.lookup(sha)
|
29
27
|
source_files = []
|
30
28
|
commit.tree.walk_blobs do |root, blob_hash|
|
31
|
-
|
29
|
+
name = blob_hash[:name]
|
30
|
+
next unless File.extname(name) == '.rb'
|
31
|
+
path = root.empty? ? name : File.join(root, name)
|
32
32
|
source = @repository.lookup(blob_hash[:oid]).text
|
33
33
|
source_files << SourceFile.new(path: path, source: source)
|
34
34
|
end
|
35
35
|
source_files
|
36
36
|
end
|
37
37
|
|
38
|
+
def author
|
39
|
+
commit.author
|
40
|
+
end
|
41
|
+
|
42
|
+
def message
|
43
|
+
commit.message
|
44
|
+
end
|
45
|
+
|
38
46
|
def ==(other)
|
39
47
|
sha == other.sha
|
40
48
|
end
|
@@ -46,5 +54,11 @@ module MethodLog
|
|
46
54
|
def to_s
|
47
55
|
sha
|
48
56
|
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def commit
|
61
|
+
@commit ||= @repository.lookup(sha)
|
62
|
+
end
|
49
63
|
end
|
50
64
|
end
|
@@ -13,8 +13,20 @@ module MethodLog
|
|
13
13
|
[commit, method_definition].hash
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def sha
|
17
|
+
commit.sha
|
18
|
+
end
|
19
|
+
|
20
|
+
def author
|
21
|
+
commit.author
|
22
|
+
end
|
23
|
+
|
24
|
+
def message
|
25
|
+
commit.message
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_source
|
29
|
+
method_definition && method_definition.source + $/
|
18
30
|
end
|
19
31
|
|
20
32
|
protected
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'diffy'
|
2
|
+
|
3
|
+
module MethodLog
|
4
|
+
class MethodDiff
|
5
|
+
def initialize(commit: nil, parent: nil)
|
6
|
+
@commit, @parent = commit, parent
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s(mode = :text)
|
10
|
+
Diffy::Diff.new(@commit.method_source, @parent.method_source).to_s(mode)
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty?
|
14
|
+
to_s.chomp.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,51 +1,90 @@
|
|
1
|
-
require '
|
1
|
+
require 'parser/current'
|
2
2
|
|
3
3
|
require 'method_log/method_definition'
|
4
|
+
require 'method_log/scope'
|
4
5
|
|
5
6
|
module MethodLog
|
6
|
-
class MethodFinder <
|
7
|
+
class MethodFinder < Parser::AST::Processor
|
7
8
|
def initialize(source_file: nil)
|
8
|
-
super(source_file.source)
|
9
9
|
@source_file = source_file
|
10
|
-
@
|
10
|
+
@scope = Scope::Root.new
|
11
11
|
@methods = {}
|
12
|
-
|
13
|
-
|
12
|
+
ast = Parser::CurrentRuby.parse(source_file.source)
|
13
|
+
process(ast)
|
14
14
|
end
|
15
15
|
|
16
16
|
def find(method_identifier)
|
17
17
|
@methods[method_identifier]
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
def on_module(node)
|
21
|
+
const_node = node.children.first
|
22
|
+
constants = process_const(const_node)
|
23
|
+
new_constant = constants.pop
|
24
|
+
with_scope(@scope.for(constants).define(new_constant)) { super }
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :on_class, :on_module
|
28
|
+
|
29
|
+
def on_sclass(node)
|
30
|
+
target_node = node.children.first
|
31
|
+
with_scope(singleton_scope_for(target_node)) { super }
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_def(node)
|
35
|
+
name, args_node, body_node = *node
|
36
|
+
record_method_definition(@scope, name, node)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_defs(node)
|
41
|
+
definee_node, name, args_node, body_node = *node
|
42
|
+
scope = singleton_scope_for(definee_node)
|
43
|
+
record_method_definition(scope, name, node)
|
44
|
+
super
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def singleton_scope_for(node)
|
50
|
+
case node.type
|
51
|
+
when :self
|
52
|
+
@scope.singleton
|
53
|
+
when :const
|
54
|
+
constants = process_const(node)
|
55
|
+
@scope.for(constants).singleton
|
56
|
+
else
|
57
|
+
@scope.define(node.inspect).singleton
|
26
58
|
end
|
27
59
|
end
|
28
60
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
61
|
+
def process_const(node, namespaces = [])
|
62
|
+
scope_node, name = *node
|
63
|
+
namespaces.unshift(name)
|
64
|
+
if scope_node
|
65
|
+
process_const(scope_node, namespaces)
|
33
66
|
end
|
67
|
+
namespaces
|
34
68
|
end
|
35
69
|
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
70
|
+
def lines_for(node)
|
71
|
+
expression = node.location.expression
|
72
|
+
first_line = expression.line - 1
|
73
|
+
last_line = expression.source_buffer.decompose_position(expression.end_pos).first - 1
|
74
|
+
first_line..last_line
|
41
75
|
end
|
42
76
|
|
43
|
-
def
|
44
|
-
@
|
77
|
+
def record_method_definition(scope, name, node)
|
78
|
+
definition = MethodDefinition.new(source_file: @source_file, lines: lines_for(node))
|
79
|
+
identifier = scope.method_identifier(name)
|
80
|
+
@methods[identifier] = definition
|
45
81
|
end
|
46
82
|
|
47
|
-
def
|
48
|
-
@
|
83
|
+
def with_scope(scope, &block)
|
84
|
+
@scope = scope
|
85
|
+
yield
|
86
|
+
ensure
|
87
|
+
@scope = scope.parent
|
49
88
|
end
|
50
89
|
end
|
51
90
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module MethodLog
|
2
|
+
class Scope
|
3
|
+
class Null < Scope
|
4
|
+
def initialize
|
5
|
+
super(name: 'null')
|
6
|
+
end
|
7
|
+
|
8
|
+
def lookup(name)
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Root < Scope
|
14
|
+
def initialize
|
15
|
+
super(name: 'root', parent: Scope::Null.new)
|
16
|
+
end
|
17
|
+
|
18
|
+
def names
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :parent
|
24
|
+
|
25
|
+
def initialize(name: nil, parent: nil, singleton: false)
|
26
|
+
@name = name
|
27
|
+
@parent = parent
|
28
|
+
@singleton = singleton
|
29
|
+
@modules = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def for(modules)
|
33
|
+
scope = self
|
34
|
+
modules.each do |mod|
|
35
|
+
scope = scope.lookup(mod) || scope.define(mod)
|
36
|
+
end
|
37
|
+
scope
|
38
|
+
end
|
39
|
+
|
40
|
+
def define(name)
|
41
|
+
@modules[name] = Scope.new(name: name, parent: self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def lookup(name)
|
45
|
+
@modules.fetch(name) { @parent.lookup(name) }
|
46
|
+
end
|
47
|
+
|
48
|
+
def singleton
|
49
|
+
Scope.new(name: @name, parent: self, singleton: true)
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_identifier(name)
|
53
|
+
[names.join('::'), separator, name].join
|
54
|
+
end
|
55
|
+
|
56
|
+
protected
|
57
|
+
|
58
|
+
def names
|
59
|
+
names = @singleton ? [] : [@name]
|
60
|
+
names = @parent.names + names
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def separator
|
66
|
+
@singleton ? '.' : '#'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/method_log/version.rb
CHANGED
data/method_log.gemspec
CHANGED
data/spec/api_spec.rb
CHANGED
@@ -23,7 +23,7 @@ describe MethodLog::API do
|
|
23
23
|
foo_1 = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
24
24
|
class Foo
|
25
25
|
def bar
|
26
|
-
# implementation
|
26
|
+
# implementation 1
|
27
27
|
end
|
28
28
|
end
|
29
29
|
}.strip)
|
@@ -32,7 +32,7 @@ end
|
|
32
32
|
# move method definition down one line
|
33
33
|
class Foo
|
34
34
|
def bar
|
35
|
-
# implementation
|
35
|
+
# implementation 2
|
36
36
|
end
|
37
37
|
end
|
38
38
|
}.strip)
|
@@ -49,7 +49,7 @@ end
|
|
49
49
|
|
50
50
|
repository = MethodLog::Repository.new(path: repository_path)
|
51
51
|
api = MethodLog::API.new(repository: repository)
|
52
|
-
method_commits = api.history('Foo#bar')
|
52
|
+
method_commits = api.history('Foo#bar').to_a
|
53
53
|
|
54
54
|
method_definition_1 = MethodLog::MethodDefinition.new(source_file: foo_1, lines: 1..3)
|
55
55
|
method_definition_2 = MethodLog::MethodDefinition.new(source_file: foo_2, lines: 2..4)
|
@@ -58,5 +58,13 @@ end
|
|
58
58
|
method_commit_2 = MethodLog::MethodCommit.new(commit: commit_2, method_definition: method_definition_2)
|
59
59
|
|
60
60
|
expect(method_commits).to eq([method_commit_2, method_commit_1])
|
61
|
+
|
62
|
+
method_commit, method_diff = api.diffs('Foo#bar').first
|
63
|
+
expect(method_diff.to_s.strip).to eq(%{
|
64
|
+
def bar
|
65
|
+
- # implementation 2
|
66
|
+
+ # implementation 1
|
67
|
+
end
|
68
|
+
}.strip)
|
61
69
|
end
|
62
70
|
end
|
data/spec/commit_spec.rb
CHANGED
@@ -41,5 +41,45 @@ describe MethodLog::Commit do
|
|
41
41
|
commit = repository.commits.first
|
42
42
|
expect(commit.source_files).to eq([source_one, source_two])
|
43
43
|
end
|
44
|
+
|
45
|
+
it 'only includes source files with ruby file extension' do
|
46
|
+
source_file = MethodLog::SourceFile.new(path: 'path/to/source_one.py', source: 'source-file')
|
47
|
+
|
48
|
+
repository = MethodLog::Repository.new(path: repository_path)
|
49
|
+
commit = repository.build_commit
|
50
|
+
commit.add(source_file)
|
51
|
+
commit.apply
|
52
|
+
|
53
|
+
repository = MethodLog::Repository.new(path: repository_path)
|
54
|
+
commit = repository.commits.first
|
55
|
+
expect(commit.source_files).to be_empty
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'makes author available' do
|
59
|
+
user = { email: 'test@example.com', name: 'test', time: Time.now.round }
|
60
|
+
source_file = MethodLog::SourceFile.new(path: 'path/to/source.rb', source: 'source')
|
61
|
+
|
62
|
+
repository = MethodLog::Repository.new(path: repository_path)
|
63
|
+
commit = repository.build_commit
|
64
|
+
commit.add(source_file)
|
65
|
+
commit.apply(user: user)
|
66
|
+
|
67
|
+
repository = MethodLog::Repository.new(path: repository_path)
|
68
|
+
commit = repository.commits.first
|
69
|
+
expect(commit.author).to eq(user)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'makes message available' do
|
73
|
+
source_file = MethodLog::SourceFile.new(path: 'path/to/source.rb', source: 'source')
|
74
|
+
|
75
|
+
repository = MethodLog::Repository.new(path: repository_path)
|
76
|
+
commit = repository.build_commit
|
77
|
+
commit.add(source_file)
|
78
|
+
commit.apply(message: 'commit-message')
|
79
|
+
|
80
|
+
repository = MethodLog::Repository.new(path: repository_path)
|
81
|
+
commit = repository.commits.first
|
82
|
+
expect(commit.message).to eq('commit-message')
|
83
|
+
end
|
44
84
|
end
|
45
85
|
end
|
data/spec/method_commit_spec.rb
CHANGED
@@ -5,8 +5,8 @@ require 'method_log/method_definition'
|
|
5
5
|
|
6
6
|
describe MethodLog::MethodCommit do
|
7
7
|
let(:commit) { MethodLog::Commit.new(sha: nil) }
|
8
|
-
let(:source_file) { MethodLog::SourceFile.new(path: '/path/to/source.rb', source:
|
9
|
-
let(:method_definition) { MethodLog::MethodDefinition.new(source_file: source_file, lines:
|
8
|
+
let(:source_file) { MethodLog::SourceFile.new(path: '/path/to/source.rb', source: "line 0\nline 1\nline 2\n") }
|
9
|
+
let(:method_definition) { MethodLog::MethodDefinition.new(source_file: source_file, lines: 0..1) }
|
10
10
|
let(:method_commit) {
|
11
11
|
MethodLog::MethodCommit.new(commit: commit, method_definition: method_definition)
|
12
12
|
}
|
@@ -21,4 +21,13 @@ describe MethodLog::MethodCommit do
|
|
21
21
|
it 'has same hash as another method commit with same commit and method definition' do
|
22
22
|
expect(method_commit.hash).to eq(method_commit_with_same_commit_and_method_definition.hash)
|
23
23
|
end
|
24
|
+
|
25
|
+
it 'returns method source with trailing newline' do
|
26
|
+
expect(method_commit.method_source).to eq("line 0\nline 1\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns nil if no method definition' do
|
30
|
+
method_commit = MethodLog::MethodCommit.new(commit: commit, method_definition: nil)
|
31
|
+
expect(method_commit.method_source).to be_nil
|
32
|
+
end
|
24
33
|
end
|
@@ -26,14 +26,9 @@ end
|
|
26
26
|
expect(definition_one.hash).to eq(definition_two.hash)
|
27
27
|
end
|
28
28
|
|
29
|
-
it '
|
29
|
+
it 'provides access to the method source' do
|
30
30
|
definition = MethodLog::MethodDefinition.new(source_file: source_file, lines: 1..3)
|
31
31
|
|
32
|
-
expect(definition.
|
33
|
-
path/to/source.rb:1..3
|
34
|
-
def bar
|
35
|
-
# implementation
|
36
|
-
end
|
37
|
-
}.strip)
|
32
|
+
expect(definition.source).to eq(%{ def bar\n # implementation\n end})
|
38
33
|
end
|
39
34
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'method_log/method_diff'
|
2
|
+
|
3
|
+
describe MethodLog::MethodDiff do
|
4
|
+
let(:commit) { double(:commit) }
|
5
|
+
let(:parent) { double(:parent) }
|
6
|
+
let(:diff) { MethodLog::MethodDiff.new(commit: commit, parent: parent) }
|
7
|
+
|
8
|
+
it 'generates text diff of the method source for two commits' do
|
9
|
+
commit.stub(:method_source).and_return(%{line 1\nline 2\n})
|
10
|
+
parent.stub(:method_source).and_return(%{line 2\nline 3\n})
|
11
|
+
expect(diff.to_s).to eq(%{-line 1\n line 2\n+line 3\n})
|
12
|
+
end
|
13
|
+
end
|
data/spec/method_finder_spec.rb
CHANGED
@@ -50,4 +50,179 @@ end
|
|
50
50
|
|
51
51
|
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 2..4))
|
52
52
|
end
|
53
|
+
|
54
|
+
it 'finds definition of instance method on namespaced class within previously defined module' do
|
55
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
56
|
+
module Foo
|
57
|
+
end
|
58
|
+
|
59
|
+
class Foo::Bar
|
60
|
+
def baz
|
61
|
+
# implementation
|
62
|
+
end
|
63
|
+
end
|
64
|
+
}.strip)
|
65
|
+
|
66
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
67
|
+
method_definition = method_finder.find('Foo::Bar#baz')
|
68
|
+
|
69
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 4..6))
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'finds definition of instance method on namespaced module within previously defined module' do
|
73
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
74
|
+
module Foo
|
75
|
+
end
|
76
|
+
|
77
|
+
module Foo::Bar
|
78
|
+
def baz
|
79
|
+
# implementation
|
80
|
+
end
|
81
|
+
end
|
82
|
+
}.strip)
|
83
|
+
|
84
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
85
|
+
method_definition = method_finder.find('Foo::Bar#baz')
|
86
|
+
|
87
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 4..6))
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'finds definition of class method on class' do
|
91
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
92
|
+
module Foo
|
93
|
+
def self.bar
|
94
|
+
# implementation
|
95
|
+
end
|
96
|
+
end
|
97
|
+
}.strip)
|
98
|
+
|
99
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
100
|
+
method_definition = method_finder.find('Foo.bar')
|
101
|
+
|
102
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 1..3))
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'finds definition of class method on class with explicit reference to class' do
|
106
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
107
|
+
module Foo
|
108
|
+
def Foo.bar
|
109
|
+
# implementation
|
110
|
+
end
|
111
|
+
end
|
112
|
+
}.strip)
|
113
|
+
|
114
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
115
|
+
method_definition = method_finder.find('Foo.bar')
|
116
|
+
|
117
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 1..3))
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'finds definition of class method on class with explicit reference to namespaced class' do
|
121
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
122
|
+
module Foo
|
123
|
+
class Bar
|
124
|
+
def (Foo::Bar).baz
|
125
|
+
# implementation
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
}.strip)
|
130
|
+
|
131
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
132
|
+
method_definition = method_finder.find('Foo::Bar.baz')
|
133
|
+
|
134
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 2..4))
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'finds definition of class method on class with explicit reference to namespaced class outside current scope' do
|
138
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
139
|
+
class Foo
|
140
|
+
end
|
141
|
+
|
142
|
+
class Bar
|
143
|
+
def Foo.baz
|
144
|
+
# implementation
|
145
|
+
end
|
146
|
+
end
|
147
|
+
}.strip)
|
148
|
+
|
149
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
150
|
+
method_definition = method_finder.find('Foo.baz')
|
151
|
+
|
152
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 4..6))
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'finds definition of class method on class when singleton class is re-opened' do
|
156
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
157
|
+
class Foo
|
158
|
+
class << self
|
159
|
+
def bar
|
160
|
+
# implementation
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
}.strip)
|
165
|
+
|
166
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
167
|
+
method_definition = method_finder.find('Foo.bar')
|
168
|
+
|
169
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 2..4))
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'finds definition of class method on class when singleton class is re-opened with explicit reference to class' do
|
173
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
174
|
+
class Foo
|
175
|
+
class << Foo
|
176
|
+
def bar
|
177
|
+
# implementation
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
}.strip)
|
182
|
+
|
183
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
184
|
+
method_definition = method_finder.find('Foo.bar')
|
185
|
+
|
186
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 2..4))
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'finds definition of class method on class when singleton class is re-opened with explicit reference to class outside current scope' do
|
190
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
191
|
+
class Foo
|
192
|
+
end
|
193
|
+
|
194
|
+
class Bar
|
195
|
+
class << Foo
|
196
|
+
def bar
|
197
|
+
# implementation
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
}.strip)
|
202
|
+
|
203
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
204
|
+
method_definition = method_finder.find('Foo.bar')
|
205
|
+
|
206
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 5..7))
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'finds definition of class method on unknown object when singleton class is re-opened' do
|
210
|
+
foo = MethodLog::SourceFile.new(path: 'foo.rb', source: %{
|
211
|
+
class Foo
|
212
|
+
def initialize
|
213
|
+
@foo = new
|
214
|
+
class << @foo
|
215
|
+
def bar
|
216
|
+
# implementation
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
}.strip)
|
222
|
+
|
223
|
+
method_finder = MethodLog::MethodFinder.new(source_file: foo)
|
224
|
+
method_definition = method_finder.find('Foo::(ivar :@foo).bar')
|
225
|
+
|
226
|
+
expect(method_definition).to eq(MethodLog::MethodDefinition.new(source_file: foo, lines: 4..6))
|
227
|
+
end
|
53
228
|
end
|
data/spec/scope_spec.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'method_log/scope'
|
4
|
+
|
5
|
+
describe MethodLog::Scope do
|
6
|
+
let(:root) { MethodLog::Scope::Root.new }
|
7
|
+
|
8
|
+
it 'ruby interactive style method identifier for top-level module' do
|
9
|
+
a = root.define(:A)
|
10
|
+
expect(a.method_identifier('foo')).to eq('A#foo')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'ruby interactive style method identifier for module nested inside top-level module' do
|
14
|
+
a = root.define(:A)
|
15
|
+
b = a.define(:B)
|
16
|
+
expect(b.method_identifier('foo')).to eq('A::B#foo')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'ruby interactive style method identifier for module nested inside module nested inside top-level module' do
|
20
|
+
a = root.define(:A)
|
21
|
+
b = a.define(:B)
|
22
|
+
c = b.define(:C)
|
23
|
+
expect(c.method_identifier('foo')).to eq('A::B::C#foo')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'ruby interactive style method identifier for method on singleton class' do
|
27
|
+
a = root.define(:A)
|
28
|
+
singleton = a.singleton
|
29
|
+
expect(singleton.method_identifier('foo')).to eq('A.foo')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'ruby interactive style method identifier for method on singleton class nested inside top-level module' do
|
33
|
+
a = root.define(:A)
|
34
|
+
b = a.define(:B)
|
35
|
+
singleton = b.singleton
|
36
|
+
expect(singleton.method_identifier('foo')).to eq('A::B.foo')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'looks up scope for top-level module' do
|
40
|
+
a = root.define(:A)
|
41
|
+
expect(a.lookup(:A)).to eq(a)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'looks up scopes for top-level module and nested module from nested module' do
|
45
|
+
a = root.define(:A)
|
46
|
+
b = a.define(:B)
|
47
|
+
expect(b.lookup(:A)).to eq(a)
|
48
|
+
expect(b.lookup(:B)).to eq(b)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'looks up scopes for modules from double-nested module' do
|
52
|
+
a = root.define(:A)
|
53
|
+
b = a.define(:B)
|
54
|
+
c = b.define(:C)
|
55
|
+
expect(c.lookup(:A)).to eq(a)
|
56
|
+
expect(c.lookup(:B)).to eq(b)
|
57
|
+
expect(c.lookup(:C)).to eq(c)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'looks up qualified module' do
|
61
|
+
a = root.define(:A)
|
62
|
+
b = a.define(:B)
|
63
|
+
c = b.define(:C)
|
64
|
+
expect(c.for([:A])).to eq(a)
|
65
|
+
expect(c.for([:A, :B])).to eq(b)
|
66
|
+
expect(c.for([:A, :B, :C])).to eq(c)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'defines missing modules' do
|
70
|
+
root.for([:A, :B, :C])
|
71
|
+
a = root.lookup(:A)
|
72
|
+
b = a.lookup(:B)
|
73
|
+
c = b.lookup(:C)
|
74
|
+
expect(a).not_to be_nil
|
75
|
+
expect(b).not_to be_nil
|
76
|
+
expect(c).not_to be_nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
metadata
CHANGED
@@ -1,55 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method_log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Mead
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rugged
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: parser
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: diffy
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
25
53
|
- !ruby/object:Gem::Version
|
26
54
|
version: '0'
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rake
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
30
58
|
requirements:
|
31
|
-
- -
|
59
|
+
- - ">="
|
32
60
|
- !ruby/object:Gem::Version
|
33
61
|
version: '0'
|
34
62
|
type: :development
|
35
63
|
prerelease: false
|
36
64
|
version_requirements: !ruby/object:Gem::Requirement
|
37
65
|
requirements:
|
38
|
-
- -
|
66
|
+
- - ">="
|
39
67
|
- !ruby/object:Gem::Version
|
40
68
|
version: '0'
|
41
69
|
- !ruby/object:Gem::Dependency
|
42
70
|
name: rspec
|
43
71
|
requirement: !ruby/object:Gem::Requirement
|
44
72
|
requirements:
|
45
|
-
- -
|
73
|
+
- - ">="
|
46
74
|
- !ruby/object:Gem::Version
|
47
75
|
version: '0'
|
48
76
|
type: :development
|
49
77
|
prerelease: false
|
50
78
|
version_requirements: !ruby/object:Gem::Requirement
|
51
79
|
requirements:
|
52
|
-
- -
|
80
|
+
- - ">="
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '0'
|
55
83
|
description:
|
@@ -60,8 +88,8 @@ executables:
|
|
60
88
|
extensions: []
|
61
89
|
extra_rdoc_files: []
|
62
90
|
files:
|
63
|
-
- .gitignore
|
64
|
-
- .travis.yml
|
91
|
+
- ".gitignore"
|
92
|
+
- ".travis.yml"
|
65
93
|
- Gemfile
|
66
94
|
- LICENSE
|
67
95
|
- README.md
|
@@ -72,8 +100,10 @@ files:
|
|
72
100
|
- lib/method_log/commit.rb
|
73
101
|
- lib/method_log/method_commit.rb
|
74
102
|
- lib/method_log/method_definition.rb
|
103
|
+
- lib/method_log/method_diff.rb
|
75
104
|
- lib/method_log/method_finder.rb
|
76
105
|
- lib/method_log/repository.rb
|
106
|
+
- lib/method_log/scope.rb
|
77
107
|
- lib/method_log/source_file.rb
|
78
108
|
- lib/method_log/version.rb
|
79
109
|
- method_log.gemspec
|
@@ -93,7 +123,9 @@ files:
|
|
93
123
|
- spec/commit_spec.rb
|
94
124
|
- spec/method_commit_spec.rb
|
95
125
|
- spec/method_definition_spec.rb
|
126
|
+
- spec/method_diff_spec.rb
|
96
127
|
- spec/method_finder_spec.rb
|
128
|
+
- spec/scope_spec.rb
|
97
129
|
- spec/source_file_spec.rb
|
98
130
|
- spec/spec_helper.rb
|
99
131
|
homepage: http://jamesmead.org
|
@@ -106,17 +138,17 @@ require_paths:
|
|
106
138
|
- lib
|
107
139
|
required_ruby_version: !ruby/object:Gem::Requirement
|
108
140
|
requirements:
|
109
|
-
- -
|
141
|
+
- - ">="
|
110
142
|
- !ruby/object:Gem::Version
|
111
143
|
version: '0'
|
112
144
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
145
|
requirements:
|
114
|
-
- -
|
146
|
+
- - ">="
|
115
147
|
- !ruby/object:Gem::Version
|
116
148
|
version: '0'
|
117
149
|
requirements: []
|
118
150
|
rubyforge_project:
|
119
|
-
rubygems_version: 2.0
|
151
|
+
rubygems_version: 2.2.0
|
120
152
|
signing_key:
|
121
153
|
specification_version: 4
|
122
154
|
summary: Trace the history of an individual method in a git repository.
|
@@ -125,6 +157,8 @@ test_files:
|
|
125
157
|
- spec/commit_spec.rb
|
126
158
|
- spec/method_commit_spec.rb
|
127
159
|
- spec/method_definition_spec.rb
|
160
|
+
- spec/method_diff_spec.rb
|
128
161
|
- spec/method_finder_spec.rb
|
162
|
+
- spec/scope_spec.rb
|
129
163
|
- spec/source_file_spec.rb
|
130
164
|
- spec/spec_helper.rb
|