poro 0.1.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.
- data/LICENSE.txt +24 -0
- data/README.rdoc +215 -0
- data/Rakefile +37 -0
- data/lib/poro.rb +14 -0
- data/lib/poro/context.rb +459 -0
- data/lib/poro/context_factories.rb +1 -0
- data/lib/poro/context_factories/README.txt +8 -0
- data/lib/poro/context_factories/single_store.rb +34 -0
- data/lib/poro/context_factories/single_store/hash_factory.rb +19 -0
- data/lib/poro/context_factories/single_store/mongo_factory.rb +36 -0
- data/lib/poro/context_factory.rb +70 -0
- data/lib/poro/contexts.rb +4 -0
- data/lib/poro/contexts/hash_context.rb +177 -0
- data/lib/poro/contexts/mongo_context.rb +474 -0
- data/lib/poro/modelify.rb +100 -0
- data/lib/poro/persistify.rb +42 -0
- data/lib/poro/util.rb +2 -0
- data/lib/poro/util/inflector.rb +103 -0
- data/lib/poro/util/inflector/inflections.rb +213 -0
- data/lib/poro/util/inflector/methods.rb +153 -0
- data/lib/poro/util/module_finder.rb +66 -0
- data/spec/context_factory_spec.rb +71 -0
- data/spec/context_spec.rb +110 -0
- data/spec/hash_context_spec.rb +231 -0
- data/spec/inflector_spec.rb +32 -0
- data/spec/modelfy.rb +75 -0
- data/spec/module_finder_spec.rb +57 -0
- data/spec/mongo_context_spec.rb +28 -0
- data/spec/single_store_spec.rb +55 -0
- data/spec/spec_helper.rb +4 -0
- metadata +95 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
module Poro
|
2
|
+
# When this module is mixed into a a persistent object, it adds the
|
3
|
+
# object oriented methods directly to the class, making it behave like a model
|
4
|
+
# in other ORMs.
|
5
|
+
#
|
6
|
+
# If this module is not mixed in, the object can still persist, but the context
|
7
|
+
# must be directly accessed to fetch, save, and remove the data.
|
8
|
+
#
|
9
|
+
# Additionally, this module also ensures that the appropriate
|
10
|
+
# primary key accessor methods exist on the object, creating them if necessary.
|
11
|
+
#
|
12
|
+
# WARNING: You should configure the primary keys on the class' Context before
|
13
|
+
# including this module if you want to use an accessor other than the
|
14
|
+
# Context's default.
|
15
|
+
#
|
16
|
+
# See Modelify::ClassMethods and Modelify::InstanceMethods for the methods
|
17
|
+
# added by this mixin.
|
18
|
+
#
|
19
|
+
# = TODO: Piecewise Modelfication
|
20
|
+
#
|
21
|
+
# Modelfication should be done in a piece-meal way, so that we can layer
|
22
|
+
# in features such as pk accesor generation, basic model methods, find methods,
|
23
|
+
# and hook methods. We may still keep this top-level include to add the full
|
24
|
+
# suite, but we should break up the pieces. (In doing this, we could to manage
|
25
|
+
# dependencies so that we get the basic pieces included in when we need things
|
26
|
+
# they depend on, but by not managing this, it gives more flexability for
|
27
|
+
# advanced users to replace segments.
|
28
|
+
module Modelify
|
29
|
+
|
30
|
+
def self.included(mod) # :nodoc:
|
31
|
+
mod.send(:extend, ClassMethods)
|
32
|
+
mod.send(:include, InstanceMethods)
|
33
|
+
mod.send(:include, FindMethods)
|
34
|
+
|
35
|
+
context = Context.fetch(mod)
|
36
|
+
pk = context.primary_key.to_s.gsub(/[^A-Za-z0-9]/, '_').strip
|
37
|
+
raise NameError, "Cannot create a primary key method from #{context.primary_key.inspect}" if pk.nil? || pk.empty?
|
38
|
+
mod.class_eval("def #{pk}; return @#{pk}; end")
|
39
|
+
mod.class_eval("def #{pk}=(value); @#{pk} = value; end")
|
40
|
+
end
|
41
|
+
|
42
|
+
# These methods are added as class methods when including Modelify.
|
43
|
+
# See Modelify for more information.
|
44
|
+
module ClassMethods
|
45
|
+
# Find the object for the given id.
|
46
|
+
def fetch(id)
|
47
|
+
return context.fetch(id)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get the context instance for this class.
|
51
|
+
def context
|
52
|
+
return Context.fetch(self)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# These methods are addes as instance methods when including Modelify.
|
57
|
+
# See Modelify for more information.
|
58
|
+
module InstanceMethods
|
59
|
+
|
60
|
+
# Save the given object to persistent storage.
|
61
|
+
def save
|
62
|
+
return context.save(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Remove the given object from persistent storage.
|
66
|
+
def remove
|
67
|
+
return context.remove(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the context instance for this object.
|
71
|
+
def context
|
72
|
+
return Context.fetch(self)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# The find methods. See FindMethods::ClassMethods for details.
|
77
|
+
module FindMethods
|
78
|
+
|
79
|
+
def self.included(mod) # :nodoc:
|
80
|
+
mod.send(:extend, ClassMethods)
|
81
|
+
end
|
82
|
+
|
83
|
+
# The model find class methods.
|
84
|
+
module ClassMethods
|
85
|
+
|
86
|
+
# Calls find on the Context. See Poro::Context::FindMethods for details.
|
87
|
+
def find(arg, opts={})
|
88
|
+
return context.find(arg, opts)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Calls data_store_find on the Context. See Poro::Context::FindMethods for details.
|
92
|
+
def data_store_find(first_or_all, *args, &block)
|
93
|
+
return context.data_store_find(first_or_all, *args, &block)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Poro
|
2
|
+
# Include this module into any class in order to flag it for persistence.
|
3
|
+
#
|
4
|
+
# This is the only required change to a class in order to make it persistent.
|
5
|
+
# This flags it so that the factories know that it is okay to generate a
|
6
|
+
# context for this class and persist it.
|
7
|
+
#
|
8
|
+
# The only method this adds is a convenience class method for configuring
|
9
|
+
# the Context instance that backs the class it is included in.
|
10
|
+
#
|
11
|
+
# This module represents the only required breech of the hands off your code
|
12
|
+
# philosophy that Poro embodies.
|
13
|
+
#
|
14
|
+
# For those looking to add more model like behaviors, include Poro::Modelify
|
15
|
+
# as well.
|
16
|
+
module Persistify
|
17
|
+
|
18
|
+
def self.included(mod) # :nodoc:
|
19
|
+
mod.send(:extend, ClassMethods)
|
20
|
+
|
21
|
+
# Force the initialization of the context now, as one would expect it to
|
22
|
+
# exist after including this module. This also makes sure that on load
|
23
|
+
# all the contexts that are going to exist are known for introspection.
|
24
|
+
Context.fetch(mod)
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
|
29
|
+
# A convenience method to more easily call
|
30
|
+
# <tt>Context.configure_for_class</tt> from within a class decleration.
|
31
|
+
#
|
32
|
+
# This was added to ease the transition from existing model based ORMs,
|
33
|
+
# and is up for debate. It may be better to directly use
|
34
|
+
# <tt>Context.configure_for_class</tt>.
|
35
|
+
def configure_context(&configuration_block)
|
36
|
+
return Context.configure_for_class(self, &configuration_block)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/lib/poro/util.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
module Poro
|
2
|
+
module Util
|
3
|
+
# = License
|
4
|
+
#
|
5
|
+
# Inflector is from AcitveSupport, which is distributed via the following
|
6
|
+
# MIT license:
|
7
|
+
#
|
8
|
+
# Copyright (c) 2005-2010 David Heinemeier Hansson
|
9
|
+
#
|
10
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
11
|
+
# a copy of this software and associated documentation files (the
|
12
|
+
# "Software"), to deal in the Software without restriction, including
|
13
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
14
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
15
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
16
|
+
# the following conditions:
|
17
|
+
#
|
18
|
+
# The above copyright notice and this permission notice shall be
|
19
|
+
# included in all copies or substantial portions of the Software.
|
20
|
+
#
|
21
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
22
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
23
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
24
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
25
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
26
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
27
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
28
|
+
#
|
29
|
+
# = Overview
|
30
|
+
#
|
31
|
+
# This module contains the inflector from ActiveSupport, but in its own
|
32
|
+
# namespace and without being injected into the String class. This prevents
|
33
|
+
# Poro's implementation from clobbering that of ActiveSupport, should the
|
34
|
+
# rest of your project be using it, even if you include ActiveSupport before
|
35
|
+
# Poro.
|
36
|
+
module Inflector
|
37
|
+
# Space intentionally left blank.
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'poro/util/inflector/inflections'
|
43
|
+
require 'poro/util/inflector/methods'
|
44
|
+
|
45
|
+
module Poro
|
46
|
+
module Util
|
47
|
+
Inflector.inflections do |inflect|
|
48
|
+
inflect.plural(/$/, 's')
|
49
|
+
inflect.plural(/s$/i, 's')
|
50
|
+
inflect.plural(/(ax|test)is$/i, '\1es')
|
51
|
+
inflect.plural(/(octop|vir)us$/i, '\1i')
|
52
|
+
inflect.plural(/(alias|status)$/i, '\1es')
|
53
|
+
inflect.plural(/(bu)s$/i, '\1ses')
|
54
|
+
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
|
55
|
+
inflect.plural(/([ti])um$/i, '\1a')
|
56
|
+
inflect.plural(/sis$/i, 'ses')
|
57
|
+
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
|
58
|
+
inflect.plural(/(hive)$/i, '\1s')
|
59
|
+
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
60
|
+
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
|
61
|
+
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
|
62
|
+
inflect.plural(/([m|l])ouse$/i, '\1ice')
|
63
|
+
inflect.plural(/^(ox)$/i, '\1en')
|
64
|
+
inflect.plural(/(quiz)$/i, '\1zes')
|
65
|
+
|
66
|
+
inflect.singular(/s$/i, '')
|
67
|
+
inflect.singular(/(n)ews$/i, '\1ews')
|
68
|
+
inflect.singular(/([ti])a$/i, '\1um')
|
69
|
+
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
|
70
|
+
inflect.singular(/(^analy)ses$/i, '\1sis')
|
71
|
+
inflect.singular(/([^f])ves$/i, '\1fe')
|
72
|
+
inflect.singular(/(hive)s$/i, '\1')
|
73
|
+
inflect.singular(/(tive)s$/i, '\1')
|
74
|
+
inflect.singular(/([lr])ves$/i, '\1f')
|
75
|
+
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
76
|
+
inflect.singular(/(s)eries$/i, '\1eries')
|
77
|
+
inflect.singular(/(m)ovies$/i, '\1ovie')
|
78
|
+
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
|
79
|
+
inflect.singular(/([m|l])ice$/i, '\1ouse')
|
80
|
+
inflect.singular(/(bus)es$/i, '\1')
|
81
|
+
inflect.singular(/(o)es$/i, '\1')
|
82
|
+
inflect.singular(/(shoe)s$/i, '\1')
|
83
|
+
inflect.singular(/(cris|ax|test)es$/i, '\1is')
|
84
|
+
inflect.singular(/(octop|vir)i$/i, '\1us')
|
85
|
+
inflect.singular(/(alias|status)es$/i, '\1')
|
86
|
+
inflect.singular(/^(ox)en/i, '\1')
|
87
|
+
inflect.singular(/(vert|ind)ices$/i, '\1ex')
|
88
|
+
inflect.singular(/(matr)ices$/i, '\1ix')
|
89
|
+
inflect.singular(/(quiz)zes$/i, '\1')
|
90
|
+
inflect.singular(/(database)s$/i, '\1')
|
91
|
+
|
92
|
+
inflect.irregular('person', 'people')
|
93
|
+
inflect.irregular('man', 'men')
|
94
|
+
inflect.irregular('child', 'children')
|
95
|
+
inflect.irregular('sex', 'sexes')
|
96
|
+
inflect.irregular('move', 'moves')
|
97
|
+
inflect.irregular('cow', 'kine')
|
98
|
+
|
99
|
+
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Poro
|
2
|
+
module Util
|
3
|
+
module Inflector
|
4
|
+
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
|
5
|
+
# inflection rules. Examples:
|
6
|
+
#
|
7
|
+
# ActiveSupport::Inflector.inflections do |inflect|
|
8
|
+
# inflect.plural /^(ox)$/i, '\1\2en'
|
9
|
+
# inflect.singular /^(ox)en/i, '\1'
|
10
|
+
#
|
11
|
+
# inflect.irregular 'octopus', 'octopi'
|
12
|
+
#
|
13
|
+
# inflect.uncountable "equipment"
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
|
17
|
+
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
|
18
|
+
# already have been loaded.
|
19
|
+
class Inflections
|
20
|
+
def self.instance
|
21
|
+
@__instance__ ||= new
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :plurals, :singulars, :uncountables, :humans
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@plurals, @singulars, @uncountables, @humans = [], [], [], []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
|
31
|
+
# The replacement should always be a string that may include references to the matched data from the rule.
|
32
|
+
def plural(rule, replacement)
|
33
|
+
@uncountables.delete(rule) if rule.is_a?(String)
|
34
|
+
@uncountables.delete(replacement)
|
35
|
+
@plurals.insert(0, [rule, replacement])
|
36
|
+
end
|
37
|
+
|
38
|
+
# Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
|
39
|
+
# The replacement should always be a string that may include references to the matched data from the rule.
|
40
|
+
def singular(rule, replacement)
|
41
|
+
@uncountables.delete(rule) if rule.is_a?(String)
|
42
|
+
@uncountables.delete(replacement)
|
43
|
+
@singulars.insert(0, [rule, replacement])
|
44
|
+
end
|
45
|
+
|
46
|
+
# Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
|
47
|
+
# for strings, not regular expressions. You simply pass the irregular in singular and plural form.
|
48
|
+
#
|
49
|
+
# Examples:
|
50
|
+
# irregular 'octopus', 'octopi'
|
51
|
+
# irregular 'person', 'people'
|
52
|
+
def irregular(singular, plural)
|
53
|
+
@uncountables.delete(singular)
|
54
|
+
@uncountables.delete(plural)
|
55
|
+
if singular[0,1].upcase == plural[0,1].upcase
|
56
|
+
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
|
57
|
+
plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
|
58
|
+
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
|
59
|
+
else
|
60
|
+
plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
|
61
|
+
plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
|
62
|
+
plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
|
63
|
+
plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
|
64
|
+
singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
|
65
|
+
singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add uncountable words that shouldn't be attempted inflected.
|
70
|
+
#
|
71
|
+
# Examples:
|
72
|
+
# uncountable "money"
|
73
|
+
# uncountable "money", "information"
|
74
|
+
# uncountable %w( money information rice )
|
75
|
+
def uncountable(*words)
|
76
|
+
(@uncountables << words).flatten!
|
77
|
+
end
|
78
|
+
|
79
|
+
# Specifies a humanized form of a string by a regular expression rule or by a string mapping.
|
80
|
+
# When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
|
81
|
+
# When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
|
82
|
+
#
|
83
|
+
# Examples:
|
84
|
+
# human /_cnt$/i, '\1_count'
|
85
|
+
# human "legacy_col_person_name", "Name"
|
86
|
+
def human(rule, replacement)
|
87
|
+
@humans.insert(0, [rule, replacement])
|
88
|
+
end
|
89
|
+
|
90
|
+
# Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
|
91
|
+
# Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
|
92
|
+
# <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
|
93
|
+
#
|
94
|
+
# Examples:
|
95
|
+
# clear :all
|
96
|
+
# clear :plurals
|
97
|
+
def clear(scope = :all)
|
98
|
+
case scope
|
99
|
+
when :all
|
100
|
+
@plurals, @singulars, @uncountables = [], [], []
|
101
|
+
else
|
102
|
+
instance_variable_set "@#{scope}", []
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Yields a singleton instance of Inflector::Inflections so you can specify additional
|
108
|
+
# inflector rules.
|
109
|
+
#
|
110
|
+
# Example:
|
111
|
+
# ActiveSupport::Inflector.inflections do |inflect|
|
112
|
+
# inflect.uncountable "rails"
|
113
|
+
# end
|
114
|
+
def inflections
|
115
|
+
if block_given?
|
116
|
+
yield Inflections.instance
|
117
|
+
else
|
118
|
+
Inflections.instance
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns the plural form of the word in the string.
|
123
|
+
#
|
124
|
+
# Examples:
|
125
|
+
# "post".pluralize # => "posts"
|
126
|
+
# "octopus".pluralize # => "octopi"
|
127
|
+
# "sheep".pluralize # => "sheep"
|
128
|
+
# "words".pluralize # => "words"
|
129
|
+
# "CamelOctopus".pluralize # => "CamelOctopi"
|
130
|
+
def pluralize(word)
|
131
|
+
result = word.to_s.dup
|
132
|
+
|
133
|
+
if word.empty? || inflections.uncountables.include?(result.downcase)
|
134
|
+
result
|
135
|
+
else
|
136
|
+
inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
137
|
+
result
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
142
|
+
#
|
143
|
+
# Examples:
|
144
|
+
# "posts".singularize # => "post"
|
145
|
+
# "octopi".singularize # => "octopus"
|
146
|
+
# "sheep".singularize # => "sheep"
|
147
|
+
# "word".singularize # => "word"
|
148
|
+
# "CamelOctopi".singularize # => "CamelOctopus"
|
149
|
+
def singularize(word)
|
150
|
+
result = word.to_s.dup
|
151
|
+
|
152
|
+
if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i }
|
153
|
+
result
|
154
|
+
else
|
155
|
+
inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
156
|
+
result
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Capitalizes the first word and turns underscores into spaces and strips a
|
161
|
+
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
|
162
|
+
#
|
163
|
+
# Examples:
|
164
|
+
# "employee_salary" # => "Employee salary"
|
165
|
+
# "author_id" # => "Author"
|
166
|
+
def humanize(lower_case_and_underscored_word)
|
167
|
+
result = lower_case_and_underscored_word.to_s.dup
|
168
|
+
|
169
|
+
inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
170
|
+
result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
171
|
+
end
|
172
|
+
|
173
|
+
# Capitalizes all the words and replaces some characters in the string to create
|
174
|
+
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
175
|
+
# used in the Rails internals.
|
176
|
+
#
|
177
|
+
# +titleize+ is also aliased as as +titlecase+.
|
178
|
+
#
|
179
|
+
# Examples:
|
180
|
+
# "man from the boondocks".titleize # => "Man From The Boondocks"
|
181
|
+
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
|
182
|
+
def titleize(word)
|
183
|
+
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
|
184
|
+
end
|
185
|
+
|
186
|
+
# Create the name of a table like Rails does for models to table names. This method
|
187
|
+
# uses the +pluralize+ method on the last word in the string.
|
188
|
+
#
|
189
|
+
# Examples
|
190
|
+
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
|
191
|
+
# "egg_and_ham".tableize # => "egg_and_hams"
|
192
|
+
# "fancyCategory".tableize # => "fancy_categories"
|
193
|
+
def tableize(class_name)
|
194
|
+
pluralize(underscore(class_name))
|
195
|
+
end
|
196
|
+
|
197
|
+
# Create a class name from a plural table name like Rails does for table names to models.
|
198
|
+
# Note that this returns a string and not a Class. (To convert to an actual class
|
199
|
+
# follow +classify+ with +constantize+.)
|
200
|
+
#
|
201
|
+
# Examples:
|
202
|
+
# "egg_and_hams".classify # => "EggAndHam"
|
203
|
+
# "posts".classify # => "Post"
|
204
|
+
#
|
205
|
+
# Singular names are not handled correctly:
|
206
|
+
# "business".classify # => "Busines"
|
207
|
+
def classify(table_name)
|
208
|
+
# strip out any leading schema name
|
209
|
+
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|