metric_fu 4.1.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. data/.travis.yml +7 -4
  2. data/.yardopts +5 -0
  3. data/CONTRIBUTING.md +28 -13
  4. data/Gemfile +7 -3
  5. data/HISTORY.md +12 -0
  6. data/README.md +17 -0
  7. data/TODO.md +74 -79
  8. data/lib/metric_fu.rb +8 -0
  9. data/lib/metric_fu/configuration.rb +8 -3
  10. data/lib/metric_fu/core_ext.rb +2 -0
  11. data/lib/metric_fu/core_ext/inflector/inflections.rb +214 -0
  12. data/lib/metric_fu/core_ext/inflector/methods.rb +153 -0
  13. data/lib/metric_fu/core_ext/object.rb +3 -0
  14. data/lib/metric_fu/core_ext/object/blank.rb +106 -0
  15. data/lib/metric_fu/core_ext/object/to_json.rb +12 -0
  16. data/lib/metric_fu/core_ext/string.rb +2 -0
  17. data/lib/metric_fu/core_ext/string/inflections.rb +195 -0
  18. data/lib/metric_fu/initial_requires.rb +0 -8
  19. data/lib/metric_fu/metrics/base_template.rb +28 -1
  20. data/lib/metric_fu/metrics/churn/churn.rb +2 -2
  21. data/lib/metric_fu/metrics/churn/template_awesome/churn.html.erb +8 -6
  22. data/lib/metric_fu/metrics/hotspots/analysis/code_issue.rb +6 -0
  23. data/lib/metric_fu/metrics/hotspots/init.rb +1 -0
  24. data/lib/metric_fu/reporting/templates/awesome/awesome_template.rb +37 -20
  25. data/lib/metric_fu/reporting/templates/awesome/css/bluff.css +15 -0
  26. data/lib/metric_fu/reporting/templates/awesome/css/default.css +0 -48
  27. data/lib/metric_fu/reporting/templates/awesome/css/rcov.css +32 -0
  28. data/lib/metric_fu/reporting/templates/awesome/index.html.erb +4 -32
  29. data/lib/metric_fu/reporting/templates/awesome/layout.html.erb +2 -0
  30. data/lib/metric_fu/version.rb +1 -1
  31. data/metric_fu.gemspec +23 -11
  32. data/spec/cli/helper_spec.rb +38 -28
  33. data/spec/metric_fu/configuration_spec.rb +35 -22
  34. data/spec/metric_fu/metrics/base_template_spec.rb +7 -7
  35. data/spec/metric_fu/metrics/churn/churn_spec.rb +9 -2
  36. data/spec/metric_fu/metrics/flog/flog_spec.rb +57 -55
  37. data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +0 -2
  38. data/spec/run_spec.rb +0 -8
  39. data/spec/support/suite.rb +3 -1
  40. metadata +74 -125
  41. data/home_page/back_all.jpg +0 -0
  42. data/home_page/cc.rb.gif +0 -0
  43. data/home_page/churn.gif +0 -0
  44. data/home_page/cruise_control_1.gif +0 -0
  45. data/home_page/cyclomatic.gif +0 -0
  46. data/home_page/flay.gif +0 -0
  47. data/home_page/flog.gif +0 -0
  48. data/home_page/flog2.gif +0 -0
  49. data/home_page/footer.gif +0 -0
  50. data/home_page/header.jpg +0 -0
  51. data/home_page/hotspot.gif +0 -0
  52. data/home_page/img09.gif +0 -0
  53. data/home_page/index.html +0 -138
  54. data/home_page/rcov.gif +0 -0
  55. data/home_page/reek.gif +0 -0
  56. data/home_page/robots.txt +0 -5
  57. data/home_page/roodi.gif +0 -0
  58. data/home_page/saikuro.gif +0 -0
  59. data/home_page/stats.gif +0 -0
  60. data/home_page/styles.css +0 -245
  61. data/home_page/title.gif +0 -0
  62. data/home_page/title_back.gif +0 -0
  63. data/lib/metric_fu/metrics/cane/template_standard/cane.html.erb +0 -95
  64. data/lib/metric_fu/metrics/churn/template_standard/churn.html.erb +0 -31
  65. data/lib/metric_fu/metrics/flay/template_standard/flay.html.erb +0 -34
  66. data/lib/metric_fu/metrics/flog/template_standard/flog.html.erb +0 -57
  67. data/lib/metric_fu/metrics/hotspots/template_standard/hotspots.html.erb +0 -54
  68. data/lib/metric_fu/metrics/rails_best_practices/template_standard/rails_best_practices.html.erb +0 -29
  69. data/lib/metric_fu/metrics/rcov/template_standard/rcov.html.erb +0 -43
  70. data/lib/metric_fu/metrics/reek/template_standard/reek.html.erb +0 -42
  71. data/lib/metric_fu/metrics/roodi/template_standard/roodi.html.erb +0 -29
  72. data/lib/metric_fu/metrics/saikuro/template_standard/saikuro.html.erb +0 -84
  73. data/lib/metric_fu/metrics/stats/template_standard/stats.html.erb +0 -55
  74. data/lib/metric_fu/reporting/templates/standard/default.css +0 -64
  75. data/lib/metric_fu/reporting/templates/standard/index.html.erb +0 -44
  76. data/lib/metric_fu/reporting/templates/standard/standard_template.rb +0 -26
data/.travis.yml CHANGED
@@ -1,11 +1,14 @@
1
1
  language: ruby
2
+ bundler_args: --without development
3
+ script: bundle exec rspec
4
+ # gemfile:
5
+ # - gemfiles/Gemfile.ci
6
+ # before_install: some_command
7
+ # env:
8
+ # - "rack=1.3.4"
2
9
  rvm:
3
10
  - 2.0.0
4
11
  - 1.9.3
5
12
  - 1.9.2
6
13
  - jruby-19mode # JRuby in 1.9 mode
7
14
  - rbx-19mode
8
- matrix:
9
- allow_failures:
10
- - rvm: jruby-19mode
11
- - rvm: rbx-19mode
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ --readme README.md
3
+ lib/metric_fu.rb
4
+ lib/metric_fu_requires.rb
5
+ lib/metric_fu/**/*.rb
data/CONTRIBUTING.md CHANGED
@@ -1,19 +1,34 @@
1
1
  How to contribute:
2
2
 
3
- 1. Fork metric_fu on github.
4
- 2. bundle install
5
- 3. Run the tests ('bundle exec rake')
6
- 4. Run metric_fu on itself ('bundle exec metric_fu -r')
7
- 5. Make the changes you want and back them up with tests.
8
- 6. Make sure two important rake tests still run ('bundle exec rake' and 'bundle exec rake metrics:all')
9
- 7. Commit and send me a pull request with details as to what has been changed.
3
+ ## Bug reports / Issues
10
4
 
11
- Extra Credit:
5
+ * Is something broken or not working as expected? Check for an existing issue or [create a new one](https://github.com/metricfu/metric_fu/issues/new)
6
+ * See [Quick guide to writing good bug reports](https://github.com/metricfu/metric_fu/wiki/Issues:-Quick-guide-to-writing-good-bug-reports)
12
7
 
13
- 1. Make sure your changes work in 1.8.7, Ruby Enterprise Edition, and 1.9.3 (Hint use 'rvm' to help install multiple rubies)
14
- 2. Update the documentation here or the rubyforge web page inside the `'home_page'` folder
15
- 3. Update the History and give yourself credit.
8
+ ## Code
16
9
 
17
- The more of the above steps you do the easier it will be for me to merge in which will greatly increase you chances of getting your changes accepted.
10
+ 1. Clone the repo: `git clone git://github.com/metricfu/metric_fu.git && cd metric_fu`
11
+ 2. Install the gem dependencies: `bundle install`
12
+ 3. Make the changes you want and back them up with tests.
13
+ * Run the tests (`bundle exec rake`)
14
+ * Run metric_fu on itself (`bundle exec rake metrics:all`)
15
+ 4. Update the HISTORY.md file with your changes and give yourself credit
16
+ 5. Commit and create a pull request with details as to what has been changed and why
17
+ * Use well-described, small (atomic) commits.
18
+ * Include links to any relevant github issues.
19
+ * *Don't* change the VERSION file.
20
+ 6. Extra Credit: [Confirm it runs and tests pass on the rubies specified in the travis config](.travis.yml). I will otherwise confirm it runs on these.
18
21
 
19
- Also see [Quick guide to writing good bug reports](https://github.com/metricfu/metric_fu/wiki/Issues:-Quick-guide-to-writing-good-bug-reports)
22
+ How I handle pull requests:
23
+
24
+ * If the tests pass and the pull request looks good, I will merge it.
25
+ * If the pull request needs to be changed,
26
+ * you can change it by updating the branch you generated the pull request from
27
+ * either by adding more commits, or
28
+ * by force pushing to it
29
+ * I can make any changes myself and manually merge the code in.
30
+
31
+ ## Documentation
32
+
33
+ * If relevant, you may update [the metric_fu website](https://github.com/metricfu/metricfu.github.com) in a separate pull request to that repo
34
+ * Update the wiki
data/Gemfile CHANGED
@@ -1,10 +1,14 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
- group :development, :test do
4
+ # alternative graphing gem
5
+ gem "googlecharts"
6
+ group :development do
7
+
8
+ end
9
+ group :test do
5
10
  gem "rspec", '>2'
6
- gem "test-construct", ">= 1.2.0"
7
- gem "googlecharts"
11
+ gem 'test-construct'
8
12
  if ENV['COVERAGE']
9
13
  gem 'simplecov'
10
14
  # https://github.com/kina/simplecov-rcov-text
data/HISTORY.md CHANGED
@@ -1,5 +1,17 @@
1
1
  ### Master
2
2
 
3
+ ### MetricFu 4.1.3 / 2013-05-13
4
+
5
+ * Features
6
+ * Tests now pass in JRuby and Rubinius! (Benjamin Fleischer)
7
+ * Fixes
8
+ * Line numbers now display and link properly in annotated code (Benjamin Fleischer)
9
+ * No longer remove historical metrics when testing metric_fu
10
+ * Churn metric handler won't crash when no source control found (Dan Mayer)
11
+ * Misc (Benjamin Fleischer)
12
+ * Removed StandardTemplate, had no additional value and needed to be maintained
13
+ * Removed most template references to specific metrics
14
+
3
15
  ### MetricFu 4.1.2 / 2013-04-17
4
16
 
5
17
  * Fixes
data/README.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # MetricFu [![Gem Version](https://badge.fury.io/rb/metric_fu.png)](http://badge.fury.io/rb/metric_fu) [![Build Status](https://travis-ci.org/metricfu/metric_fu.png?branch=master)](http://travis-ci.org/metricfu/metric_fu) [![Code Climate](https://codeclimate.com/github/metricfu/metric_fu.png)](https://codeclimate.com/github/metricfu/metric_fu) [![Dependency Status](https://gemnasium.com/metricfu/metric_fu.png)](https://gemnasium.com/metricfu/metric_fu)
2
2
 
3
+ ## Metrics
4
+
5
+ * [Cane](https://rubygems.org/gems/cane), [Source](http://github.com/square/cane)
6
+ * [Churn](https://rubygems.org/gems/churn), [Source](http://github.com/danmayer/churn)
7
+ * [Flog](https://rubygems.org/gems/flog), [Source](https://github.com/seattlerb/flog)
8
+ * [Flay](https://rubygems.org/gems/flay), [Source](https://github.com/seattlerb/flay)
9
+ * [Reek](https://rubygems.org/gems/reek) [Source](https://github.com/troessner/reek)
10
+ * [Roodi](https://rubygems.org/gems/metric_fu-roodi), [Source](https://github.com/metricfu/roodi)
11
+ * [Saikuro](https://rubygems.org/gems/japgolly-Saikuro), [Source](https://github.com/japgolly/Saikuro)
12
+ * Rails-only
13
+ * [Rails Best Practices](https://rubygems.org/gems/rails_best_practices), [Source](https://github.com/railsbp/rails_best_practices)
14
+ * Rails `rake stats` task (see [gem](https://rubygems.org/gems/code_statistics), [Source](https://github.com/danmayer/code_statistics) )
15
+ * Test Coverage
16
+ * 1.9: [SimpleCov](http://rubygems.org/gems/simplecov) and [SimpleCov-Rcov-Text](http://rubygems.org/gems/simplecov-rcov-text)
17
+ * 1.8 [Rcov](http://rubygems.org/gems/rcov)
18
+ * Hotspots (a meta-metric of the above)
19
+
3
20
  ## Installation
4
21
 
5
22
  gem install metric_fu
data/TODO.md CHANGED
@@ -2,107 +2,86 @@
2
2
 
3
3
  * Keep HISTORY.md in master up to date
4
4
 
5
- * [Fix jruby/rbx errors on travis](https://travis-ci.org/metricfu/metric_fu)
5
+ Items in each category are in generally order of decreasing priority.
6
+ The categories themselves are not in any priority order.
6
7
 
7
- ## Before next release
8
-
9
- * Move code that references rcov out of
10
-
11
- lib/reporting/templates/awesome/css/default.css
12
- lib/reporting/templates/awesome/index.html.erb
13
- lib/reporting/templates/standard/default.css
14
- lib/reporting/templates/standard/index.html.erb
15
-
16
- * Move code that references flog out of
17
-
18
- lib/configuration.rb
19
- lib/metrics/hotspots/analysis/code_issue.rb
20
- lib/reporting/templates/awesome/index.html.erb
21
- lib/reporting/templates/standard/index.html.erb
22
-
23
- * Move code that references flay out of
24
-
25
- lib/metrics/generator.rb
26
- lib/metrics/hotspots/analysis/code_issue.rb
27
- lib/reporting/templates/awesome/index.html.erb
28
- lib/reporting/templates/standard/index.html.erb
29
-
30
- * Move code that references churn out of
31
-
32
- lib/metrics/hotspots/analysis/code_issue.rb
33
- lib/metrics/hotspots/init.rb
34
- lib/reporting/templates/awesome/index.html.erb
35
- lib/reporting/templates/standard/index.html.erb
36
-
37
- * Move code that references rails_best_practices out of
38
-
39
- lib/metrics/hotspots/analysis/code_issue.rb
40
- lib/reporting/templates/awesome/index.html.erb
41
- lib/reporting/templates/standard/index.html.erb
42
-
43
-
44
- * Move code that references reek out of
45
-
46
- lib/metrics/hotspots/analysis/code_issue.rb
47
- lib/reporting/templates/awesome/index.html.erb
48
- lib/reporting/templates/standard/index.html.erb
49
-
50
-
51
- * Move code that references roodi out of
52
-
53
- lib/metrics/hotspots/analysis/code_issue.rb
54
- lib/reporting/templates/awesome/index.html.erb
55
- lib/reporting/templates/standard/index.html.erb
56
-
57
- * Move code that references saikuro out of
58
-
59
- lib/metrics/hotspots/analysis/code_issue.rb
60
- lib/reporting/templates/awesome/index.html.erb
61
- lib/reporting/templates/standard/index.html.erb
62
-
63
- * Move code that references stats out of
64
-
65
- lib/reporting/templates/awesome/index.html.erb
66
- lib/reporting/templates/standard/index.html.erb
67
-
68
- * Review how metric_fu uses each tools to generate metrics, e.g. by shelling out commands, rake task, modifying the output, etc.
69
-
70
- * Change MetricFu.report.add(metric) to e.g. MetricFu.generate_report(metric) to make clear that this actually runs the tool
71
-
72
- * Allow the coverage task to specify the command it runs plus any flags (see bundler/capistrano options)
73
-
74
- * Test against a rails app
8
+ Also see [CONTRIBUTING](./CONTRIBUTING.md)
75
9
 
76
10
  ## Features
77
11
 
78
- * Look into adding
79
- * https://github.com/metricfu/code_statistics
80
- * brakeman https://github.com/metricfu/brakeman
81
- * laser https://github.com/metricfu/laser
12
+ * Make it easier to whitelist metrics when running from the commanline (cli)
13
+ * Be able to specify folders to run against rather than just app and lib
14
+ * Be able to run metric tools from metric_fu without shelling out
15
+ * Allow the coverage task to specify the command it runs plus any flags (see bundler/capistrano options)
82
16
  * Add configurable logger to all output streams
83
- * Color code flog results with scale from: http://jakescruggs.blogspot.com/2008/08/whats-good-flog-score.html
84
- * Make running metric_fu on metric_fu less embarrassing
85
17
  * Load all gems at config time so you fail fast if one is missing
86
- * Refactor the hotspots code
87
-
18
+ * Color code flog results with scale from: http://jakescruggs.blogspot.com/2008/08/whats-good-flog-score.html
19
+ * Make the template pages prettier (hold off until [61](https://github.com/metricfu/metric_fu/pull/61) is merged)
20
+ * Be able to generate historical metrics for eg gem releases (tagged with appropriate date)
88
21
 
89
22
  ## Testing
90
23
 
24
+ * Test against a rails app, see [yui-rails](https://github.com/nextmat/yui-rails/tree/master/test/dummy)
91
25
  * Determine how to test metric_fu against codebases that are not metric_fu, to ensure it works on most applications
92
26
  * This is especially true for rails applications
93
27
  * Remove / Modify Devver code from the generators/hotspots_spec and base/hotspot_analzyer_spec
28
+ * Add tests
29
+ * Remove useless tests
30
+ * Remove tests that use StandardTemplate. Will require updating tests yml output, which may not be easy
94
31
 
95
32
  ## Bugs / Fixes
96
33
 
34
+ * See issues
35
+
97
36
  ## Misc
98
37
 
38
+ ### Improvements
39
+
40
+ * See TODO items in the code
41
+ * Change how config works to not metaprogrammatically create so many
42
+ instance variables and accessors
43
+ * Clarify the execution path and what a metric's api should be, (repeat for templates and graphs)
44
+ * Change MetricFu.report.add(metric) to e.g. MetricFu.generate_report(metric) to make clear that this actually runs the tool
45
+ * Clarify hotspot weighting
46
+ * Update the wiki with use cases
47
+ * Review how metric_fu uses each tools to generate metrics, e.g. by shelling out commands, rake task, modifying the output, etc.
48
+ * Understand and explain s-expressions and how they're used (or should be ) in [line_numbers.rb](https://github.com/metricfu/metric_fu/blob/master/lib/metric_fu/data_structures/line_numbers.rb) (via the ruby_parser)
49
+ * maybe see [reek tree dresser](https://github.com/troessner/reek/blob/master/lib/reek/source/tree_dresser.rb) and [reek code parser](https://github.com/troessner/reek/blob/master/lib/reek/core/code_parser.rb) or
50
+ * [ripper-tags](https://github.com/tmm1/ripper-tags)
51
+ * Remove dead code
99
52
  * Determine if CodeIssue is used, else remove it
100
53
  * Remove references to Ruport from the Devver / Caliper code
101
- * Update or move the homepage http://metric-fu.rubyforge.org/
54
+ * Understand and explain how each metric can be used
55
+ * Improve metric_fu code metrics
56
+ * Refactor the hotspots code
102
57
  * Is there any reason not to remove the Manifest.txt?
58
+ * Consider removing need for the core extensions (ActiveSupport)
59
+
60
+ ### Documentation
61
+
62
+ * Get the rdoc (or yard) published
63
+ * Add more inline documentation
103
64
  * See other documentation code for examples to improve ours:
104
65
  * https://github.com/charliesome/better_errors/blob/master/CONTRIBUTING.md
105
66
  * https://github.com/charliesome/better_errors/blob/master/README.md
67
+
68
+ ### Other
69
+
70
+ * Look into issues for the tools metric_fu uses
71
+ * Look into other tools that might work well
72
+ * Update contributing or issue guidlines
73
+ * Suggest commit message guidelines
74
+ * [Update the homepage](https://github.com/metricfu/metricfu.github.com)
75
+
76
+ ## Future Thoughts
77
+
78
+ * Look into how to manage plugins or otherwise load abritrary metrics
79
+ * [Hoe](https://github.com/seattlerb/hoe/blob/master/lib/hoe.rb#L301)
80
+ * CLI [Flog](https://github.com/seattlerb/flog/blob/master/lib/flog_cli.rb) Plugins [Flog](https://github.com/seattlerb/flog/blob/master/lib/flog_cli.rb#L34)
81
+ * Look into adding
82
+ * https://github.com/metricfu/code_statistics [1](https://github.com/cloudability/code_statistics)
83
+ * brakeman https://github.com/metricfu/brakeman
84
+ * laser https://github.com/metricfu/laser
106
85
  * Other intersting libraries to consider:
107
86
  * https://gist.github.com/4562865 for generating Flog on ERB templates by jamesmartin
108
87
  * https://github.com/chad/turbulence churn and complexity (flog)
@@ -112,7 +91,23 @@ lib/reporting/templates/standard/index.html.erb
112
91
  * https://github.com/metricfu/gauntlet
113
92
  * https://github.com/metricfu/repodepot-ruby https://twitter.com/jakescruggs/status/70521977303076865
114
93
  * https://github.com/eladmeidar/rails_indexes
115
- * https://github.com/trptcolin/consistency_fail
94
+ * https://github.com/trptcolin/consistency_fail
116
95
  * https://github.com/thoughtbot/appraisal
117
96
  * https://github.com/jenkinsci/rubymetrics-plugin
118
97
  * https://github.com/holman/hopper
98
+ * https://github.com/eric/metriks
99
+
100
+ ## Useful Links
101
+
102
+ ### Ruby Guides
103
+
104
+ * https://github.com/cwgem/RubyGuide
105
+ * https://github.com/bbatsov/rubocop
106
+ * https://github.com/bbatsov/ruby-style-guide
107
+ * https://github.com/bbatsov/rails-style-guide
108
+ * [Learning resources](http://www.benjaminfleischer.com/learning/ruby/tutorials.html) [Source](https://github.com/bf4/learning/tree/gh-pages)
109
+
110
+ ### Perf tools
111
+
112
+ * https://github.com/tmm1/perftools.rb
113
+ * https://twitter.com/mperham/status/311332913641840641
data/lib/metric_fu.rb CHANGED
@@ -41,6 +41,7 @@ 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' }
44
45
  MetricFu.lib_require { 'configuration' }
45
46
  init_files = Dir.glob(File.join(MetricFu.metrics_dir, '**/init.rb')).reject do |file|
46
47
  if file =~ /rcov/o
@@ -66,6 +67,13 @@ module MetricFu
66
67
  end
67
68
  MetricFu.configuration
68
69
  end
70
+ def self.mri_only_metrics
71
+ if MetricFu.configuration.mri?
72
+ []
73
+ else
74
+ [:cane, :flog, :rails_best_practices]
75
+ end
76
+ end
69
77
  def self.run_rcov
70
78
  load File.join(MetricFu.metrics_dir, 'rcov/init.rb')
71
79
  end
@@ -119,11 +119,11 @@ module MetricFu
119
119
  end
120
120
 
121
121
  def jruby?
122
- @jruby ||= RedCard.check(:jruby)
122
+ @jruby ||= !!RedCard.check(:jruby)
123
123
  end
124
124
 
125
125
  def mri?
126
- @mri ||= RedCard.check(:ruby)
126
+ @mri ||= !!RedCard.check(:ruby)
127
127
  end
128
128
 
129
129
  def platform #:nodoc:
@@ -133,8 +133,13 @@ module MetricFu
133
133
  end
134
134
 
135
135
  def self.ruby_strangely_makes_accessors_private?
136
- RedCard.check('1.9.2') || jruby?
136
+ ruby192? || jruby?
137
137
  end
138
+
139
+ def self.ruby192?
140
+ @ruby192 ||= !!RedCard.check('1.9.2')
141
+ end
142
+
138
143
  protected unless ruby_strangely_makes_accessors_private?
139
144
 
140
145
  def add_promiscuous_instance_variable(name,value)
@@ -0,0 +1,2 @@
1
+ MetricFu.lib_require { 'core_ext/object' }
2
+ MetricFu.lib_require { 'core_ext/string' }
@@ -0,0 +1,214 @@
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