corefines 1.2.0 → 1.3.0

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