selector 0.0.1
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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +20 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +7 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +147 -0
- data/Rakefile +27 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +14 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +72 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/lib/selector.rb +55 -0
- data/lib/selector/and.rb +64 -0
- data/lib/selector/anything.rb +30 -0
- data/lib/selector/array.rb +42 -0
- data/lib/selector/collection.rb +37 -0
- data/lib/selector/condition.rb +99 -0
- data/lib/selector/function.rb +36 -0
- data/lib/selector/not.rb +50 -0
- data/lib/selector/nothing.rb +30 -0
- data/lib/selector/or.rb +54 -0
- data/lib/selector/regexp.rb +49 -0
- data/lib/selector/version.rb +9 -0
- data/selector.gemspec +25 -0
- data/spec/integration/blacklist_spec.rb +33 -0
- data/spec/integration/composition_spec.rb +34 -0
- data/spec/integration/negation_spec.rb +13 -0
- data/spec/integration/whitelist_spec.rb +33 -0
- data/spec/shared/generator.rb +8 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/unit/selector/and_spec.rb +100 -0
- data/spec/unit/selector/anything_spec.rb +29 -0
- data/spec/unit/selector/array_spec.rb +76 -0
- data/spec/unit/selector/collection_spec.rb +48 -0
- data/spec/unit/selector/condition_spec.rb +135 -0
- data/spec/unit/selector/function_spec.rb +46 -0
- data/spec/unit/selector/not_spec.rb +61 -0
- data/spec/unit/selector/nothing_spec.rb +29 -0
- data/spec/unit/selector/or_spec.rb +88 -0
- data/spec/unit/selector/regexp_spec.rb +69 -0
- data/spec/unit/selector_spec.rb +148 -0
- metadata +145 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Selector
|
4
|
+
|
5
|
+
# The condition checks if the function returns truthy result for a value
|
6
|
+
#
|
7
|
+
# @example (see #[])
|
8
|
+
#
|
9
|
+
class Function < Condition
|
10
|
+
|
11
|
+
# Initializes the condition with a function
|
12
|
+
#
|
13
|
+
# @param [#call] function
|
14
|
+
#
|
15
|
+
def initialize(_)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
# Checks if the function returns truthy for value
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# condition = Selector::Function.new -> v { v[/foo/] }
|
23
|
+
# condition[:foo] # => true
|
24
|
+
# condition[:bar] # => false
|
25
|
+
#
|
26
|
+
# @param (see Selector::Condition#[])
|
27
|
+
#
|
28
|
+
# @return (see Selector::Condition#[])
|
29
|
+
#
|
30
|
+
def [](value)
|
31
|
+
attribute.call(value) ? true : false
|
32
|
+
end
|
33
|
+
|
34
|
+
end # class Function
|
35
|
+
|
36
|
+
end # module Selector
|
data/lib/selector/not.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Selector
|
4
|
+
|
5
|
+
# The inversion of another condition
|
6
|
+
#
|
7
|
+
class Not < Condition
|
8
|
+
|
9
|
+
# @private
|
10
|
+
def self.new(source)
|
11
|
+
return NOTHING if source.equal? ANYTHING
|
12
|
+
return ANYTHING if source.equal? NOTHING
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
# Checks if the value doesn't satisfy inverted condition
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# condition = Selector.new(only: [:foo])
|
20
|
+
# inversion[:foo] # => true
|
21
|
+
# inversion[:bar] # => false
|
22
|
+
#
|
23
|
+
# inversion = Not.new(condition)
|
24
|
+
# inversion[:foo] # => false
|
25
|
+
# inversion[:bar] # => true
|
26
|
+
#
|
27
|
+
# @param (see Selector::Condition#[])
|
28
|
+
#
|
29
|
+
# @return (see Selector::Condition#[])
|
30
|
+
#
|
31
|
+
def [](value)
|
32
|
+
!attribute[value]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the inverted condition (avoids double negation)
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# condition = Selector.new(only: [:foo])
|
39
|
+
# inversion = Not.new(condition)
|
40
|
+
# !inversion == condition # => true
|
41
|
+
#
|
42
|
+
# @return (see Selector::Condition#!)
|
43
|
+
#
|
44
|
+
def !
|
45
|
+
attribute
|
46
|
+
end
|
47
|
+
|
48
|
+
end # class Not
|
49
|
+
|
50
|
+
end # module Selector
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Selector
|
4
|
+
|
5
|
+
# The condition that accepts any value
|
6
|
+
#
|
7
|
+
# @example (see #[])
|
8
|
+
#
|
9
|
+
class Nothing < Condition
|
10
|
+
|
11
|
+
include Singleton
|
12
|
+
|
13
|
+
# @!method [](value)
|
14
|
+
# Returns false
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# condition = Selector::Nothing.instance # singleton
|
18
|
+
# condition[:foo] # => false
|
19
|
+
#
|
20
|
+
# @param (see Selector::Condition#[])
|
21
|
+
#
|
22
|
+
# @return [false]
|
23
|
+
#
|
24
|
+
def [](_value)
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
end # class Nothing
|
29
|
+
|
30
|
+
end # module Selector
|
data/lib/selector/or.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Selector
|
4
|
+
|
5
|
+
# The composition of several conditions. Requires any of them to be satisfied
|
6
|
+
#
|
7
|
+
# @example (see #[])
|
8
|
+
#
|
9
|
+
class Or < Condition
|
10
|
+
|
11
|
+
# @private
|
12
|
+
def self.new(*attributes)
|
13
|
+
attrs = attributes.uniq - [NOTHING]
|
14
|
+
|
15
|
+
return NOTHING if attrs.empty?
|
16
|
+
return attrs.first if attrs.count.equal?(1)
|
17
|
+
return ANYTHING if attrs.include? ANYTHING
|
18
|
+
return ANYTHING if (attrs & attrs.map(&:!)).any?
|
19
|
+
|
20
|
+
super(*attrs)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Checks if value satisfies any of composed conditions
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# left = Selector.new only: /foo/
|
27
|
+
# right = Selector.new only: /bar/
|
28
|
+
# composition = Selector::Or.new(left, right)
|
29
|
+
#
|
30
|
+
# composition[:foo] # => true
|
31
|
+
# composition[:bar] # => true
|
32
|
+
# composition[:baz] # => false
|
33
|
+
#
|
34
|
+
# @param (see Selector::Composition#[])
|
35
|
+
#
|
36
|
+
# @return (see Selector::Composition#[])
|
37
|
+
#
|
38
|
+
def [](value)
|
39
|
+
attributes.detect { |part| part[value] } ? true : false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Adds the other condition to the composition (avoids nesting)
|
43
|
+
#
|
44
|
+
# @param (see Selector::Composition#|)
|
45
|
+
#
|
46
|
+
# @return (see Selector::Composition#|)
|
47
|
+
#
|
48
|
+
def |(other)
|
49
|
+
Or.new(*attributes, other)
|
50
|
+
end
|
51
|
+
|
52
|
+
end # class Or
|
53
|
+
|
54
|
+
end # module Selector
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Selector
|
4
|
+
|
5
|
+
# The condition checks if a value matches the regexp
|
6
|
+
#
|
7
|
+
# @example (see #[])
|
8
|
+
#
|
9
|
+
class Regexp < Condition
|
10
|
+
|
11
|
+
# Initializes the condition with the regexp
|
12
|
+
#
|
13
|
+
# @param [::Regexp] regexp
|
14
|
+
#
|
15
|
+
def initialize(_)
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
# Checks if the stringified value matches the regexp
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# condition = Selector::Regexp.new /1/
|
23
|
+
# condition[11] # => true
|
24
|
+
# condition[22] # => false
|
25
|
+
#
|
26
|
+
# @param (see Selector::Condition#[])
|
27
|
+
#
|
28
|
+
# @return (see Selector::Condition#[])
|
29
|
+
#
|
30
|
+
def [](value)
|
31
|
+
value.to_s[attribute] ? true : false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates an OR composition
|
35
|
+
#
|
36
|
+
# If other value is a regexp, then creates modified regexp to avoid nesting
|
37
|
+
#
|
38
|
+
# @param (see Selector::Composition#|)
|
39
|
+
#
|
40
|
+
# @return (see Selector::Composition#|)
|
41
|
+
#
|
42
|
+
def |(other)
|
43
|
+
return super unless other.instance_of? Regexp
|
44
|
+
Regexp.new(/(#{attribute})|(#{other.attribute})/)
|
45
|
+
end
|
46
|
+
|
47
|
+
end # class Regexp < Condition
|
48
|
+
|
49
|
+
end # module Selector
|
data/selector.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "selector/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
|
6
|
+
gem.name = "selector"
|
7
|
+
gem.version = Selector::VERSION.dup
|
8
|
+
gem.author = "Andrew Kozin"
|
9
|
+
gem.email = "andrew.kozin@gmail.com"
|
10
|
+
gem.homepage = "https://github.com/nepalez/selector"
|
11
|
+
gem.summary = "Composable multi-type conditions."
|
12
|
+
gem.license = "MIT"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
15
|
+
gem.test_files = Dir["spec/**/*.rb"]
|
16
|
+
gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
|
19
|
+
gem.required_ruby_version = "~> 1.9.3"
|
20
|
+
|
21
|
+
gem.add_runtime_dependency "ice_nine", "~> 0.11"
|
22
|
+
|
23
|
+
gem.add_development_dependency "hexx-rspec", "~> 0.5"
|
24
|
+
|
25
|
+
end # Gem::Specification
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe "Blacklist" do
|
4
|
+
|
5
|
+
it "works with array" do
|
6
|
+
selector = Selector.new except: [:foo, :qux]
|
7
|
+
|
8
|
+
expect(selector[:foo]).to eql(false)
|
9
|
+
expect(selector[:bar]).to eql(true)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "works with regexp" do
|
13
|
+
selector = Selector.new except: /foo/
|
14
|
+
|
15
|
+
expect(selector[:foobar]).to eql(false)
|
16
|
+
expect(selector[:bar]).to eql(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "works with range" do
|
20
|
+
selector = Selector.new except: 1..3
|
21
|
+
|
22
|
+
expect(selector[2.4]).to eql(false)
|
23
|
+
expect(selector[0]).to eql(true)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "works with proc" do
|
27
|
+
selector = Selector.new except: -> value { value.is_a? Hash }
|
28
|
+
|
29
|
+
expect(selector[foo: :FOO]).to eql(false)
|
30
|
+
expect(selector[:foo]).to eql(true)
|
31
|
+
end
|
32
|
+
|
33
|
+
end # describe Whitelist
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe "Composition" do
|
4
|
+
|
5
|
+
it "& works" do
|
6
|
+
blacklist = Selector.new except: /bar/
|
7
|
+
whitelist = Selector.new only: /foo/
|
8
|
+
selector = whitelist & blacklist
|
9
|
+
|
10
|
+
expect(selector[:foobaz]).to eql(true)
|
11
|
+
expect(selector[:foobar]).to eql(false)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "- works" do
|
15
|
+
whitelist = Selector.new only: /foo/
|
16
|
+
blacklist = Selector.new except: /bar/
|
17
|
+
selector = whitelist - blacklist
|
18
|
+
|
19
|
+
expect(selector[:foobar]).to eql(true)
|
20
|
+
expect(selector[:bar]).to eql(false)
|
21
|
+
expect(selector[:foo]).to eql(false)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "| works" do
|
25
|
+
whitelist = Selector.new only: 4..8
|
26
|
+
blacklist = Selector.new except: 1..5
|
27
|
+
selector = whitelist | blacklist
|
28
|
+
|
29
|
+
expect(selector[0.5]).to eql(true)
|
30
|
+
expect(selector[5.5]).to eql(true)
|
31
|
+
expect(selector[2.5]).to eql(false)
|
32
|
+
end
|
33
|
+
|
34
|
+
end # describe Composition
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe "Whitelist" do
|
4
|
+
|
5
|
+
it "works with array" do
|
6
|
+
selector = Selector.new only: [:foo, :qux]
|
7
|
+
|
8
|
+
expect(selector[:foo]).to eql(true)
|
9
|
+
expect(selector[:bar]).to eql(false)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "works with regexp" do
|
13
|
+
selector = Selector.new only: /foo/
|
14
|
+
|
15
|
+
expect(selector[:foobar]).to eql(true)
|
16
|
+
expect(selector[:bar]).to eql(false)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "works with range" do
|
20
|
+
selector = Selector.new only: 1..3
|
21
|
+
|
22
|
+
expect(selector[2.4]).to eql(true)
|
23
|
+
expect(selector[0]).to eql(false)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "works with proc" do
|
27
|
+
selector = Selector.new only: -> value { value.is_a? Hash }
|
28
|
+
|
29
|
+
expect(selector[foo: :FOO]).to eql(true)
|
30
|
+
expect(selector[:foo]).to eql(false)
|
31
|
+
end
|
32
|
+
|
33
|
+
end # describe Whitelist
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "shared/generator"
|
4
|
+
|
5
|
+
module Selector
|
6
|
+
|
7
|
+
describe Selector::And do
|
8
|
+
|
9
|
+
let(:foo) { generate(/foo/) }
|
10
|
+
let(:bar) { generate(/bar/) }
|
11
|
+
let(:baz) { generate(/baz/) }
|
12
|
+
|
13
|
+
let(:composition) { described_class.new(foo, bar) }
|
14
|
+
|
15
|
+
describe ".new" do
|
16
|
+
|
17
|
+
subject { composition }
|
18
|
+
|
19
|
+
it { is_expected.to be_kind_of Condition }
|
20
|
+
it { is_expected.to be_frozen }
|
21
|
+
|
22
|
+
it "returns ANYTHING when possible" do
|
23
|
+
subject = described_class.new ANYTHING
|
24
|
+
expect(subject).to eql ANYTHING
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns NOTHING if exists" do
|
28
|
+
subject = described_class.new foo, bar, NOTHING
|
29
|
+
expect(subject).to eql NOTHING
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns NOTHING when possible" do
|
33
|
+
subject = described_class.new foo, !foo
|
34
|
+
expect(subject).to eql NOTHING
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns the only attribute" do
|
38
|
+
subject = described_class.new foo, ANYTHING
|
39
|
+
expect(subject).to eql foo
|
40
|
+
end
|
41
|
+
|
42
|
+
it "ignores duplication" do
|
43
|
+
subject = described_class.new foo, bar, foo
|
44
|
+
expect(subject.attributes).to eql [foo, bar]
|
45
|
+
end
|
46
|
+
|
47
|
+
it "ignores ANYTHING" do
|
48
|
+
subject = described_class.new foo, bar, ANYTHING
|
49
|
+
expect(subject.attributes).to eql [foo, bar]
|
50
|
+
end
|
51
|
+
|
52
|
+
end # describe .new
|
53
|
+
|
54
|
+
describe "#[]" do
|
55
|
+
|
56
|
+
subject { composition[value] }
|
57
|
+
|
58
|
+
context "when all conditions are satisfied" do
|
59
|
+
|
60
|
+
let(:value) { "foobar" }
|
61
|
+
it { is_expected.to eql(true) }
|
62
|
+
|
63
|
+
end # context
|
64
|
+
|
65
|
+
context "when any condition isn't satisfied" do
|
66
|
+
|
67
|
+
let(:value) { "foo" }
|
68
|
+
it { is_expected.to eql(false) }
|
69
|
+
|
70
|
+
end # context
|
71
|
+
|
72
|
+
end # describe #[]
|
73
|
+
|
74
|
+
describe "#&" do
|
75
|
+
|
76
|
+
subject { composition & baz }
|
77
|
+
|
78
|
+
it { is_expected.to be_kind_of(described_class) }
|
79
|
+
|
80
|
+
it "updates conditions (avoids nesting)" do
|
81
|
+
expect(subject.attributes).to eql [foo, bar, baz]
|
82
|
+
end
|
83
|
+
|
84
|
+
end # describe #&
|
85
|
+
|
86
|
+
describe "#-" do
|
87
|
+
|
88
|
+
subject { composition - baz }
|
89
|
+
|
90
|
+
it { is_expected.to be_kind_of(described_class) }
|
91
|
+
|
92
|
+
it "updates conditions (avoids nesting)" do
|
93
|
+
expect(subject.attributes).to contain_exactly(foo, bar, !baz)
|
94
|
+
end
|
95
|
+
|
96
|
+
end # describe #-
|
97
|
+
|
98
|
+
end # describe Selector::And
|
99
|
+
|
100
|
+
end # module Selector
|