flexus 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d4fb081e1ddb588f626630927563e06b07bf29d2
4
+ data.tar.gz: 9a17ef6e6ec631441073dc6b018617d7677fe9f6
5
+ SHA512:
6
+ metadata.gz: 8e17f42debb31c4392d530067560de527d8ee7d842aad65fd4400d5884e168332331fa953993f1f6479bdf759f87a4481a89e8160489e66b5ebeffb7ef4c5778
7
+ data.tar.gz: 7d2f42bec41028173062ef36fd3dcba84f923f98cd4c283de691024b235ff2ab169f7d5e361a9135f7240c0980c0ba60183e43f75fc450c58b7b8ada738e597c
data/.gitignore ADDED
@@ -0,0 +1,37 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## Rubinius
17
+ *.rbc
18
+ .rbx
19
+
20
+ ## PROJECT::GENERAL
21
+ *.gem
22
+ coverage
23
+ profiling
24
+ turbulence
25
+ rdoc
26
+ pkg
27
+ tmp
28
+ doc
29
+ log
30
+ .yardoc
31
+ measurements
32
+
33
+ ## BUNDLER
34
+ .bundle
35
+ Gemfile.lock
36
+
37
+ ## PROJECT::SPECIFIC
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,37 @@
1
+ language: ruby
2
+ cache: bundler
3
+ before_install: gem install bundler
4
+ bundler_args: --without yard guard benchmarks
5
+ script: "bundle exec rake ci:metrics"
6
+ rvm:
7
+ - 2.1.8
8
+ - 2.2.6
9
+ - 2.3.3
10
+ - 2.4.0
11
+ - ruby-head
12
+ - rbx-3.81
13
+ notifications:
14
+ irc:
15
+ channels:
16
+ - "irc.freenode.org#rom-rb"
17
+ on_success: never
18
+ on_failure: change
19
+ email:
20
+ on_success: never
21
+ on_failure: change
22
+ matrix:
23
+ include:
24
+ - rvm: jruby-19mode
25
+ env: JRUBY_OPTS="$JRUBY_OPTS --debug"
26
+ - rvm: jruby-head
27
+ env: JRUBY_OPTS="$JRUBY_OPTS --debug"
28
+ - rvm: 2.4.0
29
+ env: WITH_MUTANT=true
30
+ script: "bundle exec rake ci" # include mutant
31
+ allow_failures:
32
+ - rvm: ruby-head
33
+ - rvm: rbx-3.81
34
+ - rvm: jruby-19mode
35
+ - rvm: jruby-head
36
+ - env: WITH_MUTANT=true
37
+ fast_finish: true
data/Changelog.md ADDED
@@ -0,0 +1,7 @@
1
+ # v0.0.2 2013-01-20
2
+
3
+ [change] Do not depend on backports
4
+
5
+ # v0.0.1 2012-12-12
6
+
7
+ First public release!
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ gemspec
6
+
7
+ group :development, :test do
8
+ gem 'devtools', '~> 0.1.x'
9
+ end
data/Guardfile ADDED
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ guard :bundler do
4
+ watch('Gemfile')
5
+ end
6
+
7
+ guard :rspec do
8
+ # run all specs if the spec_helper or supporting files files are modified
9
+ watch('spec/spec_helper.rb') { 'spec' }
10
+ watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec' }
11
+
12
+ # run unit specs if associated lib code is modified
13
+ watch(%r{\Alib/(.+)\.rb\z}) { |m| Dir["spec/unit/#{m[1]}"] }
14
+ watch("lib/#{File.basename(File.expand_path('../', __FILE__))}.rb") { 'spec' }
15
+
16
+ # run a spec if it is modified
17
+ watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
18
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) The rails, merb & datamapper team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ flexus
2
+ ========
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/flexus.png)][gem]
5
+ [![Build Status](https://secure.travis-ci.org/Ptico/flexus.png?branch=master)][travis]
6
+ [![Dependency Status](https://gemnasium.com/badges/github.com/Ptico/flexus.svg)][gemnasium]
7
+ [![Code Climate](https://codeclimate.com/github/Ptico/flexus.png)][codeclimate]
8
+ [![Coverage Status](https://coveralls.io/repos/Ptico/flexus/badge.png?branch=master)][coveralls]
9
+
10
+ [gem]: https://rubygems.org/gems/flexus
11
+ [travis]: https://travis-ci.org/Ptico/flexus
12
+ [gemnasium]: https://gemnasium.com/Ptico/flexus
13
+ [codeclimate]: https://codeclimate.com/github/Ptico/flexus
14
+ [coveralls]: https://coveralls.io/r/Ptico/flexus
15
+
16
+ This gem is a resurrection of [inflecto](https://github.com/mbj/inflecto)
17
+
18
+ Inflecto is a standalone inflector ripped out from [dm-core](https://github.com/datamapper/dm-core)
19
+
20
+ The dm-core inflector originated from [extlib](https://github.com/datamapper/extlib)
21
+
22
+ The extlib inflecto originated from [active_support](https://github.com/rails/rails)
23
+
24
+ Migration from `inflecto`
25
+ -------------------------
26
+
27
+ Remove inflecto and add flexus to your Gemfile.
28
+
29
+ ```ruby
30
+ gem 'flexus', '1.0.0'
31
+ ```
32
+
33
+ At your code, change all `Inflecto` references to `Flexus` and it should work out of the box for flexus version 1.0.0. Please report any issues.
34
+
35
+ Flexus will try the best to respect semantic versioning as http://semver.org/
36
+ So, expect api breaking changes on major version releases.
37
+
38
+ We'll make pre release gems for 2.0.0 version. To experiment with that you should add this to your Gemfile.
39
+
40
+ ```ruby
41
+ gem 'flexus', '>= 2.0.0.beta'
42
+ ```
43
+
44
+ Examples
45
+ --------
46
+
47
+ ```ruby
48
+ require 'flexus'
49
+
50
+ Flexus.underscore("CamelCase")
51
+ # => "camel_case"
52
+ ```
53
+
54
+ Credits
55
+ -------
56
+
57
+ The rails, merb & datamapper team
58
+
59
+ The original gem was extracted and maintained by Markus Schirp ([mbj](https://github.com/mbj)) with help from [indrekj](https://github.com/indrekj).
60
+
61
+ Current maintainers: Andrii Savchenko [Ptico](https://github.com/Ptico) and Abinoam P. Marques Jr. [abinoam](https://github.com/abinoam)
62
+
63
+ Contributing
64
+ -------------
65
+
66
+ * If you want your code merged into the mainline, please discuss the proposed changes with me before doing any work on it. This library is still in early development, and the direction it is going may not always be clear. Some features may not be appropriate yet, may need to be deferred until later when the foundation for them is laid, or may be more applicable in a plugin.
67
+ * Fork the project.
68
+ * Make your feature addition or bug fix.
69
+ * Follow this [style guide](https://github.com/dkubb/styleguide).
70
+ * Add specs for it. This is important so I don't break it in a future version unintentionally. Tests must cover all branches within the code, and code must be fully covered.
71
+ * Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
72
+ * Run "rake ci". This must pass and not show any regressions in the metrics for the code to be merged.
73
+ * Send me a pull request. Bonus points for topic branches.
74
+
75
+ License
76
+ -------
77
+
78
+ See LICENSE.txt for details
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "devtools"
6
+
7
+ # rake metrics and rake ci
8
+ Devtools.init_rake_tasks
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task default: :spec
data/TODO ADDED
@@ -0,0 +1 @@
1
+ * Find the guys to mention in credits.
data/flexus.gemspec ADDED
@@ -0,0 +1,33 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "flexus/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "flexus"
8
+ spec.version = Flexus::VERSION
9
+ spec.authors =
10
+ [ "The rails, merb & datamapper team",
11
+ "Markus Schirp",
12
+ "Andrii Savchenko",
13
+ "Abinoam P. Marques Jr." ]
14
+ spec.email = ["andrey@aejis.eu", "abinoam@gmail.com"]
15
+ spec.description = "Inflector for strings"
16
+ spec.summary = spec.description
17
+ spec.homepage = "https://github.com/Ptico/flexus"
18
+ spec.license = "MIT"
19
+
20
+ spec.require_paths = %w[lib]
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features|config)/})
24
+ end
25
+
26
+ spec.extra_rdoc_files = %w[LICENSE.txt README.md TODO]
27
+
28
+ spec.required_ruby_version = ">= 2.1"
29
+
30
+ spec.add_development_dependency "bundler"
31
+ spec.add_development_dependency "rake"
32
+ spec.add_development_dependency "rspec"
33
+ end
@@ -0,0 +1,59 @@
1
+ Flexus.inflections do |inflect|
2
+ inflect.plural(/\z/, 's')
3
+ inflect.plural(/s\z/i, 's')
4
+ inflect.plural(/(ax|test)is\z/i, '\1es')
5
+ inflect.plural(/(octop|vir)us\z/i, '\1i')
6
+ inflect.plural(/(octop|vir)i\z/i, '\1i')
7
+ inflect.plural(/(alias|status)\z/i, '\1es')
8
+ inflect.plural(/(bu)s\z/i, '\1ses')
9
+ inflect.plural(/(buffal|tomat)o\z/i, '\1oes')
10
+ inflect.plural(/([ti])um\z/i, '\1a')
11
+ inflect.plural(/([ti])a\z/i, '\1a')
12
+ inflect.plural(/sis\z/i, 'ses')
13
+ inflect.plural(/(?:([^f])fe|([lr])f)\z/i, '\1\2ves')
14
+ inflect.plural(/(hive)\z/i, '\1s')
15
+ inflect.plural(/([^aeiouy]|qu)y\z/i, '\1ies')
16
+ inflect.plural(/(x|ch|ss|sh)\z/i, '\1es')
17
+ inflect.plural(/(matr|vert|ind)(?:ix|ex)\z/i, '\1ices')
18
+ inflect.plural(/([m|l])ouse\z/i, '\1ice')
19
+ inflect.plural(/([m|l])ice\z/i, '\1ice')
20
+ inflect.plural(/^(ox)\z/i, '\1en')
21
+ inflect.plural(/^(oxen)\z/i, '\1')
22
+ inflect.plural(/(quiz)\z/i, '\1zes')
23
+
24
+ inflect.singular(/s\z/i, '')
25
+ inflect.singular(/(n)ews\z/i, '\1ews')
26
+ inflect.singular(/([ti])a\z/i, '\1um')
27
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses\z/i, '\1\2sis')
28
+ inflect.singular(/(^analy)ses\z/i, '\1sis')
29
+ inflect.singular(/([^f])ves\z/i, '\1fe')
30
+ inflect.singular(/(hive)s\z/i, '\1')
31
+ inflect.singular(/(tive)s\z/i, '\1')
32
+ inflect.singular(/([lr])ves\z/i, '\1f')
33
+ inflect.singular(/([^aeiouy]|qu)ies\z/i, '\1y')
34
+ inflect.singular(/(s)eries\z/i, '\1eries')
35
+ inflect.singular(/(m)ovies\z/i, '\1ovie')
36
+ inflect.singular(/(ss)\z/i, '\1')
37
+ inflect.singular(/(x|ch|ss|sh)es\z/i, '\1')
38
+ inflect.singular(/([m|l])ice\z/i, '\1ouse')
39
+ inflect.singular(/(bus)es\z/i, '\1')
40
+ inflect.singular(/(o)es\z/i, '\1')
41
+ inflect.singular(/(shoe)s\z/i, '\1')
42
+ inflect.singular(/(cris|ax|test)es\z/i, '\1is')
43
+ inflect.singular(/(octop|vir)i\z/i, '\1us')
44
+ inflect.singular(/(alias|status)es\z/i, '\1')
45
+ inflect.singular(/^(ox)en/i, '\1')
46
+ inflect.singular(/(vert|ind)ices\z/i, '\1ex')
47
+ inflect.singular(/(matr)ices\z/i, '\1ix')
48
+ inflect.singular(/(quiz)zes\z/i, '\1')
49
+ inflect.singular(/(database)s\z/i, '\1')
50
+
51
+ inflect.irregular('person', 'people')
52
+ inflect.irregular('man', 'men')
53
+ inflect.irregular('child', 'children')
54
+ inflect.irregular('sex', 'sexes')
55
+ inflect.irregular('move', 'moves')
56
+ inflect.irregular('cow', 'cows')
57
+
58
+ inflect.uncountable(%w(hovercraft moose milk rain Swiss grass equipment information rice money species series fish sheep jeans))
59
+ end
@@ -0,0 +1,232 @@
1
+ module Flexus
2
+ # A singleton instance of this class is yielded by Flexus.inflections, which can then be used to specify additional
3
+ # inflection rules. Examples:
4
+ #
5
+ # Flexus.inflections do |inflect|
6
+ # inflect.plural /^(ox)$/i, '\1\2en'
7
+ # inflect.singular /^(ox)en/i, '\1'
8
+ #
9
+ # inflect.irregular 'octopus', 'octopi'
10
+ #
11
+ # inflect.uncountable "equipment"
12
+ # end
13
+ #
14
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
15
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
16
+ # already have been loaded.
17
+ #
18
+ class Inflections
19
+
20
+ # Return instance
21
+ #
22
+ # @return [Inflections]
23
+ #
24
+ # @api private
25
+ #
26
+ def self.instance
27
+ @__instance__ ||= new
28
+ end
29
+
30
+ # Return plurals
31
+ #
32
+ # @return [Array]
33
+ #
34
+ # @api private
35
+ #
36
+ attr_reader :plurals
37
+
38
+ # Return singulars
39
+ #
40
+ # @return [Array]
41
+ #
42
+ # @api private
43
+ #
44
+ attr_reader :singulars
45
+
46
+ # Return uncountables
47
+ #
48
+ # @return [Array]
49
+ #
50
+ # @api private
51
+ #
52
+ attr_reader :uncountables
53
+
54
+ # Return humans
55
+ #
56
+ # @return [Array]
57
+ #
58
+ # @api private
59
+ #
60
+ #
61
+ attr_reader :humans
62
+
63
+ # Initialize object
64
+ #
65
+ # @return [undefined]
66
+ #
67
+ # @api private
68
+ #
69
+ def initialize
70
+ @plurals = RulesCollection.new
71
+ @singulars = RulesCollection.new
72
+ @humans = RulesCollection.new
73
+ @uncountables = Set[]
74
+ end
75
+
76
+ # Add a new plural role
77
+ #
78
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
79
+ # The replacement should always be a string that may include references to the matched data from the rule.
80
+ #
81
+ # @param [String, Regexp] rule
82
+ # @param [String, Regexp] replacement
83
+ #
84
+ # @return [self]
85
+ #
86
+ # @api private
87
+ #
88
+ def plural(rule, replacement)
89
+ rule(rule, replacement, plurals)
90
+
91
+ self
92
+ end
93
+
94
+ # Add a new singular rule
95
+ #
96
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
97
+ # The replacement should always be a string that may include references to the matched data from the rule.
98
+ #
99
+ # @param [String, Regexp] rule
100
+ # @param [String, Regexp] replacement
101
+ #
102
+ # @return [self]
103
+ #
104
+ # @api private
105
+ #
106
+ def singular(rule, replacement)
107
+ rule(rule, replacement, singulars)
108
+
109
+ self
110
+ end
111
+
112
+ # Add a new irregular pluralization
113
+ #
114
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
115
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
116
+ #
117
+ # @example
118
+ #
119
+ # Flexus.irregular('octopus', 'octopi')
120
+ # Flexus.irregular('person', 'people')
121
+ #
122
+ # @param [String] singular
123
+ # @param [String] plural
124
+ #
125
+ # @return [self]
126
+ #
127
+ # @api private
128
+ #
129
+ def irregular(singular, plural)
130
+ uncountables.delete(singular)
131
+ uncountables.delete(plural)
132
+
133
+ add_irregular(singular, plural, plurals)
134
+ add_irregular(plural, singular, singulars)
135
+
136
+ self
137
+ end
138
+
139
+ # Add uncountable words
140
+ #
141
+ # Uncountable will not be inflected
142
+ #
143
+ # @example
144
+ #
145
+ # Flexus.uncountable "money"
146
+ # Flexus.uncountable "money", "information"
147
+ # Flexus.uncountable %w( money information rice )
148
+ #
149
+ # @param [Enumerable<String>] words
150
+ #
151
+ # @return [self]
152
+ #
153
+ # @api private
154
+ #
155
+ def uncountable(*words)
156
+ uncountables.merge(words.flatten)
157
+
158
+ self
159
+ end
160
+
161
+ # Add humanize rule
162
+ #
163
+ # Specifies a humanized form of a string by a regular expression rule or by a string mapping.
164
+ # When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
165
+ # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
166
+ #
167
+ # @example
168
+ # Flexus.human(/_cnt$/i, '\1_count')
169
+ # Flexus.human("legacy_col_person_name", "Name")
170
+ #
171
+ # @param [String, Regexp] rule
172
+ # @param [String, Regexp] replacement
173
+ #
174
+ # @api private
175
+ #
176
+ # @return [self]
177
+ #
178
+ def human(rule, replacement)
179
+ humans.insert(0, [rule, replacement])
180
+ self
181
+ end
182
+
183
+ # Clear all inflection rules
184
+ #
185
+ # @example
186
+ #
187
+ # Flexus.clear
188
+ #
189
+ # @return [self]
190
+ #
191
+ # @api private
192
+ #
193
+ def clear
194
+ initialize
195
+ self
196
+ end
197
+
198
+ private
199
+
200
+ # Add irregular inflection
201
+ #
202
+ # @param [String] rule
203
+ # @param [String] replacement
204
+ #
205
+ # @return [undefined]
206
+ #
207
+ # @api private
208
+ #
209
+ def add_irregular(rule, replacement, target)
210
+ head, *tail = rule.chars.to_a
211
+ rule(/(#{head})#{tail.join}\z/i, '\1' + replacement[1..-1], target)
212
+ end
213
+
214
+ # Add a new rule
215
+ #
216
+ # @param [String, Regexp] rule
217
+ # @param [String, Regexp] replacement
218
+ # @param [Array] target
219
+ #
220
+ # @return [undefined]
221
+ #
222
+ # @api private
223
+ #
224
+ def rule(rule, replacement, target)
225
+ uncountables.delete(rule)
226
+ uncountables.delete(replacement)
227
+
228
+ target.insert(0, [rule, replacement])
229
+ end
230
+
231
+ end
232
+ end
@@ -0,0 +1,38 @@
1
+ module Flexus
2
+ # Wraps inflections array
3
+ #
4
+ class RulesCollection
5
+ extend Forwardable
6
+
7
+ def_delegators :@collection,
8
+ :each, :insert, :empty?,
9
+ :size, :last, :include?
10
+
11
+ def initialize(*args)
12
+ @collection = Array.new(*args)
13
+ end
14
+
15
+ # Applies first found rule to given word
16
+ #
17
+ # @param [String] word
18
+ #
19
+ # @return [String]
20
+ # modified word
21
+ #
22
+ # @api private
23
+ #
24
+ def apply_to(word)
25
+ result = word.dup
26
+ each { |rule, replacement| break if result.gsub!(rule, replacement) }
27
+ result
28
+ end
29
+
30
+ def ==(other)
31
+ @collection == other.to_a
32
+ end
33
+
34
+ def to_a
35
+ @collection.dup
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Flexus
2
+ VERSION = "1.0.0.beta.1"
3
+ end
data/lib/flexus.rb ADDED
@@ -0,0 +1,331 @@
1
+ require 'set'
2
+
3
+ # The Flexus transforms words from singular to plural, class names to table names, modularized class names to ones without,
4
+ # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
5
+ # in inflections.rb.
6
+ #
7
+ # The Rails core team has stated patches for the inflections library will not be accepted
8
+ # in order to avoid breaking legacy applications which may be relying on errant inflections.
9
+ # If you discover an incorrect inflection and require it for your application, you'll need
10
+ # to correct it yourself (explained below).
11
+ module Flexus
12
+
13
+ # Convert input to UpperCamelCase
14
+ #
15
+ # Will also convert '/' to '::' which is useful for converting paths to namespaces.
16
+ #
17
+ # @param [String] input
18
+ #
19
+ # @example
20
+ # Flexus.camelize("data_mapper") # => "DataMapper"
21
+ # Flexus.camelize("data_mapper/errors") # => "DataMapper::Errors"
22
+ #
23
+ # @return [String]
24
+ #
25
+ # @api public
26
+ #
27
+ def self.camelize(input)
28
+ input.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:\A|_)(.)/) { $1.upcase }
29
+ end
30
+
31
+ # Convert input to underscored, lowercase string
32
+ #
33
+ # Changes '::' to '/' to convert namespaces to paths.
34
+ #
35
+ # @param [String] input
36
+ #
37
+ # @example
38
+ # Flexus.underscore("DataMapper") # => "data_mapper"
39
+ # Flexus.underscore("DataMapper::Errors") # => "data_mapper/errors"
40
+ #
41
+ # @return [String]
42
+ #
43
+ # @api public
44
+ #
45
+ def self.underscore(input)
46
+ word = input.gsub(/::/, '/')
47
+ underscorize(word)
48
+ end
49
+
50
+ # Convert input underscores to dashes
51
+ #
52
+ # @param [String] input
53
+ #
54
+ # @example
55
+ # Flexus.dasherize("foo_bar") # => "foo-bar"
56
+ #
57
+ # @return [String]
58
+ #
59
+ # @api public
60
+ #
61
+ def self.dasherize(input)
62
+ input.tr('_', '-')
63
+ end
64
+
65
+ # Return unscoped constant name
66
+ #
67
+ # @param [String] input
68
+ #
69
+ # @example
70
+ #
71
+ # Flexus.demodulize("DataMapper::Error") # => "Error"
72
+ # Flexus.demodulize("DataMapper") # => "DataMapper"
73
+ #
74
+ # @return [String]
75
+ #
76
+ # @api public
77
+ #
78
+ def self.demodulize(input)
79
+ input.split('::').last
80
+ end
81
+
82
+ # Creates a foreign key name
83
+ #
84
+ # @param [String] input
85
+ #
86
+ # @example
87
+ #
88
+ # Flexus.foreign_key("Message") => "message_id"
89
+ #
90
+ # @return [String]
91
+ #
92
+ # @api public
93
+ #
94
+ def self.foreign_key(input)
95
+ "#{underscorize(demodulize(input))}_id"
96
+ end
97
+
98
+ # Find a constant with the name specified in the argument string
99
+ #
100
+ # The name is assumed to be the one of a top-level constant, constant scope of caller is ignored
101
+ #
102
+ # @param [String] input
103
+ #
104
+ # @example
105
+ #
106
+ # Flexus.constantize("Module") # => Module
107
+ # Flexus.constantize("DataMapper::Error") # => DataMapper::Error
108
+ #
109
+ # @return [Class, Module]
110
+ #
111
+ # @api public
112
+ #
113
+ def self.constantize(input)
114
+ names = input.split('::')
115
+ names.shift if names.first.empty?
116
+
117
+ names.inject(Object) do |constant, name|
118
+ if constant.const_defined?(name, false)
119
+ constant.const_get(name)
120
+ else
121
+ constant.const_missing(name)
122
+ end
123
+ end
124
+ end
125
+
126
+ ORDINALIZE_TH = (4..16).to_set.freeze
127
+
128
+ # Convert a number into an ordinal string
129
+ #
130
+ # @param [Fixnum] number
131
+ #
132
+ # @example
133
+ #
134
+ # ordinalize(1) # => "1st"
135
+ # ordinalize(2) # => "2nd"
136
+ # ordinalize(1002) # => "1002nd"
137
+ # ordinalize(1003) # => "1003rd"
138
+ #
139
+ # @return [String]
140
+ #
141
+ # @api public
142
+ #
143
+ def self.ordinalize(number)
144
+ abs_value = number.abs
145
+
146
+ if ORDINALIZE_TH.include?(abs_value % 100)
147
+ "#{number}th"
148
+ else
149
+ case abs_value % 10
150
+ when 1; "#{number}st"
151
+ when 2; "#{number}nd"
152
+ when 3; "#{number}rd"
153
+ end
154
+ end
155
+ end
156
+
157
+ # Yields a singleton instance of Flexus::Inflections
158
+ #
159
+ # @example
160
+ #
161
+ # Flexus.inflections do |inflect|
162
+ # inflect.uncountable "rails"
163
+ # end
164
+ #
165
+ # @return [Flexus::Inflections]
166
+ #
167
+ # @api public
168
+ #
169
+ def self.inflections
170
+ instance = Inflections.instance
171
+ block_given? ? yield(instance) : instance
172
+ end
173
+
174
+ # Convert input word string to plural
175
+ #
176
+ # @param [String] word
177
+ #
178
+ # @example
179
+ #
180
+ # Flexus.pluralize("post") # => "posts"
181
+ # Flexus.pluralize("octopus") # => "octopi"
182
+ # Flexus.pluralize("sheep") # => "sheep"
183
+ # Flexus.pluralize("words") # => "words"
184
+ # Flexus.pluralize("CamelOctopus") # => "CamelOctopi"
185
+ #
186
+ # @return [String]
187
+ #
188
+ # @api public
189
+ #
190
+ def self.pluralize(word)
191
+ return word if uncountable?(word)
192
+ inflections.plurals.apply_to(word)
193
+ end
194
+
195
+ # Convert word to singular
196
+ #
197
+ # @param [String] word
198
+ #
199
+ # @example
200
+ #
201
+ # Flexus.singularize("posts") # => "post"
202
+ # Flexus.singularize("octopi") # => "octopus"
203
+ # Flexus.singularize("sheep") # => "sheep"
204
+ # Flexus.singularize("word") # => "word"
205
+ # Flexus.singularize("CamelOctopi") # => "CamelOctopus"
206
+ #
207
+ # @return [String]
208
+ #
209
+ # @api public
210
+ #
211
+ def self.singularize(word)
212
+ return word if uncountable?(word)
213
+ inflections.singulars.apply_to(word)
214
+ end
215
+
216
+ # Humanize string
217
+ #
218
+ # @param [String] input
219
+ #
220
+ # capitalizes the first word and turns underscores into spaces and strips a # trailing "_id", if any.
221
+ # Like +titleize+, this is meant for creating pretty output.
222
+ #
223
+ # @example
224
+ #
225
+ # Flexus.humanize("employee_salary") # => "Employee salary"
226
+ # Flexus.humanize("author_id") # => "Author"
227
+ #
228
+ # @return [String]
229
+ #
230
+ # @api public
231
+ #
232
+ def self.humanize(input)
233
+ result = inflections.humans.apply_to(input)
234
+ result.gsub!(/_id\z/, "")
235
+ result.tr!('_', " ")
236
+ result.capitalize!
237
+ result
238
+ end
239
+
240
+ # Tabelize input string
241
+ #
242
+ # @param [String] input
243
+ #
244
+ # Create the name of a table like Rails does for models to table names.
245
+ # This method # uses the +pluralize+ method on the last word in the string.
246
+ #
247
+ # @example
248
+ #
249
+ # Flexus.tableize("RawScaledScorer") # => "raw_scaled_scorers"
250
+ # Flexus.tableize("egg_and_ham") # => "egg_and_hams"
251
+ # Flexus.tableize("fancyCategory") # => "fancy_categories"
252
+ #
253
+ # @return [String]
254
+ #
255
+ # @api public
256
+ #
257
+ def self.tableize(input)
258
+ word = input.gsub(/::/, '_')
259
+ pluralize(underscorize(word))
260
+ end
261
+
262
+ # Classify input
263
+ #
264
+ # Create a class name from a plural table name like Rails does for table names to models.
265
+ # Note that this returns a string and not a Class.
266
+ #
267
+ # To convert to an actual class # follow +classify+ with +constantize+.
268
+ #
269
+ # @examples:
270
+ #
271
+ # Flexus.classify("egg_and_hams") # => "EggAndHam"
272
+ # Flexus.classify("posts") # => "Post"
273
+ #
274
+ # # Singular names are not handled correctly:
275
+ # Flexus.classify("business") # => "Busines"
276
+ #
277
+ # @return [String]
278
+ #
279
+ # @api public
280
+ #
281
+ def self.classify(table_name)
282
+ # strip out any leading schema name
283
+ camelize(singularize(table_name.sub(/.*\./, '')))
284
+ end
285
+
286
+ # Test if word is uncountable
287
+ #
288
+ # @example
289
+ #
290
+ # Flexus.uncountable?('rice') #=> true
291
+ # Flexus.uncountable?('apple') #=> false
292
+ #
293
+ # @param [String] word
294
+ #
295
+ # @return [Boolean]
296
+ # true, if word is uncountable
297
+ #
298
+ # @api public
299
+ #
300
+ def self.uncountable?(word)
301
+ word.empty? || inflections.uncountables.include?(word.downcase)
302
+ end
303
+
304
+ # Convert input to underscored, lowercase string
305
+ #
306
+ # Contains main logic for .underscore and .tableize
307
+ # Does nothing with '::' divider
308
+ #
309
+ # @param [String] input
310
+ #
311
+ # @example
312
+ # Flexus.underscorize("DataMapper") # => "data_mapper"
313
+ # Flexus.underscorize("DataMapper::Errors") # => "data_mapper::errors"
314
+ #
315
+ # @return [String]
316
+ #
317
+ # @api private
318
+ #
319
+ def self.underscorize(word)
320
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
321
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
322
+ word.tr!('-', '_')
323
+ word.downcase!
324
+ word
325
+ end
326
+ private_class_method :underscorize
327
+ end
328
+
329
+ require 'flexus/rules_collection'
330
+ require 'flexus/inflections'
331
+ require 'flexus/defaults'
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flexus
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.beta.1
5
+ platform: ruby
6
+ authors:
7
+ - The rails, merb & datamapper team
8
+ - Markus Schirp
9
+ - Andrii Savchenko
10
+ - Abinoam P. Marques Jr.
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2017-11-03 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: bundler
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: rspec
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ description: Inflector for strings
59
+ email:
60
+ - andrey@aejis.eu
61
+ - abinoam@gmail.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files:
65
+ - LICENSE.txt
66
+ - README.md
67
+ - TODO
68
+ files:
69
+ - ".gitignore"
70
+ - ".rspec"
71
+ - ".travis.yml"
72
+ - Changelog.md
73
+ - Gemfile
74
+ - Guardfile
75
+ - LICENSE.txt
76
+ - README.md
77
+ - Rakefile
78
+ - TODO
79
+ - flexus.gemspec
80
+ - lib/flexus.rb
81
+ - lib/flexus/defaults.rb
82
+ - lib/flexus/inflections.rb
83
+ - lib/flexus/rules_collection.rb
84
+ - lib/flexus/version.rb
85
+ homepage: https://github.com/Ptico/flexus
86
+ licenses:
87
+ - MIT
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '2.1'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">"
101
+ - !ruby/object:Gem::Version
102
+ version: 1.3.1
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.6.8
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Inflector for strings
109
+ test_files: []