sigterm_extensions 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +17 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE.md +0 -0
  5. data/README.md +0 -0
  6. data/bin/ctxirb +156 -0
  7. data/lib/git.rb +166 -0
  8. data/lib/git/LICENSE +21 -0
  9. data/lib/git/author.rb +14 -0
  10. data/lib/git/base.rb +551 -0
  11. data/lib/git/base/factory.rb +75 -0
  12. data/lib/git/branch.rb +126 -0
  13. data/lib/git/branches.rb +71 -0
  14. data/lib/git/config.rb +22 -0
  15. data/lib/git/diff.rb +159 -0
  16. data/lib/git/index.rb +5 -0
  17. data/lib/git/lib.rb +1041 -0
  18. data/lib/git/log.rb +128 -0
  19. data/lib/git/object.rb +312 -0
  20. data/lib/git/path.rb +31 -0
  21. data/lib/git/remote.rb +36 -0
  22. data/lib/git/repository.rb +6 -0
  23. data/lib/git/stash.rb +27 -0
  24. data/lib/git/stashes.rb +55 -0
  25. data/lib/git/status.rb +199 -0
  26. data/lib/git/version.rb +5 -0
  27. data/lib/git/working_directory.rb +4 -0
  28. data/lib/sigterm_extensions.rb +75 -0
  29. data/lib/sigterm_extensions/all.rb +12 -0
  30. data/lib/sigterm_extensions/backtrace_cleaner.rb +129 -0
  31. data/lib/sigterm_extensions/callbacks.rb +847 -0
  32. data/lib/sigterm_extensions/concern.rb +169 -0
  33. data/lib/sigterm_extensions/configurable.rb +38 -0
  34. data/lib/sigterm_extensions/core_ext.rb +4 -0
  35. data/lib/sigterm_extensions/core_ext/array.rb +3 -0
  36. data/lib/sigterm_extensions/core_ext/array/extract.rb +19 -0
  37. data/lib/sigterm_extensions/core_ext/array/extract_options.rb +29 -0
  38. data/lib/sigterm_extensions/core_ext/class.rb +3 -0
  39. data/lib/sigterm_extensions/core_ext/class/attribute.rb +139 -0
  40. data/lib/sigterm_extensions/core_ext/class/attribute_accessors.rb +4 -0
  41. data/lib/sigterm_extensions/core_ext/class/subclasses.rb +52 -0
  42. data/lib/sigterm_extensions/core_ext/custom.rb +12 -0
  43. data/lib/sigterm_extensions/core_ext/digest.rb +3 -0
  44. data/lib/sigterm_extensions/core_ext/digest/uuid.rb +51 -0
  45. data/lib/sigterm_extensions/core_ext/enumerable.rb +232 -0
  46. data/lib/sigterm_extensions/core_ext/file.rb +3 -0
  47. data/lib/sigterm_extensions/core_ext/file/atomic.rb +68 -0
  48. data/lib/sigterm_extensions/core_ext/hash.rb +3 -0
  49. data/lib/sigterm_extensions/core_ext/hash/deep_merge.rb +41 -0
  50. data/lib/sigterm_extensions/core_ext/hash/deep_transform_values.rb +44 -0
  51. data/lib/sigterm_extensions/core_ext/hash/except.rb +22 -0
  52. data/lib/sigterm_extensions/core_ext/hash/keys.rb +141 -0
  53. data/lib/sigterm_extensions/core_ext/hash/reverse_merge.rb +23 -0
  54. data/lib/sigterm_extensions/core_ext/hash/slice.rb +24 -0
  55. data/lib/sigterm_extensions/core_ext/kernel.rb +3 -0
  56. data/lib/sigterm_extensions/core_ext/kernel/concern.rb +12 -0
  57. data/lib/sigterm_extensions/core_ext/kernel/reporting.rb +43 -0
  58. data/lib/sigterm_extensions/core_ext/kernel/singleton_class.rb +6 -0
  59. data/lib/sigterm_extensions/core_ext/load_error.rb +7 -0
  60. data/lib/sigterm_extensions/core_ext/module.rb +3 -0
  61. data/lib/sigterm_extensions/core_ext/module/aliasing.rb +29 -0
  62. data/lib/sigterm_extensions/core_ext/module/anonymous.rb +28 -0
  63. data/lib/sigterm_extensions/core_ext/module/attr_internal.rb +36 -0
  64. data/lib/sigterm_extensions/core_ext/module/attribute_accessors.rb +208 -0
  65. data/lib/sigterm_extensions/core_ext/module/attribute_accessors_per_thread.rb +146 -0
  66. data/lib/sigterm_extensions/core_ext/module/concerning.rb +132 -0
  67. data/lib/sigterm_extensions/core_ext/module/delegation.rb +319 -0
  68. data/lib/sigterm_extensions/core_ext/module/redefine_method.rb +38 -0
  69. data/lib/sigterm_extensions/core_ext/module/remove_method.rb +15 -0
  70. data/lib/sigterm_extensions/core_ext/name_error.rb +36 -0
  71. data/lib/sigterm_extensions/core_ext/object.rb +3 -0
  72. data/lib/sigterm_extensions/core_ext/object/blank.rb +153 -0
  73. data/lib/sigterm_extensions/core_ext/object/colors.rb +39 -0
  74. data/lib/sigterm_extensions/core_ext/object/duplicable.rb +47 -0
  75. data/lib/sigterm_extensions/core_ext/object/inclusion.rb +27 -0
  76. data/lib/sigterm_extensions/core_ext/object/instance_variables.rb +28 -0
  77. data/lib/sigterm_extensions/core_ext/object/methods.rb +61 -0
  78. data/lib/sigterm_extensions/core_ext/object/with_options.rb +80 -0
  79. data/lib/sigterm_extensions/core_ext/range.rb +3 -0
  80. data/lib/sigterm_extensions/core_ext/range/compare_range.rb +74 -0
  81. data/lib/sigterm_extensions/core_ext/range/conversions.rb +39 -0
  82. data/lib/sigterm_extensions/core_ext/range/overlaps.rb +8 -0
  83. data/lib/sigterm_extensions/core_ext/securerandom.rb +43 -0
  84. data/lib/sigterm_extensions/core_ext/string.rb +3 -0
  85. data/lib/sigterm_extensions/core_ext/string/access.rb +93 -0
  86. data/lib/sigterm_extensions/core_ext/string/filters.rb +143 -0
  87. data/lib/sigterm_extensions/core_ext/string/starts_ends_with.rb +4 -0
  88. data/lib/sigterm_extensions/core_ext/string/strip.rb +25 -0
  89. data/lib/sigterm_extensions/core_ext/tryable.rb +132 -0
  90. data/lib/sigterm_extensions/descendants_tracker.rb +108 -0
  91. data/lib/sigterm_extensions/gem_methods.rb +47 -0
  92. data/lib/sigterm_extensions/hash_binding.rb +16 -0
  93. data/lib/sigterm_extensions/inflector.rb +339 -0
  94. data/lib/sigterm_extensions/inflector/acronyms.rb +42 -0
  95. data/lib/sigterm_extensions/inflector/inflections.rb +249 -0
  96. data/lib/sigterm_extensions/inflector/inflections/defaults.rb +117 -0
  97. data/lib/sigterm_extensions/inflector/rules.rb +37 -0
  98. data/lib/sigterm_extensions/inflector/version.rb +8 -0
  99. data/lib/sigterm_extensions/interactive_editor.rb +120 -0
  100. data/lib/sigterm_extensions/lazy.rb +34 -0
  101. data/lib/sigterm_extensions/lazy_load_hooks.rb +79 -0
  102. data/lib/sigterm_extensions/option_merger.rb +32 -0
  103. data/lib/sigterm_extensions/ordered_hash.rb +48 -0
  104. data/lib/sigterm_extensions/ordered_options.rb +83 -0
  105. data/lib/sigterm_extensions/paths.rb +235 -0
  106. data/lib/sigterm_extensions/per_thread_registry.rb +58 -0
  107. data/lib/sigterm_extensions/proxy_object.rb +14 -0
  108. data/lib/sigterm_extensions/staging/boot.rb +31 -0
  109. data/lib/sigterm_extensions/staging/boot/bundler_patch.rb +24 -0
  110. data/lib/sigterm_extensions/staging/boot/command.rb +26 -0
  111. data/lib/sigterm_extensions/staging/boot/gemfile_next_auto_sync.rb +79 -0
  112. data/lib/sigterm_extensions/version.rb +4 -0
  113. data/lib/sigterm_extensions/wrappable.rb +16 -0
  114. data/sigterm_extensions.gemspec +42 -0
  115. data/templates/dotpryrc.rb.erb +124 -0
  116. metadata +315 -0
@@ -0,0 +1,4 @@
1
+ class String
2
+ alias_method :starts_with?, :start_with?
3
+ alias_method :ends_with?, :end_with?
4
+ end
@@ -0,0 +1,25 @@
1
+ class String
2
+ # Strips indentation in heredocs.
3
+ #
4
+ # For example in
5
+ #
6
+ # if options[:usage]
7
+ # puts <<-USAGE.strip_heredoc
8
+ # This command does such and such.
9
+ #
10
+ # Supported options are:
11
+ # -h This message
12
+ # ...
13
+ # USAGE
14
+ # end
15
+ #
16
+ # the user would see the usage message aligned against the left margin.
17
+ #
18
+ # Technically, it looks for the least indented non-empty line
19
+ # in the whole string, and removes that amount of leading whitespace.
20
+ def strip_heredoc
21
+ gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "").tap do |stripped|
22
+ stripped.freeze if frozen?
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,132 @@
1
+ module ActiveSupport
2
+ module Tryable #:nodoc:
3
+ def try(method_name = nil, *args, &b)
4
+ if method_name.nil? && block_given?
5
+ if b.arity == 0
6
+ instance_eval(&b)
7
+ else
8
+ yield self
9
+ end
10
+ elsif respond_to?(method_name)
11
+ public_send(method_name, *args, &b)
12
+ end
13
+ end
14
+
15
+ def try!(method_name = nil, *args, &b)
16
+ if method_name.nil? && block_given?
17
+ if b.arity == 0
18
+ instance_eval(&b)
19
+ else
20
+ yield self
21
+ end
22
+ else
23
+ public_send(method_name, *args, &b)
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ class Object
30
+ include ActiveSupport::Tryable
31
+ ##
32
+ # :method: try
33
+ #
34
+ # :call-seq:
35
+ # try(*a, &b)
36
+ #
37
+ # Invokes the public method whose name goes as first argument just like
38
+ # +public_send+ does, except that if the receiver does not respond to it the
39
+ # call returns +nil+ rather than raising an exception.
40
+ #
41
+ # This method is defined to be able to write
42
+ #
43
+ # @person.try(:name)
44
+ #
45
+ # instead of
46
+ #
47
+ # @person.name if @person
48
+ #
49
+ # +try+ calls can be chained:
50
+ #
51
+ # @person.try(:spouse).try(:name)
52
+ #
53
+ # instead of
54
+ #
55
+ # @person.spouse.name if @person && @person.spouse
56
+ #
57
+ # +try+ will also return +nil+ if the receiver does not respond to the method:
58
+ #
59
+ # @person.try(:non_existing_method) # => nil
60
+ #
61
+ # instead of
62
+ #
63
+ # @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
64
+ #
65
+ # +try+ returns +nil+ when called on +nil+ regardless of whether it responds
66
+ # to the method:
67
+ #
68
+ # nil.try(:to_i) # => nil, rather than 0
69
+ #
70
+ # Arguments and blocks are forwarded to the method if invoked:
71
+ #
72
+ # @posts.try(:each_slice, 2) do |a, b|
73
+ # ...
74
+ # end
75
+ #
76
+ # The number of arguments in the signature must match. If the object responds
77
+ # to the method the call is attempted and +ArgumentError+ is still raised
78
+ # in case of argument mismatch.
79
+ #
80
+ # If +try+ is called without arguments it yields the receiver to a given
81
+ # block unless it is +nil+:
82
+ #
83
+ # @person.try do |p|
84
+ # ...
85
+ # end
86
+ #
87
+ # You can also call try with a block without accepting an argument, and the block
88
+ # will be instance_eval'ed instead:
89
+ #
90
+ # @person.try { upcase.truncate(50) }
91
+ #
92
+ # Please also note that +try+ is defined on +Object+. Therefore, it won't work
93
+ # with instances of classes that do not have +Object+ among their ancestors,
94
+ # like direct subclasses of +BasicObject+.
95
+
96
+ ##
97
+ # :method: try!
98
+ #
99
+ # :call-seq:
100
+ # try!(*a, &b)
101
+ #
102
+ # Same as #try, but raises a +NoMethodError+ exception if the receiver is
103
+ # not +nil+ and does not implement the tried method.
104
+ #
105
+ # "a".try!(:upcase) # => "A"
106
+ # nil.try!(:upcase) # => nil
107
+ # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Integer
108
+ end
109
+
110
+ class NilClass
111
+ # Calling +try+ on +nil+ always returns +nil+.
112
+ # It becomes especially helpful when navigating through associations that may return +nil+.
113
+ #
114
+ # nil.try(:name) # => nil
115
+ #
116
+ # Without +try+
117
+ # @person && @person.children.any? && @person.children.first.name
118
+ #
119
+ # With +try+
120
+ # @person.try(:children).try(:first).try(:name)
121
+ def try(_method_name = nil, *, **)
122
+ nil
123
+ end
124
+
125
+ # Calling +try!+ on +nil+ always returns +nil+.
126
+ #
127
+ # nil.try!(:name) # => nil
128
+ def try!(_method_name = nil, *, **)
129
+ nil
130
+ end
131
+ end
132
+
@@ -0,0 +1,108 @@
1
+ require "weakref"
2
+
3
+ module SigtermExtensions
4
+ # This module provides an internal implementation to track descendants
5
+ # which is faster than iterating through ObjectSpace.
6
+ module DescendantsTracker
7
+ @@direct_descendants = {}
8
+
9
+ class << self
10
+ def direct_descendants(klass)
11
+ descendants = @@direct_descendants[klass]
12
+ descendants ? descendants.to_a : []
13
+ end
14
+
15
+ def descendants(klass)
16
+ arr = []
17
+ accumulate_descendants(klass, arr)
18
+ arr
19
+ end
20
+
21
+ def clear
22
+ if defined? ActiveSupport::Dependencies
23
+ @@direct_descendants.each do |klass, descendants|
24
+ if Dependencies.autoloaded?(klass)
25
+ @@direct_descendants.delete(klass)
26
+ else
27
+ descendants.reject! { |v| Dependencies.autoloaded?(v) }
28
+ end
29
+ end
30
+ else
31
+ @@direct_descendants.clear
32
+ end
33
+ end
34
+
35
+ # This is the only method that is not thread safe, but is only ever called
36
+ # during the eager loading phase.
37
+ def store_inherited(klass, descendant)
38
+ (@@direct_descendants[klass] ||= DescendantsArray.new) << descendant
39
+ end
40
+
41
+ private
42
+ def accumulate_descendants(klass, acc)
43
+ if direct_descendants = @@direct_descendants[klass]
44
+ direct_descendants.each do |direct_descendant|
45
+ acc << direct_descendant
46
+ accumulate_descendants(direct_descendant, acc)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def inherited(base)
53
+ DescendantsTracker.store_inherited(self, base)
54
+ super
55
+ end
56
+
57
+ def direct_descendants
58
+ DescendantsTracker.direct_descendants(self)
59
+ end
60
+
61
+ def descendants
62
+ DescendantsTracker.descendants(self)
63
+ end
64
+
65
+ # DescendantsArray is an array that contains weak references to classes.
66
+ class DescendantsArray # :nodoc:
67
+ include Enumerable
68
+
69
+ def initialize
70
+ @refs = []
71
+ end
72
+
73
+ def initialize_copy(orig)
74
+ @refs = @refs.dup
75
+ end
76
+
77
+ def <<(klass)
78
+ @refs << WeakRef.new(klass)
79
+ end
80
+
81
+ def each
82
+ @refs.reject! do |ref|
83
+ yield ref.__getobj__
84
+ false
85
+ rescue WeakRef::RefError
86
+ true
87
+ end
88
+ self
89
+ end
90
+
91
+ def refs_size
92
+ @refs.size
93
+ end
94
+
95
+ def cleanup!
96
+ @refs.delete_if { |ref| !ref.weakref_alive? }
97
+ end
98
+
99
+ def reject!
100
+ @refs.reject! do |ref|
101
+ yield ref.__getobj__
102
+ rescue WeakRef::RefError
103
+ true
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,47 @@
1
+ module SigtermExtensions
2
+ module GemMethods
3
+
4
+ # This can require a gem that's not in the Gemfile or gemspec
5
+ def unbundled_require(gem)
6
+ if defined?(::Bundler)
7
+ spec_path = Dir.glob("#{Gem.dir}/specifications/#{gem}-*.gemspec").last
8
+ if spec_path.nil?
9
+ warn "Couldn't find #{gem}"
10
+ return
11
+ end
12
+
13
+ spec = Gem::Specification.load spec_path
14
+ spec.activate
15
+ end
16
+
17
+ begin
18
+ require gem
19
+ yield if block_given?
20
+ rescue Exception => err
21
+ warn "Couldn't load #{gem}: #{err}"
22
+ end
23
+ end
24
+
25
+ def load_everything!
26
+ Gem::Specification.all.each do |gem|
27
+ gem.load_paths.each do |p|
28
+ $:.unshift(p) unless $LOAD_PATH.include?(p)
29
+ end
30
+ end
31
+ end
32
+
33
+ def add_gems_global_to_path
34
+ # This will load all global gems to load path
35
+ if defined?(::Bundler)
36
+ global_gemset = ENV['GEM_PATH'].split(':').grep(/ruby.*@global/).first
37
+ if global_gemset
38
+ all_global_gem_paths = Dir.glob("#{global_gemset}/gems/*")
39
+ all_global_gem_paths.each do |p|
40
+ gem_path = "#{p}/lib"
41
+ $LOAD_PATH << gem_path
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ module SigtermExtensions
2
+ class HashBinding
3
+ def initialize(hash)
4
+ @hash = hash.dup
5
+ end
6
+ def method_missing(m, *args, &block)
7
+ @hash[m.to_s]
8
+ end
9
+ def get_binding
10
+ binding
11
+ end
12
+ end
13
+ end
14
+ # while /<%=.*%>/.match(str) != nil
15
+ # str = ERB.new(str).result(binding)
16
+ # end
@@ -0,0 +1,339 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SigtermExtensions
4
+ # inflector
5
+ #
6
+ # @since 0.1.0
7
+ class Inflector
8
+ require_relative "inflector/version"
9
+ require_relative "inflector/inflections"
10
+
11
+ # Instantiate the inflector
12
+ #
13
+ # @param blk [Proc] an optional block to specify custom inflection rules
14
+ # @yieldparam [SigtermExtensions::Inflector::Inflections] the inflection rules
15
+ #
16
+ # @return [SigtermExtensions::Inflector] the inflector
17
+ #
18
+ # @since 0.1.0
19
+ #
20
+ # @example Basic usage
21
+ # require "sigterm_extensions/inflector"
22
+ #
23
+ # inflector = SigtermExtensions::Inflector.new
24
+ #
25
+ # @example Custom inflection rules
26
+ # require "sigterm_extensions/inflector"
27
+ #
28
+ # inflector = SigtermExtensions::Inflector.new do |inflections|
29
+ # inflections.plural "virus", "viruses" # specify a rule for #pluralize
30
+ # inflections.singular "thieves", "thief" # specify a rule for #singularize
31
+ # inflections.uncountable "inflector" # add an exception for an uncountable word
32
+ # end
33
+ def initialize(&blk)
34
+ @inflections = Inflections.build(&blk)
35
+ end
36
+
37
+ # Lower camelize a string
38
+ #
39
+ # @param input [String,Symbol] the input
40
+ # @return [String] the lower camelized string
41
+ #
42
+ # @since 0.1.3
43
+ #
44
+ # @example
45
+ # require "sigterm_extensions/inflector"
46
+ #
47
+ # inflector = SigtermExtensions::Inflector.new
48
+ # inflector.camelize_lower("data_mapper") # => "dataMapper"
49
+ def camelize_lower(input)
50
+ internal_camelize(input, false)
51
+ end
52
+
53
+ # Upper camelize a string
54
+ #
55
+ # @param input [String,Symbol] the input
56
+ # @return [String] the upper camelized string
57
+ #
58
+ # @since 0.1.3
59
+ #
60
+ # @example
61
+ # require "sigterm_extensions/inflector"
62
+ #
63
+ # inflector = SigtermExtensions::Inflector.new
64
+ # inflector.camelize_upper("data_mapper") # => "DataMapper"
65
+ # inflector.camelize_upper("sigterm_extensions/inflector") # => "SigtermExtensions::Inflector"
66
+ def camelize_upper(input)
67
+ internal_camelize(input, true)
68
+ end
69
+
70
+ alias :camelize :camelize_upper
71
+
72
+
73
+ # Find a constant with the name specified in the argument string
74
+ #
75
+ # The name is assumed to be the one of a top-level constant,
76
+ # constant scope of caller is ignored
77
+ #
78
+ # @param input [String,Symbol] the input
79
+ # @return [Class, Module] the class or module
80
+ #
81
+ # @since 0.1.0
82
+ #
83
+ # @example
84
+ # require "sigterm_extensions/inflector"
85
+ #
86
+ # inflector = SigtermExtensions::Inflector.new
87
+ # inflector.constantize("Module") # => Module
88
+ # inflector.constantize("SigtermExtensions::Inflector") # => SigtermExtensions::Inflector
89
+ def constantize(input)
90
+ Object.const_get(input)
91
+ end
92
+
93
+ # Classify a string
94
+ #
95
+ # @param input [String,Symbol] the input
96
+ # @return [String] the classified string
97
+ #
98
+ # @since 0.1.0
99
+ #
100
+ # @example
101
+ # require "sigterm_extensions/inflector"
102
+ #
103
+ # inflector = SigtermExtensions::Inflector.new
104
+ # inflector.classify("books") # => "Book"
105
+ def classify(input)
106
+ camelize(singularize(input.to_s.sub(/.*\./, "")))
107
+ end
108
+
109
+ # Dasherize a string
110
+ #
111
+ # @param input [String,Symbol] the input
112
+ # @return [String] the dasherized string
113
+ #
114
+ # @since 0.1.0
115
+ #
116
+ # @example
117
+ # require "sigterm_extensions/inflector"
118
+ #
119
+ # inflector = SigtermExtensions::Inflector.new
120
+ # inflector.dasherize("sigterm_extensions_inflector") # => "sigterm-extensions-inflector"
121
+ def dasherize(input)
122
+ input.to_s.tr("_", "-")
123
+ end
124
+
125
+ # Demodulize a string
126
+ #
127
+ # @param input [String,Symbol] the input
128
+ # @return [String] the demodulized string
129
+ #
130
+ # @since 0.1.0
131
+ #
132
+ # @example
133
+ # require "sigterm_extensions/inflector"
134
+ #
135
+ # inflector = SigtermExtensions::Inflector.new
136
+ # inflector.demodulize("SigtermExtensions::Inflector") # => "Inflector"
137
+ def demodulize(input)
138
+ input.to_s.split("::").last
139
+ end
140
+
141
+ # Humanize a string
142
+ #
143
+ # @param input [String,Symbol] the input
144
+ # @return [String] the humanized string
145
+ #
146
+ # @since 0.1.0
147
+ #
148
+ # @example
149
+ # require "sigterm_extensions/inflector"
150
+ #
151
+ # inflector = SigtermExtensions::Inflector.new
152
+ # inflector.humanize("dry_inflector") # => "SigtermExtensions inflector"
153
+ # inflector.humanize("author_id") # => "Author"
154
+ def humanize(input)
155
+ input = input.to_s
156
+ result = inflections.humans.apply_to(input)
157
+ result.chomp!("_id")
158
+ result.tr!("_", " ")
159
+ match = /(?<separator>\W)/.match(result)
160
+ separator = match ? match[:separator] : DEFAULT_SEPARATOR
161
+ result.split(separator).map.with_index { |word, index|
162
+ inflections.acronyms.apply_to(word, index.zero?)
163
+ }.join(separator)
164
+ end
165
+
166
+ # Creates a foreign key name
167
+ #
168
+ # @param input [String, Symbol] the input
169
+ # @return [String] foreign key
170
+ #
171
+ # @example
172
+ # require "sigterm_extensions/inflector"
173
+ #
174
+ # inflector = SigtermExtensions::Inflector.new
175
+ # inflector.foreign_key("Message") => "message_id"
176
+ def foreign_key(input)
177
+ "#{underscore(demodulize(input))}_id"
178
+ end
179
+
180
+ # Ordinalize a number
181
+ #
182
+ # @param number [Integer] the input
183
+ # @return [String] the ordinalized number
184
+ #
185
+ # @since 0.1.0
186
+ #
187
+ # @example
188
+ # require "sigterm_extensions/inflector"
189
+ #
190
+ # inflector = SigtermExtensions::Inflector.new
191
+ # inflector.ordinalize(1) # => "1st"
192
+ # inflector.ordinalize(2) # => "2nd"
193
+ # inflector.ordinalize(3) # => "3rd"
194
+ # inflector.ordinalize(10) # => "10th"
195
+ # inflector.ordinalize(23) # => "23rd"
196
+ def ordinalize(number) # rubocop:disable Metrics/MethodLength
197
+ abs_value = number.abs
198
+
199
+ if ORDINALIZE_TH.key?(abs_value % 100)
200
+ "#{number}th"
201
+ else
202
+ case abs_value % 10
203
+ when 1 then "#{number}st"
204
+ when 2 then "#{number}nd"
205
+ when 3 then "#{number}rd"
206
+ else "#{number}th"
207
+ end
208
+ end
209
+ end
210
+
211
+ # Pluralize a string
212
+ #
213
+ # @param input [String,Symbol] the input
214
+ # @return [String] the pluralized string
215
+ #
216
+ # @since 0.1.0
217
+ #
218
+ # @example
219
+ # require "sigterm_extensions/inflector"
220
+ #
221
+ # inflector = SigtermExtensions::Inflector.new
222
+ # inflector.pluralize("book") # => "books"
223
+ # inflector.pluralize("money") # => "money"
224
+ def pluralize(input)
225
+ input = input.to_s
226
+ return input if uncountable?(input)
227
+ inflections.plurals.apply_to(input)
228
+ end
229
+
230
+ # Singularize a string
231
+ #
232
+ # @param input [String] the input
233
+ # @return [String] the singularized string
234
+ #
235
+ # @since 0.1.0
236
+ #
237
+ # @example
238
+ # require "sigterm_extensions/inflector"
239
+ #
240
+ # inflector = SigtermExtensions::Inflector.new
241
+ # inflector.singularize("books") # => "book"
242
+ # inflector.singularize("money") # => "money"
243
+ def singularize(input)
244
+ input = input.to_s
245
+ return input if uncountable?(input)
246
+ inflections.singulars.apply_to(input)
247
+ end
248
+
249
+ # Tableize a string
250
+ #
251
+ # @param input [String,Symbol] the input
252
+ # @return [String] the tableized string
253
+ #
254
+ # @since 0.1.0
255
+ #
256
+ # @example
257
+ # require "sigterm_extensions/inflector"
258
+ #
259
+ # inflector = SigtermExtensions::Inflector.new
260
+ # inflector.tableize("Book") # => "books"
261
+ def tableize(input)
262
+ input = input.to_s.gsub(/::/, "_")
263
+ pluralize(underscore(input))
264
+ end
265
+
266
+ # Underscore a string
267
+ #
268
+ # @param input [String,Symbol] the input
269
+ # @return [String] the underscored string
270
+ #
271
+ # @since 0.1.0
272
+ #
273
+ # @example
274
+ # require "sigterm_extensions/inflector"
275
+ #
276
+ # inflector = SigtermExtensions::Inflector.new
277
+ # inflector.underscore("sigterm-extensions-inflector") # => "sigterm_extensions_inflector"
278
+ def underscore(input)
279
+ input = input.to_s.gsub("::", "/")
280
+ input.gsub!(inflections.acronyms.regex) do
281
+ m1 = Regexp.last_match(1)
282
+ m2 = Regexp.last_match(2)
283
+ "#{m1 ? '_' : '' }#{m2.downcase}"
284
+ end
285
+ input.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
286
+ input.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
287
+ input.tr!("-", "_")
288
+ input.downcase!
289
+ input
290
+ end
291
+
292
+ # Check if the input is an uncountable word
293
+ #
294
+ # @param input [String] the input
295
+ # @return [TrueClass,FalseClass] the result of the check
296
+ #
297
+ # @since 0.1.0
298
+ # @api private
299
+ def uncountable?(input)
300
+ !(input =~ /\A[[:space:]]*\z/).nil? || inflections.uncountables.include?(input.downcase)
301
+ end
302
+
303
+ # @return [String]
304
+ #
305
+ # @since 0.2.0
306
+ # @api public
307
+ def to_s
308
+ '#<SigtermExtensions::Inflector>'
309
+ end
310
+ alias inspect to_s
311
+
312
+ private
313
+
314
+ # @since 0.1.0
315
+ # @api private
316
+ ORDINALIZE_TH = (11..13).each_with_object({}) { |n, ret| ret[n] = true }.freeze
317
+
318
+ # @since 0.1.2
319
+ # @api private
320
+ DEFAULT_SEPARATOR = " "
321
+
322
+ attr_reader :inflections
323
+
324
+ # @since 0.1.3
325
+ # @api private
326
+ def internal_camelize(input, upper)
327
+ input = input.to_s.dup
328
+ input.sub!(/^[a-z\d]*/) { |match| inflections.acronyms.apply_to(match, upper) }
329
+ input.gsub!(%r{(?:_|(/))([a-z\d]*)}i) do
330
+ m1 = Regexp.last_match(1)
331
+ m2 = Regexp.last_match(2)
332
+ "#{m1}#{inflections.acronyms.apply_to(m2)}"
333
+ end
334
+ input.gsub!("/", "::")
335
+ input
336
+ end
337
+
338
+ end
339
+ end