corefines 1.2.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c7839672b793a5df3d5ffa892272260eccf0c0db
4
- data.tar.gz: 0a263d7e00645c8451d93461bfe9ed81cd8ccc1c
3
+ metadata.gz: de04dfa5cd06fc172ea71b0f428f04b76b6b0527
4
+ data.tar.gz: 0deb05f19ce1dddd0ca60d13e8f9101dee5d49c0
5
5
  SHA512:
6
- metadata.gz: 93f5a5a21061dea9e9aaddf7ea5520e7743a0c84c9635d42c63cdaed032fae12e328e141a60d40ac22ea7d986b3cda8a258374a583df7fdbcafbf7d8856bc317
7
- data.tar.gz: 7b31c6b28bb018d1b0ee9fd9368922a0a282a545e461c0e6f488111ea0837abcdee533249d97560c7f31adc47aaf8cc2698fd4dc03bf353ce2435b226aef7e2d
6
+ metadata.gz: 078f83c0f36e11534267b88cb445aad00f8483c6ddf9e1e3c2f804044d09f619655b75560038bc2f99cafccf577f0468ea0e32fca166069ed2c5ff7bcb78e5e2
7
+ data.tar.gz: 366f0084b4bac8eec6666fc40fb6091896be88c1d3d90f9c59047227e354bcd99ce82b991a26935639c35f1b483e59471ea0a717492dd94918d281846cbc72a5
data/CHANGELOG.adoc CHANGED
@@ -3,6 +3,14 @@
3
3
  :doc-base-url: http://www.rubydoc.info/github/jirutka/corefines/Corefines
4
4
  :issue-uri: {repo-uri}/issues
5
5
 
6
+ == 1.3.0 (2015-04-29)
7
+
8
+ * Add new refinement {doc-base-url}/String/ToRegexp[String#to_regexp].
9
+ * Add new refinement {doc-base-url}/String/RelativePathFrom[String#relative_path_from].
10
+ * Add new refinement {doc-base-url}/Hash/Except[Hash#except].
11
+ * Add new refinement {doc-base-url}/Hash/Only[Hash#only].
12
+
13
+
6
14
  == 1.2.0 (2015-04-27)
7
15
 
8
16
  * Add new refinement {doc-base-url}/String/Indent[String#indent].
data/README.adoc CHANGED
@@ -41,12 +41,12 @@ TODO
41
41
  Add this line to your application’s Gemfile:
42
42
 
43
43
  [source]
44
- gem 'corefines', '~> 1.2'
44
+ gem 'corefines', '~> 1.3'
45
45
 
46
46
  or to your gemspec:
47
47
 
48
48
  [source]
49
- s.add_runtime_dependency 'corefines', '~> 1.2'
49
+ s.add_runtime_dependency 'corefines', '~> 1.3'
50
50
 
51
51
  and then execute:
52
52
 
@@ -139,6 +139,10 @@ Not ideal indeed, but probably the best of what we can achieve.
139
139
  ** {doc-base-url}/Hash/OpAdd[#+]
140
140
  ** {doc-base-url}/Hash/Compact[#compact]
141
141
  ** {doc-base-url}/Hash/Compact[#compact!]
142
+ ** {doc-base-url}/Hash/Except[#except]
143
+ ** {doc-base-url}/Hash/Except[#except!]
144
+ ** {doc-base-url}/Hash/Only[#only]
145
+ ** {doc-base-url}/Hash/Only[#only!]
142
146
  ** {doc-base-url}/Hash/Rekey[#rekey]
143
147
  ** {doc-base-url}/Hash/Rekey[#rekey!]
144
148
  ** {doc-base-url}/Hash/SymbolizeKeys[#symbolize_keys]
@@ -159,11 +163,13 @@ Not ideal indeed, but probably the best of what we can achieve.
159
163
  ** {doc-base-url}/Object/Try[#try!]
160
164
  * {doc-base-url}/String[String]
161
165
  ** {doc-base-url}/String/Color[#color]
162
- ** {doc-base-url}/String/Concat[#concat]
166
+ ** {doc-base-url}/String/Concat[#concat!]
163
167
  ** {doc-base-url}/String/Decolor[#decolor]
164
168
  ** {doc-base-url}/String/Indent[#indent]
169
+ ** {doc-base-url}/String/RelativePathFrom[#relative_path_from]
165
170
  ** {doc-base-url}/String/Remove[#remove]
166
171
  ** {doc-base-url}/String/ToB[#to_b]
172
+ ** {doc-base-url}/String/ToRegexp[#to_regexp]
167
173
  ** {doc-base-url}/String/Unindent[#unindent] (alias `#strip_heredoc`)
168
174
  * {doc-base-url}/Symbol[Symbol]
169
175
  ** {doc-base-url}/Symbol/Call[#call]
@@ -177,6 +183,7 @@ Most of the extension methods are based on, or highly inspired from:
177
183
  * https://github.com/rubyworks/facets[Ruby Facets]
178
184
  * https://github.com/gregwebs/methodchain[methodchain]
179
185
  * https://github.com/fazibear/colorize[colorize]
186
+ * https://github.com/seamusabshere/to_regexp[to_regexp]
180
187
 
181
188
  Very useful articles about refinements and how to “trick” them:
182
189
 
@@ -31,6 +31,86 @@ module Corefines
31
31
  end
32
32
  end
33
33
 
34
+ ##
35
+ # @!method except(*keys)
36
+ # @example
37
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
38
+ # hash.except(:a, :d) # => { b: 2, c: 3 }
39
+ # hash # => { a: 1, b: 2, c: 3, d: 4 }
40
+ #
41
+ # @param *keys the keys to exclude from the hash.
42
+ # @return [Hash] a new hash without the specified key/value pairs.
43
+ #
44
+ # @!method except!(*keys)
45
+ # Removes the specified keys/value pairs in-place.
46
+ #
47
+ # @example
48
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
49
+ # hash.except(:a, :d) # => { b: 2, c: 3 }
50
+ # hash # => { b: 2, c: 3 }
51
+ #
52
+ # @see #except
53
+ # @param *keys (see #except)
54
+ # @return [Hash] a hash containing the removed key/value pairs.
55
+ #
56
+ module Except
57
+ refine ::Hash do
58
+ def except(*keys)
59
+ keys.each_with_object(dup) do |k, hash|
60
+ hash.delete(k)
61
+ end
62
+ end
63
+
64
+ def except!(*keys)
65
+ keys.each_with_object(dup.clear) do |k, deleted|
66
+ deleted[k] = delete(k) if has_key? k
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ ##
73
+ # @!method only(*keys)
74
+ # @example
75
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
76
+ # hash.only(:a, :d) # => { a: 1, d: 4 }
77
+ # hash # => { a: 1, b: 2, c: 3, d: 4 }
78
+ #
79
+ # @param *keys the keys to include in the hash.
80
+ # @return [Hash] a new hash with only the specified key/value pairs.
81
+ #
82
+ # @!method only!(*keys)
83
+ # Removes all key/value pairs except the ones specified by _keys_.
84
+ #
85
+ # @example
86
+ # hash = { a: 1, b: 2, c: 3, d: 4 }
87
+ # hash.only(:a, :d) # => { a: 1, d: 4 }
88
+ # hash # => { a: 1, d: 4 }
89
+ #
90
+ # @see #only
91
+ # @param *keys (see #only)
92
+ # @return [Hash] a hash containing the removed key/value pairs.
93
+ #
94
+ module Only
95
+ refine ::Hash do
96
+ def only(*keys)
97
+ # Note: self.dup is used to preserve the default_proc.
98
+ keys.each_with_object(dup.clear) do |k, hash|
99
+ hash[k] = self[k] if has_key? k
100
+ end
101
+ end
102
+
103
+ def only!(*keys)
104
+ deleted = keys.each_with_object(dup) do |k, hash|
105
+ hash.delete(k)
106
+ end
107
+ replace only(*keys)
108
+
109
+ deleted
110
+ end
111
+ end
112
+ end
113
+
34
114
  ##
35
115
  # @!method +(other_hash)
36
116
  # Alias for +#merge+.
@@ -157,6 +237,8 @@ module Corefines
157
237
 
158
238
  class << self
159
239
  alias_method :compact!, :compact
240
+ alias_method :except!, :except
241
+ alias_method :only!, :only
160
242
  alias_method :rekey!, :rekey
161
243
  alias_method :symbolize_keys!, :symbolize_keys
162
244
  end
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  require 'corefines/support/alias_submodules'
2
3
 
3
4
  module Corefines
@@ -170,6 +171,32 @@ module Corefines
170
171
  end
171
172
  end
172
173
 
174
+ ##
175
+ # @!method relative_path_from(base_dir)
176
+ # Returns a relative path from the given _base_dir_ to the path
177
+ # represented by this _str_. This method doesn't access the filesystem
178
+ # and assumes no symlinks.
179
+ #
180
+ # If _str_ is absolute, then _base_dir_ must be absolute too.
181
+ # If _str_ is relative, then _base_dir_ must be relative too.
182
+ #
183
+ # @example
184
+ # '/home/flynn/tron'.relative_path_from('/home') # => flynn/tron
185
+ # '/home'.relative_path_from('/home/flynn/tron') # => ../..
186
+ #
187
+ # @param base_dir [String, Pathname] the base directory to calculate
188
+ # relative path from.
189
+ # @return [String] a relative path from _base_dir_ to this _str_.
190
+ # @raise ArgumentError if it cannot find a relative path.
191
+ #
192
+ module RelativePathFrom
193
+ refine ::String do
194
+ def relative_path_from(base_dir)
195
+ ::Pathname.new(self).relative_path_from(::Pathname.new(base_dir)).to_s
196
+ end
197
+ end
198
+ end
199
+
173
200
  ##
174
201
  # @!method remove(*patterns)
175
202
  # Returns a copy of this string with *all* occurrences of the _patterns_
@@ -236,6 +263,68 @@ module Corefines
236
263
  end
237
264
  end
238
265
 
266
+ ##
267
+ # @!method to_regexp(opts = {})
268
+ # Returns a regular expression represented by this string.
269
+ #
270
+ # @example
271
+ # '/^foo/'.to_regexp # => /^foo/
272
+ # '/foo/i'.to_regexp # => /foo/i
273
+ # 'foo'.to_regexp # => nil
274
+ #
275
+ # 'foo'.to_regexp(literal: true) # => /foo/
276
+ # '^foo*'.to_regexp(literal: true) # => /\^foo\*/
277
+ #
278
+ # '/foo/'.to_regexp(detect: true) # => /foo/
279
+ # '$foo/'.to_regexp(detect: true) # => /\$foo\//
280
+ # ''.to_regexp(detect: true) # => nil
281
+ #
282
+ # '/foo/'.to_regexp(multiline: true) # => /foo/m
283
+ #
284
+ # @param opts [Hash] options
285
+ # @option opts :literal [Boolean] treat meta characters and other regexp
286
+ # codes as just a text. Never returns +nil+. (default: false)
287
+ # @option opts :detect [Boolean] if string starts and ends with a slash,
288
+ # treat it as a regexp, otherwise interpret it literally.
289
+ # (default: false)
290
+ # @option opts :ignore_case [Boolean] same as +/foo/i+. (default: false)
291
+ # @option opts :multiline [Boolean] same as +/foo/m+. (default: false)
292
+ # @option opts :extended [Boolean] same as +/foo/x+. (default: false)
293
+ #
294
+ # @return [Regexp, nil] a regexp, or +nil+ if +:literal+ is not set or
295
+ # +false+ and this string doesn't represent a valid regexp or is empty.
296
+ #
297
+ module ToRegexp
298
+ refine ::String do
299
+ def to_regexp(opts = {})
300
+
301
+ if opts[:literal]
302
+ content = ::Regexp.escape(self)
303
+
304
+ elsif self =~ %r{\A/(.*)/([imxnesu]*)\z}
305
+ content, inline_opts = $1, $2
306
+ content.gsub! '\\/', '/'
307
+
308
+ { ignore_case: 'i', multiline: 'm', extended: 'x' }.each do |k, v|
309
+ opts[k] ||= inline_opts.include? v
310
+ end if inline_opts
311
+
312
+ elsif opts[:detect] && !self.empty?
313
+ content = ::Regexp.escape(self)
314
+ else
315
+ return
316
+ end
317
+
318
+ options = 0
319
+ options |= ::Regexp::IGNORECASE if opts[:ignore_case]
320
+ options |= ::Regexp::MULTILINE if opts[:multiline]
321
+ options |= ::Regexp::EXTENDED if opts[:extended]
322
+
323
+ ::Regexp.new(content, options)
324
+ end
325
+ end
326
+ end
327
+
239
328
  ##
240
329
  # @!method unindent
241
330
  # Remove excessive indentation. Useful for multi-line strings embeded in
@@ -1,3 +1,3 @@
1
1
  module Corefines
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'
3
3
  end
@@ -0,0 +1,65 @@
1
+ describe Hash do
2
+ using Corefines::Hash::except
3
+
4
+ subject(:hash) { {:one => 'ONE', 'two' => 'TWO', 3 => 'THREE'} }
5
+
6
+ let(:hash_with_proc) { Hash.new(&default_proc) }
7
+ let(:default_proc) { proc {} }
8
+
9
+ let(:hash_with_default) { Hash.new(default_value) }
10
+ let(:default_value) { 42 }
11
+
12
+
13
+ describe '#except' do
14
+
15
+ context "existing keys" do
16
+ it "returns a copy of the hash without the specified keys" do
17
+ expect( hash.except(:one, 3) ).to eql({'two' => 'TWO'})
18
+ expect( hash.except('two') ).to eql({:one => 'ONE', 3 => 'THREE'})
19
+ end
20
+ end
21
+
22
+ context "non-existing key" do
23
+ it "returns a copy of the hash unchanged" do
24
+ expect( hash.except(:x) ).to eql hash
25
+ expect( hash.except(:x) ).to_not equal(hash)
26
+ end
27
+ end
28
+
29
+ it "preserves the default_proc" do
30
+ expect( hash_with_proc.except(:one).default_proc ).to equal default_proc
31
+ end
32
+
33
+ it "preserves the default" do
34
+ expect( hash_with_default.except(:one).default ).to eq default_value
35
+ end
36
+ end
37
+
38
+
39
+ describe '#except!' do
40
+
41
+ context "existing keys" do
42
+ it "removes the specified keys/value pairs and returns them" do
43
+ expect( hash.except!(:one, 3) ).to eql({:one => 'ONE', 3 => 'THREE'})
44
+ is_expected.to eq({'two' => 'TWO'})
45
+ end
46
+ end
47
+
48
+ context "non-existing key" do
49
+ it "keeps the hash unchanged and returns an empty hash" do
50
+ expect( hash.except!(:x) ).to eq({})
51
+ expect( hash ).to equal hash
52
+ end
53
+ end
54
+
55
+ it "preserves the default_proc" do
56
+ expect( hash_with_proc.except!(:one).default_proc ).to equal default_proc
57
+ expect( hash_with_proc.default_proc ).to equal default_proc
58
+ end
59
+
60
+ it "preserves the default" do
61
+ expect( hash_with_default.except!(:one).default ).to eq default_value
62
+ expect( hash_with_default.default ).to eq default_value
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,67 @@
1
+ describe Hash do
2
+ using Corefines::Hash::only
3
+
4
+ let(:hash) { {:one => 'ONE', 'two' => 'TWO', 3 => 'THREE'} }
5
+ let!(:original) { hash.dup }
6
+
7
+ let(:hash_with_proc) { Hash.new(&default_proc) }
8
+ let(:default_proc) { proc {} }
9
+
10
+ let(:hash_with_default) { Hash.new(default_value) }
11
+ let(:default_value) { 42 }
12
+
13
+
14
+ describe '#only' do
15
+
16
+ context "existing keys" do
17
+ it "returns a copy of the hash with only the specified keys" do
18
+ expect( hash.only(:one, 3) ).to eq({:one => 'ONE', 3 => 'THREE'})
19
+ expect( hash.only('two') ).to eq({'two' => 'TWO'})
20
+ end
21
+ end
22
+
23
+ context "non-existing key" do
24
+ it "returns an empty hash" do
25
+ expect( hash.only(:x) ).to eq({})
26
+ end
27
+ end
28
+
29
+ it "preserves the default_proc" do
30
+ expect( hash_with_proc.only(:one).default_proc ).to equal default_proc
31
+ end
32
+
33
+ it "preserves the default" do
34
+ expect( hash_with_default.only(:one).default ).to eq default_value
35
+ end
36
+ end
37
+
38
+
39
+ describe '#only!' do
40
+
41
+ context "existing keys" do
42
+ it "removes all key/value pairs except the specified and returns the removed" do
43
+ expect( hash.only!(:one, 3) ).to eq({'two' => 'TWO'})
44
+ expect( hash ).to eq({:one => 'ONE', 3 => 'THREE'})
45
+ end
46
+ end
47
+
48
+ context "non-existing key" do
49
+ it "removes all key/value pairs and returns a copy of the original hash" do
50
+ result = hash.only!(:x)
51
+ expect( result ).to eq original
52
+ expect( result ).to_not equal original
53
+ expect( hash ).to eq({})
54
+ end
55
+ end
56
+
57
+ it "preserves the default_proc" do
58
+ expect( hash_with_proc.only!(:one).default_proc ).to equal default_proc
59
+ expect( hash_with_proc.default_proc ).to equal default_proc
60
+ end
61
+
62
+ it "preserves the default" do
63
+ expect( hash_with_default.only!(:one).default ).to eq default_value
64
+ expect( hash_with_default.default ).to eq default_value
65
+ end
66
+ end
67
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'simplecov'
1
+ require 'simplecov' unless RUBY_ENGINE == 'jruby'
2
2
  require 'corefines'
3
3
 
4
4
  RSpec.configure do |config|
@@ -0,0 +1,29 @@
1
+ describe String do
2
+ using Corefines::String::relative_path_from
3
+
4
+ describe '#relative_path_from' do
5
+
6
+ [
7
+ [ '/home/flynn/tron', '/home', 'flynn/tron' ],
8
+ [ '/home', '/home/flynn/tron', '../..' ],
9
+ [ 'home/flynn/tron', 'home', 'flynn/tron' ],
10
+ [ '../bin/run', '.', '../bin/run' ],
11
+ [ '/usr/bin/../lib', '/', 'usr/lib' ]
12
+ ]
13
+ .each do |path, base, expected|
14
+ it "returns '#{expected}' for path '#{path}' and base '#{base}'" do
15
+ expect(path.relative_path_from(base)).to eql expected
16
+ end
17
+ end
18
+
19
+ context "mismatched relative and absolute path" do
20
+ {
21
+ '/home/flynn' => 'home',
22
+ 'home/flynn' => '/home'
23
+ }
24
+ .each do |path, base|
25
+ it { expect { path.relative_path_from(base) }.to raise_error ArgumentError }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,75 @@
1
+ # coding: utf-8
2
+
3
+ describe String do
4
+ using Corefines::String::to_regexp
5
+
6
+ describe 'to_regexp' do
7
+
8
+ context "with defaults" do
9
+ {
10
+ '/foo/' => /foo/,
11
+ '/foo/i' => /foo/i,
12
+ '/foo/m' => /foo/m,
13
+ '/foo/x' => /foo/x,
14
+ '/foo/imx' => /foo/imx,
15
+ '/foo.*(o)/' => /foo.*(o)/,
16
+ '/česk[yý]/' => /česk[yý]/,
17
+ '/foo\/bar/' => %r{foo/bar},
18
+ '/foo' => nil,
19
+ '/foo/z' => nil
20
+ }
21
+ .each do |str, expected|
22
+ it "returns regexp '#{expected.inspect}' for string '#{str}'" do
23
+ expect(str.to_regexp).to eql expected
24
+ end
25
+ end
26
+
27
+ %w[n e s u nesu].each do |kcode|
28
+ it "ignores encoding option: #{kcode}" do
29
+ expect("/foo/#{kcode}".to_regexp).to eql /foo/
30
+ end
31
+ end
32
+ end
33
+
34
+ context "with literal" do
35
+ {
36
+ '/(foo)*/' => %r{/\(foo\)\*/},
37
+ '^foo)' => /\^foo\)/
38
+ }
39
+ .each do |str, expected|
40
+ it "returns regexp '#{expected.inspect}' for string '#{str}" do
41
+ expect(str.to_regexp(literal: true)).to eql expected
42
+ end
43
+ end
44
+ end
45
+
46
+ context "with detect" do
47
+ {
48
+ '' => nil,
49
+ 'foo' => /foo/,
50
+ '(foo)*' => /\(foo\)\*/,
51
+ '/[a-z]' => %r{/\[a\-z\]},
52
+ '^.foo/x' => %r{\^\.foo/x},
53
+ '//' => //,
54
+ '/(foo)*/im' => /(foo)*/im
55
+ }
56
+ .each do |str, expected|
57
+ it "returns regexp '#{expected.inspect}' for string '#{str}'" do
58
+ expect(str.to_regexp(detect: true)).to eql expected
59
+ end
60
+ end
61
+ end
62
+
63
+ context "with ignore_case" do
64
+ it { expect('/foo/'.to_regexp(ignore_case: true)).to eql /foo/i }
65
+ end
66
+
67
+ context "with multiline" do
68
+ it { expect('/foo/'.to_regexp(multiline: true)).to eql /foo/m }
69
+ end
70
+
71
+ context "with extended" do
72
+ it { expect('/foo/'.to_regexp(extended: true)).to eql /foo/x }
73
+ end
74
+ end
75
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: corefines
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Jirutka
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-26 00:00:00.000000000 Z
11
+ date: 2015-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -125,6 +125,8 @@ files:
125
125
  - spec/enumerable/index_by_spec.rb
126
126
  - spec/enumerable/map_send_spec.rb
127
127
  - spec/hash/compact_spec.rb
128
+ - spec/hash/except_spec.rb
129
+ - spec/hash/only_spec.rb
128
130
  - spec/hash/op_add_spec.rb
129
131
  - spec/hash/rekey_spec.rb
130
132
  - spec/hash/symbolize_keys_spec.rb
@@ -143,8 +145,10 @@ files:
143
145
  - spec/string/concat_spec.rb
144
146
  - spec/string/decolor_spec.rb
145
147
  - spec/string/indent_spec.rb
148
+ - spec/string/relative_path_from_spec.rb
146
149
  - spec/string/remove_spec.rb
147
150
  - spec/string/to_b_spec.rb
151
+ - spec/string/to_regexp_spec.rb
148
152
  - spec/string/unindent_spec.rb
149
153
  - spec/support/alias_submodules_spec.rb
150
154
  - spec/support/classes_including_module_spec.rb