mire 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 361606807951fe02817f5a4f71a2810c914f79c7
4
+ data.tar.gz: bfd3d06e0f857abb7709c5992bb647c1493486e2
5
+ SHA512:
6
+ metadata.gz: 5b1c3198a31536633dd44ec35d1f89cdb4f36011b6bdf5071b8d43b1d296f85597eb6d3b3b02f63e1972cd08aee3b47f1aa65970a4cb366df7db5be732544971
7
+ data.tar.gz: 7759ff2bd79fd0ace1be9375c7058604ec0e2a278134fa059673d393ba1110d07c80539cea7d3d963fdbb17221bb51e7f6650a6f1af3329d79f13c02d8a8fbc3
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ .ruby-version
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ require: rubocop-rspec
2
+ inherit_from: .rubocop_todo.yml
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,15 @@
1
+ # This configuration was generated by `rubocop --auto-gen-config`
2
+ # on 2015-04-15 12:03:43 +0200 using RuboCop version 0.23.0.
3
+ # The point is for the user to remove these configuration records
4
+ # one by one as the offenses are removed from the code base.
5
+ # Note that changes in the inspected code, or installation of new
6
+ # versions of RuboCop, may require this file to be generated again.
7
+
8
+ # Offense count: 1
9
+ Style/CyclomaticComplexity:
10
+ Max: 10
11
+
12
+ # Offense count: 2
13
+ # Configuration parameters: CountComments.
14
+ Style/MethodLength:
15
+ Max: 22
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Change log
2
+
3
+ ## master (unreleased)
4
+
5
+ ## 0.1.1
6
+
7
+ * Add configuration option to exclude files / folders from analyzing
8
+ * Add configuration initializer option
9
+ * Switch from OptionParser to Slop as option parser
10
+ * Extend analyzer to handle modifier ifs and special blocks
11
+
12
+ ## 0.1.0
13
+
14
+ Initial version. This version is not stable yet, but feel free to test
15
+ it and feedback or (even better) pull requests are highly welcome.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 XING EVENTS GmbH
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # mire [ˈmɪʀɛ]
2
+
3
+ mire analyzes a Ruby project and helps you to find dependencies, call
4
+ stacks and unused methods. It parses Ruby and Haml files and collects
5
+ all method definitions and invocations.
6
+
7
+ * [Installation](#installation)
8
+ * [Usage](#usage)
9
+ * [Configuration](#configuration)
10
+ * [Dependencies](#dependencies)
11
+ * [TODO](#todo)
12
+
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ ```ruby
19
+ gem 'mire'
20
+ ```
21
+
22
+ And then execute:
23
+
24
+ ```bash
25
+ $ bundle
26
+ ```
27
+
28
+ Or install it yourself as:
29
+
30
+ ```bash
31
+ $ gem install mire
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ First you need to analyze the code and create a `.mire_analysis.yml`
37
+ file.
38
+
39
+ ```bash
40
+ bundle exec mire -a
41
+ ```
42
+
43
+ A Ruby code like
44
+
45
+ ```ruby
46
+ class Foo
47
+ def bar
48
+ buz
49
+ end
50
+ end
51
+ ```
52
+
53
+ will lead to this `.mire_analysis.yml` file.
54
+
55
+ ```yaml
56
+ :bar:
57
+ :definitions:
58
+ - :class: Foo
59
+ :method: :bar
60
+ :file: foo.rb
61
+ :line: 2
62
+ :invocations: []
63
+ :buz:
64
+ :definition: []
65
+ :invocations:
66
+ - :class: Foo
67
+ :method: :bar
68
+ :file: foo.rb
69
+ :line: 3
70
+ ```
71
+
72
+ After this the `.mire_analysis.yml` file can be used to find unused
73
+ methods for example.
74
+
75
+ ```bash
76
+ bundle exec mire -u
77
+
78
+ Checking for unused methods
79
+ foo.rb:2 Foo.bar
80
+ ```
81
+
82
+ This result can only be taken as a hint for unused methods. For example ERB files are
83
+ not being analysed yet. Also dynamic method definitions or invocations are not
84
+ considered. So mire can't find every usage of a method.
85
+
86
+ ## Configuration
87
+
88
+ mire can be configured with a `.mire.yml` file in your project folder.
89
+ With `mire -i` a initial configuration file is created.
90
+
91
+ You can configure which files or folders should be excluded while
92
+ analyzing the code or when displaying the unused methods.
93
+
94
+ ```yaml
95
+ excluded_files:
96
+ - vendor/**/*
97
+
98
+ output:
99
+ unused:
100
+ excluded_files:
101
+ - db/migrate/**/*
102
+ - spec/**/*
103
+ - script/**/*
104
+ - lib/**/*
105
+ ```
106
+
107
+ ## Dependencies
108
+
109
+ Why is [HAML-Lint](https://github.com/brigade/haml-lint) needed?
110
+
111
+ HAML-Lint did a great job to write a Ruby code extractor for Haml files.
112
+ mire is using this extractor.
113
+
114
+ ## TODO
115
+
116
+ The current implementation of mire is really basic. It needs to become
117
+ more robust and the parsed file types (e.g. `.erb`) needs to be
118
+ extended.
119
+
120
+ ## Contributing
121
+
122
+ 1. Fork it ( https://github.com/xing/mire/fork )
123
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
124
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
125
+ 4. Push to the branch (`git push origin my-new-feature`)
126
+ 5. Create a new Pull Request
127
+
128
+ ## Authors
129
+
130
+ [Nils Gemeinhardt](https://github.com/geniou) and [Marcus Lankenau](https://github.com/mlankenau)
131
+
132
+ Copyright (c) 2015 [XING EVENTS GmbH](http://de.amiando.com/)
133
+
134
+ Released under the MIT license. For full details see LICENSE included in this distribution.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
data/bin/mire ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
4
+
5
+ require 'mire'
6
+
7
+ Mire::CLI.parse
data/lib/mire.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'parser/current'
2
+ require 'yaml'
3
+ require 'slop'
4
+ require 'ruby-progressbar'
5
+ require 'haml_lint'
6
+
7
+ require 'mire/configuration'
8
+ require 'mire/configuration_methods'
9
+ require 'mire/analyzer'
10
+ require 'mire/cli'
11
+
12
+ require 'mire/output/base'
13
+ require 'mire/output/occurrence'
14
+ require 'mire/output/unused'
@@ -0,0 +1,160 @@
1
+ module Mire
2
+ # analyze all ruby files in a folder and find there usage
3
+ class Analyzer
4
+ include ConfigurationMethods
5
+
6
+ attr_reader :methods
7
+
8
+ BLACKLIST = %i(lambda new inspect to_i to_a [] []= * | ! != !~ % & + -@ / <
9
+ << <= <=> == === =~ > >= - __callee__ __send__ initialize
10
+ method_missing)
11
+
12
+ CALLBACKS = %i(after_commit after_create after_destroy after_save
13
+ after_update after_validation before_commit before_create
14
+ before_destroy before_save before_update before_validation)
15
+
16
+ BLOCKS = %i(task)
17
+
18
+ FILE = '.mire_analysis.yml'
19
+
20
+ def initialize(files: nil)
21
+ @namespace = []
22
+ @methods = {}
23
+ @files = files || Dir['**/*.{rb,haml}']
24
+ end
25
+
26
+ def run
27
+ progress_bar = ProgressBar.create(total: @files.count)
28
+ @files.each do |file|
29
+ @method = nil
30
+ next if excluded_file?(file)
31
+ @filename = file
32
+ case file_type(file)
33
+ when :haml
34
+ parse_haml_file(file)
35
+ when :rb
36
+ parse_file(file)
37
+ end
38
+ @filename = nil
39
+ progress_bar.increment
40
+ end
41
+ self
42
+ end
43
+
44
+ def save
45
+ IO.write(FILE, @methods.to_yaml)
46
+ end
47
+
48
+ private
49
+
50
+ def file_type(filename)
51
+ filename.split('.').last.to_sym
52
+ end
53
+
54
+ def excluded_files
55
+ @excluded_files ||= configuration.read(:excluded_files) || []
56
+ end
57
+
58
+ def parse_file(filename)
59
+ file_content = IO.read(filename)
60
+ ast = Parser::CurrentRuby.parse(file_content)
61
+ parse(ast)
62
+ rescue
63
+ nil
64
+ end
65
+
66
+ def parse_haml_file(filename)
67
+ file_content = IO.read(filename)
68
+ parser = HamlLint::Parser.new(file_content)
69
+ extractor = HamlLint::ScriptExtractor.new(parser)
70
+ ast = Parser::CurrentRuby.parse(extractor.extract.strip)
71
+ parse_method(ast)
72
+ rescue
73
+ nil
74
+ end
75
+
76
+ def location(ast)
77
+ {
78
+ class: @namespace.join('::'),
79
+ method: @method,
80
+ file: @filename,
81
+ line: ast.loc.line
82
+ }
83
+ end
84
+
85
+ def add_method(to, definition: nil, invocation: nil)
86
+ # TODO: this class check should not be necessary - it looks like the code
87
+ # is still messing up by determine the method
88
+ return unless [String, Symbol, NilClass].include?(@method.class)
89
+ return if BLACKLIST.include?(to.to_sym)
90
+
91
+ @methods[to] ||= { definitions: [], invocations: [] }
92
+ @methods[to][:definitions] << location(definition) if definition
93
+ @methods[to][:invocations] << location(invocation) if invocation
94
+ end
95
+
96
+ def get_const(ast)
97
+ fail "#{ast} is not a constant" unless ast.type == :const
98
+ ast.children.last.to_s
99
+ end
100
+
101
+ def parse_method(ast)
102
+ return unless ast.respond_to?(:type) && ast.respond_to?(:children)
103
+ if ast.type == :send
104
+ add_method(ast.children[1], invocation: ast)
105
+ elsif ast.type == :sym
106
+ add_method(ast.children[0], invocation: ast)
107
+ end
108
+ parse_children(ast, :parse_method)
109
+ end
110
+
111
+ def parse(ast)
112
+ return unless ast.respond_to?(:type) && ast.respond_to?(:children)
113
+ parse_method = "parse_#{ast.type}_node"
114
+ return send(parse_method, ast) if respond_to?(parse_method, true)
115
+ parse_children(ast)
116
+ rescue
117
+ raise "Error while parsing #{@filename}:#{ast.loc.line}"
118
+ end
119
+
120
+ def parse_children(ast, method = :parse)
121
+ ast.children.each { |c| send(method, c) }
122
+ end
123
+
124
+ def parse_class_node(ast)
125
+ @namespace.push(get_const(ast.children.first))
126
+ parse_children(ast)
127
+ @namespace.pop
128
+ end
129
+ alias_method :parse_module_node, :parse_class_node
130
+
131
+ def parse_def_node(ast)
132
+ @method = ast.children[ast.type == :defs ? 1 : 0]
133
+ add_method(@method, definition: ast)
134
+ parse_children(ast, :parse_method)
135
+ end
136
+ alias_method :parse_defs_node, :parse_def_node
137
+
138
+ def parse_send_node(ast)
139
+ unless (CALLBACKS + %i(scope validate)).include?(ast.children[1])
140
+ return parse_children(ast)
141
+ end
142
+ return unless ast.children[2]
143
+
144
+ @method = ast.children[2].children.last
145
+ parse_children(ast, :parse_method)
146
+ end
147
+
148
+ def parse_block_node(ast)
149
+ unless BLOCKS.include?(ast.children.first.children[1])
150
+ return parse_children(ast)
151
+ end
152
+ parse_method(ast.children[2])
153
+ end
154
+
155
+ def parse_if_node(ast)
156
+ parse_method(ast.children[0])
157
+ parse_children(ast)
158
+ end
159
+ end
160
+ end
data/lib/mire/cli.rb ADDED
@@ -0,0 +1,63 @@
1
+ module Mire
2
+ # Command line interface for mire
3
+ class CLI
4
+ class << self
5
+ def parse # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
6
+ slop = Slop.parse do |opts|
7
+ opts.on('-h', '--help', 'Show this help') do
8
+ puts opts
9
+ exit
10
+ end
11
+ opts.on('-a', '--analyze', 'Analyze ruby project') do
12
+ analyze
13
+ exit
14
+ end
15
+ opts.on('-c', '--check TERM', 'Check term and find usages') do |f|
16
+ check(f)
17
+ exit
18
+ end
19
+ opts.on('-u', '--unused', 'Check for unused methods') do
20
+ unused
21
+ exit
22
+ end
23
+ opts.on('-i', '--initialize', 'Create initial configuration file') do
24
+ init
25
+ exit
26
+ end
27
+ end
28
+
29
+ puts slop
30
+ rescue Slop::UnknownOption
31
+ puts 'Unknown option - use --help to see all options'
32
+ end
33
+
34
+ private
35
+
36
+ def init
37
+ Mire::Configuration.copy_example
38
+ puts "Configuration file #{Mire::Configuration::FILE} created"
39
+ rescue Mire::Configuration::ExistingFile
40
+ puts "Existing #{Mire::Configuration::FILE} file found - nothing done"
41
+ end
42
+
43
+ def analyze
44
+ analyzer = Analyzer.new
45
+ puts 'Analyzing project'
46
+ analyzer.run
47
+ analyzer.save
48
+ end
49
+
50
+ def check(f)
51
+ puts "Checking term #{f}"
52
+ occurrence = Output::Occurrence.new
53
+ puts occurrence.check(f)
54
+ end
55
+
56
+ def unused
57
+ puts 'Checking for unused methods'
58
+ occurrence = Output::Unused.new
59
+ puts occurrence.check
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,44 @@
1
+ module Mire
2
+ # load configuration
3
+ class Configuration
4
+ FILE = '.mire.yml'
5
+
6
+ class << self
7
+ def copy_example
8
+ fail ExistingFile if File.exist?(FILE)
9
+ FileUtils.cp(example_file, FILE)
10
+ end
11
+
12
+ private
13
+
14
+ def example_file
15
+ File.join(File.dirname(__FILE__), 'configuration_example.yml')
16
+ end
17
+ end
18
+
19
+ def initialize
20
+ @config = if File.exist?(FILE)
21
+ symbolize_keys(YAML.load_file(FILE))
22
+ else
23
+ {}
24
+ end
25
+ end
26
+
27
+ def read(*args)
28
+ args.reduce(@config) do |c, key|
29
+ return nil unless c
30
+ c[key]
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def symbolize_keys(hash)
37
+ hash.each_with_object({}) do |(key, value), result|
38
+ result[key.to_sym] = value.is_a?(Hash) ? symbolize_keys(value) : value
39
+ end
40
+ end
41
+
42
+ class ExistingFile < StandardError; end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
1
+ excluded_files:
2
+ - vendor/**/*
3
+
4
+ output:
5
+ unused:
6
+ excluded_files:
7
+ - app/controllers/**/*
8
+ - config/**/*
9
+ - db/migrate/**/*
10
+ - lib/**/*
11
+ - script/**/*
12
+ - spec/**/*
@@ -0,0 +1,12 @@
1
+ module Mire
2
+ module ConfigurationMethods
3
+
4
+ def configuration
5
+ @configuration ||= Mire::Configuration.new
6
+ end
7
+
8
+ def excluded_file?(file)
9
+ excluded_files.any? { |e| File.fnmatch(e, file, File::FNM_PATHNAME) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ module Mire
2
+ module Output
3
+ # base class of output classes
4
+ class Base
5
+ include ConfigurationMethods
6
+
7
+ protected
8
+
9
+ def methods
10
+ @methods ||= YAML.load_file(Mire::Analyzer::FILE)
11
+ end
12
+
13
+ def location(location)
14
+ [location_file(location), location_method(location)]
15
+ .reject { |s| s.to_s.empty? }
16
+ .join(' ')
17
+ end
18
+
19
+ private
20
+
21
+ def location_method(location)
22
+ [location[:class], location[:method]]
23
+ .reject { |s| s.to_s.empty? }
24
+ .join('.')
25
+ end
26
+
27
+ def location_file(location)
28
+ [location[:file], location[:line]]
29
+ .reject { |s| s.to_s.empty? }
30
+ .join(':')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ module Mire
2
+ module Output
3
+ # checks for a term and print out the occurrences and the occurrences of
4
+ # this and so on
5
+ class Occurrence < Base
6
+ def check(term, levels: 2, indenting: 0, output: [])
7
+ term = term.to_sym if term
8
+ return unless methods[term]
9
+ methods[term][:invocations].each_with_object(output) do |method, o|
10
+ o << "#{' ' * indenting * 2}#{location(method)}"
11
+
12
+ next unless levels > 0
13
+ check(method[:method], levels: levels - 1,
14
+ indenting: indenting + 1,
15
+ output: o)
16
+ end
17
+ output
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module Mire
2
+ module Output
3
+ # Check for unused methods
4
+ class Unused < Base
5
+ def check
6
+ methods
7
+ .select { |_, m| m[:invocations].empty? }
8
+ .map { |_, m| definitions(m[:definitions]) }
9
+ .flatten
10
+ .compact
11
+ .sort
12
+ end
13
+
14
+ private
15
+
16
+ def definitions(definitions)
17
+ definitions.map do |d|
18
+ location(d) unless excluded_file?(d[:file])
19
+ end
20
+ end
21
+
22
+ def excluded_file?(file)
23
+ excluded_files.any? { |e| File.fnmatch(e, file, File::FNM_PATHNAME) }
24
+ end
25
+
26
+ def excluded_files
27
+ @excluded_files ||=
28
+ configuration.read(:output, :unused, :excluded_files) || []
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module Mire
2
+ VERSION = '0.1.1'
3
+ end
data/mire.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mire/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'mire'
8
+ spec.version = Mire::VERSION
9
+ spec.authors = ['Nils Gemeinhardt', 'Marcus Lankenau']
10
+ spec.email = ['nils.gemeinhardt@xing.com', 'marcus.lankenau@xing.com']
11
+ spec.summary = 'Analyzes a Ruby project.'
12
+ spec.description = <<-TEXT
13
+ Analyzes a Ruby project and helps you to find dependencies, call stacks and
14
+ unused methods.
15
+ TEXT
16
+ spec.homepage = 'https://github.com/xing/mire'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0")
20
+ spec.executables = spec.files.grep(/bin\//) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_runtime_dependency 'parser'
25
+ spec.add_runtime_dependency 'slop', '~> 4.0'
26
+ spec.add_runtime_dependency 'ruby-progressbar'
27
+ spec.add_runtime_dependency 'haml-lint'
28
+ spec.add_development_dependency 'bundler', '~> 1.7'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency 'byebug'
32
+ spec.add_development_dependency 'rubocop'
33
+ spec.add_development_dependency 'rubocop-rspec'
34
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe Mire::Analyzer, type: :class do
5
+ subject { analyzer.run.save }
6
+ let(:analyzer) { described_class.new(files: Dir[@file]) }
7
+ let(:methods) { YAML.load_file(Mire::Analyzer::FILE) }
8
+ def file(content, extension = :rb)
9
+ @file = Tempfile.new(['mire', ".#{extension}"]).tap do |file|
10
+ file << content
11
+ file.close
12
+ end
13
+ end
14
+ let(:found_methods) { methods.map { |k, _v| k } }
15
+
16
+ it 'finds method calls inside method' do
17
+ file <<-RUBY
18
+ class Foo
19
+ def bar
20
+ buz
21
+ end
22
+ end
23
+ RUBY
24
+ subject
25
+ expect(methods[:buz]).not_to be_nil
26
+ expect(methods[:buz][:invocations]).not_to be_empty
27
+ expect(methods[:buz][:invocations].first[:class]).to eq('Foo')
28
+ expect(methods[:buz][:invocations].first[:method]).to eq(:bar)
29
+ expect(methods[:buz][:invocations].first[:line]).to eq(3)
30
+ expect(methods[:bar][:definitions]).not_to be_empty
31
+ expect(methods[:bar][:definitions].first[:class]).to eq('Foo')
32
+ expect(methods[:bar][:definitions].first[:method]).to eq(:bar)
33
+ expect(methods[:bar][:definitions].first[:line]).to eq(2)
34
+ end
35
+
36
+ it 'ignores excluded files' do
37
+ file <<-RUBY
38
+ class Foo
39
+ def bar
40
+ buz
41
+ end
42
+ end
43
+ RUBY
44
+ configuration = Tempfile.new('mire_configuration')
45
+ configuration << {
46
+ excluded_files: [
47
+ @file.path
48
+ ]
49
+ }.to_yaml
50
+ configuration.close
51
+ stub_const('Mire::Configuration::FILE', configuration.path)
52
+
53
+ subject
54
+ expect(methods[:buz]).to be_nil
55
+ end
56
+
57
+ it 'finds method calls inside class.method' do
58
+ file <<-RUBY
59
+ class Foo
60
+ def self.bar
61
+ buz
62
+ end
63
+ end
64
+ RUBY
65
+ subject
66
+ expect(found_methods).to match_array(%i(bar buz))
67
+ end
68
+
69
+ it 'finds callbacks' do
70
+ file <<-RUBY
71
+ class Foo
72
+ before_destroy :bar
73
+ def bar
74
+ true
75
+ end
76
+ end
77
+ RUBY
78
+ subject
79
+ expect(found_methods).to match_array(%i(bar))
80
+ end
81
+
82
+ it 'finds validation calls' do
83
+ file <<-RUBY
84
+ class Foo
85
+ validate :bar
86
+ end
87
+ RUBY
88
+ subject
89
+ expect(found_methods).to match_array(%i(bar))
90
+ end
91
+
92
+ it 'finds method calls inside blocks - e.g. rake tasks' do
93
+ file <<-RUBY
94
+ namespace :foo do
95
+ task bar: :environment do
96
+ Foo.bar
97
+ Foo.bax
98
+ end
99
+ task bax: :environment do
100
+ Foo.bur
101
+ end
102
+ end
103
+ RUBY
104
+ subject
105
+ expect(found_methods).to match_array(%i(bar bax bur))
106
+ end
107
+
108
+ it 'finds method calls in modifier ifs' do
109
+ file <<-RUBY
110
+ class Foo
111
+ validate :bar unless bux
112
+ validate :bar if buy
113
+ def bax
114
+ foo if bur
115
+ end
116
+ end
117
+ RUBY
118
+ subject
119
+ expect(found_methods).to match_array(%i(bar bux buy bax bur foo))
120
+ end
121
+
122
+ it 'adds also not called methods' do
123
+ file <<-RUBY
124
+ class Foo
125
+ def self.bar
126
+ true
127
+ end
128
+ def baz
129
+ self.bar
130
+ end
131
+ end
132
+ RUBY
133
+ subject
134
+ expect(found_methods).to match_array(%i(bar baz))
135
+ end
136
+
137
+ context 'haml files' do
138
+ it 'parses haml files' do
139
+ file(['- if bar', ' %b= foo'].join("\n"), :haml)
140
+ subject
141
+ expect(methods[:bar]).not_to be_nil
142
+ expect(methods[:bar][:invocations]).not_to be_empty
143
+ expect(methods[:bar][:invocations].first[:file]).to eq(@file.path)
144
+ expect(methods[:foo]).not_to be_nil
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe Mire::Configuration, type: :model do
5
+ before do
6
+ file = Tempfile.new('mire_configuration')
7
+ file << configuration.to_yaml
8
+ file.close
9
+
10
+ stub_const('Mire::Configuration::FILE', file.path)
11
+ end
12
+
13
+ let(:configuration) do
14
+ {
15
+ output: {
16
+ unused: {
17
+ excluded_files: [
18
+ 'boo/**/*'
19
+ ]
20
+ }
21
+ }
22
+ }
23
+ end
24
+
25
+ it 'fetches values' do
26
+ expect(subject.read(:output, :unused, :excluded_files)).to eq(['boo/**/*'])
27
+ end
28
+
29
+ it 'returns nil for not existing keys' do
30
+ expect(subject.read(:not, :known, :attribute)).to be_nil
31
+ end
32
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mire::Output::Occurrence, type: :model do
4
+ it 'returns occurrences of a given method' do
5
+ methods = {
6
+ foo: {
7
+ definitions: [
8
+ {
9
+ class: 'Foo',
10
+ method: 'bar',
11
+ file: 'foo.rb',
12
+ line: '123'
13
+ }
14
+ ],
15
+ invocations: [
16
+ {
17
+ class: 'Foo',
18
+ method: 'baz',
19
+ file: 'foo.rb',
20
+ line: '10'
21
+ },
22
+ {
23
+ class: 'Foo',
24
+ method: 'buz',
25
+ file: 'foo.rb',
26
+ line: '20'
27
+ }
28
+ ]
29
+ },
30
+ buz: {
31
+ definitions: [
32
+ {
33
+ class: 'Foo',
34
+ method: 'buz',
35
+ file: 'foo.rb',
36
+ line: '1234'
37
+ }
38
+ ],
39
+ invocations: [
40
+ {
41
+ class: 'Foo',
42
+ method: 'biz',
43
+ file: 'foo.rb',
44
+ line: '30'
45
+ }
46
+ ]
47
+ }
48
+ }
49
+ allow(subject).to receive(:methods).and_return(methods)
50
+ expect(subject.check(:foo)).to eq [
51
+ 'foo.rb:10 Foo.baz',
52
+ 'foo.rb:20 Foo.buz',
53
+ ' foo.rb:30 Foo.biz'
54
+ ]
55
+ end
56
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe Mire::Output::Unused, class: :model do
5
+ before do
6
+ methods = {
7
+ foo: {
8
+ definitions: [
9
+ {
10
+ class: 'Foo',
11
+ method: 'bar',
12
+ file: 'foo/bar.rb',
13
+ line: '123'
14
+ },
15
+ {
16
+ class: 'Boo',
17
+ method: 'bar',
18
+ file: 'boo/bar.rb',
19
+ line: '12'
20
+ }
21
+ ],
22
+ invocations: []
23
+ }
24
+ }
25
+ allow(subject).to receive(:methods).and_return(methods)
26
+ end
27
+
28
+ it 'returns methods that are unused' do
29
+ expect(subject.check).to match_array([
30
+ 'foo/bar.rb:123 Foo.bar',
31
+ 'boo/bar.rb:12 Boo.bar'
32
+ ])
33
+ end
34
+
35
+ it 'ignores files when given in the configuration' do
36
+ configuration = Tempfile.new('mire_configuration')
37
+ configuration << {
38
+ output: {
39
+ unused: {
40
+ excluded_files: [
41
+ 'boo/**/*'
42
+ ]
43
+ }
44
+ }
45
+ }.to_yaml
46
+ configuration.close
47
+
48
+ stub_const('Mire::Configuration::FILE', configuration.path)
49
+ expect(subject.check).to eq(['foo/bar.rb:123 Foo.bar'])
50
+ end
51
+ end
@@ -0,0 +1,4 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'mire'
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mire
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Nils Gemeinhardt
8
+ - Marcus Lankenau
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-06-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: parser
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: slop
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '4.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '4.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: ruby-progressbar
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: haml-lint
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: bundler
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.7'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.7'
84
+ - !ruby/object:Gem::Dependency
85
+ name: rake
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '10.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '10.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rspec
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '3.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '3.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: byebug
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rubocop
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: rubocop-rspec
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ description: |2
155
+ Analyzes a Ruby project and helps you to find dependencies, call stacks and
156
+ unused methods.
157
+ email:
158
+ - nils.gemeinhardt@xing.com
159
+ - marcus.lankenau@xing.com
160
+ executables:
161
+ - mire
162
+ extensions: []
163
+ extra_rdoc_files: []
164
+ files:
165
+ - ".gitignore"
166
+ - ".rubocop.yml"
167
+ - ".rubocop_todo.yml"
168
+ - CHANGELOG.md
169
+ - Gemfile
170
+ - LICENSE.txt
171
+ - README.md
172
+ - Rakefile
173
+ - bin/mire
174
+ - lib/mire.rb
175
+ - lib/mire/analyzer.rb
176
+ - lib/mire/cli.rb
177
+ - lib/mire/configuration.rb
178
+ - lib/mire/configuration_example.yml
179
+ - lib/mire/configuration_methods.rb
180
+ - lib/mire/output/base.rb
181
+ - lib/mire/output/occurrence.rb
182
+ - lib/mire/output/unused.rb
183
+ - lib/mire/version.rb
184
+ - mire.gemspec
185
+ - spec/mire/analyzer_spec.rb
186
+ - spec/mire/configuration_spec.rb
187
+ - spec/mire/output/occurrence_spec.rb
188
+ - spec/mire/output/unused_spec.rb
189
+ - spec/spec_helper.rb
190
+ homepage: https://github.com/xing/mire
191
+ licenses:
192
+ - MIT
193
+ metadata: {}
194
+ post_install_message:
195
+ rdoc_options: []
196
+ require_paths:
197
+ - lib
198
+ required_ruby_version: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ version: '0'
203
+ required_rubygems_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ requirements: []
209
+ rubyforge_project:
210
+ rubygems_version: 2.2.3
211
+ signing_key:
212
+ specification_version: 4
213
+ summary: Analyzes a Ruby project.
214
+ test_files:
215
+ - spec/mire/analyzer_spec.rb
216
+ - spec/mire/configuration_spec.rb
217
+ - spec/mire/output/occurrence_spec.rb
218
+ - spec/mire/output/unused_spec.rb
219
+ - spec/spec_helper.rb