deathsyn-seedling 0.0.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 (43) hide show
  1. data/AUTHORS +3 -0
  2. data/CHANGELOG +72 -0
  3. data/MANIFEST +42 -0
  4. data/README +28 -0
  5. data/Rakefile +68 -0
  6. data/bin/seedling +15 -0
  7. data/lib/seedling.rb +5 -0
  8. data/lib/seedling/bin.rb +196 -0
  9. data/lib/seedling/extensions/inflector.rb +283 -0
  10. data/lib/seedling/project_creator.rb +147 -0
  11. data/lib/seedling/version.rb +3 -0
  12. data/lib/templates/core/Rakefile.seed +68 -0
  13. data/lib/templates/core/lib/library.rb.seed +5 -0
  14. data/lib/templates/core/lib/library/version.rb.seed +3 -0
  15. data/lib/templates/core/spec/helper.rb.seed +28 -0
  16. data/lib/templates/core/tasks/authors.rake +30 -0
  17. data/lib/templates/core/tasks/bacon.rake +70 -0
  18. data/lib/templates/core/tasks/changelog.rake +18 -0
  19. data/lib/templates/core/tasks/copyright.rake +21 -0
  20. data/lib/templates/core/tasks/gem.rake +23 -0
  21. data/lib/templates/core/tasks/gem_installer.rake +76 -0
  22. data/lib/templates/core/tasks/install_dependencies.rake +6 -0
  23. data/lib/templates/core/tasks/manifest.rake +4 -0
  24. data/lib/templates/core/tasks/rcov.rake +23 -0
  25. data/lib/templates/core/tasks/release.rake +52 -0
  26. data/lib/templates/core/tasks/reversion.rake +8 -0
  27. data/lib/templates/core/tasks/setup.rake.seed +15 -0
  28. data/seedling.gemspec +38 -0
  29. data/spec/helper.rb +28 -0
  30. data/tasks/authors.rake +30 -0
  31. data/tasks/bacon.rake +70 -0
  32. data/tasks/changelog.rake +18 -0
  33. data/tasks/copyright.rake +21 -0
  34. data/tasks/gem.rake +23 -0
  35. data/tasks/gem_installer.rake +76 -0
  36. data/tasks/install_dependencies.rake +6 -0
  37. data/tasks/manifest.rake +4 -0
  38. data/tasks/rcov.rake +23 -0
  39. data/tasks/release.rake +52 -0
  40. data/tasks/reversion.rake +8 -0
  41. data/tasks/setup.rake +15 -0
  42. data/tasks/yard.rake +4 -0
  43. metadata +101 -0
data/AUTHORS ADDED
@@ -0,0 +1,3 @@
1
+ Following persons have contributed to seedling.
2
+ (Sorted by number of submitted patches, then alphabetically)
3
+
data/CHANGELOG ADDED
@@ -0,0 +1,72 @@
1
+ [27deb65 | Thu May 14 15:30:14 UTC 2009] Kevin Berry <kevin@opensourcealchemist.com>
2
+
3
+ * Version 0.0.1
4
+
5
+ [4017044 | Thu May 14 14:50:26 UTC 2009] Kevin Berry <kevinberry@nrs.us>
6
+
7
+ * Remove old library dir, and correct erb output for library.rb.seed
8
+
9
+ [04d16e0 | Thu May 14 14:45:02 UTC 2009] Kevin Berry <kevinberry@nrs.us>
10
+
11
+ * Get the library module and children in the right place. TODO: Remove library directory from plant.
12
+
13
+ [25e0453 | Thu May 14 14:09:29 UTC 2009] Kevin Berry <kevinberry@nrs.us>
14
+
15
+ * Fix erb calls (Railsism doesn't work!)
16
+
17
+ [f8eeba5 | Wed May 13 18:55:39 UTC 2009] Kevin Berry <kevinberry@nrs.us>
18
+
19
+ * Add params for doc_generator, test_suite, correct email parameter.
20
+
21
+ [ead7fbf | Wed May 13 18:22:26 UTC 2009] Kevin Berry <kevinberry@nrs.us>
22
+
23
+ * Add rubyforge_project commandline option.
24
+
25
+ [fb7663b | Wed May 13 17:34:18 UTC 2009] Kevin Berry <kevinberry@nrs.us>
26
+
27
+ * Fix a couple of templates, and add more planting options.
28
+
29
+ [eb1d0d0 | Wed May 13 15:40:01 UTC 2009] Kevin Berry <kevinberry@nrs.us>
30
+
31
+ * Convert setup into a seed (erb) so that bacon/rspec and rdoc/yard choices can be made.
32
+
33
+ [771f9d3 | Tue May 12 23:12:42 UTC 2009] Kevin Berry <KevinBerry@nrs.us>
34
+
35
+ * rest of erb to seed conversion.
36
+
37
+ [72aa1b4 | Tue May 12 23:06:40 UTC 2009] Kevin Berry <KevinBerry@nrs.us>
38
+
39
+ * Adjust library.rb.erb to library.rb.seed, and adjust template data to match new standards.
40
+
41
+ [2f71f19 | Tue May 12 22:50:37 UTC 2009] TJ Vanderpoel <bougy.man@gmail.com>
42
+
43
+ * changed extension for templates to be parsed with erb to .seed to remove conflicts with actual .erb files. added most options to Rakefile.seed. TODO: finish out Rakefile.seed and move on to more .seed files where dynamic values are needed
44
+
45
+ [aff3223 | Mon May 11 19:41:52 UTC 2009] TJ Vanderpoel <bougyman@zero.(none)>
46
+
47
+ * added bin/seedling and began forming bin.rb to seedling usage
48
+
49
+ [6765184 | Sun May 10 22:32:16 UTC 2009] Kevin Berry <Kevin Berry>
50
+
51
+ * Modify Rakefile.erb to get some project info, need to get ProjectCreator to feed the right info.
52
+
53
+ [5917d53 | Sun May 10 03:42:26 UTC 2009] Kevin Berry <Kevin Berry>
54
+
55
+ * Make plant work.
56
+
57
+ [f02eaf1 | Sun May 10 03:06:22 UTC 2009] Kevin Berry <Kevin Berry>
58
+
59
+ * Bring in initial generator and ProjectCreator kit (stripped down bougyman's autumn library.)
60
+
61
+ [22fb2a5 | Fri May 08 03:19:21 UTC 2009] TJ Vanderpoel <bougy.man@gmail.com>
62
+
63
+ * changed env variable for versioning to VERSION
64
+
65
+ [6329710 | Fri May 08 03:07:50 UTC 2009] TJ Vanderpoel <bougy.man@gmail.com>
66
+
67
+ * added yard task and a README, plus a .gitignore
68
+
69
+ [41fd3c5 | Thu May 07 19:21:22 UTC 2009] TJ Vanderpoel <bougy.man@gmail.com>
70
+
71
+ * new tree to begin seedling work
72
+
data/MANIFEST ADDED
@@ -0,0 +1,42 @@
1
+ AUTHORS
2
+ CHANGELOG
3
+ MANIFEST
4
+ README
5
+ Rakefile
6
+ bin/seedling
7
+ lib/seedling.rb
8
+ lib/seedling/bin.rb
9
+ lib/seedling/extensions/inflector.rb
10
+ lib/seedling/project_creator.rb
11
+ lib/seedling/version.rb
12
+ lib/templates/core/Rakefile.seed
13
+ lib/templates/core/lib/library.rb.seed
14
+ lib/templates/core/lib/library/version.rb.seed
15
+ lib/templates/core/spec/helper.rb.seed
16
+ lib/templates/core/tasks/authors.rake
17
+ lib/templates/core/tasks/bacon.rake
18
+ lib/templates/core/tasks/changelog.rake
19
+ lib/templates/core/tasks/copyright.rake
20
+ lib/templates/core/tasks/gem.rake
21
+ lib/templates/core/tasks/gem_installer.rake
22
+ lib/templates/core/tasks/install_dependencies.rake
23
+ lib/templates/core/tasks/manifest.rake
24
+ lib/templates/core/tasks/rcov.rake
25
+ lib/templates/core/tasks/release.rake
26
+ lib/templates/core/tasks/reversion.rake
27
+ lib/templates/core/tasks/setup.rake.seed
28
+ seedling.gemspec
29
+ spec/helper.rb
30
+ tasks/authors.rake
31
+ tasks/bacon.rake
32
+ tasks/changelog.rake
33
+ tasks/copyright.rake
34
+ tasks/gem.rake
35
+ tasks/gem_installer.rake
36
+ tasks/install_dependencies.rake
37
+ tasks/manifest.rake
38
+ tasks/rcov.rake
39
+ tasks/release.rake
40
+ tasks/reversion.rake
41
+ tasks/setup.rake
42
+ tasks/yard.rake
data/README ADDED
@@ -0,0 +1,28 @@
1
+ ==Seedling
2
+ ----------
3
+
4
+ Seedling is a tool for creating ruby projects. It aims to add
5
+ tasks (via a Rakefile and some tasks/*.rake) which allow for quick
6
+ and easy publishing of your new ruby library, via github and or rubyforge.
7
+ It automates some of the monotony such as
8
+
9
+ * Re-Versioning
10
+ * AUTHORS updating
11
+ * .gemspec updates
12
+ * building a gem, tarball, zip, source export tree
13
+ * Test (spec) running, via a bacon task and bacon spec helper (we love bacon)
14
+ * Documentation via yard
15
+ * CHANGELOG updating
16
+
17
+ == Download
18
+
19
+ using git
20
+ git clone git://rubyists.com/gits/seedling
21
+
22
+ ==Usage
23
+ -------
24
+
25
+ We're getting there. Will be
26
+ seedling plant /path/to/tree
27
+
28
+ Copyright(c) 2009 The Rubyists
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ begin; require 'rubygems'; rescue LoadError; end
2
+
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/gempackagetask'
6
+ require 'time'
7
+ require 'date'
8
+ require "lib/seedling"
9
+
10
+ PROJECT_SPECS = FileList[
11
+ 'spec/**/*.rb'
12
+ ]
13
+
14
+ PROJECT_MODULE = 'Seedling'
15
+ PROJECT_README = 'README'
16
+ #PROJECT_RUBYFORGE_GROUP_ID = 3034
17
+ PROJECT_COPYRIGHT = [
18
+ "# Copyright (c) #{Time.now.year} The Rubyists rubyists@rubyists.com",
19
+ "# Distributed under the terms of the MIT license."
20
+ ]
21
+
22
+ # To release the monthly version do:
23
+ # $ PROJECT_VERSION=2009.03 rake release
24
+ IGNORE_FILES = [/\.gitignore/]
25
+
26
+ GEMSPEC = Gem::Specification.new{|s|
27
+ s.name = 'seedling'
28
+ s.author = "Kevin Berry"
29
+ s.summary = "A lightweight tool to create new ruby library trees, with helpers to make gemming and maintaining a breeze."
30
+ s.description = s.summary
31
+ s.email = 'deathsyn@gmail.com'
32
+ s.homepage = 'http://github.com/deathsyn/seedling'
33
+ s.platform = Gem::Platform::RUBY
34
+ s.version = (ENV['VERSION'] || (begin;Object.const_get(PROJECT_MODULE)::VERSION;rescue;Date.today.strftime("%Y.%m.%d");end))
35
+ s.files = `git ls-files`.split("\n").sort.reject { |f| IGNORE_FILES.detect { |exp| f.match(exp) } }
36
+ s.has_rdoc = true
37
+ s.require_path = 'lib'
38
+ s.bindir = "bin"
39
+ s.executables = ["seedling"]
40
+ s.rubyforge_project = "seedling"
41
+
42
+ s.post_install_message = <<MESSAGE.strip
43
+ ============================================================
44
+
45
+ Thank you for installing Seedling!
46
+ Begin by planting your ruby project with
47
+ # seedling plant /path/to/new/project
48
+
49
+ ============================================================
50
+ MESSAGE
51
+ }
52
+
53
+ Dir['tasks/*.rake'].each{|f| import(f) }
54
+
55
+ task :default => [:bacon]
56
+
57
+ CLEAN.include %w[
58
+ **/.*.sw?
59
+ *.gem
60
+ .config
61
+ **/*~
62
+ **/{data.db,cache.yaml}
63
+ *.yaml
64
+ pkg
65
+ rdoc
66
+ ydoc
67
+ *coverage*
68
+ ]
data/bin/seedling ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ require "pathname"
4
+ local_seedling = Pathname.new(__FILE__).expand_path.dirname.join("..", "lib", "seedling.rb")
5
+ if local_seedling.file?
6
+ require local_seedling
7
+ else
8
+ require "seedling"
9
+ end
10
+ require "seedling/bin"
11
+ rescue LoadError
12
+ require "rubygems"
13
+ require "seedling/bin"
14
+ end
15
+ Seedling::Bin::Cmd.run(ARGV)
data/lib/seedling.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "pathname"
2
+ $LOAD_PATH.unshift(Pathname.new(__FILE__).dirname.expand_path.to_s)
3
+ module Seedling
4
+ end
5
+ require "seedling/version"
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require "seedling/extensions/inflector"
4
+ ### This module offers the functionality to create.
5
+ module Seedling
6
+ module Bin
7
+ class Cmd # This class contains the command methods {{{
8
+ #include Helpers
9
+ attr_accessor :command
10
+
11
+ def initialize(args = nil)
12
+ args ||= ARGV
13
+ raise "arguments must be an array!" unless args.respond_to?(:detect)
14
+ @ourargs = args.dup
15
+ @command = args.detect { |arg| arg.match(/^(?:--?)?(?:plant|create|h(?:elp)?|v(?:ersion)?|console)/) }
16
+ if command.nil?
17
+ @command = ""
18
+ else
19
+ args.delete(@command)
20
+ end
21
+ ARGV.replace(args)
22
+ end
23
+
24
+ # {{{ #run is called when we're interactive ($0 == __FILE__)
25
+ def self.run(args = nil)
26
+ cmd = new(args)
27
+ case cmd.command
28
+ when /^(?:--?)?(?:plant|create)$/
29
+ require "seedling/project_creator"
30
+ cmd.plant(cmd.command)
31
+ when /^(?:--?)?console$/
32
+ require "irb"
33
+ require "irb/completion"
34
+ cmd.include_seedling
35
+ IRB.start
36
+ puts "Seedling session has ended."
37
+ when /^(?:--?)?h(elp)?$/
38
+ puts cmd.usage
39
+ when /^(?:--?)?v(ersion)?$/
40
+ cmd.include_seedling
41
+ puts Seedling::VERSION
42
+ exit
43
+ when /^$/
44
+ puts "Must supply a valid command"
45
+ puts cmd.usage
46
+ exit 1
47
+ else
48
+ puts "#{command} not implemented"
49
+ puts cmd.usage
50
+ exit 1
51
+ end
52
+ end # }}}
53
+
54
+ def include_seedling # {{{
55
+ begin
56
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '/../lib')
57
+ require 'seedling'
58
+ rescue LoadError
59
+ $LOAD_PATH.shift
60
+
61
+ begin
62
+ require 'rubygems'
63
+ rescue LoadError
64
+ end
65
+ require 'seedling'
66
+ end
67
+ end # }}}
68
+
69
+ def usage # {{{
70
+ txt = [
71
+ "\n Usage:",
72
+ "seedling <plant|create|console> PROJECT [options]\n",
73
+ "Commands:\n",
74
+ " plant - Creates a new prototype Seedling application in a directory named PROJECT in",
75
+ " the current directory. seedling create foo would make ./foo containing a",
76
+ " seedling prototype.\n",
77
+ " create - Synonymous with plant.\n",
78
+ " console - Starts an irb console with seedling (and irb completion) loaded.",
79
+ " ARGV is passed on to IRB.\n\n"
80
+ ].join("\n\t")
81
+
82
+ txt << "* All commands take PROJECT as the directory the seedling lives in.\n\n"
83
+ txt << plant_options.to_s << "\n"
84
+ #if is_windows?
85
+ #txt << %x{ruby #{rackup_path} --help}.split("\n").reject { |line| line.match(/^Usage:/) }.join("\n\t")
86
+ #else
87
+ #txt << %x{#{rackup_path} --help}.split("\n").reject { |line| line.match(/^Usage:/) }.join("\n\t")
88
+ #end
89
+ end # }}}
90
+
91
+ def al_root
92
+ require "pathname"
93
+ dir = nil
94
+ if ARGV.size == 1
95
+ dir = Pathname.new(ARGV.shift)
96
+ elsif ARGV.size > 1
97
+ $stderr.puts "Unknown options given #{ARGV.join(" ")}"
98
+ puts usage
99
+ exit 1
100
+ end
101
+ if dir.nil? or not dir.directory?
102
+ dir = Pathname.new(ENV["PWD"]).expand_path
103
+ $stderr.puts "Path to seedling tree not given or invalid, using #{dir}"
104
+ end
105
+ Object.const_set("AL_ROOT", dir.expand_path.to_s)
106
+ Dir.chdir(AL_ROOT)
107
+ end
108
+
109
+ ### Methods for commands {{{
110
+ def plant_options(opts = {})
111
+ @plant_opts ||= OptionParser.new do |o|
112
+ o.banner = "Planting Options"
113
+ o.on("-nSUMMARY", "--summary SUMMARY", "Short description of this project") { |yn| opts[:summary] = yn }
114
+ o.on("-dDESCRIPTION", "--description DESCRIPTION", "Longer description (Default: summary)") { |des| opts[:description] = des }
115
+ o.on("-lLIBNAME", "--libname LIBNAME", "Library name (Default: path specifcation)") { |libname| opts[:lib_name] = libname }
116
+ o.on("-gDOCGENERATOR", "--doc-generator DOCGENERATOR", "Preferred documentation generator (Default: yard)") { |docgenerator| opts[:doc_generator] = docgenerator }
117
+ o.on("-tTESTSUITE", "--test-suite TESTSUITE", "Preferred test suite (Default: bacon)") { |testsuite| opts[:test_suite] = testsuite }
118
+ o.on("-vVER", "--version VER", "Initial version number (Default: 0.0.1)") { |ver| opts[:version] = ver }
119
+ o.on("-rRUBYFORGE", "--rubyforge RUBYFORGE", "Rubyforge project name") { |rubyforge| opts[:rubyforge_project] = rubyforge }
120
+
121
+ o.separator ""
122
+ o.separator "Author Options"
123
+ o.on("-sAUTHOR", "--summary AUTHOR", "Author's Name") { |yn| opts[:author_name] = yn }
124
+ o.on("-eEMAIL", "--email EMAIL", "Author's Email") { |yn| opts[:author_email] = yn }
125
+ o.on("-uURL", "--url URL", "Project URL/homepage") { |url| opts[:project_url] = url }
126
+
127
+ o.separator ""
128
+ o.separator "Directory Creation Options"
129
+ o.on("-f", "--force", "Force creation if dir already exists") { |yn| opts[:force] = true }
130
+ o.on("-a", "--amend", "Update a tree") { |yn| opts[:amend] = true }
131
+ end
132
+ end
133
+
134
+ def plant(command) # {{{
135
+ plant_options(o = {}).parse!(ARGV)
136
+ unless ARGV.size == 1
137
+ $stderr.puts "Invalid options given: #{ARGV.join(" ")}"
138
+ exit 1
139
+ end
140
+ project_root = ARGV.shift
141
+ if project_root.nil?
142
+ $stderr.puts "Must supply a valid directory to install your project, you gave none."
143
+ puts usage
144
+ exit 1
145
+ end
146
+ o[:lib_name] ||= Pathname.new(project_root).basename.to_s.classify
147
+ o[:lib_name_u] ||= o[:lib_name].underscore
148
+ opts = plant_defaults(o)
149
+ # need to titleize this
150
+ include_seedling
151
+ Seedling::ProjectCreator.new(project_root, opts).create
152
+ end # }}}
153
+
154
+ private
155
+
156
+ # Sets all of our default settings to make a sane rakefile, pulling from everywhere that makes sense
157
+ def plant_defaults(o = {:lib_name => "Seedling"})
158
+ # this shouldn't happen, but if so let's be descriptive
159
+ raise "plant_defaults requires a :lib_name in the calling argument" unless o[:lib_name]
160
+ o[:lib_name_u] = o[:lib_name].underscore
161
+ [:author_name, :author_email].each do |opt|
162
+ o[opt] = self.send(opt)
163
+ end
164
+ o[:summary] ||= "The #{o[:lib_name].classify.titleize} library, by #{o[:author_name]}"
165
+ o[:description] ||= o[:summary]
166
+ o[:version] ||= "0.0.1"
167
+ o[:test_suite] ||= "bacon"
168
+ o[:doc_generator] ||= "yard"
169
+ o
170
+ end
171
+
172
+ def author_email
173
+ gitted = %x{git config --global --get user.email}
174
+ return gitted.to_s.strip if gitted.to_s.match(/\w/)
175
+ return ENV["EMAIL"] if ENV["EMAIL"]
176
+ return [ENV["LOGUSER"], ENV["HOSTNAME"]].join("@") if ENV["LOGUSER"] and ENV["HOSTNAME"]
177
+ raise "Cannot find author email, please use --email \"you@your.domain.com\" or set the EMAIL environment variable"
178
+ end
179
+
180
+ def author_name
181
+ gitted = %x{git config --global --get user.name}
182
+ return gitted.to_s.strip if gitted.to_s.match(/\w/)
183
+ return ENV["NAME"] if ENV["NAME"]
184
+ return ENV["LOGUSER"] if ENV["LOGUSER"]
185
+ return Pathname.new(ENV["HOME"]).expand_path.basename.to_s if ENV["HOME"]
186
+ raise "Cannot find author name, please use --author \"Your Name\" or set the NAME or LOGUSER environment variables"
187
+ end
188
+
189
+ ### End of command methods }}}
190
+ end # }}}
191
+ end
192
+ end
193
+
194
+ if $0 == __FILE__
195
+ Seedling::Bin::Cmd.run(ARGV)
196
+ end
@@ -0,0 +1,283 @@
1
+ # Add inflection methods to String, which allows the easy transformation of
2
+ # words from singular to plural,class names to table names, modularized class
3
+ # names to ones without, and class names to foreign keys.
4
+
5
+ class String
6
+ # This module acts as a singleton returned/yielded by String.inflections,
7
+ # which is used to override or specify additional inflection rules. Examples:
8
+ #
9
+ # String.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
+ module Inflections
22
+ @plurals, @singulars, @uncountables = [], [], []
23
+
24
+ class << self
25
+ attr_reader :plurals, :singulars, :uncountables
26
+ end
27
+
28
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
29
+ # the options are: :plurals, :singulars, :uncountables
30
+ #
31
+ # Examples:
32
+ # clear :all
33
+ # clear :plurals
34
+ def self.clear(scope = :all)
35
+ case scope
36
+ when :all
37
+ @plurals, @singulars, @uncountables = [], [], []
38
+ else
39
+ instance_variable_set("@#{scope}", [])
40
+ end
41
+ end
42
+
43
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
44
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
45
+ #
46
+ # Examples:
47
+ # irregular 'octopus', 'octopi'
48
+ # irregular 'person', 'people'
49
+ def self.irregular(singular, plural)
50
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
51
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
52
+ end
53
+
54
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
55
+ # The replacement should always be a string that may include references to the matched data from the rule.
56
+ #
57
+ # Example:
58
+ # plural(/(x|ch|ss|sh)$/i, '\1es')
59
+ def self.plural(rule, replacement)
60
+ @plurals.insert(0, [rule, replacement])
61
+ end
62
+
63
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
64
+ # The replacement should always be a string that may include references to the matched data from the rule.
65
+ #
66
+ # Example:
67
+ # singular(/([^aeiouy]|qu)ies$/i, '\1y')
68
+ def self.singular(rule, replacement)
69
+ @singulars.insert(0, [rule, replacement])
70
+ end
71
+
72
+ # Add uncountable words that shouldn't be attempted inflected.
73
+ #
74
+ # Examples:
75
+ # uncountable "money"
76
+ # uncountable "money", "information"
77
+ # uncountable %w( money information rice )
78
+ def self.uncountable(*words)
79
+ (@uncountables << words).flatten!
80
+ end
81
+
82
+ # Setup the default inflections
83
+ plural(/$/, 's')
84
+ plural(/s$/i, 's')
85
+ plural(/(ax|test)is$/i, '\1es')
86
+ plural(/(octop|vir)us$/i, '\1i')
87
+ plural(/(alias|status)$/i, '\1es')
88
+ plural(/(bu)s$/i, '\1ses')
89
+ plural(/(buffal|tomat)o$/i, '\1oes')
90
+ plural(/([ti])um$/i, '\1a')
91
+ plural(/sis$/i, 'ses')
92
+ plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
93
+ plural(/(hive)$/i, '\1s')
94
+ plural(/([^aeiouy]|qu)y$/i, '\1ies')
95
+ plural(/(x|ch|ss|sh)$/i, '\1es')
96
+ plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
97
+ plural(/([m|l])ouse$/i, '\1ice')
98
+ plural(/^(ox)$/i, '\1en')
99
+ plural(/(quiz)$/i, '\1zes')
100
+
101
+ singular(/s$/i, '')
102
+ singular(/(n)ews$/i, '\1ews')
103
+ singular(/([ti])a$/i, '\1um')
104
+ singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
105
+ singular(/(^analy)ses$/i, '\1sis')
106
+ singular(/([^f])ves$/i, '\1fe')
107
+ singular(/(hive)s$/i, '\1')
108
+ singular(/(tive)s$/i, '\1')
109
+ singular(/([lr])ves$/i, '\1f')
110
+ singular(/([^aeiouy]|qu)ies$/i, '\1y')
111
+ singular(/(s)eries$/i, '\1eries')
112
+ singular(/(m)ovies$/i, '\1ovie')
113
+ singular(/(x|ch|ss|sh)es$/i, '\1')
114
+ singular(/([m|l])ice$/i, '\1ouse')
115
+ singular(/(bus)es$/i, '\1')
116
+ singular(/(o)es$/i, '\1')
117
+ singular(/(shoe)s$/i, '\1')
118
+ singular(/(cris|ax|test)es$/i, '\1is')
119
+ singular(/(octop|vir)i$/i, '\1us')
120
+ singular(/(alias|status)es$/i, '\1')
121
+ singular(/^(ox)en/i, '\1')
122
+ singular(/(vert|ind)ices$/i, '\1ex')
123
+ singular(/(matr)ices$/i, '\1ix')
124
+ singular(/(quiz)zes$/i, '\1')
125
+
126
+ irregular('person', 'people')
127
+ irregular('man', 'men')
128
+ irregular('child', 'children')
129
+ irregular('sex', 'sexes')
130
+ irregular('move', 'moves')
131
+
132
+ uncountable(%w(equipment information rice money species series fish sheep))
133
+ end
134
+
135
+ # Yield the Inflections module if a block is given, and return
136
+ # the Inflections module.
137
+ def self.inflections
138
+ yield Inflections if block_given?
139
+ Inflections
140
+ end
141
+
142
+ # By default, camelize converts the string to UpperCamelCase. If the argument to camelize
143
+ # is set to :lower then camelize produces lowerCamelCase.
144
+ #
145
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
146
+ #
147
+ # Examples
148
+ # "active_record".camelize #=> "ActiveRecord"
149
+ # "active_record".camelize(:lower) #=> "activeRecord"
150
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
151
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
152
+ def camelize(first_letter_in_uppercase = :upper)
153
+ s = gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
154
+ s[0...1] = s[0...1].downcase unless first_letter_in_uppercase == :upper
155
+ s
156
+ end
157
+ alias_method :camelcase, :camelize
158
+
159
+ # Singularizes and camelizes the string. Also strips out all characters preceding
160
+ # and including a period (".").
161
+ #
162
+ # Examples
163
+ # "egg_and_hams".classify #=> "EggAndHam"
164
+ # "post".classify #=> "Post"
165
+ # "schema.post".classify #=> "Post"
166
+ def classify
167
+ sub(/.*\./, '').singularize.camelize
168
+ end
169
+
170
+ # Constantize tries to find a declared constant with the name specified
171
+ # in the string. It raises a NameError when the name is not in CamelCase
172
+ # or is not initialized.
173
+ #
174
+ # Examples
175
+ # "Module".constantize #=> Module
176
+ # "Class".constantize #=> Class
177
+ def constantize
178
+ raise(NameError, "#{inspect} is not a valid constant name!") unless m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(self)
179
+ Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
180
+ end
181
+
182
+ # Replaces underscores with dashes in the string.
183
+ #
184
+ # Example
185
+ # "puni_puni".dasherize #=> "puni-puni"
186
+ def dasherize
187
+ gsub(/_/, '-')
188
+ end
189
+
190
+ # Removes the module part from the expression in the string
191
+ #
192
+ # Examples
193
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
194
+ # "Inflections".demodulize #=> "Inflections"
195
+ def demodulize
196
+ gsub(/^.*::/, '')
197
+ end
198
+
199
+ # Creates a foreign key name from a class name.
200
+ # +use_underscore+ sets whether the method should put '_' between the name and 'id'.
201
+ #
202
+ # Examples
203
+ # "Message".foreign_key #=> "message_id"
204
+ # "Message".foreign_key(false) #=> "messageid"
205
+ # "Admin::Post".foreign_key #=> "post_id"
206
+ def foreign_key(use_underscore = true)
207
+ "#{demodulize.underscore}#{'_' if use_underscore}id"
208
+ end
209
+
210
+ # Capitalizes the first word and turns underscores into spaces and strips _id.
211
+ # Like titleize, this is meant for creating pretty output.
212
+ #
213
+ # Examples
214
+ # "employee_salary" #=> "Employee salary"
215
+ # "author_id" #=> "Author"
216
+ def humanize
217
+ gsub(/_id$/, "").gsub(/_/, " ").capitalize
218
+ end
219
+
220
+ # Returns the plural form of the word in the string.
221
+ #
222
+ # Examples
223
+ # "post".pluralize #=> "posts"
224
+ # "octopus".pluralize #=> "octopi"
225
+ # "sheep".pluralize #=> "sheep"
226
+ # "words".pluralize #=> "words"
227
+ # "the blue mailman".pluralize #=> "the blue mailmen"
228
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
229
+ def pluralize
230
+ result = dup
231
+ Inflections.plurals.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(downcase)
232
+ result
233
+ end
234
+
235
+ # The reverse of pluralize, returns the singular form of a word in a string.
236
+ #
237
+ # Examples
238
+ # "posts".singularize #=> "post"
239
+ # "octopi".singularize #=> "octopus"
240
+ # "sheep".singluarize #=> "sheep"
241
+ # "word".singluarize #=> "word"
242
+ # "the blue mailmen".singularize #=> "the blue mailman"
243
+ # "CamelOctopi".singularize #=> "CamelOctopus"
244
+ def singularize
245
+ result = dup
246
+ Inflections.singulars.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(downcase)
247
+ result
248
+ end
249
+
250
+ # Underscores and pluralizes the string.
251
+ #
252
+ # Examples
253
+ # "RawScaledScorer".tableize #=> "raw_scaled_scorers"
254
+ # "egg_and_ham".tableize #=> "egg_and_hams"
255
+ # "fancyCategory".tableize #=> "fancy_categories"
256
+ def tableize
257
+ underscore.pluralize
258
+ end
259
+
260
+ # Capitalizes all the words and replaces some characters in the string to create
261
+ # a nicer looking title. Titleize is meant for creating pretty output.
262
+ #
263
+ # titleize is also aliased as as titlecase
264
+ #
265
+ # Examples
266
+ # "man from the boondocks".titleize #=> "Man From The Boondocks"
267
+ # "x-men: the last stand".titleize #=> "X Men: The Last Stand"
268
+ def titleize
269
+ underscore.humanize.gsub(/\b([a-z])/){|x| x[-1..-1].upcase}
270
+ end
271
+ alias_method :titlecase, :titleize
272
+
273
+ # The reverse of camelize. Makes an underscored form from the expression in the string.
274
+ # Also changes '::' to '/' to convert namespaces to paths.
275
+ #
276
+ # Examples
277
+ # "ActiveRecord".underscore #=> "active_record"
278
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
279
+ def underscore
280
+ gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
281
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
282
+ end
283
+ end