mago 0.0.4 → 0.0.5

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 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