roda-tags 0.1.1 → 0.2.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.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +24 -0
- data/.github/workflows/ruby.yml +45 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +33 -0
- data/.rubocop_todo.yml +7 -0
- data/CODE_OF_CONDUCT.md +22 -16
- data/Gemfile +86 -0
- data/Guardfile +25 -0
- data/README.md +234 -236
- data/Rakefile +14 -11
- data/lib/core_ext/blank.rb +37 -36
- data/lib/core_ext/hash.rb +39 -16
- data/lib/core_ext/object.rb +2 -2
- data/lib/core_ext/string.rb +290 -116
- data/lib/roda/plugins/tag_helpers.rb +795 -575
- data/lib/roda/plugins/tags.rb +532 -276
- data/lib/roda/tags/version.rb +2 -3
- data/lib/roda/tags.rb +2 -0
- data/roda-tags.gemspec +30 -32
- metadata +44 -128
- data/.travis.yml +0 -4
data/lib/core_ext/string.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
# The inflector extension adds inflection instance methods to String, which allows the easy
|
2
4
|
# transformation of words from singular to plural, class names to table names, modularized class
|
3
|
-
# names to ones without, and class names to foreign keys. It exists for
|
5
|
+
# names to ones without, and class names to foreign keys. It exists for
|
4
6
|
# backwards compatibility to legacy Sequel code.
|
5
7
|
#
|
6
8
|
# To load the extension:
|
@@ -17,16 +19,21 @@ class String
|
|
17
19
|
# inflect.uncountable "equipment"
|
18
20
|
# end
|
19
21
|
#
|
20
|
-
# New rules are added at the top. So in the example above, the irregular rule for octopus will
|
21
|
-
# now be the first of the pluralization and singularization rules that is runs. This guarantees
|
22
|
+
# New rules are added at the top. So in the example above, the irregular rule for octopus will
|
23
|
+
# now be the first of the pluralization and singularization rules that is runs. This guarantees
|
22
24
|
# that your rules run before any of the rules that may already have been loaded.
|
25
|
+
#
|
23
26
|
module Inflections
|
27
|
+
# Array to store plural inflection rules
|
24
28
|
@plurals = []
|
29
|
+
# Array to store singular inflection rules
|
25
30
|
@singulars = []
|
31
|
+
# Array to store words that are the same in singular and plural forms
|
26
32
|
@uncountables = []
|
27
|
-
|
33
|
+
|
28
34
|
# Proc that is instance evaled to create the default inflections for both the
|
29
35
|
# model inflector and the inflector extension.
|
36
|
+
# rubocop:disable Metrics/BlockLength
|
30
37
|
DEFAULT_INFLECTIONS_PROC = proc do
|
31
38
|
plural(/$/, 's')
|
32
39
|
plural(/s$/i, 's')
|
@@ -66,29 +73,45 @@ class String
|
|
66
73
|
irregular('quiz', 'quizzes')
|
67
74
|
irregular('testis', 'testes')
|
68
75
|
|
69
|
-
uncountable(%w
|
76
|
+
uncountable(%w[equipment information rice money species series fish sheep news])
|
70
77
|
end
|
71
|
-
|
72
|
-
|
78
|
+
# rubocop:enable Metrics/BlockLength
|
79
|
+
|
73
80
|
class << self
|
74
|
-
# Array
|
75
|
-
#
|
81
|
+
# An Array that stores the pluralization rules.
|
82
|
+
# Each rule is a 2-element array containing:
|
83
|
+
# - A regular expression pattern for matching words to pluralize
|
84
|
+
# - A substitution pattern (e.g. '\1es') for transforming the match into plural form
|
85
|
+
# Rules are processed in reverse order, so newer rules take precedence.
|
76
86
|
attr_reader :plurals
|
77
87
|
|
78
|
-
# Array
|
79
|
-
#
|
88
|
+
# An Array that stores the singularization rules.
|
89
|
+
# Each rule is a 2-element array containing:
|
90
|
+
# - A regular expression pattern for matching plural words
|
91
|
+
# - A substitution pattern (e.g. '\1y') for transforming the match into singular form
|
92
|
+
# Rules are processed in reverse order, so newer rules take precedence.
|
80
93
|
attr_reader :singulars
|
81
94
|
|
82
|
-
# Array of
|
95
|
+
# An Array of uncountable word strings that should not be inflected.
|
96
|
+
# These words have the same form in both singular and plural.
|
97
|
+
# Examples: 'fish', 'money', 'species'
|
83
98
|
attr_reader :uncountables
|
84
99
|
end
|
85
100
|
|
86
|
-
#
|
87
|
-
#
|
101
|
+
# Clear inflection rules in a given scope.
|
102
|
+
# If scope is not specified, all inflection rules will be cleared.
|
103
|
+
# Passing :plurals, :singulars, or :uncountables will clear only that specific type of rule.
|
104
|
+
#
|
105
|
+
# @param scope [Symbol] The scope of rules to clear. Can be :all (default),
|
106
|
+
# :plurals, :singulars, or :uncountables
|
107
|
+
# @return [Array] An empty array
|
108
|
+
#
|
109
|
+
# @example Clear all inflection rules
|
110
|
+
# String.inflections.clear
|
111
|
+
#
|
112
|
+
# @example Clear only plural rules
|
113
|
+
# String.inflections.clear(:plurals)
|
88
114
|
#
|
89
|
-
# Examples:
|
90
|
-
# clear :all
|
91
|
-
# clear :plurals
|
92
115
|
def self.clear(scope = :all)
|
93
116
|
case scope
|
94
117
|
when :all
|
@@ -96,219 +119,370 @@ class String
|
|
96
119
|
@singulars = []
|
97
120
|
@uncountables = []
|
98
121
|
else
|
99
|
-
instance_variable_set("@#{scope}", [])
|
122
|
+
instance_variable_set(:"@#{scope}", [])
|
100
123
|
end
|
101
124
|
end
|
102
125
|
|
103
|
-
# Specifies a new irregular that
|
104
|
-
#
|
105
|
-
#
|
126
|
+
# Specifies a new irregular inflection rule that transforms between singular and plural forms.
|
127
|
+
# This method creates rules for both pluralization and singularization simultaneously.
|
128
|
+
# Unlike regular inflection rules, this only works with literal strings, not regexp.
|
129
|
+
#
|
130
|
+
# @param singular [String] The singular form of the word
|
131
|
+
# @param plural [String] The plural form of the word
|
132
|
+
#
|
133
|
+
# @example
|
134
|
+
# irregular('person', 'people') # Creates rules to transform person <-> people
|
135
|
+
# irregular('child', 'children') # Creates rules to transform child <-> children
|
106
136
|
#
|
107
|
-
# Examples:
|
108
|
-
# irregular 'octopus', 'octopi'
|
109
|
-
# irregular 'person', 'people'
|
110
137
|
def self.irregular(singular, plural)
|
111
|
-
plural(Regexp.new("(#{singular[0, 1]})#{singular[1
|
112
|
-
singular(Regexp.new("(#{plural[0, 1]})#{plural[1
|
138
|
+
plural(Regexp.new("(#{singular[0, 1]})#{singular[1..]}$", 'i'), "\\1#{plural[1..]}")
|
139
|
+
singular(Regexp.new("(#{plural[0, 1]})#{plural[1..]}$", 'i'), "\\1#{singular[1..]}")
|
113
140
|
end
|
114
141
|
|
115
|
-
# Specifies a new pluralization rule
|
116
|
-
#
|
117
|
-
#
|
142
|
+
# Specifies a new pluralization rule to transform singular words into plural forms.
|
143
|
+
# Adds the rule to the beginning of the rules array so it takes precedence over existing rules.
|
144
|
+
#
|
145
|
+
# @param rule [Regexp, String] Pattern to match words that should be pluralized
|
146
|
+
# @param replacement [String] Template for constructing the plural form, can reference
|
147
|
+
# captured groups from the rule pattern using \1, \2 etc.
|
148
|
+
# @return [Array] The updated array of plural rules
|
149
|
+
#
|
150
|
+
# @example Add rule to pluralize words ending in 'y'
|
151
|
+
# plural(/([^aeiou])y$/i, '\1ies') # changes 'fly' to 'flies'
|
118
152
|
#
|
119
|
-
# Example:
|
120
|
-
# plural(/(x|ch|ss|sh)$/i, '\1es')
|
121
153
|
def self.plural(rule, replacement)
|
122
154
|
@plurals.insert(0, [rule, replacement])
|
123
155
|
end
|
124
156
|
|
125
|
-
# Specifies a new singularization rule
|
126
|
-
#
|
127
|
-
#
|
157
|
+
# Specifies a new singularization rule to transform plural words into singular forms.
|
158
|
+
# Adds the rule to the beginning of the rules array so it takes precedence over existing rules.
|
159
|
+
#
|
160
|
+
# @param rule [Regexp, String] Pattern to match words that should be singularized
|
161
|
+
# @param replacement [String] Template for constructing the singular form, can reference
|
162
|
+
# captured groups from the rule pattern using \1, \2 etc.
|
163
|
+
#
|
164
|
+
# @return [Array] The updated array of singular rules
|
165
|
+
#
|
166
|
+
# @example Add rule to singularize words ending in 'ies'
|
167
|
+
# singular(/([^aeiou])ies$/i, '\1y') # changes 'flies' to 'fly'
|
128
168
|
#
|
129
|
-
# Example:
|
130
|
-
# singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
131
169
|
def self.singular(rule, replacement)
|
132
170
|
@singulars.insert(0, [rule, replacement])
|
133
171
|
end
|
134
172
|
|
135
|
-
#
|
173
|
+
# Adds words that have the same singular and plural form to the uncountables list.
|
174
|
+
# These words will be skipped by the inflector and returned unchanged.
|
175
|
+
#
|
176
|
+
# @param words [Array<String>] One or more words to mark as uncountable
|
177
|
+
# @return [Array] The flattened array of all uncountable words
|
178
|
+
#
|
179
|
+
# @example Add a single uncountable word
|
180
|
+
# uncountable "fish"
|
181
|
+
#
|
182
|
+
# @example Add multiple uncountable words
|
183
|
+
# uncountable "rice", "equipment"
|
184
|
+
#
|
185
|
+
# @example Add an array of uncountable words
|
186
|
+
# uncountable %w(sheep species)
|
136
187
|
#
|
137
|
-
# Examples:
|
138
|
-
# uncountable "money"
|
139
|
-
# uncountable "money", "information"
|
140
|
-
# uncountable %w( money information rice )
|
141
188
|
def self.uncountable(*words)
|
142
189
|
(@uncountables << words).flatten!
|
143
190
|
end
|
144
191
|
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
|
192
|
+
# Execute the default inflection rules defined in DEFAULT_INFLECTIONS_PROC
|
193
|
+
# This sets up the basic plural/singular transformations and irregular/uncountable words
|
194
|
+
# that the inflector will use by default
|
195
|
+
instance_exec(&DEFAULT_INFLECTIONS_PROC)
|
149
196
|
end
|
150
197
|
|
151
|
-
#
|
152
|
-
# the Inflections module.
|
198
|
+
# Provides access to the Inflections module for defining custom inflection rules.
|
199
|
+
# If a block is given, yields the Inflections module to the block.
|
200
|
+
# Always returns the Inflections module.
|
201
|
+
#
|
202
|
+
# @yield [Inflections] The Inflections module if a block is given
|
203
|
+
#
|
204
|
+
# @return [Inflections] The Inflections module
|
205
|
+
#
|
206
|
+
# @example Define custom inflection rules
|
207
|
+
# String.inflections do |inflect|
|
208
|
+
# inflect.plural /^(ox)$/i, '\1\2en'
|
209
|
+
# inflect.singular /^(ox)en/i, '\1'
|
210
|
+
# end
|
211
|
+
#
|
153
212
|
def self.inflections
|
154
|
-
yield Inflections if
|
213
|
+
yield Inflections if defined?(yield)
|
155
214
|
Inflections
|
156
215
|
end
|
157
216
|
|
158
|
-
#
|
159
|
-
#
|
217
|
+
# Converts the string to CamelCase format.
|
218
|
+
# - Replaces forward slashes with double colons (e.g. 'foo/bar' -> 'Foo::Bar')
|
219
|
+
# - Converts underscores to camelized format (e.g. 'foo_bar' -> 'FooBar')
|
160
220
|
#
|
161
|
-
#
|
221
|
+
# @param first_letter_in_uppercase [Symbol] Whether first letter should be
|
222
|
+
# uppercase (:upper) or lowercase (:lower)
|
162
223
|
#
|
163
|
-
#
|
224
|
+
# @return [String] The camelized string
|
225
|
+
#
|
226
|
+
# @example Convert to UpperCamelCase
|
164
227
|
# "active_record".camelize #=> "ActiveRecord"
|
228
|
+
#
|
229
|
+
# @example Convert to lowerCamelCase
|
165
230
|
# "active_record".camelize(:lower) #=> "activeRecord"
|
231
|
+
#
|
232
|
+
# @example Convert path to namespace
|
166
233
|
# "active_record/errors".camelize #=> "ActiveRecord::Errors"
|
167
|
-
#
|
234
|
+
#
|
168
235
|
def camelize(first_letter_in_uppercase = :upper)
|
169
|
-
s = gsub(%r{/(.?)})
|
170
|
-
.gsub(/(^|_)(.)/) { |x| x[-1
|
236
|
+
s = gsub(%r{/(.?)}) { |x| "::#{x[-1..].upcase unless x == "/"}" }
|
237
|
+
.gsub(/(^|_)(.)/) { |x| x[-1..].upcase }
|
171
238
|
s[0...1] = s[0...1].downcase unless first_letter_in_uppercase == :upper
|
172
239
|
s
|
173
240
|
end
|
174
|
-
|
241
|
+
alias camelcase camelize
|
175
242
|
|
176
|
-
#
|
177
|
-
#
|
243
|
+
# Converts a string into a class name by removing any non-final period and subsequent characters,
|
244
|
+
# converting to singular form, and camelizing.
|
245
|
+
# Commonly used to obtain class name from table or file names.
|
246
|
+
#
|
247
|
+
# @return [String] A camelized singular form suitable for a class name
|
178
248
|
#
|
179
|
-
#
|
249
|
+
# @example Convert database table name to class name
|
180
250
|
# "egg_and_hams".classify #=> "EggAndHam"
|
181
|
-
#
|
251
|
+
#
|
252
|
+
# @example Remove schema prefix
|
182
253
|
# "schema.post".classify #=> "Post"
|
254
|
+
#
|
255
|
+
# @example Basic conversion
|
256
|
+
# "post".classify #=> "Post"
|
257
|
+
#
|
183
258
|
def classify
|
184
259
|
sub(/.*\./, '').singularize.camelize
|
185
260
|
end
|
186
261
|
|
187
|
-
#
|
188
|
-
#
|
189
|
-
#
|
262
|
+
# Finds and returns a Ruby constant from a string name.
|
263
|
+
# The string must be a valid constant name in CamelCase format.
|
264
|
+
# Can handle namespaced constants using double colons (::).
|
265
|
+
# Raises NameError if the constant name is invalid or not defined.
|
190
266
|
#
|
191
|
-
#
|
267
|
+
# @return [Object] The Ruby constant corresponding to the string name
|
268
|
+
#
|
269
|
+
# @raise [NameError] If string is not a valid constant name or constant is not defined
|
270
|
+
#
|
271
|
+
# @example Get Module class
|
192
272
|
# "Module".constantize #=> Module
|
193
|
-
#
|
273
|
+
#
|
274
|
+
# @example Get namespaced constant
|
275
|
+
# "ActiveRecord::Base".constantize #=> ActiveRecord::Base
|
276
|
+
#
|
277
|
+
# @example Invalid constant name
|
278
|
+
# "invalid_name".constantize #=> NameError: invalid_name is not a valid constant name!
|
279
|
+
#
|
194
280
|
def constantize
|
195
|
-
unless m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(self)
|
196
|
-
raise(NameError, "#{inspect} is not a valid constant name!")
|
281
|
+
unless (m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(self))
|
282
|
+
raise(NameError, "#{inspect} is not a valid constant name!")
|
197
283
|
end
|
198
|
-
|
284
|
+
|
285
|
+
# rubcop:disable Style/DocumentDynamicEvalDefinition
|
286
|
+
Object.module_eval("::#{m[1]}", __FILE__, __LINE__) # ::Post
|
287
|
+
# rubcop:enable Style/DocumentDynamicEvalDefinition
|
199
288
|
end
|
200
289
|
|
201
|
-
# Replaces underscores
|
290
|
+
# Replaces underscores (_) in a string with dashes (-).
|
291
|
+
# A helper method commonly used for URL slugs and CSS class names.
|
292
|
+
#
|
293
|
+
# @return [String] The string with underscores replaced by dashes
|
294
|
+
#
|
295
|
+
# @example
|
296
|
+
# "hello_world".dasherize #=> "hello-world"
|
297
|
+
# "foo_bar_baz".dasherize #=> "foo-bar-baz"
|
202
298
|
#
|
203
|
-
# Example
|
204
|
-
# "puni_puni".dasherize #=> "puni-puni"
|
205
299
|
def dasherize
|
206
300
|
tr('_', '-')
|
207
301
|
end
|
208
302
|
|
209
|
-
# Removes the module part from
|
303
|
+
# Removes the module part from a fully-qualified Ruby constant name,
|
304
|
+
# returning just the rightmost portion after the last double colon (::).
|
305
|
+
#
|
306
|
+
# @return [String] The final constant name without any module namespacing
|
307
|
+
#
|
308
|
+
# @example Remove module namespace from fully-qualified name
|
309
|
+
# "ActiveRecord::Base::Table".demodulize #=> "Table"
|
310
|
+
#
|
311
|
+
# @example No change when no modules present
|
312
|
+
# "String".demodulize #=> "String"
|
210
313
|
#
|
211
|
-
# Examples
|
212
|
-
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
|
213
|
-
# "Inflections".demodulize #=> "Inflections"
|
214
314
|
def demodulize
|
215
315
|
gsub(/^.*::/, '')
|
216
316
|
end
|
217
317
|
|
218
|
-
# Creates a foreign key name from a class name
|
219
|
-
#
|
318
|
+
# Creates a foreign key name from a class name by removing any module namespacing,
|
319
|
+
# underscoring the remaining name, and appending 'id'. The underscore before 'id'
|
320
|
+
# is optional.
|
321
|
+
#
|
322
|
+
# @param use_underscore [Boolean] Whether to include an underscore before 'id'
|
323
|
+
#
|
324
|
+
# @return [String] The foreign key name
|
220
325
|
#
|
221
|
-
#
|
326
|
+
# @example Basic usage
|
222
327
|
# "Message".foreign_key #=> "message_id"
|
223
|
-
#
|
328
|
+
#
|
329
|
+
# @example Without underscore
|
330
|
+
# "Message".foreign_key(use_underscore: false) #=> "messageid"
|
331
|
+
#
|
332
|
+
# @example With namespaced class
|
224
333
|
# "Admin::Post".foreign_key #=> "post_id"
|
225
|
-
|
226
|
-
|
334
|
+
#
|
335
|
+
def foreign_key(use_underscore: true)
|
336
|
+
"#{demodulize.underscore}#{"_" if use_underscore}id"
|
227
337
|
end
|
228
338
|
|
229
|
-
#
|
230
|
-
#
|
339
|
+
# Converts a string into a more human-readable format by:
|
340
|
+
# - Removing any trailing '_id'
|
341
|
+
# - Converting underscores to spaces
|
342
|
+
# - Capitalizing the first letter
|
343
|
+
#
|
344
|
+
# @return [String] A human-friendly version of the string
|
345
|
+
#
|
346
|
+
# @example Convert a database column name
|
347
|
+
# "employee_salary".humanize #=> "Employee salary"
|
348
|
+
#
|
349
|
+
# @example Remove ID suffix
|
350
|
+
# "user_id".humanize #=> "User"
|
351
|
+
#
|
352
|
+
# @example Basic conversion
|
353
|
+
# "hello_world".humanize #=> "Hello world"
|
231
354
|
#
|
232
|
-
# Examples
|
233
|
-
# "employee_salary" #=> "Employee salary"
|
234
|
-
# "author_id" #=> "Author"
|
235
355
|
def humanize
|
236
356
|
gsub(/_id$/, '').tr('_', ' ').capitalize
|
237
357
|
end
|
238
358
|
|
239
|
-
#
|
359
|
+
# Transforms a word into its plural form according to standard English language rules
|
360
|
+
# and any custom rules defined through String.inflections.
|
240
361
|
#
|
241
|
-
#
|
362
|
+
# If the word is in the uncountable list (e.g. "sheep", "fish"), returns it unchanged.
|
363
|
+
# Otherwise applies plural transformation rules in order until one matches.
|
364
|
+
#
|
365
|
+
# @return [String] The plural form of the word
|
366
|
+
#
|
367
|
+
# @example Basic pluralization
|
242
368
|
# "post".pluralize #=> "posts"
|
243
369
|
# "octopus".pluralize #=> "octopi"
|
244
|
-
#
|
245
|
-
#
|
370
|
+
#
|
371
|
+
# @example Uncountable words
|
372
|
+
# "fish".pluralize #=> "fish"
|
373
|
+
#
|
374
|
+
# @example Complex phrases
|
246
375
|
# "the blue mailman".pluralize #=> "the blue mailmen"
|
247
376
|
# "CamelOctopus".pluralize #=> "CamelOctopi"
|
377
|
+
#
|
248
378
|
def pluralize
|
249
379
|
result = dup
|
250
380
|
unless Inflections.uncountables.include?(downcase)
|
251
|
-
Inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
381
|
+
Inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
252
382
|
end
|
253
383
|
result
|
254
384
|
end
|
255
385
|
|
256
|
-
#
|
386
|
+
# Transforms a word into its singular form according to standard English language rules
|
387
|
+
# and any custom rules defined through String.inflections.
|
257
388
|
#
|
258
|
-
#
|
389
|
+
# If the word is in the uncountable list (e.g. "sheep", "fish"), returns it unchanged.
|
390
|
+
# Otherwise applies singular transformation rules in order until one matches.
|
391
|
+
#
|
392
|
+
# @return [String] The singular form of the word
|
393
|
+
#
|
394
|
+
# @example Basic singularization
|
259
395
|
# "posts".singularize #=> "post"
|
260
|
-
# "
|
261
|
-
#
|
262
|
-
#
|
396
|
+
# "matrices".singularize #=> "matrix"
|
397
|
+
#
|
398
|
+
# @example Uncountable words
|
399
|
+
# "fish".singularize #=> "fish"
|
400
|
+
#
|
401
|
+
# @example Complex phrases
|
263
402
|
# "the blue mailmen".singularize #=> "the blue mailman"
|
264
403
|
# "CamelOctopi".singularize #=> "CamelOctopus"
|
404
|
+
#
|
265
405
|
def singularize
|
266
406
|
result = dup
|
267
407
|
unless Inflections.uncountables.include?(downcase)
|
268
|
-
Inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
408
|
+
Inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
269
409
|
end
|
270
410
|
result
|
271
411
|
end
|
272
412
|
|
273
|
-
#
|
413
|
+
# Converts a class name or CamelCase word to a suitable database table name
|
414
|
+
# by underscoring and pluralizing it. Namespaces are converted to paths.
|
415
|
+
# Used to derive table names from model class names.
|
274
416
|
#
|
275
|
-
#
|
417
|
+
# @return [String] The table name (underscored, pluralized form)
|
418
|
+
#
|
419
|
+
# @example Convert class name to table name
|
276
420
|
# "RawScaledScorer".tableize #=> "raw_scaled_scorers"
|
277
|
-
#
|
421
|
+
#
|
422
|
+
# @example Handle namespaces
|
423
|
+
# "Admin::Post".tableize #=> "admin/posts"
|
424
|
+
#
|
425
|
+
# @example Basic conversion
|
278
426
|
# "fancyCategory".tableize #=> "fancy_categories"
|
427
|
+
#
|
279
428
|
def tableize
|
280
429
|
underscore.pluralize
|
281
430
|
end
|
282
431
|
|
283
|
-
#
|
284
|
-
#
|
432
|
+
# Converts a string into a more human-readable title format by:
|
433
|
+
# - Converting underscores and dashes to spaces
|
434
|
+
# - Capitalizing each word
|
435
|
+
# - Applying human-friendly formatting
|
285
436
|
#
|
286
437
|
# titleize is also aliased as as titlecase
|
287
438
|
#
|
288
|
-
#
|
289
|
-
#
|
439
|
+
# @return [String] A titleized version of the string
|
440
|
+
#
|
441
|
+
# @example Convert basic string to title
|
442
|
+
# "hello_world".titleize #=> "Hello World"
|
443
|
+
#
|
444
|
+
# @example Convert with special characters
|
290
445
|
# "x-men: the last stand".titleize #=> "X Men: The Last Stand"
|
446
|
+
#
|
447
|
+
# @example Convert camelCase to title
|
448
|
+
# "camelCase".titleize #=> "Camel Case"
|
449
|
+
#
|
291
450
|
def titleize
|
292
|
-
underscore.humanize.gsub(/\b([a-z])/) { |x| x[-1
|
451
|
+
underscore.humanize.gsub(/\b([a-z])/) { |x| x[-1..].upcase }
|
293
452
|
end
|
294
|
-
|
453
|
+
alias titlecase titleize
|
295
454
|
|
296
|
-
#
|
297
|
-
#
|
455
|
+
# Converts a CamelCase or camelCase string into an underscored format.
|
456
|
+
# - Replaces '::' with '/' for namespace/path conversion
|
457
|
+
# - Adds underscores between words including:
|
458
|
+
# - Between runs of capital letters: 'ABC' -> 'a_b_c'
|
459
|
+
# - Before first lowercase letter after capitals: 'HTMLParser' -> 'html_parser'
|
460
|
+
# - Before capitals after lowercase/numbers: 'fooBar' -> 'foo_bar'
|
461
|
+
# - Converts all dashes to underscores
|
462
|
+
# - Converts everything to lowercase
|
298
463
|
#
|
299
|
-
#
|
464
|
+
# @return [String] The underscored version of the string
|
465
|
+
#
|
466
|
+
# @example Convert camelCase
|
467
|
+
# "camelCase".underscore #=> "camel_case"
|
300
468
|
# "ActiveRecord".underscore #=> "active_record"
|
301
|
-
#
|
469
|
+
#
|
470
|
+
# @example Convert namespace
|
471
|
+
# "ActiveRecord::Errors".underscore #=> 'active_record/errors'
|
472
|
+
#
|
473
|
+
# @example Convert complex CamelCase
|
474
|
+
# "HTMLParser".underscore #=> "html_parser"
|
475
|
+
#
|
302
476
|
def underscore
|
303
|
-
gsub(
|
304
|
-
|
477
|
+
gsub('::', '/').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
478
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2').tr('-', '_').downcase
|
305
479
|
end
|
306
480
|
end
|
307
481
|
|
308
|
-
|
309
482
|
# Ripped from the Sequel gem by Jeremy Evans
|
310
|
-
#
|
311
|
-
#
|
483
|
+
# https://github.com/jeremyevans/sequel/blob/master/lib/sequel/extensions/inflector.rb
|
484
|
+
#
|
485
|
+
#
|
312
486
|
# Copyright (c) 2007-2008 Sharon Rosner
|
313
487
|
# Copyright (c) 2008-2015 Jeremy Evans
|
314
488
|
#
|