reactive_extensions 0.4.0.beta2 → 0.5.0.beta
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 +4 -4
- data/README.md +2 -2
- data/lib/reactive_extensions/sanitation.rb +170 -0
- data/lib/reactive_extensions/sets.rb +80 -0
- data/lib/reactive_extensions/subtraction.rb +18 -0
- data/lib/reactive_extensions/try_rescue.rb +20 -14
- data/lib/reactive_extensions.rb +1 -0
- data/reactive_extensions.gemspec +1 -1
- data/spec/array_spec.rb +81 -2
- data/spec/hash_spec.rb +321 -0
- data/version.rb +2 -2
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7bca1cd49086c183e79ac85eaf3a58a6adc0108
|
4
|
+
data.tar.gz: 515ab851d82683790e91dd98ca40d6ad485f6030
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f11b8b5d55f1e4fa906377f1a5a971478a065030d41833078b67b14483bf697668904a2030ef4cc0ed1f54be9fb09a30011088021923c45197cdeb48d052932
|
7
|
+
data.tar.gz: 4c35089ef2785298090e0cbd6991163eaf9ca80e4264e8f6a15c80e9019cec0a7154715dea75784500e449f04f5d3bdb9bd50be11e1d84f7bb5999c0283c1411
|
data/README.md
CHANGED
@@ -6,14 +6,14 @@ in the spirit of [ActiveSupport](https://github.com/rails/activesupport). This g
|
|
6
6
|
be used in any kind of project and is not dependent on any frameworks, gemsets, etc.
|
7
7
|
Its only runtime dependency is [ReactiveSupport](https://github.com/danascheider/reactive_support).
|
8
8
|
To add ReactiveExtensions to your project, add this to your Gemfile and run `bundle install`:
|
9
|
-
<pre><code>gem 'reactive_extensions', '~> 0.
|
9
|
+
<pre><code>gem 'reactive_extensions', '~> 0.5.0.beta'</code></pre>
|
10
10
|
To install locally:
|
11
11
|
<pre><code>sudo gem install reactive_extensions</code></pre>
|
12
12
|
Or if you're using RVM:
|
13
13
|
<pre><code>gem install reactive_extensions</code></pre>
|
14
14
|
|
15
15
|
You can also point your Gemfile to this repo:
|
16
|
-
<pre><code>gem 'reactive_extensions', '~> 0.
|
16
|
+
<pre><code>gem 'reactive_extensions', '~> 0.5.0.beta', git: 'https://github.com/danascheider/reactive_extensions.git</code></pre>
|
17
17
|
|
18
18
|
After installing, simply include this in your main project file:
|
19
19
|
<pre><code>require 'reactive_support'</code></pre>
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# This file adds the +#clean+, +#clean!+, +#only+, +#only!+, +#standardize+,
|
2
|
+
# and +#standardize!+ methods to Ruby's core Hash class. These methods enable
|
3
|
+
# the user to easily add or remove particular keys from a hash.
|
4
|
+
|
5
|
+
# Ruby's core Has class. See documentation for version
|
6
|
+
# 2.1.5[http://ruby-doc.org/core-2.1.5/Hash.html],
|
7
|
+
# 2.0.0[http://ruby-doc.org/core-2.0.0/Hash.html], or
|
8
|
+
# 1.9.3[http://ruby-doc.org/core-1.9.3/Hash.html].
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
|
12
|
+
# The +#clean+ method returns a duplicate of the calling hash with the
|
13
|
+
# specified +*bad_keys+, if present, removed. If none of the bad keys
|
14
|
+
# are present, +#clean+ will simply return a duplicate hash.
|
15
|
+
#
|
16
|
+
# +#clean+ is a non-destructive method. The original hash will still be
|
17
|
+
# unchanged after it is called.
|
18
|
+
#
|
19
|
+
# +#clean+ may also be called by the aliases +#not+ and +#except+.
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
23
|
+
# hash.clean(:foo) # => {:bar => 'baz'}
|
24
|
+
# hash.not(:foo, :bar) # => {}
|
25
|
+
# hash.except(:bar, :norf) # => {:foo => 'bar'}
|
26
|
+
# hash.clean(:norf) # => {:foo => 'bar', :bar => 'baz'}
|
27
|
+
# hash # => {:foo => bar', :bar => 'baz'}
|
28
|
+
|
29
|
+
def clean(*bad_keys)
|
30
|
+
dup = self.reject {|key, value| bad_keys.flatten.include?(key) }
|
31
|
+
dup
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method :not, :clean
|
35
|
+
alias_method :except, :clean
|
36
|
+
|
37
|
+
# The +#clean!+ method returns a duplicate of the calling hash with the
|
38
|
+
# specified +*bad_keys+, if present, removed. If none of the bad keys
|
39
|
+
# are present, +#clean!+ will simply return a duplicate hash.
|
40
|
+
#
|
41
|
+
# +#clean!+ is a non-destructive method. The original hash will still be
|
42
|
+
# unchanged after it is called.
|
43
|
+
#
|
44
|
+
# +#clean!+ may also be called by the aliases +#not!+ and +#except!+.
|
45
|
+
#
|
46
|
+
# Examples:
|
47
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
48
|
+
# hash.clean!(:foo) # => {:bar => 'baz'}
|
49
|
+
# hash # => {:bar => 'baz'}
|
50
|
+
#
|
51
|
+
# hash.not!(:foo, :bar) # => {}
|
52
|
+
# hash # => {}
|
53
|
+
#
|
54
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
55
|
+
# hash.except!(:bar, :norf) # => {:foo => 'bar'}
|
56
|
+
# hash # => {:foo => 'bar'}
|
57
|
+
|
58
|
+
def clean!(*bad_keys)
|
59
|
+
reject! {|key, value| bad_keys.flatten.include?(key) }
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
alias_method :not!, :clean!
|
64
|
+
alias_method :except!, :clean!
|
65
|
+
|
66
|
+
# The +#only+ method returns a duplicate of the calling hash with only the
|
67
|
+
# specified +*good_keys+, if present. If none of the good keys
|
68
|
+
# are present, +#only+ will simply return an empty hash.
|
69
|
+
#
|
70
|
+
# +#only+ is a non-destructive method. The original hash will still be
|
71
|
+
# unchanged after it is called.
|
72
|
+
#
|
73
|
+
# Examples:
|
74
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
75
|
+
# hash.only(:foo) # => {:foo => 'bar'}
|
76
|
+
# hash.only(:qux) # => {}
|
77
|
+
# hash # => {:foo => 'bar', :bar => 'baz'}
|
78
|
+
|
79
|
+
def only(*good_keys)
|
80
|
+
dup = self.select {|key, value| good_keys.flatten.include?(key) }
|
81
|
+
end
|
82
|
+
|
83
|
+
# The +#only!+ method returns a duplicate of the calling hash with only the
|
84
|
+
# specified +*good_keys+, if present. If none of the good keys
|
85
|
+
# are present, +#only!+ will simply return an empty hash.
|
86
|
+
#
|
87
|
+
# +#only!+ is a destructive method. The original hash will be changed in place
|
88
|
+
# when it is called.
|
89
|
+
#
|
90
|
+
# Examples:
|
91
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
92
|
+
# hash.only!(:foo) # => {:foo => 'bar'}
|
93
|
+
# hash.only!(:qux) # => {}
|
94
|
+
# hash # => {}
|
95
|
+
|
96
|
+
def only!(*good_keys)
|
97
|
+
select! {|key, value| good_keys.flatten.include?(key) }
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
# The +#standardize+ method returns a duplicate of the calling hash with
|
102
|
+
# the +*keys+ specified as arguments. Any other keys in the hash will be
|
103
|
+
# removed. Behavior when adding keys depends on whether the +:errors+
|
104
|
+
# option is set to +true+ or +false+ (default).
|
105
|
+
#
|
106
|
+
# If the +:errors+ option is set to +false+, the method will add any missing
|
107
|
+
# keys to the hash, populating them with +nil+ values. If +:errors+ is set to
|
108
|
+
# +true+, then an +ArgumentError+ will be raised unless all values are
|
109
|
+
# present in the hash already.
|
110
|
+
#
|
111
|
+
# Examples with +:errors => false+:
|
112
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
113
|
+
# hash.standardize(:foo) # => {:foo => 'bar'}
|
114
|
+
# hash.standardize(:foo, :qux) # => {:foo => 'bar', :qux => nil}
|
115
|
+
# hash # => {:foo => 'bar', :bar => 'baz'}
|
116
|
+
#
|
117
|
+
# Examples with +:errors => true+:
|
118
|
+
# hash = {:foo => 'bar', :bar 'baz'}
|
119
|
+
# hash.standardize(:foo, :errors => true) # => {:foo => 'bar'}
|
120
|
+
# hash.standardize(:foo, :qux, :errors => true) # => ArgumentError
|
121
|
+
|
122
|
+
def standardize(*kees)
|
123
|
+
options = kees.extract_options!
|
124
|
+
dup = deep_dup.only(kees)
|
125
|
+
|
126
|
+
kees.each do |key|
|
127
|
+
unless dup.has_key? key
|
128
|
+
raise ArgumentError.new("#{self} is missing required key #{key}") if options[:errors]
|
129
|
+
dup[key] = nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
dup
|
134
|
+
end
|
135
|
+
|
136
|
+
# The +#standardize!+ method modifies the calling hash such that its
|
137
|
+
# keys match the +*kees+ specified as arguments. Any other keys in the hash
|
138
|
+
# will be removed. If passed +:errors => true+, an +ArgumentError+ will be
|
139
|
+
# raised in the event keys present in the arguments are not present in the
|
140
|
+
# calling hash. Otherwise, any such keys will be added to the hash and
|
141
|
+
# populated with nil values.
|
142
|
+
#
|
143
|
+
# +#standardize!+ is a destructive method; the calling hash will be changed
|
144
|
+
# in place when it is called.
|
145
|
+
#
|
146
|
+
# Examples with +:errors => false+:
|
147
|
+
# hash = {:foo => 'bar', :bar => 'baz'}
|
148
|
+
# hash.standardize!(:foo, :qux) # => {:foo => 'bar', :qux => nil}
|
149
|
+
# hash.standardize!(:foo) # => {:foo => 'bar'}
|
150
|
+
# hash # => {:foo => 'bar'}
|
151
|
+
#
|
152
|
+
# Examples with +:errors => true+:
|
153
|
+
# hash = {:foo => 'bar', :bar 'baz'}
|
154
|
+
# hash.standardize!(:foo, :errors => true) # => {:foo => 'bar'}
|
155
|
+
# hash.standardize!(:foo, :qux, :errors => true) # => ArgumentError
|
156
|
+
# hash # => {:foo => 'bar'}
|
157
|
+
|
158
|
+
def standardize!(*kees)
|
159
|
+
options = kees.extract_options!
|
160
|
+
|
161
|
+
kees.each do |key|
|
162
|
+
unless only!(kees).has_key? key
|
163
|
+
raise ArgumentError.new("#{self} is missing required key #{key}") if options[:errors]
|
164
|
+
self[key] = nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
self
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# The +#subset_of?+ method checks whether the calling hash is a subset
|
4
|
+
# of the given +hash+. It returns true if all key-value pairs in
|
5
|
+
# the calling hash are also present in the hash passed as an argument.
|
6
|
+
# If the calling hash is empty, the method returns true.
|
7
|
+
#
|
8
|
+
# Examples:
|
9
|
+
# hash = {:foo => 'bar', :bar => 'baz', :baz => 'qux'}
|
10
|
+
# {:baz => 'qux'}.subset_of? hash # => true
|
11
|
+
# {:bar => 'baz', :norf => 'foo'}.subset_of? hash # => false
|
12
|
+
# {:norf => 'foo'}.subset_of? hash # => false
|
13
|
+
# {:foo => 'qux'}.subset_of? hash # => false
|
14
|
+
# {}.subset_of? hash # => true
|
15
|
+
# {:foo => 'bar'}.subset_of? 'foobar' # => ArgumentError
|
16
|
+
|
17
|
+
def subset_of?(hash)
|
18
|
+
raise ArgumentError.new("Argument of #subset_of? must share the class of the calling object") unless hash.instance_of? Hash
|
19
|
+
each {|key, value| return false unless hash[key] === value }
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
# The +#superset_of?+ method checks whether the calling hash is a superset
|
24
|
+
# of the given +hash+. It returns true if all key-value pairs in
|
25
|
+
# the calling hash are also present in the hash passed as an argument.
|
26
|
+
# If the argument hash is empty, the method returns true.
|
27
|
+
#
|
28
|
+
# Examples:
|
29
|
+
# hash = {:foo => 'bar', :bar => 'baz', :baz => 'qux'}
|
30
|
+
# hash.superset_of? {:baz => 'qux'} # => true
|
31
|
+
# hash.superset_of? {:bar => 'baz', :norf => 'foo'} # => false
|
32
|
+
# hash.superset_of? {:norf => 'foo'} # => false
|
33
|
+
# hash.superset_of? {:foo => 'qux'} # => false
|
34
|
+
# hash.superset_of? {} # => true
|
35
|
+
# {:foo => 'bar'}.superset_of? 'foobar' # => ArgumentError
|
36
|
+
|
37
|
+
def superset_of?(hash)
|
38
|
+
raise ArgumentError.new("Argument of Hash#superset_of? must be a hash") unless hash.instance_of? Hash
|
39
|
+
hash.subset_of? self
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Array
|
44
|
+
|
45
|
+
# The +#subset_of?+ method checks whether the calling array is a subset
|
46
|
+
# of the given +array+. It returns true if all objects in the calling
|
47
|
+
# array are also present in the hash passed as an argument, regardless
|
48
|
+
# of order. If the calling array is empty, the method returns true.
|
49
|
+
#
|
50
|
+
# Examples:
|
51
|
+
# array = [1, 2, 3, 4]
|
52
|
+
# [1, 3].subset_of? array # => true
|
53
|
+
# [1, 6].subset_of? array # => false
|
54
|
+
# [].subset_of? array # => true
|
55
|
+
# array.subset_of? 'foobar' # => ArgumentError
|
56
|
+
|
57
|
+
def subset_of?(array)
|
58
|
+
raise ArgumentError.new("Argument of Array#subset_of? must be an array") unless array.instance_of? Array
|
59
|
+
each {|i| return false unless array.include? i }
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
# The +#superset_of?+ method checks whether the calling array is a superset
|
64
|
+
# of the given +array+. It returns true if all objects in the calling array
|
65
|
+
# are also present in the array passed as an argument. If the argument array
|
66
|
+
# is empty, the method returns true.
|
67
|
+
#
|
68
|
+
# Examples:
|
69
|
+
# array = [1, 2, 3, 4]
|
70
|
+
# array.superset_of? [1, 2] # => true
|
71
|
+
# array.superset_of? [1, 2, 3, 4] # => true
|
72
|
+
# array.superset_of? [3, 6] # => false
|
73
|
+
# array.superset_of? [] # => true
|
74
|
+
# array.superset_of? {} # => ArgumentError
|
75
|
+
|
76
|
+
def superset_of?(array)
|
77
|
+
raise ArgumentError.new("Argument of Array#superset_of? must be an array") unless array.instance_of? Array
|
78
|
+
array.subset_of? self
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# The +#-+ method takes a single hash as an argument. It
|
4
|
+
# deletes the key-value pairs of the argument from the calling
|
5
|
+
# hash. An ArgumentError is raised if the argument is not a
|
6
|
+
# hash or not a subset of the
|
7
|
+
#
|
8
|
+
# Examples:
|
9
|
+
# {:foo => 'bar', :baz => :qux} - {:baz => :qux} # => {:foo => 'bar'}
|
10
|
+
# {:foo => 'bar'} - {:norf => :raboo} # => {:foo => 'bar'}
|
11
|
+
# {:foo => 'bar'} - {:foo => 'bar'}
|
12
|
+
# {:foo => 'bar'} - [1, 2, 3] # => ArgumentError
|
13
|
+
|
14
|
+
def - (hash)
|
15
|
+
raise ArgumentError.new("Hash can only be subtracted from its superset") unless superset_of? hash
|
16
|
+
deep_dup.reject {|key, value| hash.has_key? key }
|
17
|
+
end
|
18
|
+
end
|
@@ -35,22 +35,28 @@ class Object
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
# Like the +#try+ method, +#try_rescue+ takes 1 or more arguments. The first argument
|
43
|
-
# is the method to be called on the calling object, passed as a symbol. The others
|
44
|
-
# are zero or more arguments that will be passed through to that method, and an
|
45
|
-
# optional block to be likewise passed through.
|
46
|
-
#
|
47
|
-
# When called on NilClass, +#try_rescue+ always returns nil.
|
48
|
-
#
|
49
|
-
# Example:
|
50
|
-
# foo = nil
|
51
|
-
# foo.try_rescue(:has_key?, :bar) # => nil
|
38
|
+
# Ruby's core NilClass. See documentation for version
|
39
|
+
# 2.1.5[http://ruby-doc.org/core-2.1.5/NilClass.html],
|
40
|
+
# 2.0.0[http://ruby-doc.org/core-2.0.0/NilClass.html], or
|
41
|
+
# 1.9.3[http://ruby-doc.org/core-1.9.3/NilClass.html].
|
52
42
|
|
53
43
|
class NilClass
|
44
|
+
|
45
|
+
# The +#try_rescue+ method extends ReactiveSupport's +#try+ method so it rescues
|
46
|
+
# NoMethodErrors and TypeErrors as well as returning +nil+ when called on a +nil+
|
47
|
+
# value.
|
48
|
+
#
|
49
|
+
# Like the +#try+ method, +#try_rescue+ takes 1 or more arguments. The first argument
|
50
|
+
# is the method to be called on the calling object, passed as a symbol. The others
|
51
|
+
# are zero or more arguments that will be passed through to that method, and an
|
52
|
+
# optional block to be likewise passed through.
|
53
|
+
#
|
54
|
+
# When called on NilClass, +#try_rescue+ always returns nil.
|
55
|
+
#
|
56
|
+
# Example:
|
57
|
+
# foo = nil
|
58
|
+
# foo.try_rescue(:has_key?, :bar) # => nil
|
59
|
+
|
54
60
|
def try_rescue(*args, &block)
|
55
61
|
nil
|
56
62
|
end
|
data/lib/reactive_extensions.rb
CHANGED
@@ -2,6 +2,7 @@ require 'reactive_support/core_ext/object/duplicable'
|
|
2
2
|
require 'reactive_support/core_ext/object/deep_dup'
|
3
3
|
require 'reactive_support/core_ext/object/inclusion'
|
4
4
|
require 'reactive_support/core_ext/object/try'
|
5
|
+
require 'reactive_support/core_ext/array/extract_options'
|
5
6
|
|
6
7
|
Dir['./lib/reactive_extensions/**/*.rb'].each {|f| require f }
|
7
8
|
|
data/reactive_extensions.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
|
9
9
|
s.name = 'reactive_extensions'
|
10
10
|
s.version = ReactiveExtensions.gem_version
|
11
|
-
s.date = '2014-11-
|
11
|
+
s.date = '2014-11-30'
|
12
12
|
|
13
13
|
s.description = 'Handy extensions to core Ruby classes'
|
14
14
|
s.summary = 'ReactiveExtensions, a spinoff of ReactiveSupport, adds a variety of useful methods to core Ruby classes.'
|
data/spec/array_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'reactive_support/core_ext/hash/keys'
|
3
|
+
require 'reactive_support/core_ext/array/extract_options'
|
3
4
|
|
4
5
|
describe Array do
|
5
6
|
describe 'array scoping' do
|
@@ -9,7 +10,7 @@ describe Array do
|
|
9
10
|
let(:camus) { { 'name' => 'Albert Camus', 'nationality' => 'French' } }
|
10
11
|
let(:array) { [sartre, russell, wittgenstein, camus] }
|
11
12
|
|
12
|
-
describe '
|
13
|
+
describe 'scope' do
|
13
14
|
context 'symbol keys' do
|
14
15
|
context 'single value' do
|
15
16
|
it 'returns scoped hashes' do
|
@@ -41,7 +42,7 @@ describe Array do
|
|
41
42
|
end
|
42
43
|
end
|
43
44
|
|
44
|
-
describe '
|
45
|
+
describe 'where_not' do
|
45
46
|
context 'symbol keys' do
|
46
47
|
context 'single value' do
|
47
48
|
it 'returns scoped hashes' do
|
@@ -73,4 +74,82 @@ describe Array do
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
end
|
77
|
+
|
78
|
+
describe 'sets' do
|
79
|
+
describe 'subset_of?' do
|
80
|
+
context 'when the argument is a strict superset of the subject' do
|
81
|
+
it 'returns true' do
|
82
|
+
expect([1, 2].subset_of? [1, 2, 3, 4]).to be true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when the argument is equal to the subject' do
|
87
|
+
it 'returns true' do
|
88
|
+
expect([1, 2].subset_of? [1, 2]).to be true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the calling array is empty' do
|
93
|
+
it 'returns true' do
|
94
|
+
expect([].subset_of?(['foo', 'bar', 'baz'])).to be true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when the elements are not consecutive' do
|
99
|
+
it 'returns true' do
|
100
|
+
expect([2, 4].subset_of? [1, 2, 3, 4]).to be true
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context 'when the calling object is inside the argument' do
|
105
|
+
it 'returns false' do
|
106
|
+
expect([1, 2].subset_of? [[1, 2], 3, 4]).to be false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when the argument is not an array' do
|
111
|
+
it 'raises an error' do
|
112
|
+
expect{[1, 2].subset_of? {}}.to raise_error(ArgumentError)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe 'superset_of?' do
|
118
|
+
context 'when the argument is a strict subset of the subject' do
|
119
|
+
it 'returns true' do
|
120
|
+
expect([1, 2, 3, 4].superset_of? [1, 2]).to be true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when the argument is equal to the subject' do
|
125
|
+
it 'returns true' do
|
126
|
+
expect([1, 2].superset_of? [1, 2]).to be true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when the argument array is empty' do
|
131
|
+
it 'returns true' do
|
132
|
+
expect(['foo', 'bar', 'baz'].superset_of?([])).to be true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'when the elements are not consecutive' do
|
137
|
+
it 'returns true' do
|
138
|
+
expect([1, 2, 3, 4].superset_of? [1, 4]).to be true
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'when the argument is a single index' do
|
143
|
+
it 'returns false' do
|
144
|
+
expect([[1, 2], 3, 4].superset_of? [1, 2]).to be false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when the argument is not an array' do
|
149
|
+
it 'raises an error' do
|
150
|
+
expect{[1, 2].superset_of? 427}.to raise_error(ArgumentError)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
76
155
|
end
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,321 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hash do
|
4
|
+
let(:hash) { { :foo => 'bar', :baz => 'qux' } }
|
5
|
+
|
6
|
+
describe '-' do
|
7
|
+
context 'when the argument is a subset of the calling hash' do
|
8
|
+
it 'removes the keys of the argument from the calling hash' do
|
9
|
+
expect(hash - {:baz => 'qux'}).to eql({:foo => 'bar'})
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when the argument has additional keys' do
|
14
|
+
it 'raises an argument error' do
|
15
|
+
expect{ hash - {:baz => 'qux', :norf => 'raboof'} }.to raise_error(ArgumentError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'shared keys with unshared values' do
|
20
|
+
it 'raises an error' do
|
21
|
+
expect{ hash - {:foo => 'baz'} }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when the argument is not a hash' do
|
26
|
+
it 'raises an ArgumentError' do
|
27
|
+
expect{ hash - 'foobar' }.to raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'clean' do
|
33
|
+
it 'removes specified keys' do
|
34
|
+
expect(hash.clean(:baz)).to eql({:foo => 'bar'})
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'is non-destructive' do
|
38
|
+
expect{ hash.clean(:baz) }.not_to change(hash, :length)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'ignores absent keys' do
|
42
|
+
expect{ hash.clean(:raboof) }.not_to raise_error
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'accepts multiple arguments' do
|
46
|
+
expect(hash.clean(:baz, :raboof, :norf)).to eql({:foo => 'bar'})
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when all keys are bad' do
|
50
|
+
it 'returns an empty hash' do
|
51
|
+
expect(hash.clean(:foo, :baz)).to eql({})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'clean!' do
|
57
|
+
it 'removes specified keys' do
|
58
|
+
expect(hash.clean!(:baz)).to eql({:foo => 'bar'})
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'is destructive' do
|
62
|
+
expect{ hash.clean!(:baz) }.to change(hash, :length)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'ignores absent keys' do
|
66
|
+
expect{ hash.clean!(:raboof) }.not_to raise_error
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'accepts multiple arguments' do
|
70
|
+
expect(hash.clean!(:baz, :raboof, :norf)).to eql({:foo => 'bar'})
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when all keys are bad' do
|
74
|
+
it 'returns an empty hash' do
|
75
|
+
expect(hash.clean!(:foo, :baz)).to eql({})
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'only' do
|
81
|
+
it 'retains only listed keys' do
|
82
|
+
expect(hash.only(:foo)).to eql({:foo => 'bar'})
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'is non-destructive' do
|
86
|
+
expect{ hash.only(:foo) }.not_to change(hash, :length)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'accepts multiple arguments' do
|
90
|
+
expect(hash.only(:foo, :baz)).to eql({:foo => 'bar', :baz => 'qux'})
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'when no keys are allowed' do
|
94
|
+
it 'returns an empty hash' do
|
95
|
+
expect(hash.only(:raboof)).to eql({})
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'doesn\'t raise an error' do
|
99
|
+
expect{ hash.only(:raboof) }.not_to raise_error
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'only!' do
|
105
|
+
it 'retains only listed keys' do
|
106
|
+
expect(hash.only!(:foo)).to eql({:foo => 'bar'})
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'is destructive' do
|
110
|
+
expect{ hash.only!(:foo) }.to change(hash, :keys)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'accepts multiple arguments' do
|
114
|
+
expect(hash.only!(:foo, :baz, :norf)).to eql({:foo => 'bar', :baz => 'qux'})
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when no keys are allowed' do
|
118
|
+
it 'empties the hash' do
|
119
|
+
expect(hash.only!(:raboof)).to eql({})
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'doesn\'t raise an error' do
|
123
|
+
expect{ hash.only!(:raboof) }.not_to raise_error
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe 'standardize' do
|
129
|
+
let(:hash) { { :foo => 'bar', :bar => 'baz', :baz => 'qux' } }
|
130
|
+
|
131
|
+
it 'is non-destructive' do
|
132
|
+
expect{ hash.standardize(:foo, :bar) }.not_to change(hash, :keys)
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'exact match' do
|
136
|
+
it 'returns the hash as-is' do
|
137
|
+
expect(hash.standardize(:foo, :bar, :baz)).to eql hash
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'keys removed' do
|
142
|
+
it 'removes keys not given in args' do
|
143
|
+
expect(hash.standardize(:foo, :baz)).to eql({:foo => 'bar', :baz => 'qux'})
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'args include keys not present in hash' do
|
148
|
+
let(:with_nil) { {:foo => 'bar', :bar => 'baz', :baz => 'qux', :norf => nil } }
|
149
|
+
|
150
|
+
context 'default' do
|
151
|
+
it 'populates with nil values' do
|
152
|
+
expect(hash.standardize(:foo, :bar, :baz, :norf)).to eql(with_nil)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'doesn\'t raise an error' do
|
156
|
+
expect{ hash.standardize(:foo, :bar, :baz, :norf) }.not_to raise_error
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'with errors option set to false' do
|
161
|
+
it 'populates with nil values' do
|
162
|
+
expect(hash.standardize(:foo, :bar, :baz, :norf, :errors => false)).to eql(with_nil)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'doesn\'t raise an error' do
|
166
|
+
expect{ hash.standardize(:foo, :bar, :baz, :norf, :errors => false) }.not_to raise_error
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'with errors option set to true' do
|
171
|
+
it 'raises an error' do
|
172
|
+
expect{ hash.standardize(:foo, :bar, :baz, :norf, :errors => true) }.to raise_error(ArgumentError)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'standardize!' do
|
179
|
+
let(:hash) { { :foo => 'bar', :bar => 'baz', :baz => 'qux' } }
|
180
|
+
|
181
|
+
it 'is destructive' do
|
182
|
+
expect{ hash.standardize!(:foo, :bar) }.to change(hash, :keys)
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'exact match' do
|
186
|
+
it 'returns the hash as-is' do
|
187
|
+
expect(hash.standardize!(:foo, :bar, :baz)).to eql hash
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'keys removed' do
|
192
|
+
it 'removes keys not given in args' do
|
193
|
+
expect(hash.standardize!(:foo, :baz)).to eql({:foo => 'bar', :baz => 'qux'})
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context 'args include keys not present in hash' do
|
198
|
+
let(:with_nil) { {:foo => 'bar', :bar => 'baz', :baz => 'qux', :norf => nil } }
|
199
|
+
|
200
|
+
context 'default' do
|
201
|
+
it 'populates with nil values' do
|
202
|
+
expect(hash.standardize!(:foo, :bar, :baz, :norf)).to eql(with_nil)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'doesn\'t raise an error' do
|
206
|
+
expect{ hash.standardize!(:foo, :bar, :baz, :norf) }.not_to raise_error
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context 'with errors option set to false' do
|
211
|
+
it 'populates with nil values' do
|
212
|
+
expect(hash.standardize!(:foo, :bar, :baz, :norf, :errors => false)).to eql(with_nil)
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'doesn\'t raise an error' do
|
216
|
+
expect{ hash.standardize!(:foo, :bar, :baz, :norf, :errors => false) }.not_to raise_error
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'wtih errors option set to true' do
|
221
|
+
it 'raises an error' do
|
222
|
+
expect{ hash.standardize!(:foo, :bar, :baz, :norf, :errors => true) }.to raise_error(ArgumentError)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe 'subset_of?' do
|
229
|
+
let(:hash) { {:foo => 'bar', :bar => 'baz', :baz => 'qux'} }
|
230
|
+
|
231
|
+
context 'when true' do
|
232
|
+
it 'returns true' do
|
233
|
+
expect({:bar => 'baz'}.subset_of?(hash)).to be true
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'when the calling hash is empty' do
|
238
|
+
it 'returns true' do
|
239
|
+
expect({}.subset_of?(hash)).to be true
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
context 'when not all keys are shared' do
|
244
|
+
it 'returns false' do
|
245
|
+
expect({:bar => 'baz', :norf => 'qux'}.subset_of?(hash)).to be false
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'when keys are shared but not values' do
|
250
|
+
it 'returns false' do
|
251
|
+
expect({:bar => 'foo'}.subset_of?(hash)).to be false
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context 'when there are no shared keys' do
|
256
|
+
it 'returns false' do
|
257
|
+
expect({:norf => 'baz'}.subset_of?(hash)).to be false
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
context 'when the hash constitutes a value in a larger hash' do
|
262
|
+
it 'returns false' do
|
263
|
+
expect(hash.subset_of?({:norf => hash})).to be false
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'when argument is not a hash' do
|
268
|
+
it 'raises an error' do
|
269
|
+
expect{ hash.subset_of?('foobar') }.to raise_error(ArgumentError)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context 'when the calling hash is '
|
274
|
+
end
|
275
|
+
|
276
|
+
describe 'superset_of?' do
|
277
|
+
let(:hash) { {:foo => 'bar', :bar => 'baz', :baz => 'qux'} }
|
278
|
+
|
279
|
+
context 'when true' do
|
280
|
+
it 'returns true' do
|
281
|
+
expect(hash.superset_of?({:bar => 'baz'})).to be true
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
context 'when the argument hash is empty' do
|
286
|
+
it 'returns true' do
|
287
|
+
expect(hash.superset_of?({})).to be true
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
context 'when not all keys are shared' do
|
292
|
+
it 'returns false' do
|
293
|
+
expect(hash.superset_of?({:bar => 'baz', :norf => 'qux'})).to be false
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
context 'when keys are shared but not values' do
|
298
|
+
it 'returns false' do
|
299
|
+
expect(hash.superset_of?({:bar => 'foo'})).to be false
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
context 'when there are no shared keys' do
|
304
|
+
it 'returns false' do
|
305
|
+
expect(hash.superset_of?({:norf => 'baz'})).to be false
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'when the hash constitutes a value in a larger hash' do
|
310
|
+
it 'returns false' do
|
311
|
+
expect({:norf => hash}.superset_of?(hash)).to be false
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
context 'when argument is not a hash' do
|
316
|
+
it 'raises an error' do
|
317
|
+
expect{ hash.superset_of?('foobar') }.to raise_error(ArgumentError)
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
data/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reactive_extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0.beta
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dana Scheider
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: reactive_support
|
@@ -116,9 +116,13 @@ extra_rdoc_files:
|
|
116
116
|
files:
|
117
117
|
- "./lib/reactive_extensions.rb"
|
118
118
|
- "./lib/reactive_extensions/errors.rb"
|
119
|
+
- "./lib/reactive_extensions/sanitation.rb"
|
119
120
|
- "./lib/reactive_extensions/scope.rb"
|
121
|
+
- "./lib/reactive_extensions/sets.rb"
|
122
|
+
- "./lib/reactive_extensions/subtraction.rb"
|
120
123
|
- "./lib/reactive_extensions/try_rescue.rb"
|
121
124
|
- "./spec/array_spec.rb"
|
125
|
+
- "./spec/hash_spec.rb"
|
122
126
|
- "./spec/object_spec.rb"
|
123
127
|
- "./spec/proc_spec.rb"
|
124
128
|
- "./spec/spec_helper.rb"
|