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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +9 -0
  4. data/.metrics +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +2 -0
  7. data/.travis.yml +20 -0
  8. data/.yardopts +3 -0
  9. data/CHANGELOG.md +5 -0
  10. data/Gemfile +7 -0
  11. data/Guardfile +14 -0
  12. data/LICENSE +21 -0
  13. data/README.md +147 -0
  14. data/Rakefile +27 -0
  15. data/config/metrics/STYLEGUIDE +230 -0
  16. data/config/metrics/cane.yml +5 -0
  17. data/config/metrics/churn.yml +6 -0
  18. data/config/metrics/flay.yml +2 -0
  19. data/config/metrics/metric_fu.yml +14 -0
  20. data/config/metrics/reek.yml +1 -0
  21. data/config/metrics/roodi.yml +24 -0
  22. data/config/metrics/rubocop.yml +72 -0
  23. data/config/metrics/saikuro.yml +3 -0
  24. data/config/metrics/simplecov.yml +6 -0
  25. data/config/metrics/yardstick.yml +37 -0
  26. data/lib/selector.rb +55 -0
  27. data/lib/selector/and.rb +64 -0
  28. data/lib/selector/anything.rb +30 -0
  29. data/lib/selector/array.rb +42 -0
  30. data/lib/selector/collection.rb +37 -0
  31. data/lib/selector/condition.rb +99 -0
  32. data/lib/selector/function.rb +36 -0
  33. data/lib/selector/not.rb +50 -0
  34. data/lib/selector/nothing.rb +30 -0
  35. data/lib/selector/or.rb +54 -0
  36. data/lib/selector/regexp.rb +49 -0
  37. data/lib/selector/version.rb +9 -0
  38. data/selector.gemspec +25 -0
  39. data/spec/integration/blacklist_spec.rb +33 -0
  40. data/spec/integration/composition_spec.rb +34 -0
  41. data/spec/integration/negation_spec.rb +13 -0
  42. data/spec/integration/whitelist_spec.rb +33 -0
  43. data/spec/shared/generator.rb +8 -0
  44. data/spec/spec_helper.rb +12 -0
  45. data/spec/unit/selector/and_spec.rb +100 -0
  46. data/spec/unit/selector/anything_spec.rb +29 -0
  47. data/spec/unit/selector/array_spec.rb +76 -0
  48. data/spec/unit/selector/collection_spec.rb +48 -0
  49. data/spec/unit/selector/condition_spec.rb +135 -0
  50. data/spec/unit/selector/function_spec.rb +46 -0
  51. data/spec/unit/selector/not_spec.rb +61 -0
  52. data/spec/unit/selector/nothing_spec.rb +29 -0
  53. data/spec/unit/selector/or_spec.rb +88 -0
  54. data/spec/unit/selector/regexp_spec.rb +69 -0
  55. data/spec/unit/selector_spec.rb +148 -0
  56. 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
@@ -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
@@ -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
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ module Selector
4
+
5
+ # The semantic version of the gem.
6
+ # @see http://semver.org/ Semantic versioning 2.0
7
+ VERSION = "0.0.1".freeze
8
+
9
+ 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,13 @@
1
+ # encoding: utf-8
2
+
3
+ describe "Negation" do
4
+
5
+ it "works" do
6
+ blacklist = Selector.new except: /bar/
7
+ selector = !blacklist
8
+
9
+ expect(selector[:foo]).to eql(false)
10
+ expect(selector[:bar]).to eql(true)
11
+ end
12
+
13
+ end # describe Negation
@@ -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
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+
3
+ # Generates the condition object with regexp
4
+ def generate(regexp)
5
+ klass = Class.new(Selector::Condition)
6
+ klass.send(:define_method, :[]) { |value| nil ^ value[regexp] }
7
+ klass.new
8
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require "hexx-suit"
5
+ Hexx::Suit.load_metrics_for(self)
6
+ rescue LoadError
7
+ require "hexx-rspec"
8
+ Hexx::RSpec.load_metrics_for(self)
9
+ end
10
+
11
+ # Loads the code under test
12
+ require "selector"
@@ -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