dyoder-waves 0.7.7 → 0.8.0

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 (171) hide show
  1. data/bin/waves +19 -56
  2. data/doc/HISTORY +1 -0
  3. data/doc/LICENSE +22 -0
  4. data/doc/README +1 -0
  5. data/doc/VERSION +1 -0
  6. data/lib/caches/file.rb +48 -0
  7. data/lib/caches/memcached.rb +40 -0
  8. data/lib/caches/simple.rb +25 -0
  9. data/lib/caches/synchronized.rb +25 -0
  10. data/lib/commands/console.rb +35 -0
  11. data/lib/commands/generate.rb +52 -0
  12. data/lib/commands/help.rb +5 -0
  13. data/lib/commands/{waves-server.rb → server.rb} +16 -3
  14. data/lib/dispatchers/base.rb +21 -20
  15. data/lib/dispatchers/default.rb +11 -63
  16. data/lib/ext/float.rb +13 -0
  17. data/lib/{utilities → ext}/hash.rb +2 -2
  18. data/lib/{utilities → ext}/integer.rb +5 -2
  19. data/lib/ext/kernel.rb +20 -0
  20. data/lib/ext/module.rb +20 -0
  21. data/lib/{utilities → ext}/object.rb +3 -5
  22. data/lib/ext/string.rb +20 -0
  23. data/lib/ext/symbol.rb +11 -0
  24. data/lib/ext/tempfile.rb +5 -0
  25. data/lib/foundations/classic.rb +64 -0
  26. data/lib/foundations/compact.rb +50 -0
  27. data/lib/helpers/basic.rb +11 -0
  28. data/lib/helpers/doc_type.rb +34 -0
  29. data/lib/helpers/extended.rb +21 -0
  30. data/lib/helpers/form.rb +3 -1
  31. data/lib/helpers/formatting.rb +3 -3
  32. data/lib/helpers/layouts.rb +37 -0
  33. data/lib/helpers/model.rb +8 -4
  34. data/lib/helpers/view.rb +2 -4
  35. data/lib/layers/inflect/english.rb +67 -0
  36. data/lib/layers/mvc.rb +18 -18
  37. data/lib/layers/mvc/controllers.rb +41 -0
  38. data/lib/layers/mvc/extensions.rb +52 -0
  39. data/lib/layers/orm/{active_record.rb → providers/active_record.rb} +12 -18
  40. data/lib/layers/orm/{active_record → providers/active_record}/migrations/empty.rb.erb +0 -0
  41. data/lib/layers/orm/{active_record → providers/active_record}/tasks/generate.rb +1 -1
  42. data/lib/layers/orm/{active_record → providers/active_record}/tasks/schema.rb +1 -1
  43. data/lib/layers/orm/{data_mapper.rb → providers/data_mapper.rb} +3 -4
  44. data/lib/layers/orm/providers/filebase.rb +25 -0
  45. data/lib/layers/orm/{sequel.rb → providers/sequel.rb} +16 -12
  46. data/lib/layers/orm/{sequel → providers/sequel}/migrations/empty.rb.erb +0 -0
  47. data/lib/layers/orm/{sequel → providers/sequel}/tasks/generate.rb +5 -3
  48. data/lib/layers/orm/{sequel → providers/sequel}/tasks/schema.rb +2 -2
  49. data/lib/{renderers → layers/renderers}/erubis.rb +10 -13
  50. data/lib/layers/renderers/haml.rb +47 -0
  51. data/lib/layers/renderers/markaby.rb +29 -0
  52. data/lib/matchers/accept.rb +21 -0
  53. data/lib/matchers/base.rb +30 -0
  54. data/lib/matchers/content_type.rb +17 -0
  55. data/lib/matchers/path.rb +67 -0
  56. data/lib/matchers/query.rb +21 -0
  57. data/lib/matchers/request.rb +27 -0
  58. data/lib/matchers/resource.rb +19 -0
  59. data/lib/matchers/traits.rb +19 -0
  60. data/lib/matchers/uri.rb +20 -0
  61. data/lib/renderers/mixin.rb +15 -29
  62. data/lib/resources/mixin.rb +132 -0
  63. data/lib/resources/paths.rb +34 -0
  64. data/lib/runtime/configuration.rb +55 -135
  65. data/lib/runtime/console.rb +4 -1
  66. data/lib/runtime/logger.rb +24 -48
  67. data/lib/runtime/mime_types.rb +516 -2
  68. data/lib/runtime/mocks.rb +14 -0
  69. data/lib/runtime/monitor.rb +32 -0
  70. data/lib/runtime/request.rb +107 -39
  71. data/lib/runtime/response.rb +5 -2
  72. data/lib/runtime/response_mixin.rb +43 -22
  73. data/lib/runtime/runtime.rb +67 -0
  74. data/lib/runtime/server.rb +14 -101
  75. data/lib/runtime/session.rb +4 -43
  76. data/lib/runtime/worker.rb +86 -0
  77. data/lib/servers/base.rb +42 -0
  78. data/lib/servers/mongrel.rb +13 -0
  79. data/lib/servers/webrick.rb +13 -0
  80. data/lib/tasks/gem.rb +1 -0
  81. data/lib/tasks/generate.rb +67 -62
  82. data/lib/views/errors.rb +49 -0
  83. data/lib/views/mixin.rb +34 -82
  84. data/lib/waves.rb +36 -55
  85. data/samples/blog/Rakefile +25 -0
  86. data/samples/blog/configurations/default.rb +11 -0
  87. data/samples/blog/configurations/development.rb +29 -0
  88. data/samples/blog/configurations/production.rb +26 -0
  89. data/samples/blog/models/comment.rb +23 -0
  90. data/samples/blog/models/entry.rb +31 -0
  91. data/samples/blog/public/css/site.css +13 -0
  92. data/samples/blog/public/javascript/jquery-1.2.6.min.js +32 -0
  93. data/samples/blog/public/javascript/site.js +13 -0
  94. data/samples/blog/resources/entry.rb +39 -0
  95. data/samples/blog/resources/map.rb +9 -0
  96. data/samples/blog/schema/migrations/001_initial_schema.rb +17 -0
  97. data/samples/blog/schema/migrations/002_add_comments.rb +18 -0
  98. data/samples/blog/schema/migrations/templates/empty.rb.erb +9 -0
  99. data/samples/blog/startup.rb +8 -0
  100. data/samples/blog/templates/comment/add.mab +12 -0
  101. data/samples/blog/templates/comment/list.mab +6 -0
  102. data/samples/blog/templates/entry/edit.mab +14 -0
  103. data/samples/blog/templates/entry/list.mab +16 -0
  104. data/samples/blog/templates/entry/show.mab +18 -0
  105. data/samples/blog/templates/entry/summary.mab +9 -0
  106. data/samples/blog/templates/errors/not_found_404.mab +7 -0
  107. data/{app → samples/blog}/templates/errors/server_error_500.mab +0 -0
  108. data/samples/blog/templates/layouts/default.mab +19 -0
  109. data/samples/blog/templates/waves/status.mab +85 -0
  110. data/templates/classic/Rakefile +130 -0
  111. data/templates/classic/configurations/default.rb.erb +9 -0
  112. data/{app → templates/classic}/configurations/development.rb.erb +3 -7
  113. data/{app → templates/classic}/configurations/production.rb.erb +3 -4
  114. data/templates/classic/resources/map.rb.erb +8 -0
  115. data/templates/classic/startup.rb.erb +11 -0
  116. data/{app → templates/classic}/templates/errors/not_found_404.mab +0 -0
  117. data/templates/classic/templates/errors/server_error_500.mab +2 -0
  118. data/{app → templates/classic}/templates/layouts/default.mab +0 -0
  119. data/templates/compact/startup.rb.erb +11 -0
  120. metadata +967 -144
  121. data/app/Rakefile +0 -14
  122. data/app/bin/waves-console +0 -4
  123. data/app/bin/waves-server +0 -4
  124. data/app/configurations/mapping.rb.erb +0 -13
  125. data/app/controllers/.gitignore +0 -0
  126. data/app/doc/.gitignore +0 -0
  127. data/app/helpers/.gitignore +0 -0
  128. data/app/lib/application.rb.erb +0 -5
  129. data/app/lib/tasks/.gitignore +0 -0
  130. data/app/log/.gitignore +0 -0
  131. data/app/models/.gitignore +0 -0
  132. data/app/public/css/.gitignore +0 -0
  133. data/app/public/flash/.gitignore +0 -0
  134. data/app/public/images/.gitignore +0 -0
  135. data/app/public/javascript/.gitignore +0 -0
  136. data/app/schema/migrations/.gitignore +0 -0
  137. data/app/startup.rb +0 -5
  138. data/app/tmp/sessions/.gitignore +0 -0
  139. data/app/views/.gitignore +0 -0
  140. data/bin/waves-console +0 -4
  141. data/bin/waves-server +0 -4
  142. data/lib/commands/waves-console.rb +0 -21
  143. data/lib/controllers/base.rb +0 -11
  144. data/lib/controllers/mixin.rb +0 -165
  145. data/lib/foundations/default.rb +0 -26
  146. data/lib/foundations/simple.rb +0 -30
  147. data/lib/helpers/asset_helper.rb +0 -67
  148. data/lib/helpers/common.rb +0 -66
  149. data/lib/helpers/default.rb +0 -13
  150. data/lib/helpers/number_helper.rb +0 -25
  151. data/lib/helpers/tag_helper.rb +0 -58
  152. data/lib/helpers/url_helper.rb +0 -77
  153. data/lib/layers/default_errors.rb +0 -26
  154. data/lib/layers/orm/filebase.rb +0 -22
  155. data/lib/layers/simple.rb +0 -35
  156. data/lib/layers/simple_errors.rb +0 -23
  157. data/lib/mapping/mapping.rb +0 -289
  158. data/lib/mapping/pretty_urls.rb +0 -96
  159. data/lib/renderers/markaby.rb +0 -33
  160. data/lib/runtime/application.rb +0 -69
  161. data/lib/runtime/blackboard.rb +0 -57
  162. data/lib/runtime/debugger.rb +0 -9
  163. data/lib/runtime/response_proxy.rb +0 -30
  164. data/lib/tasks/cluster.rb +0 -26
  165. data/lib/utilities/inflect.rb +0 -110
  166. data/lib/utilities/inflect/english.rb +0 -84
  167. data/lib/utilities/module.rb +0 -21
  168. data/lib/utilities/proc.rb +0 -16
  169. data/lib/utilities/string.rb +0 -61
  170. data/lib/utilities/symbol.rb +0 -10
  171. data/lib/views/base.rb +0 -9
@@ -1,96 +0,0 @@
1
- module Waves
2
- module Mapping
3
-
4
- # A set of pre-packed mapping rules for dealing with pretty URLs (that use names instead
5
- # of numbers to identify resources). There are two modules.
6
- # - GetRules, which defines all the GET methods for dealing with named resources
7
- # - RestRules, which defines add, update, and delete rules using a REST style interface
8
- #
9
- module PrettyUrls
10
-
11
- #
12
- # GetRules defines the following URL conventions:
13
- #
14
- # /resources # => get a list of all instances of resource
15
- # /resource/name # => get a specific instance of resource with the given name
16
- # /resource/name/editor # => display an edit page for the given resource
17
- #
18
- module GetRules
19
-
20
- def self.included(target)
21
-
22
- target.module_eval do
23
-
24
- extend Waves::Mapping
25
-
26
- name = '([\w\-\_\.\+\@]+)'; model = '([\w\-]+)'
27
-
28
- # get all resources for the given model
29
- path %r{^/#{model}/?$}, :method => :get do | model |
30
- resource( model.singular ) { controller { all } | view { |data| list( model => data ) } }
31
- end
32
-
33
- # get the given resource for the given model
34
- path %r{^/#{model}/#{name}/?$}, :method => :get do | model, name |
35
- resource( model ) { controller { find( name ) } | view { |data| show( model => data ) } }
36
- end
37
-
38
- # display an editor for the given resource / model
39
- path %r{^/#{model}/#{name}/editor/?$}, :method => :get do | model, name |
40
- resource( model ) { controller { find( name ) } | view { |data| editor( model => data ) } }
41
- end
42
-
43
- end
44
-
45
- end
46
-
47
- end
48
-
49
- #
50
- # RestRules defines the following URL conventions:
51
- #
52
- # POST /resources # => add a new resource
53
- # PUT /resource/name # => update the given resource
54
- # DELETE /resource/name # => delete the given resource
55
- #
56
- module RestRules
57
-
58
- def self.included(target)
59
-
60
- target.module_eval do
61
-
62
- extend Waves::Mapping
63
-
64
- name = '([\w\-\_\.\+\@]+)'; model = '([\w\-]+)'
65
-
66
- # create a new resource for the given model
67
- path %r{^/#{model}/?$}, :method => :post do | model |
68
- resource( model.singular ) do
69
- controller do
70
- instance = create
71
- redirect( "/#{model_name}/#{instance.name}/editor" )
72
- end
73
- end
74
- end
75
-
76
- # update the given resource for the given model
77
- path %r{^/#{model}/#{name}/?$}, :method => :put do | model, name |
78
- resource( model ) { controller { update( name ); redirect( url ) } }
79
- end
80
-
81
- # delete the given resource for the given model
82
- path %r{^/#{model}/#{name}/?$}, :method => :delete do | model, name |
83
- resource( model ) { controller { delete( name ) } }
84
- end
85
-
86
- end
87
-
88
- end
89
-
90
- end
91
-
92
- end
93
-
94
- end
95
-
96
- end
@@ -1,33 +0,0 @@
1
- require 'markaby'
2
-
3
- ::Markaby::Builder.set( :indent, 2 )
4
-
5
- module Waves
6
-
7
- module Renderers
8
-
9
- class Markaby
10
-
11
- include Renderers::Mixin
12
-
13
- extension :mab
14
-
15
- # capture needed here for content fragments, otherwise
16
- # you'll just get the last tag's output ...
17
- # def self.capture( template )
18
- # "capture { #{template} }"
19
- # end
20
-
21
- def self.render( path, assigns )
22
- builder = ::Markaby::Builder.new( assigns )
23
- helper = helper( path )
24
- builder.meta_eval { include( helper ) }
25
- builder.instance_eval( template( path ) )
26
- builder.to_s
27
- end
28
-
29
- end
30
-
31
- end
32
-
33
- end
@@ -1,69 +0,0 @@
1
- # See the README for an overview.
2
- module Waves
3
-
4
- class << self
5
-
6
- # Access the principal Waves application.
7
- attr_reader :application
8
-
9
- # Register a module as a Waves application.
10
- def << ( app )
11
- @application = app if Module === app
12
- end
13
-
14
- def instance ; Waves::Application.instance ; end
15
-
16
- def method_missing(name,*args,&block)
17
- instance.send(name,*args,&block)
18
- end
19
-
20
- end
21
-
22
- # An application in Waves is anything that provides access to the Waves
23
- # runtime and the registered Waves applications. This includes both
24
- # Waves::Server and Waves::Console. Waves::Application is *not* the actual
25
- # application module(s) registered as Waves applications. To access the
26
- # main Waves application, you can use +Waves+.+application+.
27
- class Application
28
-
29
- class << self; attr_accessor :instance; end
30
-
31
- # Accessor for options passed to the application.
32
- attr_reader :options
33
-
34
- # Create a new Waves application instance.
35
- def initialize( options={} )
36
- @options = options
37
- Dir.chdir options[:directory] if options[:directory]
38
- Application.instance = self
39
- Kernel.load( :lib / 'application.rb' ) if Waves.application.nil?
40
- end
41
-
42
- def synchronize( &block )
43
- ( @mutex ||= Mutex.new ).synchronize( &block )
44
- end
45
-
46
- # The 'mode' of the application determines which configuration it will run under.
47
- def mode
48
- @mode ||= @options[:mode]||:development
49
- end
50
-
51
- # Debug is true if debug is set to true in the current configuration.
52
- def debug? ; config.debug ; end
53
-
54
- # Access the current configuration. *Example:* +Waves::Server.config+
55
- def config
56
- Waves.application.configurations[ mode ]
57
- end
58
-
59
- # Access the mappings for the application.
60
- def mapping ; Waves.application.configurations[ :mapping ] ; end
61
-
62
- # Reload the modules specified in the current configuration.
63
- def reload ; config.reloadable.each { |mod| mod.reload } ; end
64
-
65
- # Returns the cache set for the current configuration
66
- def cache ; config.cache ; end
67
- end
68
-
69
- end
@@ -1,57 +0,0 @@
1
- module Waves
2
-
3
- # Encapsulates the blackboard associated with a given request. The scope of the blackboard is
4
- # the same as the request object it gets attached to.
5
- #
6
- # The Waves blackboard is a very simple shared storaged usable during the request processing.
7
- # It is available within:
8
- # - mappings
9
- # - controllers
10
- # - helpers
11
- #
12
- # Adding a value:
13
- # blackboard.value1 = 1
14
- # blackboard[:value2] = 2
15
- #
16
- # Retrieving values
17
- # blackboard.value1
18
- # blackboard[:value2]
19
- #
20
- # see also blackboard_verify.rb
21
-
22
- class Blackboard
23
-
24
- # Create a new blackboard object using the given request.
25
- def initialize( request )
26
- @request = request
27
- @data = {}
28
- end
29
-
30
- # Access a given data element of the blackboard using the given key.
31
- def [](key)
32
- @data[key]
33
- end
34
-
35
- # Set the given data element of the blackboard using the given key and value.
36
- def []=(key,val)
37
- @data[key] = val
38
- end
39
-
40
- def each(&block)
41
- @data.each(&block)
42
- end
43
-
44
- # also allow things like
45
- # blackboard.test1 instead of blackboard[:test1]
46
- # blackboard.test1 = 2 instead of blackboard[:test1] = 2
47
- def method_missing(name,*args)
48
- if (name.to_s =~ /=$/)
49
- self[name.to_s.gsub('=', '')] = args[0]
50
- else
51
- self[name.to_s]
52
- end
53
- end
54
-
55
- end
56
-
57
- end
@@ -1,9 +0,0 @@
1
- module Kernel
2
- unless respond_to?(:debugger)
3
- # Starts a debugging session if ruby-debug has been loaded (call waves-server --debugger to do load it).
4
- def debugger
5
- puts "debugger called"
6
- Waves::Logger.info "\n***** Debugger requested, but was not available: Start server with --debugger to enable *****\n"
7
- end
8
- end
9
- end
@@ -1,30 +0,0 @@
1
- module Waves
2
-
3
- # Mapping actions are evaluated in the context of a ResponseProxy.
4
- class ResponseProxy
5
-
6
- attr_reader :request
7
-
8
- include ResponseMixin
9
-
10
- def initialize(request)
11
- @request = request
12
- end
13
-
14
- def resource( resource, &block )
15
- @resource = resource; yield.call
16
- end
17
-
18
- def controller( &block )
19
- lambda { Waves.application.controllers[ @resource ].process( @request, &block ) }
20
- end
21
-
22
- def view( &block )
23
- lambda { |val| Waves.application.views[ @resource ].process( @request, val, &block ) }
24
- end
25
-
26
- def redirect(path, status = '302'); @request.redirect(path, status); end
27
-
28
- end
29
-
30
- end
data/lib/tasks/cluster.rb DELETED
@@ -1,26 +0,0 @@
1
- namespace :cluster do
2
-
3
- desc 'Start a cluster of waves applications.'
4
- task :start do |task|
5
- using_waves_src = defined?(WAVES) || ENV['WAVES'] || File.exist( File.dirname(__FILE__) / :waves )
6
- script = using_waves_src ? :bin / 'waves-server' : 'waves-server'
7
- ( Waves::Console.config.ports || [ Waves::Console.config.port ] ).each do |port|
8
- cmd = "#{script} -p #{port} -c #{ENV['mode']||'development'} -d"
9
- puts cmd ; `#{cmd}`
10
- end
11
- end
12
-
13
- desc 'Stop a cluster of waves applications.'
14
- task :stop do |task|
15
- Dir[ :log / '*.pid' ].each do |pidfile|
16
- pid = File.read(pidfile).to_i
17
- puts "Stopping process #{pid} ..."
18
- Process.kill( 'INT', pid ) rescue nil
19
- end
20
- end
21
-
22
- desc 'Restart a cluster of waves applications.'
23
- task :restart => [ :stop, :start ] do |task|
24
- end
25
-
26
- end
@@ -1,110 +0,0 @@
1
- # Much love to Facets (more specifically English) for this module
2
- # http://english.rubyforge.org/
3
- # changed slightly in the hopes of one day implementing a different set
4
- # of rules for different languages
5
- # NOTE: this is NOT implemented yet.
6
- # plural and singular work directly with the English class
7
- module Waves
8
- module Inflect # :nodoc:
9
- module InflectorMethods
10
-
11
- # Define a general exception.
12
- def word(singular, plural=nil)
13
- plural = singular unless plural
14
- singular_word(singular, plural)
15
- plural_word(singular, plural)
16
- end
17
-
18
- # Define a singularization exception.
19
- def singular_word(singular, plural)
20
- @singular_of ||= {}
21
- @singular_of[plural] = singular
22
- end
23
-
24
- # Define a pluralization exception.
25
- def plural_word(singular, plural)
26
- @plural_of ||= {}
27
- @plural_of[singular] = plural
28
- end
29
-
30
- # Define a general rule.
31
- def rule(singular, plural)
32
- singular_rule(singular, plural)
33
- plural_rule(singular, plural)
34
- end
35
-
36
- # Define a singularization rule.
37
- def singular_rule(singular, plural)
38
- (@singular_rules ||= []) << [singular, plural]
39
- end
40
-
41
- # Define a plurualization rule.
42
- def plural_rule(singular, plural)
43
- (@plural_rules ||= []) << [singular, plural]
44
- end
45
-
46
- # Read prepared singularization rules.
47
- def singularization_rules
48
- @singular_rules ||= []
49
- return @singularization_rules if @singularization_rules
50
- sorted = @singular_rules.sort_by{ |s, p| "#{p}".size }.reverse
51
- @singularization_rules = sorted.collect do |s, p|
52
- [ /#{p}$/, "#{s}" ]
53
- end
54
- end
55
-
56
- # Read prepared pluralization rules.
57
- def pluralization_rules
58
- @plural_rules ||= []
59
- return @pluralization_rules if @pluralization_rules
60
- sorted = @plural_rules.sort_by{ |s, p| "#{s}".size }.reverse
61
- @pluralization_rules = sorted.collect do |s, p|
62
- [ /#{s}$/, "#{p}" ]
63
- end
64
- end
65
-
66
- #
67
- def plural_of
68
- @plural_of ||= {}
69
- end
70
-
71
- #
72
- def singular_of
73
- @singular_of ||= {}
74
- end
75
-
76
- # Convert an English word from plural to singular.
77
- #
78
- # "boys".singular #=> boy
79
- # "tomatoes".singular #=> tomato
80
- #
81
- def singular(word)
82
- if result = singular_of[word]
83
- return result.dup
84
- end
85
- result = word.dup
86
- singularization_rules.each do |(match, replacement)|
87
- break if result.gsub!(match, replacement)
88
- end
89
- return result
90
- end
91
-
92
- # Convert an English word from singular to plurel.
93
- #
94
- # "boy".plural #=> boys
95
- # "tomato".plural #=> tomatoes
96
- #
97
- def plural(word)
98
- if result = plural_of[word]
99
- return result.dup
100
- end
101
- #return self.dup if /s$/ =~ self # ???
102
- result = word.dup
103
- pluralization_rules.each do |(match, replacement)|
104
- break if result.gsub!(match, replacement)
105
- end
106
- return result
107
- end
108
- end
109
- end
110
- end
@@ -1,84 +0,0 @@
1
- module Waves
2
- module Inflect
3
- # Extends Waves::Inflect::InflectorMethods
4
- module English
5
-
6
- extend InflectorMethods
7
-
8
- # One argument means singular and plural are the same.
9
- word 'equipment'
10
- word 'information'
11
- word 'money'
12
- word 'species'
13
- word 'series'
14
- word 'fish'
15
- word 'sheep'
16
- word 'moose'
17
- word 'hovercraft'
18
-
19
- # Two arguments defines a singular and plural exception.
20
- word 'Swiss' , 'Swiss'
21
- word 'life' , 'lives'
22
- word 'wife' , 'wives'
23
- word 'cactus' , 'cacti'
24
- word 'goose' , 'geese'
25
- word 'criterion' , 'criteria'
26
- word 'alias' , 'aliases'
27
- word 'status' , 'statuses'
28
- word 'axis' , 'axes'
29
- word 'crisis' , 'crises'
30
- word 'testis' , 'testes'
31
- word 'child' , 'children'
32
- word 'person' , 'people'
33
- word 'potato' , 'potatoes'
34
- word 'tomato' , 'tomatoes'
35
- word 'buffalo' , 'buffaloes'
36
- word 'torpedo' , 'torpedoes'
37
- word 'quiz' , 'quizes'
38
- word 'matrix' , 'matrices'
39
- word 'vertex' , 'vetices'
40
- word 'index' , 'indices'
41
- word 'ox' , 'oxen'
42
- word 'mouse' , 'mice'
43
- word 'louse' , 'lice'
44
- word 'thesis' , 'theses'
45
- word 'thief' , 'thieves'
46
- word 'analysis' , 'analyses'
47
-
48
- # One-way singularization exception (convert plural to singular).
49
- singular_word 'cactus', 'cacti'
50
-
51
- # General rules.
52
- rule 'hive' , 'hives'
53
- rule 'rf' , 'rves'
54
- rule 'af' , 'aves'
55
- rule 'ero' , 'eroes'
56
- rule 'man' , 'men'
57
- rule 'ch' , 'ches'
58
- rule 'sh' , 'shes'
59
- rule 'ss' , 'sses'
60
- rule 'ta' , 'tum'
61
- rule 'ia' , 'ium'
62
- rule 'ra' , 'rum'
63
- rule 'ay' , 'ays'
64
- rule 'ey' , 'eys'
65
- rule 'oy' , 'oys'
66
- rule 'uy' , 'uys'
67
- rule 'y' , 'ies'
68
- rule 'x' , 'xes'
69
- rule 'lf' , 'lves'
70
- rule 'us' , 'uses'
71
- rule '' , 's'
72
-
73
- # One-way singular rules.
74
- singular_rule 'of' , 'ofs' # proof
75
- singular_rule 'o' , 'oes' # hero, heroes
76
- singular_rule 'f' , 'ves'
77
-
78
- # One-way plural rules.
79
- plural_rule 'fe' , 'ves' # safe, wife
80
- plural_rule 's' , 'ses'
81
-
82
- end
83
- end
84
- end