metric_fu 4.2.0 → 4.2.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.
Files changed (38) hide show
  1. data/HISTORY.md +8 -0
  2. data/checksum/metric_fu-4.2.1.gem.sha512 +1 -0
  3. data/lib/metric_fu.rb +0 -1
  4. data/lib/metric_fu/cli/helper.rb +1 -1
  5. data/lib/metric_fu/data_structures/line_numbers.rb +6 -9
  6. data/lib/metric_fu/initial_requires.rb +1 -0
  7. data/lib/metric_fu/logging/mf_debugger.rb +20 -10
  8. data/lib/metric_fu/metrics/cane/cane_bluff_grapher.rb +1 -1
  9. data/lib/metric_fu/metrics/flay/flay_bluff_grapher.rb +1 -1
  10. data/lib/metric_fu/metrics/flog/flog.rb +2 -3
  11. data/lib/metric_fu/metrics/flog/flog_bluff_grapher.rb +1 -1
  12. data/lib/metric_fu/metrics/graph.rb +2 -2
  13. data/lib/metric_fu/metrics/rails_best_practices/rails_best_practices_bluff_grapher.rb +1 -1
  14. data/lib/metric_fu/metrics/rcov/rcov.rb +2 -2
  15. data/lib/metric_fu/metrics/rcov/rcov_bluff_grapher.rb +1 -1
  16. data/lib/metric_fu/metrics/reek/reek.rb +1 -1
  17. data/lib/metric_fu/metrics/reek/reek_bluff_grapher.rb +1 -1
  18. data/lib/metric_fu/metrics/roodi/roodi_bluff_grapher.rb +1 -1
  19. data/lib/metric_fu/metrics/saikuro/saikuro.rb +28 -4
  20. data/lib/metric_fu/metrics/stats/stats_bluff_grapher.rb +1 -1
  21. data/lib/metric_fu/reporting/graphs/engines/gchart.rb +1 -1
  22. data/lib/metric_fu/run.rb +5 -2
  23. data/lib/metric_fu/version.rb +1 -1
  24. data/metric_fu.gemspec +2 -0
  25. data/spec/metric_fu/metrics/flog/flog_spec.rb +1 -1
  26. data/spec/metric_fu/metrics/graph_spec.rb +4 -4
  27. data/spec/metric_fu/metrics/hotspots/hotspots_spec.rb +5 -4
  28. data/spec/metric_fu/metrics/rcov/rcov_spec.rb +2 -2
  29. data/spec/metric_fu/reporting/graphs/engines/gchart_spec.rb +1 -1
  30. metadata +19 -10
  31. data/lib/metric_fu/core_ext.rb +0 -2
  32. data/lib/metric_fu/core_ext/inflector/inflections.rb +0 -214
  33. data/lib/metric_fu/core_ext/inflector/methods.rb +0 -153
  34. data/lib/metric_fu/core_ext/object.rb +0 -3
  35. data/lib/metric_fu/core_ext/object/blank.rb +0 -106
  36. data/lib/metric_fu/core_ext/object/to_json.rb +0 -12
  37. data/lib/metric_fu/core_ext/string.rb +0 -2
  38. data/lib/metric_fu/core_ext/string/inflections.rb +0 -195
data/HISTORY.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ### Master
2
2
 
3
+ ### MetricFu 4.2.1 / 2013-05-23
4
+
5
+ * Fixes
6
+ * Remove ActiveSupport dependencies (Benjamin Fleischer #79)
7
+ * Add MultiJson to ensure JSON support in rbx and jruby (Benjamin Fleischer)
8
+ * Misc
9
+ * Improve STDOUT to show which metric is running but hide the details by default
10
+
3
11
  ### MetricFu 4.2.0 / 2013-05-20
4
12
 
5
13
  * Features
@@ -0,0 +1 @@
1
+ 529b99cf3f5fb314e9a7684ad38198e7509ec13926ded779bfef2a4b968b0538fc72248606a3b0b1bdead31163e04641bd6f565d5db62bb168ee80b854e9c580
@@ -41,7 +41,6 @@ module MetricFu
41
41
  (ENV['CC_BUILD_ARTIFACTS'] || 'tmp/metric_fu')
42
42
  end
43
43
  def self.configure
44
- MetricFu.lib_require { 'core_ext' }
45
44
  MetricFu.lib_require { 'configuration' }
46
45
  init_files = Dir.glob(File.join(MetricFu.metrics_dir, '**/init.rb')).reject do |file|
47
46
  if file =~ /rcov/o
@@ -41,7 +41,7 @@ module MetricFu
41
41
  p.version = self.version
42
42
  p.option :run, "Run all metrics with defaults", :default => false
43
43
  metrics.each do |metric|
44
- p.option metric.to_sym, "Enables or disables #{metric.to_s.titleize}", :default => true #, :value_in_set => [true, false]
44
+ p.option metric.to_sym, "Enables or disables #{metric.to_s}", :default => true #, :value_in_set => [true, false]
45
45
  end
46
46
  p.option :open, "Open report in browser", :default => true
47
47
  end.process!(argv)
@@ -4,7 +4,7 @@ module MetricFu
4
4
 
5
5
  def initialize(contents,file_path='')
6
6
  if contents.to_s.size.zero?
7
- puts "NON PARSEABLE INPUT: File is empty at path #{file_path.inspect}\n\t#{caller.join("\n\t")}"
7
+ mf_log "NON PARSEABLE INPUT: File is empty at path #{file_path.inspect}\n\t#{caller.join("\n\t")}"
8
8
  else
9
9
  rp = RubyParser.new
10
10
  @locations = {}
@@ -14,19 +14,16 @@ module MetricFu
14
14
  process_class(file_sexp)
15
15
  when :module
16
16
  process_module(file_sexp)
17
- when :block
18
- file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
19
- when :iter
20
- mf_debug "SEXP: Not parsing line number for #{file_sexp.inspect}"
21
- when :cdecl
22
- mf_debug "SEXP: Not parsing line number for #{file_sexp.inspect}"
23
17
  else
24
- puts "Unexpected sexp_type #{file_sexp[0].inspect}"
18
+ mf_debug "SEXP: Parsing line numbers for classes in sexp type #{file_sexp[0].inspect}"
19
+ mf_debug " in #{file_path}"
20
+ file_sexp.each_of_type(:module) { |sexp| process_class(sexp) }
21
+ file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
25
22
  end
26
23
  end
27
24
  rescue Exception => e
28
25
  #catch errors for files ruby_parser fails on
29
- puts "RUBY PARSE FAILURE: #{e.class}\t#{e.message}\tSEXP:#{file_sexp.inspect}\n\tCONTENT:#{contents.inspect}\n\t#{e.backtrace}"
26
+ mf_log "RUBY PARSE FAILURE: #{e.class}\t#{e.message}\tFILE:#{file_path}\tSEXP:#{file_sexp.inspect}\n\tCONTENT:#{contents.inspect}\n\t#{e.backtrace}"
30
27
  @locations
31
28
  end
32
29
 
@@ -6,6 +6,7 @@ require 'rake'
6
6
 
7
7
  require 'yaml'
8
8
  require 'redcard'
9
+ require 'multi_json'
9
10
 
10
11
  MetricFu.configure
11
12
  MetricFu.logging_require { 'mf_debugger' }
@@ -1,24 +1,34 @@
1
1
  module MfDebugger
2
+ extend self
3
+
2
4
  class Logger
3
5
  class << self
4
6
  attr_accessor :debug_on
5
7
  @debug_on = false
6
8
  end
7
- end
8
-
9
- def self.mf_debug(msg,&block)
10
- if MfDebugger::Logger.debug_on
9
+ def self.log(msg, &block)
11
10
  if block_given?
12
11
  block.call
13
12
  end
14
- STDOUT.puts msg
13
+ STDOUT.puts '*'*5 + msg.to_s
14
+ end
15
+ def self.debug(msg, &block)
16
+ if MfDebugger::Logger.debug_on
17
+ if block_given?
18
+ log(msg,&block)
19
+ else
20
+ log(msg)
21
+ end
22
+ end
15
23
  end
16
24
  end
25
+
17
26
  def mf_debug(msg,&block)
18
- if block_given?
19
- MfDebugger.mf_debug(msg,&block)
20
- else
21
- MfDebugger.mf_debug(msg)
22
- end
27
+ MfDebugger::Logger.debug(msg, &block)
23
28
  end
29
+
30
+ def mf_log(msg,&block)
31
+ MfDebugger::Logger.log(msg, &block)
32
+ end
33
+
24
34
  end
@@ -6,7 +6,7 @@ module MetricFu
6
6
  #{BLUFF_DEFAULT_OPTIONS}
7
7
  g.title = 'Cane: code quality threshold violations';
8
8
  g.data('cane', [#{@cane_violations.join(',')}]);
9
- g.labels = #{@labels.to_json};
9
+ g.labels = #{MultiJson.dump(@labels)};
10
10
  g.draw();
11
11
  EOS
12
12
  File.open(File.join(MetricFu.output_directory, 'cane.js'), 'w') {|f| f << content }
@@ -6,7 +6,7 @@ module MetricFu
6
6
  #{BLUFF_DEFAULT_OPTIONS}
7
7
  g.title = 'Flay: duplication';
8
8
  g.data('flay', [#{@flay_score.join(',')}]);
9
- g.labels = #{@labels.to_json};
9
+ g.labels = #{MultiJson.dump(@labels)};
10
10
  g.draw();
11
11
  EOS
12
12
  File.open(File.join(MetricFu.output_directory, 'flay.js'), 'w') {|f| f << content }
@@ -27,7 +27,6 @@ module MetricFu
27
27
  end
28
28
  options = ::Flog.parse_options [
29
29
  "--all",
30
- "--details",
31
30
  MetricFu.flog[:continue] ? "--continue" : nil,
32
31
  ].compact
33
32
 
@@ -37,9 +36,9 @@ module MetricFu
37
36
  rescue LoadError => e
38
37
  message = "#{e.class}\t#{e.message}\n\t#{e.backtrace.join('\n\t')}"
39
38
  if MetricFu.configuration.mri?
40
- puts "Flog Error: #{message}"
39
+ mf_log "Flog Error: #{message}"
41
40
  else
42
- puts "Flog tasks only available in MRI: #{message}"
41
+ mf_log "Flog tasks only available in MRI: #{message}"
43
42
  end
44
43
  end
45
44
 
@@ -7,7 +7,7 @@ module MetricFu
7
7
  g.title = 'Flog: code complexity';
8
8
  g.data('average', [#{@flog_average.join(',')}]);
9
9
  g.data('top 5% average', [#{@top_five_percent_average.join(',')}])
10
- g.labels = #{@labels.to_json};
10
+ g.labels = #{MultiJson.dump(@labels)};
11
11
  g.draw();
12
12
  EOS
13
13
  File.open(File.join(MetricFu.output_directory, 'flog.js'), 'w') {|f| f << content }
@@ -20,9 +20,9 @@ module MetricFu
20
20
 
21
21
  def generate
22
22
  return if self.clazz.empty?
23
- puts "Generating graphs"
23
+ mf_log "Generating graphs"
24
24
  Dir[File.join(MetricFu.data_directory, '*.yml')].sort.each do |metric_file|
25
- puts "Generating graphs for #{metric_file}"
25
+ mf_log "Generating graphs for #{metric_file}"
26
26
  date_parts = year_month_day_from_filename(metric_file)
27
27
  metrics = YAML::load(File.open(metric_file))
28
28
 
@@ -6,7 +6,7 @@ module MetricFu
6
6
  #{BLUFF_DEFAULT_OPTIONS}
7
7
  g.title = 'Rails Best Practices: design problems';
8
8
  g.data('rails_best_practices', [#{@rails_best_practices_count.join(',')}]);
9
- g.labels = #{@labels.to_json};
9
+ g.labels = #{MultiJson.dump(@labels)};
10
10
  g.draw();
11
11
  EOS
12
12
  File.open(File.join(MetricFu.output_directory, 'rails_best_practices.js'), 'w') {|f| f << content }
@@ -81,7 +81,7 @@ module MetricFu
81
81
  line_numbers = MetricFu::LineNumbers.new(file_contents)
82
82
  rescue StandardError => e
83
83
  raise e unless e.message =~ /you shouldn't be able to get here/
84
- STDOUT.puts "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Rcov information for this file."
84
+ mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Rcov information for this file."
85
85
  next
86
86
  end
87
87
 
@@ -115,7 +115,7 @@ module MetricFu
115
115
  content.map! do |raw_line|
116
116
  line = Line.new(raw_line[3..-1], !raw_line.match(/^!!/)).to_h
117
117
  end
118
- content.reject! {|line| line[:content].blank? }
118
+ content.reject! {|line| line[:content].to_s == '' }
119
119
  files[fname] = {:lines => content}
120
120
  end
121
121
  files
@@ -6,7 +6,7 @@ module MetricFu
6
6
  #{BLUFF_DEFAULT_OPTIONS}
7
7
  g.title = 'Rcov: code coverage';
8
8
  g.data('rcov', [#{@rcov_percent.join(',')}]);
9
- g.labels = #{@labels.to_json};
9
+ g.labels = #{MultiJson.dump(@labels)};
10
10
  g.draw();
11
11
  EOS
12
12
  File.open(File.join(MetricFu.output_directory, 'rcov.js'), 'w') {|f| f << content }
@@ -66,7 +66,7 @@ module MetricFu
66
66
  line_numbers = MetricFu::LineNumbers.new(File.open(file_path, 'r').read,file_path)
67
67
  rescue StandardError => e
68
68
  raise e unless e.message =~ /you shouldn't be able to get here/
69
- puts "ruby_parser blew up while trying to parse #{file_path}. You won't have method level reek information for this file."
69
+ mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level reek information for this file."
70
70
  next
71
71
  end
72
72
 
@@ -11,7 +11,7 @@ module MetricFu
11
11
  #{BLUFF_DEFAULT_OPTIONS}
12
12
  g.title = 'Reek: code smells';
13
13
  #{data}
14
- g.labels = #{@labels.to_json};
14
+ g.labels = #{MultiJson.dump(@labels)};
15
15
  g.draw();
16
16
  EOS
17
17
  File.open(File.join(MetricFu.output_directory, 'reek.js'), 'w') {|f| f << content }
@@ -6,7 +6,7 @@ module MetricFu
6
6
  #{BLUFF_DEFAULT_OPTIONS}
7
7
  g.title = 'Roodi: design problems';
8
8
  g.data('roodi', [#{@roodi_count.join(',')}]);
9
- g.labels = #{@labels.to_json};
9
+ g.labels = #{MultiJson.dump(@labels)};
10
10
  g.draw();
11
11
  EOS
12
12
  File.open(File.join(MetricFu.output_directory, 'roodi.js'), 'w') {|f| f << content }
@@ -13,11 +13,35 @@ module MetricFu
13
13
  end
14
14
 
15
15
  saikuro_bin= $:.map{|d| d+'/../bin/saikuro'}.select{|f| File.exists? f}.first || 'saikuro'
16
- sh %{#{saikuro_bin} #{options_string}} do |ok, response|
17
- unless ok
18
- puts "Saikuro failed with exit status: #{response.exitstatus}"
16
+ mf_debug(capture_output do
17
+ sh %{#{saikuro_bin} #{options_string}} do |ok, response|
18
+ unless ok
19
+ mf_log "Saikuro failed with exit status: #{response.exitstatus}"
20
+ end
21
+ end
22
+ end)
23
+ end
24
+
25
+ def capture_output(&block)
26
+ old_stdout = STDOUT.clone
27
+ pipe_r, pipe_w = IO.pipe
28
+ pipe_r.sync = true
29
+ output = ""
30
+ reader = Thread.new do
31
+ begin
32
+ loop do
33
+ output << pipe_r.readpartial(1024)
34
+ end
35
+ rescue EOFError
19
36
  end
20
37
  end
38
+ STDOUT.reopen(pipe_w)
39
+ yield
40
+ ensure
41
+ STDOUT.reopen(old_stdout)
42
+ pipe_w.close
43
+ reader.join
44
+ return output
21
45
  end
22
46
 
23
47
  def format_directories
@@ -56,7 +80,7 @@ module MetricFu
56
80
  line_numbers = MetricFu::LineNumbers.new(File.open(file_data[:filename], 'r').read)
57
81
  rescue StandardError => e
58
82
  raise e unless e.message =~ /you shouldn't be able to get here/
59
- puts "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Saikuro information for this file."
83
+ mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Saikuro information for this file."
60
84
  next
61
85
  end
62
86
 
@@ -7,7 +7,7 @@ module MetricFu
7
7
  g.title = 'Stats: LOC & LOT';
8
8
  g.data('LOC', [#{@loc_counts.join(',')}]);
9
9
  g.data('LOT', [#{@lot_counts.join(',')}])
10
- g.labels = #{@labels.to_json};
10
+ g.labels = #{MultiJson.dump(@labels)};
11
11
  g.draw();
12
12
  EOS
13
13
  File.open(File.join(MetricFu.output_directory, 'stats.js'), 'w') {|f| f << content }
@@ -25,7 +25,7 @@ module MetricFu
25
25
  def self.require_graphing_gem
26
26
  require 'gchart' if MetricFu.graph_engine == :gchart
27
27
  rescue LoadError
28
- puts "#"*99 + "\n" +
28
+ mf_log "#"*99 + "\n" +
29
29
  "If you want to use google charts for graphing, you'll need to install the googlecharts rubygem." +
30
30
  "\n" + "#"*99
31
31
  end
@@ -21,12 +21,13 @@ module MetricFu
21
21
  end
22
22
  def run_reports
23
23
  report_metrics.each {|metric|
24
- mf_debug "** STARTING METRIC #{metric}"
24
+ mf_log "** STARTING METRIC #{metric}"
25
25
  MetricFu.report.add(metric)
26
- mf_debug "** ENDING METRIC #{metric}"
26
+ mf_log "** ENDING METRIC #{metric}"
27
27
  }
28
28
  end
29
29
  def save_reports
30
+ mf_log "** SAVING REPORTS"
30
31
  mf_debug "** SAVING REPORT YAML OUTPUT TO #{MetricFu.base_directory}"
31
32
  MetricFu.report.save_output(MetricFu.report.as_yaml,
32
33
  MetricFu.base_directory,
@@ -39,6 +40,7 @@ module MetricFu
39
40
  MetricFu.report.save_templatized_report
40
41
  end
41
42
  def save_graphs
43
+ mf_log "** GENERATING GRAPHS"
42
44
  mf_debug "** PREPARING TO GRAPH"
43
45
  MetricFu.graphs.each {|graph|
44
46
  mf_debug "** Graphing #{graph} with #{MetricFu.graph_engine}"
@@ -52,6 +54,7 @@ module MetricFu
52
54
  mf_debug "** OPENING IN BROWSER FROM #{MetricFu.output_directory}"
53
55
  MetricFu.report.show_in_browser(MetricFu.output_directory)
54
56
  end
57
+ mf_log "** COMPLETE"
55
58
  end
56
59
  private
57
60
  def load_user_configuration
@@ -1,3 +1,3 @@
1
1
  module MetricFu
2
- VERSION = "4.2.0"
2
+ VERSION = "4.2.1"
3
3
  end
@@ -55,6 +55,8 @@ Gem::Specification.new do |s|
55
55
  "coderay" => [],
56
56
  # default graphing libraries
57
57
  "bluff" => [],
58
+ # to_json support
59
+ 'multi_json' => [],
58
60
  }.each do |gem, version|
59
61
  if version == []
60
62
  s.add_runtime_dependency(gem)
@@ -10,7 +10,7 @@ describe Flog do
10
10
  describe "emit method" do
11
11
  it "should look for files and flog them" do
12
12
  Dir.should_receive(:glob).with("lib/**/*.rb").and_return(["found/file.rb"])
13
- ::Flog.should_receive(:parse_options).with(["--all", "--details"]).and_return("options")
13
+ ::Flog.should_receive(:parse_options).with(["--all"]).and_return("options")
14
14
  ::Flog.should_receive(:new).with("options").and_return(flogger = mock('flogger'))
15
15
  flogger.should_receive(:flog).with(["found/file.rb"])
16
16
  @flog.emit
@@ -27,11 +27,11 @@ describe MetricFu::Graph do
27
27
  @graph.clazz.should_receive(:push).with(an_instance_of(RcovGchartGrapher))
28
28
  @graph.add("rcov", 'gchart')
29
29
  end
30
- end
30
+ end
31
31
 
32
32
  describe "setting the date on the graph" do
33
33
  before(:each) do
34
- @graph.stub!(:puts)
34
+ @graph.stub!(:mf_log)
35
35
  end
36
36
 
37
37
  it "should set the date once for one data point" do
@@ -41,7 +41,7 @@ describe MetricFu::Graph do
41
41
  mock_grapher = stub
42
42
  mock_grapher.should_receive(:get_metrics).with("Metrics", "11/5")
43
43
  mock_grapher.should_receive(:graph!)
44
-
44
+
45
45
  @graph.clazz = [mock_grapher]
46
46
  @graph.generate
47
47
  end
@@ -57,5 +57,5 @@ describe MetricFu::Graph do
57
57
  @graph.clazz = [mock_grapher]
58
58
  @graph.generate
59
59
  end
60
- end
60
+ end
61
61
  end
@@ -1,3 +1,4 @@
1
+ require 'multi_json'
1
2
  require "spec_helper"
2
3
 
3
4
  describe Hotspots do
@@ -69,8 +70,8 @@ END
69
70
  analyzer = HotspotAnalyzer.new(@yaml)
70
71
  hotspots.instance_variable_set(:@analyzer, analyzer)
71
72
  result = hotspots.analyze
72
- expected = JSON.parse("{\"methods\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":\"Client#client_requested_sync\",\"file_path\":\"lib/client/client.rb\",\"hash\":7919384682,\"simple_method_name\":\"#client_requested_sync\"},\"details\":{\"reek\":\"found 1 code smells\",\"flog\":\"complexity is 37.9\"}}],\"classes\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":7995629750},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\"}}],\"files\":[{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":-5738801681},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\",\"churn\":\"detected high level of churn (changed 54 times)\"}},{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/foo.rb\",\"hash\":-7081271905},\"details\":{\"churn\":\"detected high level of churn (changed 52 times)\"}}]}")
73
- compare_hashes(JSON.parse(hotspots.to_h[:hotspots].to_json), expected)
73
+ expected = MultiJson.load("{\"methods\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":\"Client#client_requested_sync\",\"file_path\":\"lib/client/client.rb\",\"hash\":7919384682,\"simple_method_name\":\"#client_requested_sync\"},\"details\":{\"reek\":\"found 1 code smells\",\"flog\":\"complexity is 37.9\"}}],\"classes\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":7995629750},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\"}}],\"files\":[{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":-5738801681},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\",\"churn\":\"detected high level of churn (changed 54 times)\"}},{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/foo.rb\",\"hash\":-7081271905},\"details\":{\"churn\":\"detected high level of churn (changed 52 times)\"}}]}")
74
+ compare_hashes(MultiJson.load(hotspots.to_h[:hotspots].to_json), expected)
74
75
  end
75
76
 
76
77
  it "should put the changes into a hash" do
@@ -78,8 +79,8 @@ END
78
79
  analyzer = HotspotAnalyzer.new(@yaml)
79
80
  hotspots.instance_variable_set(:@analyzer, analyzer)
80
81
  hotspots.analyze
81
- expected = JSON.parse("{\"methods\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":\"Client#client_requested_sync\",\"file_path\":\"lib/client/client.rb\",\"hash\":7919384682,\"simple_method_name\":\"#client_requested_sync\"},\"details\":{\"reek\":\"found 1 code smells\",\"flog\":\"complexity is 37.9\"}}],\"classes\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":7995629750},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\"}}],\"files\":[{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":-5738801681},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\",\"churn\":\"detected high level of churn (changed 54 times)\"}},{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/foo.rb\",\"hash\":-7081271905},\"details\":{\"churn\":\"detected high level of churn (changed 52 times)\"}}]}")
82
- compare_hashes(JSON.parse(hotspots.to_h[:hotspots].to_json), expected)
82
+ expected = MultiJson.load("{\"methods\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":\"Client#client_requested_sync\",\"file_path\":\"lib/client/client.rb\",\"hash\":7919384682,\"simple_method_name\":\"#client_requested_sync\"},\"details\":{\"reek\":\"found 1 code smells\",\"flog\":\"complexity is 37.9\"}}],\"classes\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":7995629750},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\"}}],\"files\":[{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":-5738801681},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\",\"churn\":\"detected high level of churn (changed 54 times)\"}},{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/foo.rb\",\"hash\":-7081271905},\"details\":{\"churn\":\"detected high level of churn (changed 52 times)\"}}]}")
83
+ compare_hashes(MultiJson.load(hotspots.to_h[:hotspots].to_json), expected)
83
84
  end
84
85
  # really testing the output of analyzed_problems#worst_items
85
86
  it "should return the worst item granularities: files, classes, methods" do
@@ -11,7 +11,7 @@ describe MetricFu::Rcov do
11
11
 
12
12
  describe "emit" do
13
13
  before :each do
14
- @rcov.stub!(:puts)
14
+ @rcov.stub!(:mf_log)
15
15
  MetricFu.rcov[:external] = nil
16
16
  end
17
17
 
@@ -65,7 +65,7 @@ describe MetricFu::Rcov do
65
65
  end
66
66
  describe "with external configuration option set" do
67
67
  before :each do
68
- @rcov.stub!(:puts)
68
+ @rcov.stub!(:mf_log)
69
69
  MetricFu.rcov[:external] = "coverage/rcov.txt"
70
70
  end
71
71
 
@@ -5,7 +5,7 @@ describe MetricFu::Grapher do
5
5
  it "should give a warning if trying to use gchart but gem is not installed" do
6
6
  MetricFu::Configuration.run {|config| config.graph_engine = :gchart}
7
7
  MetricFu::Grapher.should_receive(:require).with('gchart').and_raise(LoadError)
8
- MetricFu::Grapher.should_receive(:puts).with(/If you want to use google charts/)
8
+ MetricFu::Grapher.should_receive(:mf_log).with(/If you want to use google charts/)
9
9
  MetricFu::Grapher.require_graphing_gem
10
10
  end
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metric_fu
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -19,7 +19,7 @@ authors:
19
19
  autorequire:
20
20
  bindir: bin
21
21
  cert_chain: []
22
- date: 2013-05-20 00:00:00.000000000 Z
22
+ date: 2013-05-23 00:00:00.000000000 Z
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
25
25
  name: flay
@@ -321,6 +321,22 @@ dependencies:
321
321
  - - ! '>='
322
322
  - !ruby/object:Gem::Version
323
323
  version: '0'
324
+ - !ruby/object:Gem::Dependency
325
+ name: multi_json
326
+ requirement: !ruby/object:Gem::Requirement
327
+ none: false
328
+ requirements:
329
+ - - ! '>='
330
+ - !ruby/object:Gem::Version
331
+ version: '0'
332
+ type: :runtime
333
+ prerelease: false
334
+ version_requirements: !ruby/object:Gem::Requirement
335
+ none: false
336
+ requirements:
337
+ - - ! '>='
338
+ - !ruby/object:Gem::Version
339
+ version: '0'
324
340
  description: Code metrics from Flog, Flay, Saikuro, Churn, Reek, Roodi, Rails' stats
325
341
  task and Rails Best Practices, and optionally RCov
326
342
  email: github@benjaminfleischer.com
@@ -362,6 +378,7 @@ files:
362
378
  - bin/mf-stats
363
379
  - checksum/.gitkeep
364
380
  - checksum/metric_fu-4.2.0.gem.sha512
381
+ - checksum/metric_fu-4.2.1.gem.sha512
365
382
  - config/roodi_config.yml
366
383
  - gem_tasks/build.rake
367
384
  - lib/metric_fu.rb
@@ -369,14 +386,6 @@ files:
369
386
  - lib/metric_fu/cli/helper.rb
370
387
  - lib/metric_fu/cli/parser.rb
371
388
  - lib/metric_fu/configuration.rb
372
- - lib/metric_fu/core_ext.rb
373
- - lib/metric_fu/core_ext/inflector/inflections.rb
374
- - lib/metric_fu/core_ext/inflector/methods.rb
375
- - lib/metric_fu/core_ext/object.rb
376
- - lib/metric_fu/core_ext/object/blank.rb
377
- - lib/metric_fu/core_ext/object/to_json.rb
378
- - lib/metric_fu/core_ext/string.rb
379
- - lib/metric_fu/core_ext/string/inflections.rb
380
389
  - lib/metric_fu/data_structures/careful_array.rb
381
390
  - lib/metric_fu/data_structures/line_numbers.rb
382
391
  - lib/metric_fu/data_structures/location.rb
@@ -1,2 +0,0 @@
1
- MetricFu.lib_require { 'core_ext/object' }
2
- MetricFu.lib_require { 'core_ext/string' }
@@ -1,214 +0,0 @@
1
- # https://raw.github.com/rails/rails/20768176292cbcb883ab152b4aa9ed8c664771cd/activesupport/lib/active_support/inflector/inflections.rb
2
- # not https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/inflector/inflections.rb
3
- MetricFu.lib_require { 'core_ext/inflector/methods' }
4
- module ActiveSupport
5
- module Inflector
6
- # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
7
- # inflection rules. Examples:
8
- #
9
- # ActiveSupport::Inflector.inflections do |inflect|
10
- # inflect.plural /^(ox)$/i, '\1\2en'
11
- # inflect.singular /^(ox)en/i, '\1'
12
- #
13
- # inflect.irregular 'octopus', 'octopi'
14
- #
15
- # inflect.uncountable "equipment"
16
- # end
17
- #
18
- # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
19
- # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
20
- # already have been loaded.
21
- class Inflections
22
- def self.instance
23
- @__instance__ ||= new
24
- end
25
-
26
- attr_reader :plurals, :singulars, :uncountables, :humans
27
-
28
- def initialize
29
- @plurals, @singulars, @uncountables, @humans = [], [], [], []
30
- end
31
-
32
- # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
33
- # The replacement should always be a string that may include references to the matched data from the rule.
34
- def plural(rule, replacement)
35
- @uncountables.delete(rule) if rule.is_a?(String)
36
- @uncountables.delete(replacement)
37
- @plurals.insert(0, [rule, replacement])
38
- end
39
-
40
- # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
41
- # The replacement should always be a string that may include references to the matched data from the rule.
42
- def singular(rule, replacement)
43
- @uncountables.delete(rule) if rule.is_a?(String)
44
- @uncountables.delete(replacement)
45
- @singulars.insert(0, [rule, replacement])
46
- end
47
-
48
- # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
49
- # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
50
- #
51
- # Examples:
52
- # irregular 'octopus', 'octopi'
53
- # irregular 'person', 'people'
54
- def irregular(singular, plural)
55
- @uncountables.delete(singular)
56
- @uncountables.delete(plural)
57
- if singular[0,1].upcase == plural[0,1].upcase
58
- plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
59
- plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
60
- singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
61
- else
62
- plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
63
- plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
64
- plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
65
- plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
66
- singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
67
- singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
68
- end
69
- end
70
-
71
- # Add uncountable words that shouldn't be attempted inflected.
72
- #
73
- # Examples:
74
- # uncountable "money"
75
- # uncountable "money", "information"
76
- # uncountable %w( money information rice )
77
- def uncountable(*words)
78
- (@uncountables << words).flatten!
79
- end
80
-
81
- # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
82
- # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
83
- # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
84
- #
85
- # Examples:
86
- # human /_cnt$/i, '\1_count'
87
- # human "legacy_col_person_name", "Name"
88
- def human(rule, replacement)
89
- @humans.insert(0, [rule, replacement])
90
- end
91
-
92
- # Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
93
- # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
94
- # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
95
- #
96
- # Examples:
97
- # clear :all
98
- # clear :plurals
99
- def clear(scope = :all)
100
- case scope
101
- when :all
102
- @plurals, @singulars, @uncountables = [], [], []
103
- else
104
- instance_variable_set "@#{scope}", []
105
- end
106
- end
107
- end
108
-
109
- # Yields a singleton instance of Inflector::Inflections so you can specify additional
110
- # inflector rules.
111
- #
112
- # Example:
113
- # ActiveSupport::Inflector.inflections do |inflect|
114
- # inflect.uncountable "rails"
115
- # end
116
- def inflections
117
- if block_given?
118
- yield Inflections.instance
119
- else
120
- Inflections.instance
121
- end
122
- end
123
-
124
- # Returns the plural form of the word in the string.
125
- #
126
- # Examples:
127
- # "post".pluralize # => "posts"
128
- # "octopus".pluralize # => "octopi"
129
- # "sheep".pluralize # => "sheep"
130
- # "words".pluralize # => "words"
131
- # "CamelOctopus".pluralize # => "CamelOctopi"
132
- def pluralize(word)
133
- result = word.to_s.dup
134
-
135
- if word.empty? || inflections.uncountables.include?(result.downcase)
136
- result
137
- else
138
- inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
139
- result
140
- end
141
- end
142
-
143
- # The reverse of +pluralize+, returns the singular form of a word in a string.
144
- #
145
- # Examples:
146
- # "posts".singularize # => "post"
147
- # "octopi".singularize # => "octopus"
148
- # "sheep".singularize # => "sheep"
149
- # "word".singularize # => "word"
150
- # "CamelOctopi".singularize # => "CamelOctopus"
151
- def singularize(word)
152
- result = word.to_s.dup
153
-
154
- if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
155
- result
156
- else
157
- inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
158
- result
159
- end
160
- end
161
-
162
- # Capitalizes the first word and turns underscores into spaces and strips a
163
- # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
164
- #
165
- # Examples:
166
- # "employee_salary" # => "Employee salary"
167
- # "author_id" # => "Author"
168
- def humanize(lower_case_and_underscored_word)
169
- result = lower_case_and_underscored_word.to_s.dup
170
-
171
- inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
172
- result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
173
- end
174
-
175
- # Capitalizes all the words and replaces some characters in the string to create
176
- # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
177
- # used in the Rails internals.
178
- #
179
- # +titleize+ is also aliased as as +titlecase+.
180
- #
181
- # Examples:
182
- # "man from the boondocks".titleize # => "Man From The Boondocks"
183
- # "x-men: the last stand".titleize # => "X Men: The Last Stand"
184
- def titleize(word)
185
- humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
186
- end
187
-
188
- # Create the name of a table like Rails does for models to table names. This method
189
- # uses the +pluralize+ method on the last word in the string.
190
- #
191
- # Examples
192
- # "RawScaledScorer".tableize # => "raw_scaled_scorers"
193
- # "egg_and_ham".tableize # => "egg_and_hams"
194
- # "fancyCategory".tableize # => "fancy_categories"
195
- def tableize(class_name)
196
- pluralize(underscore(class_name))
197
- end
198
-
199
- # Create a class name from a plural table name like Rails does for table names to models.
200
- # Note that this returns a string and not a Class. (To convert to an actual class
201
- # follow +classify+ with +constantize+.)
202
- #
203
- # Examples:
204
- # "egg_and_hams".classify # => "EggAndHam"
205
- # "posts".classify # => "Post"
206
- #
207
- # Singular names are not handled correctly:
208
- # "business".classify # => "Busines"
209
- def classify(table_name)
210
- # strip out any leading schema name
211
- camelize(singularize(table_name.to_s.sub(/.*\./, '')))
212
- end
213
- end
214
- end
@@ -1,153 +0,0 @@
1
- # https://raw.github.com/rails/rails/20768176292cbcb883ab152b4aa9ed8c664771cd/activesupport/lib/active_support/inflector/methods.rb
2
- # not https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/inflector/methods.rb
3
- module ActiveSupport
4
- # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
5
- # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
6
- # in inflections.rb.
7
- #
8
- # The Rails core team has stated patches for the inflections library will not be accepted
9
- # in order to avoid breaking legacy applications which may be relying on errant inflections.
10
- # If you discover an incorrect inflection and require it for your application, you'll need
11
- # to correct it yourself (explained below).
12
- module Inflector
13
- extend self
14
-
15
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
16
- # is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
17
- #
18
- # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
19
- #
20
- # Examples:
21
- # "active_record".camelize # => "ActiveRecord"
22
- # "active_record".camelize(:lower) # => "activeRecord"
23
- # "active_record/errors".camelize # => "ActiveRecord::Errors"
24
- # "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
25
- #
26
- # As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
27
- # though there are cases where that does not hold:
28
- #
29
- # "SSLError".underscore.camelize # => "SslError"
30
- def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
31
- if first_letter_in_uppercase
32
- lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
33
- else
34
- lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
35
- end
36
- end
37
-
38
- # Makes an underscored, lowercase form from the expression in the string.
39
- #
40
- # Changes '::' to '/' to convert namespaces to paths.
41
- #
42
- # Examples:
43
- # "ActiveRecord".underscore # => "active_record"
44
- # "ActiveRecord::Errors".underscore # => active_record/errors
45
- #
46
- # As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
47
- # though there are cases where that does not hold:
48
- #
49
- # "SSLError".underscore.camelize # => "SslError"
50
- def underscore(camel_cased_word)
51
- word = camel_cased_word.to_s.dup
52
- word.gsub!(/::/, '/')
53
- word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
54
- word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
55
- word.tr!("-", "_")
56
- word.downcase!
57
- word
58
- end
59
-
60
- # Replaces underscores with dashes in the string.
61
- #
62
- # Example:
63
- # "puni_puni" # => "puni-puni"
64
- def dasherize(underscored_word)
65
- underscored_word.gsub(/_/, '-')
66
- end
67
-
68
- # Removes the module part from the expression in the string.
69
- #
70
- # Examples:
71
- # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
72
- # "Inflections".demodulize # => "Inflections"
73
- def demodulize(class_name_in_module)
74
- class_name_in_module.to_s.gsub(/^.*::/, '')
75
- end
76
-
77
- # Creates a foreign key name from a class name.
78
- # +separate_class_name_and_id_with_underscore+ sets whether
79
- # the method should put '_' between the name and 'id'.
80
- #
81
- # Examples:
82
- # "Message".foreign_key # => "message_id"
83
- # "Message".foreign_key(false) # => "messageid"
84
- # "Admin::Post".foreign_key # => "post_id"
85
- def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
86
- underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
87
- end
88
-
89
- # Ruby 1.9 introduces an inherit argument for Module#const_get and
90
- # #const_defined? and changes their default behavior.
91
- if Module.method(:const_get).arity == 1
92
- # Tries to find a constant with the name specified in the argument string:
93
- #
94
- # "Module".constantize # => Module
95
- # "Test::Unit".constantize # => Test::Unit
96
- #
97
- # The name is assumed to be the one of a top-level constant, no matter whether
98
- # it starts with "::" or not. No lexical context is taken into account:
99
- #
100
- # C = 'outside'
101
- # module M
102
- # C = 'inside'
103
- # C # => 'inside'
104
- # "C".constantize # => 'outside', same as ::C
105
- # end
106
- #
107
- # NameError is raised when the name is not in CamelCase or the constant is
108
- # unknown.
109
- def constantize(camel_cased_word)
110
- names = camel_cased_word.split('::')
111
- names.shift if names.empty? || names.first.empty?
112
-
113
- constant = Object
114
- names.each do |name|
115
- constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
116
- end
117
- constant
118
- end
119
- else
120
- def constantize(camel_cased_word) #:nodoc:
121
- names = camel_cased_word.split('::')
122
- names.shift if names.empty? || names.first.empty?
123
-
124
- constant = Object
125
- names.each do |name|
126
- constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
127
- end
128
- constant
129
- end
130
- end
131
-
132
- # Turns a number into an ordinal string used to denote the position in an
133
- # ordered sequence such as 1st, 2nd, 3rd, 4th.
134
- #
135
- # Examples:
136
- # ordinalize(1) # => "1st"
137
- # ordinalize(2) # => "2nd"
138
- # ordinalize(1002) # => "1002nd"
139
- # ordinalize(1003) # => "1003rd"
140
- def ordinalize(number)
141
- if (11..13).include?(number.to_i % 100)
142
- "#{number}th"
143
- else
144
- case number.to_i % 10
145
- when 1; "#{number}st"
146
- when 2; "#{number}nd"
147
- when 3; "#{number}rd"
148
- else "#{number}th"
149
- end
150
- end
151
- end
152
- end
153
- end
@@ -1,3 +0,0 @@
1
- # https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/object.rb
2
- MetricFu.lib_require { 'core_ext/object/blank' }
3
- MetricFu.lib_require { 'core_ext/object/to_json' }
@@ -1,106 +0,0 @@
1
- # https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/object/blank.rb
2
- # encoding: utf-8
3
-
4
- class Object
5
- # An object is blank if it's false, empty, or a whitespace string.
6
- # For example, '', ' ', +nil+, [], and {} are all blank.
7
- #
8
- # This simplifies:
9
- #
10
- # if address.nil? || address.empty?
11
- #
12
- # ...to:
13
- #
14
- # if address.blank?
15
- def blank?
16
- respond_to?(:empty?) ? empty? : !self
17
- end
18
-
19
- # An object is present if it's not <tt>blank?</tt>.
20
- def present?
21
- !blank?
22
- end
23
-
24
- # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
25
- # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
26
- #
27
- # This is handy for any representation of objects where blank is the same
28
- # as not present at all. For example, this simplifies a common check for
29
- # HTTP POST/query parameters:
30
- #
31
- # state = params[:state] if params[:state].present?
32
- # country = params[:country] if params[:country].present?
33
- # region = state || country || 'US'
34
- #
35
- # ...becomes:
36
- #
37
- # region = params[:state].presence || params[:country].presence || 'US'
38
- def presence
39
- self if present?
40
- end
41
- end
42
-
43
- class NilClass
44
- # +nil+ is blank:
45
- #
46
- # nil.blank? # => true
47
- def blank?
48
- true
49
- end
50
- end
51
-
52
- class FalseClass
53
- # +false+ is blank:
54
- #
55
- # false.blank? # => true
56
- def blank?
57
- true
58
- end
59
- end
60
-
61
- class TrueClass
62
- # +true+ is not blank:
63
- #
64
- # true.blank? # => false
65
- def blank?
66
- false
67
- end
68
- end
69
-
70
- class Array
71
- # An array is blank if it's empty:
72
- #
73
- # [].blank? # => true
74
- # [1,2,3].blank? # => false
75
- alias_method :blank?, :empty?
76
- end
77
-
78
- class Hash
79
- # A hash is blank if it's empty:
80
- #
81
- # {}.blank? # => true
82
- # { key: 'value' }.blank? # => false
83
- alias_method :blank?, :empty?
84
- end
85
-
86
- class String
87
- # A string is blank if it's empty or contains whitespaces only:
88
- #
89
- # ''.blank? # => true
90
- # ' '.blank? # => true
91
- # ' '.blank? # => true
92
- # ' something here '.blank? # => false
93
- def blank?
94
- self !~ /[^[:space:]]/
95
- end
96
- end
97
-
98
- class Numeric #:nodoc:
99
- # No number is blank:
100
- #
101
- # 1.blank? # => false
102
- # 0.blank? # => false
103
- def blank?
104
- false
105
- end
106
- end
@@ -1,12 +0,0 @@
1
- # https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/object/to_json.rb
2
- # Hack to load json gem first so we can overwrite its to_json.
3
- begin
4
- require 'json'
5
- rescue LoadError
6
- end
7
-
8
- # The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
9
- # their default behavior. That said, we need to define the basic to_json method in all of them,
10
- # otherwise they will always use to_json gem implementation, which is backwards incompatible in
11
- # several cases (for instance, the JSON implementation for Hash does not work) with inheritance
12
- # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
@@ -1,2 +0,0 @@
1
- # https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/string.rb
2
- MetricFu.lib_require { 'core_ext/string/inflections' }
@@ -1,195 +0,0 @@
1
- # https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/string/inflections.rb
2
- MetricFu.lib_require { 'core_ext/inflector/inflections' }
3
-
4
- # String inflections define new methods on the String class to transform names for different purposes.
5
- # For instance, you can figure out the name of a table from the name of a class.
6
- #
7
- # 'ScaleScore'.tableize # => "scale_scores"
8
- #
9
- class String
10
- # Returns the plural form of the word in the string.
11
- #
12
- # If the optional parameter +count+ is specified,
13
- # the singular form will be returned if <tt>count == 1</tt>.
14
- # For any other value of +count+ the plural will be returned.
15
- #
16
- # If the optional parameter +locale+ is specified,
17
- # the word will be pluralized as a word of that language.
18
- # By default, this parameter is set to <tt>:en</tt>.
19
- # You must define your own inflection rules for languages other than English.
20
- #
21
- # 'post'.pluralize # => "posts"
22
- # 'octopus'.pluralize # => "octopi"
23
- # 'sheep'.pluralize # => "sheep"
24
- # 'words'.pluralize # => "words"
25
- # 'the blue mailman'.pluralize # => "the blue mailmen"
26
- # 'CamelOctopus'.pluralize # => "CamelOctopi"
27
- # 'apple'.pluralize(1) # => "apple"
28
- # 'apple'.pluralize(2) # => "apples"
29
- # 'ley'.pluralize(:es) # => "leyes"
30
- # 'ley'.pluralize(1, :es) # => "ley"
31
- def pluralize(count = nil, locale = :en)
32
- locale = count if count.is_a?(Symbol)
33
- if count == 1
34
- self
35
- else
36
- ActiveSupport::Inflector.pluralize(self, locale)
37
- end
38
- end
39
-
40
- # The reverse of +pluralize+, returns the singular form of a word in a string.
41
- #
42
- # If the optional parameter +locale+ is specified,
43
- # the word will be singularized as a word of that language.
44
- # By default, this parameter is set to <tt>:en</tt>.
45
- # You must define your own inflection rules for languages other than English.
46
- #
47
- # 'posts'.singularize # => "post"
48
- # 'octopi'.singularize # => "octopus"
49
- # 'sheep'.singularize # => "sheep"
50
- # 'word'.singularize # => "word"
51
- # 'the blue mailmen'.singularize # => "the blue mailman"
52
- # 'CamelOctopi'.singularize # => "CamelOctopus"
53
- # 'leyes'.singularize(:es) # => "ley"
54
- def singularize(locale = :en)
55
- ActiveSupport::Inflector.singularize(self, locale)
56
- end
57
-
58
- # +constantize+ tries to find a declared constant with the name specified
59
- # in the string. It raises a NameError when the name is not in CamelCase
60
- # or is not initialized. See ActiveSupport::Inflector.constantize
61
- #
62
- # 'Module'.constantize # => Module
63
- # 'Class'.constantize # => Class
64
- # 'blargle'.constantize # => NameError: wrong constant name blargle
65
- def constantize
66
- ActiveSupport::Inflector.constantize(self)
67
- end
68
-
69
- # +safe_constantize+ tries to find a declared constant with the name specified
70
- # in the string. It returns nil when the name is not in CamelCase
71
- # or is not initialized. See ActiveSupport::Inflector.safe_constantize
72
- #
73
- # 'Module'.safe_constantize # => Module
74
- # 'Class'.safe_constantize # => Class
75
- # 'blargle'.safe_constantize # => nil
76
- def safe_constantize
77
- ActiveSupport::Inflector.safe_constantize(self)
78
- end
79
-
80
- # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
81
- # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
82
- #
83
- # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
84
- #
85
- # 'active_record'.camelize # => "ActiveRecord"
86
- # 'active_record'.camelize(:lower) # => "activeRecord"
87
- # 'active_record/errors'.camelize # => "ActiveRecord::Errors"
88
- # 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
89
- def camelize(first_letter = :upper)
90
- case first_letter
91
- when :upper
92
- ActiveSupport::Inflector.camelize(self, true)
93
- when :lower
94
- ActiveSupport::Inflector.camelize(self, false)
95
- end
96
- end
97
- alias_method :camelcase, :camelize
98
-
99
- # Capitalizes all the words and replaces some characters in the string to create
100
- # a nicer looking title. +titleize+ is meant for creating pretty output. It is not
101
- # used in the Rails internals.
102
- #
103
- # +titleize+ is also aliased as +titlecase+.
104
- #
105
- # 'man from the boondocks'.titleize # => "Man From The Boondocks"
106
- # 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
107
- def titleize
108
- ActiveSupport::Inflector.titleize(self)
109
- end
110
- alias_method :titlecase, :titleize
111
-
112
- # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
113
- #
114
- # +underscore+ will also change '::' to '/' to convert namespaces to paths.
115
- #
116
- # 'ActiveModel'.underscore # => "active_model"
117
- # 'ActiveModel::Errors'.underscore # => "active_model/errors"
118
- def underscore
119
- ActiveSupport::Inflector.underscore(self)
120
- end
121
-
122
- # Replaces underscores with dashes in the string.
123
- #
124
- # 'puni_puni'.dasherize # => "puni-puni"
125
- def dasherize
126
- ActiveSupport::Inflector.dasherize(self)
127
- end
128
-
129
- # Removes the module part from the constant expression in the string.
130
- #
131
- # 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
132
- # 'Inflections'.demodulize # => "Inflections"
133
- #
134
- # See also +deconstantize+.
135
- def demodulize
136
- ActiveSupport::Inflector.demodulize(self)
137
- end
138
-
139
- # Removes the rightmost segment from the constant expression in the string.
140
- #
141
- # 'Net::HTTP'.deconstantize # => "Net"
142
- # '::Net::HTTP'.deconstantize # => "::Net"
143
- # 'String'.deconstantize # => ""
144
- # '::String'.deconstantize # => ""
145
- # ''.deconstantize # => ""
146
- #
147
- # See also +demodulize+.
148
- def deconstantize
149
- ActiveSupport::Inflector.deconstantize(self)
150
- end
151
-
152
- # Creates the name of a table like Rails does for models to table names. This method
153
- # uses the +pluralize+ method on the last word in the string.
154
- #
155
- # 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
156
- # 'egg_and_ham'.tableize # => "egg_and_hams"
157
- # 'fancyCategory'.tableize # => "fancy_categories"
158
- def tableize
159
- ActiveSupport::Inflector.tableize(self)
160
- end
161
-
162
- # Create a class name from a plural table name like Rails does for table names to models.
163
- # Note that this returns a string and not a class. (To convert to an actual class
164
- # follow +classify+ with +constantize+.)
165
- #
166
- # 'egg_and_hams'.classify # => "EggAndHam"
167
- # 'posts'.classify # => "Post"
168
- #
169
- # Singular names are not handled correctly.
170
- #
171
- # 'business'.classify # => "Busines"
172
- def classify
173
- ActiveSupport::Inflector.classify(self)
174
- end
175
-
176
- # Capitalizes the first word, turns underscores into spaces, and strips '_id'.
177
- # Like +titleize+, this is meant for creating pretty output.
178
- #
179
- # 'employee_salary'.humanize # => "Employee salary"
180
- # 'author_id'.humanize # => "Author"
181
- def humanize
182
- ActiveSupport::Inflector.humanize(self)
183
- end
184
-
185
- # Creates a foreign key name from a class name.
186
- # +separate_class_name_and_id_with_underscore+ sets whether
187
- # the method should put '_' between the name and 'id'.
188
- #
189
- # 'Message'.foreign_key # => "message_id"
190
- # 'Message'.foreign_key(false) # => "messageid"
191
- # 'Admin::Post'.foreign_key # => "post_id"
192
- def foreign_key(separate_class_name_and_id_with_underscore = true)
193
- ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
194
- end
195
- end