mago 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 069e929006e5cab92bbebe28e6eca37476916809
4
- data.tar.gz: 44440710c44bbffecc85f180bbbe96869e03e2b5
3
+ metadata.gz: d37b226405e663e2097987a53497f9be0203feb6
4
+ data.tar.gz: 3fa69b1d2749e3d9cf2f86574a85f11318ddc255
5
5
  SHA512:
6
- metadata.gz: cebe0d24c4832b0a43669da316276faf4fc5204a1e1a3f79fc37ee7403a9ffd0bea4be21d0f9965bc58f30545a759f9aeb88813fa03ab952866e62f5239e7579
7
- data.tar.gz: 1e7f38b8af6c6ccbc10fe85bb117f670ed5899880a2b2b69a7e3773aec2d4397d146bd5d6575f88fc2474da5f4b6ba7a3a76526d7e6f5915d37f8a489c3f35cd
6
+ metadata.gz: ef14e1c8a0a321b2b70ba99e29883e58850d5edb208bcb8f3921b83910e3259d2577714c18de06eb6724785f940f008f0d9d9800a58660654d78cb48799a578d
7
+ data.tar.gz: 882f0ca028c606039c71e5893a570be4fdc2bc1d147f1fa0a60989564a833a9bf9f36ce07e4ea1c93252e002e68c59d9da7628b3dda05bf22cdb2019715bb2bc
@@ -9,7 +9,7 @@ Tool to detect magic numbers in ruby code.
9
9
  gem install mago
10
10
  ```
11
11
 
12
- ## Usage example
12
+ ## Usage
13
13
 
14
14
  Ruby code in `square.rb`:
15
15
  ```ruby
@@ -26,7 +26,7 @@ mago ./square.rb
26
26
  ./square.rb:4 detected magic number 2
27
27
  ```
28
28
 
29
- ### Ignoring specific numbers
29
+ ### Ignore specific numbers
30
30
 
31
31
  Use `--ignore` or `-i` option to ignore specific numbers. By default 0 and 1 are ignored.
32
32
 
@@ -35,14 +35,24 @@ mago -i 2,3 ./square.rb
35
35
  ./square.rb:3 detected magic number 5
36
36
  ```
37
37
 
38
+ ### Show source code
39
+
40
+ Use `--source` or `-s` option to show line of source code where magic number was found.
41
+
42
+ ```sh
43
+ mago -s ./square.rb
44
+ ./square.rb:3| r = 5
45
+ ./square.rb:4| square = P * r ** 2
46
+ ```
47
+
38
48
  ### Color output
39
49
 
40
50
  Use `--color` or `-c` option to colorize output.
41
51
 
42
52
  ## TODO
43
53
 
44
- * Support for `--show-source`(`-s`) option to show line of source code with magic number.
45
- * Support for `--expl-var` (`-e`) option to ignore explaining variables.
54
+ * Add intro about magic numbers to README.
55
+ * Create GIF with some famous probject to show how it works.
46
56
 
47
57
  ## Copyright
48
58
 
@@ -6,8 +6,8 @@ require 'mago/ruby_file'
6
6
  require 'mago/sexp_processor'
7
7
  require 'mago/detector'
8
8
  require 'mago/report'
9
- require 'mago/file_finder'
10
9
  require 'mago/version'
11
10
 
11
+ # Magic numbers detector for ruby source code.
12
12
  module Mago
13
13
  end
@@ -1,8 +1,12 @@
1
1
  require 'mago/cli/colorize'
2
- require 'mago/cli/command'
2
+ require 'mago/cli/config'
3
3
  require 'mago/cli/formatter'
4
+ require 'mago/cli/source_formatter'
5
+ require 'mago/cli/file_finder'
6
+ require 'mago/cli/command'
4
7
 
5
8
  module Mago
9
+ # Namespace for command line stuff.
6
10
  module Cli
7
11
 
8
12
  end
@@ -1,22 +1,33 @@
1
1
  module Mago
2
2
  module Cli
3
+ # Provides methods to colorize strings with ANSI colors.
3
4
  module Colorize
5
+ # Colorize text using a passed code
6
+ #
7
+ # @param text [String]
8
+ # @param color_code [Integer]
9
+ #
10
+ # @return [String] colorized text
4
11
  def colorize(text, color_code)
5
12
  "\e[#{color_code}m#{text}\e[0m"
6
13
  end
7
14
 
15
+ # :nodoc:
8
16
  def red(text)
9
17
  colorize(text, 31)
10
18
  end
11
19
 
20
+ # :nodoc:
12
21
  def green(text)
13
22
  colorize(text, 32)
14
23
  end
15
24
 
25
+ # :nodoc:
16
26
  def yellow(text)
17
27
  colorize(text, 33)
18
28
  end
19
29
 
30
+ # :nodoc:
20
31
  def pink(text)
21
32
  colorize(text, 35)
22
33
  end
@@ -1,43 +1,39 @@
1
1
  module Mago
2
2
  module Cli
3
- class Config
4
- attr_accessor :ignore, :files, :color
5
-
6
- def initialize
7
- @files = []
8
- @color = false
9
- end
10
- end
11
-
3
+ # CLI command.
4
+ #
5
+ # @example
6
+ # command = Mago::Cli::Command('-s', '-i', '0,1,2', './lib')
7
+ # command.execute
12
8
  class Command
13
- def initialize(args)
14
- @args = args
15
- @config = Config.new
9
+ # @param arguments [Array<String>] command arguments
10
+ def initialize(arguments)
11
+ @arguments = arguments
12
+ @config = Config.new
16
13
  end
17
14
 
15
+ # Execute command.
16
+ #
17
+ # @return [void]
18
18
  def execute
19
- parse_args
19
+ parse_arguments
20
20
  run
21
21
  end
22
22
 
23
- def parse_args
24
- while arg = @args.shift
23
+
24
+
25
+ private
26
+
27
+ # Parse arguments and build config.
28
+ #
29
+ # @return [void]
30
+ def parse_arguments
31
+ while arg = @arguments.shift
25
32
  case arg
26
- when '--help', '-h'
27
- show_help
28
- exit 0
29
- when '--version', '-v'
30
- show_version
31
- exit 0
32
- when '--ignore', '-i'
33
- val = @args.shift
34
- abort "--ignore option requires comma separated numbers" unless val =~ /^[0-9]/
35
- nums = val.to_s.split(',').map{ |n| n.include?('.') ? n.to_f : n.to_i }
36
- @config.ignore = nums
37
- when '--color', '--colour', '-c'
38
- @config.color = true
33
+ when /^--/
34
+ process_option(arg)
39
35
  when /^-/
40
- abort("Unknown option `#{arg}'")
36
+ arg[1..-1].each_char {|opt| process_option("-#{opt}") }
41
37
  else
42
38
  @config.files << arg
43
39
  end
@@ -46,36 +42,84 @@ module Mago
46
42
  @config.files << '.' if @config.files.empty?
47
43
  end
48
44
 
45
+ # Process option.
46
+ #
47
+ # @param option [String] option
48
+ #
49
+ # @return [void]
50
+ def process_option(option)
51
+ case option
52
+ when '--help', '-h'
53
+ print_help
54
+ exit 0
55
+ when '--version', '-v'
56
+ print_version
57
+ exit 0
58
+ when '--ignore', '-i'
59
+ val = @arguments.shift
60
+ abort "#{option} option requires comma separated numbers" unless val =~ /^[0-9]/
61
+ nums = val.to_s.split(',').map{ |n| n.include?('.') ? n.to_f : n.to_i }
62
+ @config.ignore = nums
63
+ when '--color', '--colour', '-c'
64
+ @config.color = true
65
+ when '--source', '-s'
66
+ @config.source = true
67
+ else /^-/
68
+ abort("Unknown option `#{option}'")
69
+ end
70
+ end
49
71
 
50
- private
51
-
72
+ # Run command using settings from config.
73
+ #
74
+ # @return [void]
52
75
  def run
53
- ruby_files = Mago::FileFinder.new(@config.files).find
76
+ ruby_files = Mago::Cli::FileFinder.new(@config.files).find
54
77
  detector = Mago::Detector.new(ruby_files, :ignore => @config.ignore)
55
78
  report = detector.run
56
- formatter = Mago::Cli::Formatter.new(report, :color => @config.color)
79
+
80
+ formatter_class = @config.source ? SourceFormatter : Formatter
81
+ formatter = formatter_class.new(report, :color => @config.color)
57
82
 
58
83
  puts formatter.format
59
84
  end
60
85
 
61
- def show_help
62
- puts "Magic numbers detector for Ruby programming language.\n\n" \
63
- " Syntax:\n" \
64
- " mago [OPTIONS] [FILES]\n\n" \
65
- " Options:\n" \
66
- " -i, --ignore NUMS\n" \
67
- " Comma separated numbers, which will be ignored. Default is 0,1\n\n" \
68
- " Usage:\n" \
69
- " mago # inspect ruby files in current directory\n" \
70
- " mago ./my_code.rb # inspect ./my_code.rb file\n" \
71
- " mago ./my_project/ # inspect ruby files in ./my_project/"
86
+ # Show help message.
87
+ #
88
+ # @return [void]
89
+ def print_help
90
+ puts <<-HELP
91
+ Magic numbers detector for Ruby programming language.
92
+
93
+ Syntax:
94
+ mago [OPTIONS] [FILES]
95
+
96
+ Options:
97
+ -i, --ignore NUMS
98
+ Comma separated numbers, which will be ignored. Default is 0,1
99
+
100
+ -c, --color
101
+ Colorize output
102
+
103
+ -s, --source
104
+ Show a line of source code with a magic number
105
+
106
+ Usage:
107
+ mago
108
+ mago -i 2,5 ./my_code.rb
109
+ mago -cs ./my_project/
110
+ HELP
72
111
  end
73
112
 
74
113
 
75
- def show_version
76
- puts "Mago #{Mago::VERSION}\n" \
77
- "Tool to detect magic numbers in ruby files.\n" \
78
- "Copyright (c) 2013 Sergey Potapov"
114
+ # Show program version.
115
+ #
116
+ # @return [void]
117
+ def print_version
118
+ puts <<-TEXT
119
+ Mago #{Mago::VERSION}
120
+ Tool to detect magic numbers in ruby files.
121
+ Copyright (c) 2013 Sergey Potapov
122
+ TEXT
79
123
  end
80
124
 
81
125
  end
@@ -0,0 +1,26 @@
1
+ module Mago
2
+ module Cli
3
+ # Command config. Build by option parser and is used to execute command.
4
+ class Config
5
+ # @attribute files [Array<String>] ruby files to process
6
+ attr_accessor :files
7
+
8
+ # @attribute ignore [Array<Numeric>, nil] numbers which must be ignored
9
+ attr_accessor :ignore
10
+
11
+ # @attribute color [Boolean] whether colorize output or not
12
+ attr_accessor :color
13
+
14
+ # attribute source [Boolean] whether show lines of source
15
+ # code with magic number or not
16
+ attr_accessor :source
17
+
18
+ def initialize
19
+ @files = []
20
+ @color = false
21
+ @source = false
22
+ @ignore = nil
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module Mago
2
+ module Cli
3
+ # Finds ruby files in local file system.
4
+ class FileFinder
5
+ # @param paths [Array<String>] files and directories
6
+ def initialize(paths)
7
+ @paths = paths
8
+ end
9
+
10
+ # Find ruby files.
11
+ #
12
+ # @return [Array<String>] ruby files
13
+ def find
14
+ ruby_files = []
15
+
16
+ @paths.each do |path|
17
+ if File.directory?(path)
18
+ pattern = File.join(path, '/**/*.rb')
19
+ ruby_files.concat(Dir[pattern])
20
+ else
21
+ ruby_files << path
22
+ end
23
+ end
24
+
25
+ ruby_files
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,14 +1,21 @@
1
1
  module Mago
2
2
  module Cli
3
+ # Formats report to be printed
3
4
  class Formatter
4
5
  include Colorize
5
6
 
7
+ # @param report [Mago::Report]
8
+ # @param opts [Hash]
9
+ #
10
+ # @option opts :color [Boolean] whether colorize output or no
6
11
  def initialize(report, opts = {})
7
12
  @report = report
8
-
9
- @color = opts[:color]
13
+ @color = opts[:color]
10
14
  end
11
15
 
16
+ # Format report.
17
+ #
18
+ # @return [String] formated report
12
19
  def format
13
20
  out = ''
14
21
 
@@ -23,6 +30,15 @@ module Mago
23
30
  out
24
31
  end
25
32
 
33
+
34
+ private
35
+
36
+ # Format file with magic numbers.
37
+ #
38
+ # @param file [Mago::File]
39
+ # @param out [String] string to write result
40
+ #
41
+ # @return [void]
26
42
  def format_file(file, out)
27
43
  file.magic_numbers.each do |num|
28
44
  if @color
@@ -39,6 +55,12 @@ module Mago
39
55
  end
40
56
  end
41
57
 
58
+ # Format error.
59
+ #
60
+ # @param error [String] error message
61
+ # @param out [String] string to write result
62
+ #
63
+ # @return [void]
42
64
  def format_error(error, out)
43
65
  out << "ERROR: %s\n" % [error]
44
66
  end
File without changes
@@ -0,0 +1,36 @@
1
+ module Mago
2
+ module Cli
3
+ # Formats report showing lines of source code where magic number was detected.
4
+ class SourceFormatter < Formatter
5
+ # :nodoc:
6
+ def format_file(file, out)
7
+ source_lines = File.readlines(file.path)
8
+
9
+ file.magic_numbers.each do |num|
10
+ if @color
11
+ line = yellow(num.line)
12
+ path = pink(file.path)
13
+ source_line = make_red(source_lines[num.line-1], num.value.to_s)
14
+ else
15
+ line = num.line
16
+ path = file.path
17
+ source_line = source_lines[num.line-1]
18
+ end
19
+
20
+ out << "#{path}:#{line}| #{source_line}"
21
+ end
22
+ end
23
+
24
+ # Find a substing in a string and make it red.
25
+ #
26
+ # @param str [String]
27
+ # @param substr [String]
28
+ #
29
+ # @return [String] string with red substring
30
+ def make_red(str, substr)
31
+ chunks = str.split(substr)
32
+ chunks.join(red(substr))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,14 +1,26 @@
1
1
  module Mago
2
+ # Magic numbers detector.
3
+ #
4
+ # @example
5
+ # detector = Mago::Detector.new('./Rakefile', './lib')
6
+ # detector.run # => #<Mago::Report ...>
2
7
  class Detector
3
8
  # Numbers which are ignored by default
4
9
  DEFAULT_IGNORE = [0, 1]
5
10
 
11
+ # @param file_paths [String] ruby files
12
+ # @param options [Hash]
13
+ #
14
+ # @option options :ignore [Array<Numeric>] numbers which must be ignored
6
15
  def initialize(file_paths = [], options = {})
7
16
  @file_paths = file_paths
8
17
  @report = Report.new
9
18
  @ignore = options[:ignore] || DEFAULT_IGNORE
10
19
  end
11
20
 
21
+ # Process files and build a report.
22
+ #
23
+ # @return [Mago::Report]
12
24
  def run
13
25
  @file_paths.each do |path|
14
26
  process_file(path)
@@ -16,14 +28,21 @@ module Mago
16
28
  @report
17
29
  end
18
30
 
31
+
32
+ private
33
+
34
+ # Process a file and add a result to the report.
35
+ #
36
+ # @param path [String]
37
+ #
38
+ # @return [void]
19
39
  def process_file(path)
20
- code = File.read(path)
40
+ code = File.read(path)
21
41
  sexp_node = RubyParser.new.parse(code)
22
-
23
- file = Mago::RubyFile.new(path)
42
+ file = Mago::RubyFile.new(path)
24
43
 
25
44
  sexp_processor = Mago::SexpProcessor.new(file, @ignore)
26
- sexp_processor.process sexp_node
45
+ sexp_processor.process(sexp_node)
27
46
 
28
47
  @report.files << file
29
48
  rescue Errno::ENOENT => err
@@ -1,14 +1,16 @@
1
1
  module Mago
2
+ # Magic number. Contains value of number and line number of source code.
2
3
  class MagicNumber
4
+ # @attribute value [Numeric] magic number itself
3
5
  attr_accessor :value
6
+
7
+ # @attribute line [Integer] line of source file where the magic number is located
4
8
  attr_accessor :line
5
- attr_accessor :file
6
9
 
10
+ # @param attrs [Hash]
7
11
  def initialize(attrs = {})
8
- attrs.each do |attr, value|
9
- send("#{attr}=", value)
10
- end
12
+ @value = attrs[:value]
13
+ @line = attrs[:line]
11
14
  end
12
-
13
15
  end
14
16
  end
@@ -1,9 +1,14 @@
1
1
  module Mago
2
+ # Work report built by {Mago::Detector}.
2
3
  class Report
3
- attr_reader :files, :errors
4
+ # @attribute files [Array<Mago::File>]
5
+ attr_reader :files
6
+
7
+ # @attribute errors [Array<String>]
8
+ attr_reader :errors
4
9
 
5
10
  def initialize
6
- @files = []
11
+ @files = []
7
12
  @errors = []
8
13
  end
9
14
  end
@@ -1,7 +1,13 @@
1
1
  module Mago
2
+ # Represents a ruby file with magic numbers.
2
3
  class RubyFile
3
- attr_reader :path, :magic_numbers
4
+ # @attribute path [String] file path
5
+ attr_reader :path
4
6
 
7
+ # @attribute magic_numbers [Array<Mago::MagicNumber>] magic numbers
8
+ attr_reader :magic_numbers
9
+
10
+ # @param path [String] file path
5
11
  def initialize(path)
6
12
  @path = path
7
13
  @magic_numbers = []
@@ -1,6 +1,9 @@
1
1
  module Mago
2
+ # The core of Mago.
3
+ # Iterates through AST returned by RubyParser and finds numeric literals.
2
4
  class SexpProcessor < ::SexpProcessor
3
-
5
+ # @param file [Mago::File] file where found numbers will be stored
6
+ # @param ignore [Array<Numeric>] array of numbers to ignore
4
7
  def initialize(file, ignore = [])
5
8
  super()
6
9
  self.warn_on_default = false
@@ -10,10 +13,22 @@ module Mago
10
13
  @ignore = ignore
11
14
  end
12
15
 
16
+ # Process constant declaration node. It's the case where we
17
+ # numeric literals, because they are not magic numbers.
18
+ #
19
+ # @param exp [Sexp]
20
+ #
21
+ # @return [Sexp]
13
22
  def process_cdecl(exp)
14
23
  process_default(exp)
15
24
  end
16
25
 
26
+ # Process literal node. If a literal is a number and add it to the
27
+ # collection of magic numbers.
28
+ #
29
+ # @param exp [Sexp]
30
+ #
31
+ # @return [Sexp]
17
32
  def process_lit(exp)
18
33
  exp.shift
19
34
  value = exp.shift
@@ -25,13 +40,17 @@ module Mago
25
40
  s()
26
41
  end
27
42
 
43
+ # Handler for all other nodes. They doesn't interest us, so we just skip.
44
+ #
45
+ # @param exp [Sexp]
46
+ #
47
+ # @return [Sexp]
28
48
  def process_default(exp)
29
49
  until exp.size == 0
30
50
  exp.shift
31
51
  end
32
52
  s()
33
53
  end
34
-
35
54
  end
36
55
  end
37
56
 
@@ -1,3 +1,4 @@
1
1
  module Mago
2
- VERSION = '0.0.4'
2
+ # :nodoc:
3
+ VERSION = '0.0.5'
3
4
  end
@@ -40,7 +40,7 @@ describe 'mago command' do
40
40
  end
41
41
  end
42
42
 
43
- describe '--ignore option' do
43
+ describe '--ignore' do
44
44
  it 'should ignore passed numbers' do
45
45
  expected = "./square.rb:3 detected magic number 5\n"\
46
46
  "./square.rb:3 detected magic number 1\n"
@@ -50,4 +50,14 @@ describe 'mago command' do
50
50
  end
51
51
  end
52
52
  end
53
+
54
+ describe '--source' do
55
+ it 'should show lines of source code with magic numbers' do
56
+ expected = "./square.rb:3| radius = 5 - 1\n" \
57
+ "./square.rb:4| square = P * radius ** 2\n"
58
+
59
+ mago('--source ./square.rb').should == expected
60
+ mago('-s ./square.rb').should == expected
61
+ end
62
+ end
53
63
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mago
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Potapov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-10 00:00:00.000000000 Z
11
+ date: 2013-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby_parser
@@ -50,13 +50,16 @@ extra_rdoc_files:
50
50
  files:
51
51
  - ./lib/mago.rb
52
52
  - ./lib/mago/report.rb
53
- - ./lib/mago/file_finder.rb
54
53
  - ./lib/mago/version.rb
55
54
  - ./lib/mago/ruby_file.rb
56
55
  - ./lib/mago/sexp_processor.rb
57
56
  - ./lib/mago/detector.rb
58
57
  - ./lib/mago/cli.rb
58
+ - ./lib/mago/cli/file_finder.rb
59
59
  - ./lib/mago/cli/formatter.rb
60
+ - ./lib/mago/cli/config.rb
61
+ - ./lib/mago/cli/option_parser.rb
62
+ - ./lib/mago/cli/source_formatter.rb
60
63
  - ./lib/mago/cli/command.rb
61
64
  - ./lib/mago/cli/colorize.rb
62
65
  - ./lib/mago/magic_number.rb
@@ -1,22 +0,0 @@
1
- module Mago
2
- class FileFinder
3
- def initialize(paths)
4
- @paths = paths
5
- end
6
-
7
- def find
8
- ruby_files = []
9
-
10
- @paths.each do |path|
11
- if File.directory?(path)
12
- pattern = File.join(path, '/**/*.rb')
13
- ruby_files.concat(Dir[pattern])
14
- else
15
- ruby_files << path
16
- end
17
- end
18
-
19
- ruby_files
20
- end
21
- end
22
- end