corefines 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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