corefines 1.0.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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.adoc +198 -0
  4. data/Rakefile +23 -0
  5. data/lib/corefines/array.rb +30 -0
  6. data/lib/corefines/enumerable.rb +64 -0
  7. data/lib/corefines/hash.rb +164 -0
  8. data/lib/corefines/module.rb +95 -0
  9. data/lib/corefines/object.rb +389 -0
  10. data/lib/corefines/string.rb +211 -0
  11. data/lib/corefines/support/alias_submodules.rb +103 -0
  12. data/lib/corefines/support/classes_including_module.rb +27 -0
  13. data/lib/corefines/support/fake_refinements.rb +86 -0
  14. data/lib/corefines/symbol.rb +32 -0
  15. data/lib/corefines/version.rb +3 -0
  16. data/lib/corefines.rb +14 -0
  17. data/spec/array/second_spec.rb +10 -0
  18. data/spec/array/third_spec.rb +10 -0
  19. data/spec/enumerable/index_by_spec.rb +13 -0
  20. data/spec/enumerable/map_send_spec.rb +24 -0
  21. data/spec/hash/compact_spec.rb +48 -0
  22. data/spec/hash/op_plus_spec.rb +11 -0
  23. data/spec/hash/rekey_spec.rb +100 -0
  24. data/spec/hash/symbolize_keys_spec.rb +21 -0
  25. data/spec/module/alias_class_method_spec.rb +21 -0
  26. data/spec/module/alias_method_chain_spec.rb +76 -0
  27. data/spec/object/blank_spec.rb +128 -0
  28. data/spec/object/deep_dup_spec.rb +61 -0
  29. data/spec/object/else_spec.rb +24 -0
  30. data/spec/object/in_spec.rb +21 -0
  31. data/spec/object/instance_values_spec.rb +22 -0
  32. data/spec/object/then_if_spec.rb +64 -0
  33. data/spec/object/then_spec.rb +26 -0
  34. data/spec/object/try_spec.rb +47 -0
  35. data/spec/spec_helper.rb +30 -0
  36. data/spec/string/color_spec.rb +82 -0
  37. data/spec/string/concat_spec.rb +36 -0
  38. data/spec/string/decolor_spec.rb +27 -0
  39. data/spec/string/remove_spec.rb +57 -0
  40. data/spec/string/unindent_spec.rb +66 -0
  41. data/spec/support/alias_submodules_spec.rb +83 -0
  42. data/spec/support/classes_including_module_spec.rb +35 -0
  43. data/spec/support/fake_refinements_spec.rb +118 -0
  44. data/spec/symbol/call_spec.rb +16 -0
  45. metadata +175 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a692c4abce603d86050acd275daec0cdd2c303e6
4
+ data.tar.gz: 7d8a10a41f2eff14a549d7f192ed1485ecf2348e
5
+ SHA512:
6
+ metadata.gz: 8507ca0ade2ee56e6e430a552fc5edfd32a28dbe9e434b61f8b884143a9cf25c604b3ce71b5f76dbec7663a77cda404cde7689569cbca89e7c0f568dfb046a7d
7
+ data.tar.gz: e3136911b789c35cdc559c0280fcd31bae9a64f6465b7a8eaa3428e1a368da021e65c69c406a2c3e2bf7320897739f6571d6d586054e43945e00f01826a8ac62
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright 2015 Jakub Jirutka <jakub@jirutka.cz>.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.adoc ADDED
@@ -0,0 +1,198 @@
1
+ = Corefines
2
+ Jakub Jirutka <https://github.com/jirutka[@jirutka]>
3
+ :page-layout: base
4
+ :idprefix:
5
+ ifdef::env-github[:idprefix: user-content-]
6
+ :idseparator: -
7
+ :source-language: ruby
8
+ :language: {source-language}
9
+ // custom
10
+ :gem-name: corefines
11
+ :gh-name: jirutka/{gem-name}
12
+ :gh-branch: master
13
+ :badge-style: flat
14
+ :doc-base-url: http://www.rubydoc.info/github/jirutka/corefines/Corefines
15
+
16
+ ifdef::env-github[]
17
+ image:https://img.shields.io/travis/{gh-name}/{gh-branch}.svg?style={badge-style}[Build Status, link="https://travis-ci.org/{gh-name}"]
18
+ image:https://img.shields.io/codeclimate/coverage/github/{gh-name}.svg?style={badge-style}[Test Coverage, link="https://codeclimate.com/github/{gh-name}"]
19
+ image:https://img.shields.io/codeclimate/github/{gh-name}.svg?style={badge-style}[Code Climate, link="https://codeclimate.com/github/{gh-name}"]
20
+ image:https://img.shields.io/gem/v/{gem-name}.svg?style={badge-style}[Gem Version, link="https://rubygems.org/gems/{gem-name}"]
21
+ image:https://img.shields.io/badge/yard-docs-blue.svg?style={badge-style}[Yard Docs, link="http://www.rubydoc.info/github/{gh-name}/frames"]
22
+ endif::env-github[]
23
+
24
+ Corefines is a collection of general purpose _refinements_ for extending the core capabilities of Ruby’s built-in classes.
25
+ It also provides a <<compatibility-mode>> for older Ruby versions and alternative Ruby implementations that don’t support refinements (yet).
26
+
27
+
28
+ == Why refinements?
29
+
30
+ Extending core classes with so called monkey-paching pollutes the _global_ scope, so it affects all files on the `$LOAD_PATH`, i.e. whole application including used gems.
31
+ It’s not usually so big deal when you’re doing it in your application, but it’s very dangerous when used in a gem (library).
32
+ This can result in strange and hard to debug behaviour if another gem overrides a core class with the same method as your gem, but different implementation, and both gems are used together.
33
+
34
+ Refinements basically allows you to put monkey patches in an isolated namespace, so that your changes to core classes don’t affect other code.
35
+
36
+ TODO
37
+
38
+
39
+ == Installation
40
+
41
+ Add this line to your application’s Gemfile:
42
+
43
+ [source]
44
+ gem 'corefines', '~> 1.0'
45
+
46
+ or to your gemspec:
47
+
48
+ [source]
49
+ s.add_runtime_dependency 'corefines', '~> 1.0'
50
+
51
+ and then execute:
52
+
53
+ [source, sh]
54
+ $ bundle install
55
+
56
+
57
+ == Using
58
+
59
+ First, you must require `corefines` prior using:
60
+
61
+ [source]
62
+ require 'corefines'
63
+
64
+ This will _not_ activate any extensions (just register them), even when running in compatibility mode.
65
+ Extensions (refinements) are activated selectively with the method http://ruby-doc.org/core-2.2.0/Module.html#method-i-using[`using`].
66
+
67
+ Refinements are organized into modules by class which they refine, and further into submodules for individual methods.
68
+ When an extension refines multiple classes, then it’s included in a module named after their nearest common ancestor (superclass).
69
+
70
+ [source, plain]
71
+ Corefines::<CLASS>::<METHOD>
72
+
73
+ A single extension can be imported into the current scope classically, e.g.:
74
+
75
+ [source]
76
+ using Corefines::Object::ThenIf
77
+
78
+ or preferably using its “alias”:
79
+
80
+ [source]
81
+ using Corefines::Object::then_if
82
+
83
+ If you want to include all extensions for the class, then you can just import the parent module, e.g.:
84
+
85
+ [source]
86
+ using Corefines::Object
87
+
88
+ But more often you want to include multiple extensions for the class, but not all of them, e.g.:
89
+
90
+ [source]
91
+ using Corefines::Object::then_if
92
+ using Corefines::Object::in?
93
+
94
+ this can be abbreviated to:
95
+
96
+ [source]
97
+ using Corefines::Object[:then_if, :in?]
98
+
99
+ If you feel that _Corefines_ is too long, then you can also use abbreviation _CF_ instead:
100
+
101
+ [source]
102
+ using CF::Object::then_if
103
+
104
+ Refinements can be activated (with `using`) at top-level (per file), inside a class, module or a method.
105
+
106
+
107
+ == Compatibility mode
108
+
109
+ Refinements are still a young feature, so there’s a possibility that your gem or application will have to work on a Ruby platform that doesn’t fully support refinements yet.
110
+
111
+ The main Ruby implementation, https://en.wikipedia.org/wiki/Ruby_MRI[MRI] (aka CRuby), supports refinements since version 2.1.0 (https://www.ruby-lang.org/en/news/2013/12/25/ruby-2-1-0-is-released/[released in 25 Dec 2013]).
112
+ footnote:[Actually, refinements has been introduced to MRI in 2.0.0, as an experimental feature. However, its design and implementation has been changed then, so refinements in 2.0.x and 2.1+ behaves quite differently.]
113
+ Version 2.0.0 (https://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/[released in 24 Feb 2013]) is still supported though.
114
+ http://www.jruby.org/[JRuby] doesn’t support refinements yet, it’s planned in the upcoming version 9.0.0.0 (https://github.com/jruby/jruby/issues/1062[#1062]).
115
+ http://rubini.us/[Rubinius] also doesn’t support refinements yet.
116
+
117
+ This gem is a collection of pure refinements, and yet, it works even on older Rubies that don’t support refinements.
118
+ Wait… how?
119
+ Well, when you use the gem with an older Ruby, it’s actually cheating.
120
+ Instead of locally scoped changes, it falls back to global monkey-patching.
121
+
122
+ The Corefines gem adds `refine` and `using` methods to the core classes, so you can define and use refinements just like in newer Rubies.
123
+ But internally it works very differently.
124
+ The `refine` method adds a given block to a collection of pending “refinements” inside its module.
125
+ When `using` is called _first time_ for the module, it _evaluates_ module’s “refinements” in context of the target classes (i.e. do a monkey-patch).
126
+
127
+ Not ideal indeed, but probably the best of what we can achieve.
128
+
129
+
130
+ == List of refinements
131
+
132
+ * {doc-base-url}/Array[Array]
133
+ ** {doc-base-url}/Array/Second[#second]
134
+ ** {doc-base-url}/Array/Third[#third]
135
+ * {doc-base-url}/Enumerable[Enumerable]
136
+ ** {doc-base-url}/Enumerable/IndexBy[#index_by]
137
+ ** {doc-base-url}/Enumerable/MapSend[#map_send]
138
+ * {doc-base-url}/Hash[Hash]
139
+ ** {doc-base-url}/Hash/OpPlus[#+]
140
+ ** {doc-base-url}/Hash/Compact[#compact]
141
+ ** {doc-base-url}/Hash/Compact[#compact!]
142
+ ** {doc-base-url}/Hash/Rekey[#rekey]
143
+ ** {doc-base-url}/Hash/Rekey[#rekey!]
144
+ ** {doc-base-url}/Hash/SymbolizeKeys[#symbolize_keys]
145
+ ** {doc-base-url}/Hash/SymbolizeKeys[#symbolize_keys!]
146
+ * {doc-base-url}/Module[Module]
147
+ ** {doc-base-url}/Module/AliasClassMethod[#alias_class_method]
148
+ ** {doc-base-url}/Module/AliasMethodChain[#alias_method_chain]
149
+ * {doc-base-url}/Object[Object]
150
+ ** {doc-base-url}/Object/Blank[#blank?]
151
+ ** {doc-base-url}/Object/DeepDup[#deep_dup]
152
+ ** {doc-base-url}/Object/Else[#else]
153
+ ** {doc-base-url}/Object/In[#in?]
154
+ ** {doc-base-url}/Object/InstanceValues[#instance_values]
155
+ ** {doc-base-url}/Object/Blank[#presence]
156
+ ** {doc-base-url}/Object/Then[#then]
157
+ ** {doc-base-url}/Object/ThenIf[#then_if]
158
+ ** {doc-base-url}/Object/Try[#try]
159
+ ** {doc-base-url}/Object/Try[#try!]
160
+ * {doc-base-url}/String[String]
161
+ ** {doc-base-url}/String/Color[#color]
162
+ ** {doc-base-url}/String/Concat[#concat]
163
+ ** {doc-base-url}/String/Decolor[#decolor]
164
+ ** {doc-base-url}/String/Remove[#remove]
165
+ ** {doc-base-url}/String/Unindent[#unindent] (alias `#strip_heredoc`)
166
+ * {doc-base-url}/Symbol[Symbol]
167
+ ** {doc-base-url}/Symbol/Call[#call]
168
+
169
+
170
+ == Acknowledgement
171
+
172
+ Most of the extension methods are based on, or highly inspired from:
173
+
174
+ * https://github.com/rails/rails/tree/master/activesupport[Active Support (Ruby extensions)]
175
+ * https://github.com/rubyworks/facets[Ruby Facets]
176
+ * https://github.com/gregwebs/methodchain[methodchain]
177
+ * https://github.com/fazibear/colorize[colorize]
178
+
179
+ Very useful articles about refinements and how to “trick” them:
180
+
181
+ * https://www.new-bamboo.co.uk/blog/2014/02/05/refinements-under-the-knife/[
182
+ Refinements under the knife] by https://github.com/leemachin[@leemachin]
183
+ * http://qiita.com/joker1007/items/68d066a12bc763bd2cb4[Refinement関係の小技とできない事をまとめてみた] by https://github.com/joker1007[@joker1007]
184
+
185
+
186
+ == Contributing
187
+
188
+ . Fork it.
189
+ . Create your feature branch (`git checkout -b my-new-feature`).
190
+ . Commit your changes (`git commit -am 'Add some feature'`).
191
+ . Push to the branch (`git push origin my-new-feature`).
192
+ . Create a new Pull Request.
193
+
194
+
195
+ == License
196
+
197
+ This project is licensed under http://opensource.org/licenses/MIT/[MIT License].
198
+ For the full text of the license, see the link:LICENSE[LICENSE] file.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ begin
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :test => :spec
9
+ task :default => :spec
10
+
11
+ rescue LoadError => e
12
+ warn "#{e.path} is not available"
13
+ end
14
+
15
+ begin
16
+ require 'yard'
17
+
18
+ # options are defined in .yardopts
19
+ YARD::Rake::YardocTask.new(:yard)
20
+
21
+ rescue LoadError => e
22
+ warn "#{e.path} is not available"
23
+ end
@@ -0,0 +1,30 @@
1
+ require 'corefines/support/alias_submodules'
2
+
3
+ module Corefines
4
+ module Array
5
+
6
+ ##
7
+ # @!method second
8
+ # @return the second element, or +nil+ if doesn't exist.
9
+ module Second
10
+ refine ::Array do
11
+ def second
12
+ self[1]
13
+ end
14
+ end
15
+ end
16
+
17
+ ##
18
+ # @!method third
19
+ # @return the third element, or +nil+ if doesn't exist.
20
+ module Third
21
+ refine ::Array do
22
+ def third
23
+ self[2]
24
+ end
25
+ end
26
+ end
27
+
28
+ include Support::AliasSubmodules
29
+ end
30
+ end
@@ -0,0 +1,64 @@
1
+ require 'corefines/support/alias_submodules'
2
+ require 'corefines/support/classes_including_module'
3
+
4
+ module Corefines
5
+ module Enumerable
6
+
7
+ ##
8
+ # @!method index_by
9
+ # Convert enumerable into a Hash, iterating over each element where the
10
+ # provided block must return the key to be used to map to the value.
11
+ #
12
+ # It's similar to {::Enumerable#group_by}, but when two elements
13
+ # corresponds to the same key, then only the last one is preserved in the
14
+ # resulting Hash.
15
+ #
16
+ # @example
17
+ # people.index_by(&:login)
18
+ # => { "flynn" => <Person @login="flynn">, "bradley" => <Person @login="bradley">, ...}
19
+ #
20
+ # @example
21
+ # people.index_by.each(&:login)
22
+ # => { "flynn" => <Person @login="flynn">, "bradley" => <Person @login="bradley">, ...}
23
+ #
24
+ # @yield [obj] gives each element to the block.
25
+ # @yieldreturn the key to be used to map to the value.
26
+ # @return [Hash]
27
+ #
28
+ module IndexBy
29
+ Support.classes_including_module(::Enumerable) do |klass|
30
+
31
+ refine klass do
32
+ def index_by
33
+ ::Hash[map { |elem| [ yield(elem), elem ] }]
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ ##
40
+ # @!method map_send(method_name, *args, &block)
41
+ # Sends a message to each element and collects the result.
42
+ #
43
+ # @example
44
+ # [1, 2, 3].map_send(:+, 3) #=> [4, 5, 6]
45
+ #
46
+ # @param method_name [Symbol] name of the method to call.
47
+ # @param args arguments to pass to the method.
48
+ # @param block [Proc] block to pass to the method.
49
+ # @return [Enumerable]
50
+ #
51
+ module MapSend
52
+ Support.classes_including_module(::Enumerable) do |klass|
53
+
54
+ refine klass do
55
+ def map_send(method_name, *args, &block)
56
+ map { |e| e.__send__(method_name, *args, &block) }
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ include Support::AliasSubmodules
63
+ end
64
+ end
@@ -0,0 +1,164 @@
1
+ require 'corefines/support/alias_submodules'
2
+
3
+ module Corefines
4
+ module Hash
5
+
6
+ ##
7
+ # @!method compact
8
+ # @example
9
+ # hash = { a: true, b: false, c: nil }
10
+ # hash.compact # => { a: true, b: false }
11
+ # hash # => { a: true, b: false, c: nil }
12
+ # { c: nil }.compact # => {}
13
+ #
14
+ # @return [Hash] a new hash with no key-value pairs which value is +nil+.
15
+ #
16
+ # @!method compact!
17
+ # Removes all key-value pairs from the hash which value is +nil+.
18
+ # Same as {#compact}, but modifies +self+.
19
+ #
20
+ # @return [Hash] self
21
+ #
22
+ module Compact
23
+ refine ::Hash do
24
+ def compact
25
+ reject { |_, value| value.nil? }
26
+ end
27
+
28
+ def compact!
29
+ delete_if { |_, value| value.nil? }
30
+ end
31
+ end
32
+ end
33
+
34
+ ##
35
+ # @!method +(other_hash)
36
+ # Alias for +#merge+.
37
+ #
38
+ # @example
39
+ # {a: 1, b: 2} + {b: 3, c: 4}
40
+ # => {a: 1, b: 3, c: 4}
41
+ #
42
+ # @param other_hash [Hash]
43
+ # @return [Hash] a new hash containing the contents of _other_hash_ and
44
+ # this hash. The value for entries with duplicate keys will be that of
45
+ # _other_hash_.
46
+ #
47
+ module OpPlus
48
+ refine ::Hash do
49
+ def +(other_hash)
50
+ merge other_hash
51
+ end
52
+ end
53
+ end
54
+
55
+ ##
56
+ # @!method rekey(key_map = nil, &block)
57
+ # Returns a new hash with keys transformed according to the given
58
+ # _key_map_ or the _block_.
59
+ #
60
+ # If no _key_map_ or _block_ is given, then all keys are converted
61
+ # to +Symbols+, as long as they respond to +to_sym+.
62
+ #
63
+ # @example
64
+ # hash = { :a => 1, 'b' => 2 }
65
+ # hash.rekey(:a => :x, :c => :y) # => { :x => 1, 'b' => 2 }
66
+ # hash.rekey(&:to_s) # => { 'a' => 1, 'b' => 2 }
67
+ # hash.rekey { |k| k.to_s.upcase } # => { 'A' => 1, 'B' => 2 }
68
+ # hash.rekey # => { :a => 1, :b => 2 }
69
+ #
70
+ # @overload rekey(key_map)
71
+ # @param key_map [Hash] a translation map from the old keys to the new
72
+ # keys; <tt>{from_key => to_key, ...}</tt>.
73
+ #
74
+ # @overload rekey
75
+ # @yield [key, value] gives every key-value pair to the block.
76
+ # The return value becomes a new key.
77
+ #
78
+ # @return [Hash] a new hash.
79
+ # @raise ArgumentError if both _key_map_ and the _block_ are given.
80
+ #
81
+ # @!method rekey!(key_map = nil, &block)
82
+ # Transforms keys according to the given _key_map_ or the _block_.
83
+ # Same as {#rekey}, but modifies +self+.
84
+ #
85
+ # @overload rekey!(key_map)
86
+ # @param key_map [Hash] a translation map from the old keys to the new
87
+ # keys; <tt>{from_key => to_key, ...}</tt>.
88
+ #
89
+ # @overload rekey!
90
+ # @yield [key, value] gives every key-value pair to the block.
91
+ # The return value becomes a new key.
92
+ #
93
+ # @return [Hash] self
94
+ # @raise (see #rekey)
95
+ #
96
+ module Rekey
97
+ refine ::Hash do
98
+ def rekey(key_map = nil, &block)
99
+ fail ArgumentError, "provide key_map, or block, not both" if key_map && block
100
+ block = ->(k, _) { k.to_sym rescue k } if !key_map && !block
101
+
102
+ # Note: self.dup is used to preserve the default_proc.
103
+ if block
104
+ # This is needed for "symbol procs" (e.g. &:to_s).
105
+ transform_key = block.arity.abs == 1 ? ->(k, _) { block[k] } : block
106
+
107
+ each_with_object(dup.clear) do |(key, value), hash|
108
+ hash[ transform_key[key, value] ] = value
109
+ end
110
+ else
111
+ key_map.each_with_object(dup) do |(from, to), hash|
112
+ hash[to] = hash.delete(from) if hash.key? from
113
+ end
114
+ end
115
+ end
116
+
117
+ def rekey!(key_map = nil, &block)
118
+ replace rekey(key_map, &block)
119
+ end
120
+ end
121
+ end
122
+
123
+ ##
124
+ # @!method symbolize_keys
125
+ # @example
126
+ # hash = { 'name' => 'Lindsey', :born => 1986 }
127
+ # hash.symbolize_keys # => { :name => 'Lindsey', :born => 1986 }
128
+ # hash # => { 'name' => 'Lindsey', :born => 1986 }
129
+ #
130
+ # @return [Hash] a new hash with all keys converted to symbols, as long
131
+ # as they respond to +to_sym+.
132
+ #
133
+ # @!method symbolize_keys!
134
+ # Converts all keys to symbols, as long as they respond to +to_sym+.
135
+ # Same as {#symbolize_keys}, but modifies +self+.
136
+ #
137
+ # @return [Hash] self
138
+ #
139
+ module SymbolizeKeys
140
+ refine ::Hash do
141
+ def symbolize_keys
142
+ each_with_object(dup.clear) do |(key, value), hash|
143
+ hash[(key.to_sym rescue key)] = value
144
+ end
145
+ end
146
+
147
+ def symbolize_keys!
148
+ keys.each do |key|
149
+ self[(key.to_sym rescue key)] = delete(key)
150
+ end
151
+ self
152
+ end
153
+ end
154
+ end
155
+
156
+ include Support::AliasSubmodules
157
+
158
+ class << self
159
+ alias_method :compact!, :compact
160
+ alias_method :rekey!, :rekey
161
+ alias_method :symbolize_keys!, :symbolize_keys
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,95 @@
1
+ require 'corefines/support/alias_submodules'
2
+
3
+ module Corefines
4
+ module Module
5
+ ##
6
+ # @!method alias_class_method(new_name, old_name)
7
+ # Makes _new_name_ a new copy of the class method _old_name_.
8
+ #
9
+ # @param new_name [Symbol] name of the new class method to create.
10
+ # @param old_name [Symbol] name of the existing class method to alias.
11
+ # @return [self]
12
+ #
13
+ module AliasClassMethod
14
+ refine ::Module do
15
+ def alias_class_method(new_name, old_name)
16
+ singleton_class.__send__(:alias_method, new_name, old_name)
17
+ self
18
+ end
19
+ end
20
+ end
21
+
22
+ ##
23
+ # @!method alias_method_chain(target, feature)
24
+ # Encapsulates the common pattern of:
25
+ #
26
+ # alias_method :meth_without_feature, :meth
27
+ # alias_method :meth, :meth_with_feature
28
+ #
29
+ # With this, you simply do:
30
+ #
31
+ # alias_method_chain :meth, :feature
32
+ #
33
+ # for example:
34
+ #
35
+ # class ChainExample
36
+ # def say
37
+ # "hello"
38
+ # end
39
+ # def say_with_accent
40
+ # "helloo!"
41
+ # end
42
+ # alias_method_chain :say, :accent
43
+ # end
44
+ #
45
+ # and both aliases are set up for you:
46
+ #
47
+ # example = ChainExample.new
48
+ # example.say #=> "helloo!"
49
+ # example.say_without_accent #=> "hello"
50
+ #
51
+ # Query and bang methods (+foo?+, +foo!+) keep the same punctuation:
52
+ #
53
+ # alias_method_chain :say!, :accent
54
+ #
55
+ # is equivalent to:
56
+ #
57
+ # alias_method :say_without_accent!, :say!
58
+ # alias_method :say!, :say_with_accent!
59
+ #
60
+ # so you can safely chain +foo+, +foo?+, and +foo!+ with the same
61
+ # feature.
62
+ #
63
+ # @param target [Symbol] name of the method to alias.
64
+ # @param feature [Symbol] suffix for the aliases.
65
+ # @return [self]
66
+ #
67
+ module AliasMethodChain
68
+ refine ::Module do
69
+ def alias_method_chain(target, feature)
70
+ # Strip out punctuation on predicates, bang or writer methods since
71
+ # e.g. target?_without_feature is not a valid method name.
72
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
73
+
74
+ with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
75
+ without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
76
+
77
+ alias_method without_method, target
78
+ alias_method target, with_method
79
+
80
+ if public_method_defined? without_method
81
+ public target
82
+ elsif protected_method_defined? without_method
83
+ protected target
84
+ elsif private_method_defined? without_method
85
+ private target
86
+ end
87
+
88
+ self
89
+ end
90
+ end
91
+ end
92
+
93
+ include Support::AliasSubmodules
94
+ end
95
+ end