roda-tags 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
#
|