wrong 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +114 -25
- data/lib/predicated/Gemfile +15 -0
- data/lib/predicated/LICENSE +20 -0
- data/lib/predicated/README.markdown +191 -0
- data/lib/predicated/Rakefile +51 -0
- data/lib/predicated/lib/predicated.rb +4 -0
- data/lib/predicated/lib/predicated/autogen_call.rb +37 -0
- data/lib/predicated/lib/predicated/constrain.rb +66 -0
- data/lib/predicated/lib/predicated/evaluate.rb +94 -0
- data/lib/predicated/lib/predicated/from/callable_object.rb +108 -0
- data/lib/predicated/lib/predicated/from/json.rb +59 -0
- data/lib/predicated/lib/predicated/from/ruby_code_string.rb +73 -0
- data/lib/predicated/lib/predicated/from/url_part.rb +104 -0
- data/lib/predicated/lib/predicated/from/xml.rb +61 -0
- data/lib/predicated/lib/predicated/gem_check.rb +34 -0
- data/lib/predicated/lib/predicated/predicate.rb +111 -0
- data/lib/predicated/lib/predicated/print.rb +62 -0
- data/lib/predicated/lib/predicated/selectable.rb +102 -0
- data/lib/predicated/lib/predicated/simple_templated_predicate.rb +79 -0
- data/lib/predicated/lib/predicated/string_utils.rb +20 -0
- data/lib/predicated/lib/predicated/to/arel.rb +41 -0
- data/lib/predicated/lib/predicated/to/json.rb +48 -0
- data/lib/predicated/lib/predicated/to/sentence.rb +94 -0
- data/lib/predicated/lib/predicated/to/solr.rb +15 -0
- data/lib/predicated/lib/predicated/to/xml.rb +67 -0
- data/lib/predicated/lib/predicated/version.rb +3 -0
- data/lib/predicated/predicated.gemspec +22 -0
- data/lib/predicated/test/autogen_call_test.rb +40 -0
- data/lib/predicated/test/canonical_transform_cases.rb +63 -0
- data/lib/predicated/test/constrain_test.rb +86 -0
- data/lib/predicated/test/enumerable_test.rb +32 -0
- data/lib/predicated/test/equality_test.rb +32 -0
- data/lib/predicated/test/evaluate_test.rb +149 -0
- data/lib/predicated/test/from/callable_object_canonical_test.rb +43 -0
- data/lib/predicated/test/from/callable_object_test.rb +78 -0
- data/lib/predicated/test/from/json_test.rb +83 -0
- data/lib/predicated/test/from/ruby_code_string_canonical_test.rb +37 -0
- data/lib/predicated/test/from/ruby_code_string_test.rb +103 -0
- data/lib/predicated/test/from/url_part_parser_test.rb +123 -0
- data/lib/predicated/test/from/url_part_test.rb +48 -0
- data/lib/predicated/test/from/xml_test.rb +57 -0
- data/lib/predicated/test/json_conversion_test.rb +33 -0
- data/lib/predicated/test/print_test.rb +66 -0
- data/lib/predicated/test/selectable_test.rb +123 -0
- data/lib/predicated/test/simple_templated_predicate_test.rb +39 -0
- data/lib/predicated/test/suite.rb +2 -0
- data/lib/predicated/test/test_helper.rb +64 -0
- data/lib/predicated/test/test_helper_with_wrong.rb +6 -0
- data/lib/predicated/test/to/arel_test.rb +85 -0
- data/lib/predicated/test/to/json_test.rb +74 -0
- data/lib/predicated/test/to/sentence_test.rb +90 -0
- data/lib/predicated/test/to/solr_test.rb +39 -0
- data/lib/predicated/test/to/xml_test.rb +72 -0
- data/lib/predicated/test/xml_conversion_test.rb +34 -0
- data/lib/predicated/test_integration/arel_integration_test.rb +52 -0
- data/lib/predicated/test_integration/canonical_integration_cases.rb +66 -0
- data/lib/predicated/test_integration/schema.xml +83 -0
- data/lib/predicated/test_integration/solr_integration_test.rb +71 -0
- data/lib/predicated/test_integration/sqlite_db +0 -0
- data/lib/predicated/test_integration/suite.rb +2 -0
- data/lib/predicated/test_integration/usage_test.rb +252 -0
- data/lib/wrong.rb +3 -1
- data/lib/wrong/adapters/test_unit.rb +1 -3
- data/lib/wrong/assert.rb +81 -24
- data/lib/wrong/chunk.rb +145 -0
- data/lib/wrong/message/string_diff.rb +2 -4
- data/lib/wrong/message/test_context.rb +2 -2
- data/lib/wrong/version.rb +2 -2
- data/test/adapters/minitest_test.rb +16 -9
- data/test/adapters/test_unit_test.rb +1 -1
- data/test/assert_test.rb +90 -0
- data/test/catch_raise_test.rb +2 -2
- data/test/chunk_test.rb +236 -0
- data/test/failures_test.rb +109 -74
- data/test/message/array_diff_test.rb +35 -19
- data/test/message/string_diff_test.rb +39 -15
- data/test/message/test_context_text.rb +2 -2
- data/test/test_helper.rb +25 -7
- metadata +86 -33
- data/test/basic_assert_test.rb +0 -38
@@ -0,0 +1,104 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
|
3
|
+
module Predicated
|
4
|
+
|
5
|
+
require_gem_version("treetop", "1.4.8")
|
6
|
+
|
7
|
+
class Predicate
|
8
|
+
def self.from_url_part(url_part)
|
9
|
+
TreetopUrlPartParser.new.parse(url_part).to_predicate
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module TreetopUrlPart
|
14
|
+
Treetop.load_from_string(%{
|
15
|
+
|
16
|
+
grammar TreetopUrlPart
|
17
|
+
|
18
|
+
include Predicated::TreetopUrlPart
|
19
|
+
|
20
|
+
rule or
|
21
|
+
( and "|" or <OrNode>) / and
|
22
|
+
end
|
23
|
+
|
24
|
+
rule and
|
25
|
+
( leaf "&" and <AndNode> ) / leaf
|
26
|
+
end
|
27
|
+
|
28
|
+
rule operation
|
29
|
+
unquoted_string sign unquoted_string <OperationNode>
|
30
|
+
end
|
31
|
+
|
32
|
+
rule parens
|
33
|
+
not? "(" or ")" <ParensNode>
|
34
|
+
end
|
35
|
+
|
36
|
+
rule not
|
37
|
+
'!'
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
rule leaf
|
43
|
+
operation / parens
|
44
|
+
end
|
45
|
+
|
46
|
+
rule unquoted_string
|
47
|
+
[0-9a-zA-Z]*
|
48
|
+
end
|
49
|
+
|
50
|
+
rule sign
|
51
|
+
('>=' / '<=' / '<' / '>' / '=' )
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
})
|
56
|
+
|
57
|
+
class OperationNode < Treetop::Runtime::SyntaxNode
|
58
|
+
def left_text; elements[0].text_value end
|
59
|
+
def sign_text; elements[1].text_value end
|
60
|
+
def right_text; elements[2].text_value end
|
61
|
+
|
62
|
+
SIGN_TO_PREDICATE_CLASS = {
|
63
|
+
"=" => Equal,
|
64
|
+
">" => GreaterThan,
|
65
|
+
"<" => LessThan,
|
66
|
+
">=" => GreaterThanOrEqualTo,
|
67
|
+
"<=" => LessThanOrEqualTo
|
68
|
+
}
|
69
|
+
|
70
|
+
def to_predicate
|
71
|
+
SIGN_TO_PREDICATE_CLASS[sign_text].new(left_text, right_text)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class AndNode < Treetop::Runtime::SyntaxNode
|
76
|
+
def left; elements[0] end
|
77
|
+
def right; elements[2] end
|
78
|
+
|
79
|
+
def to_predicate
|
80
|
+
And.new(left.to_predicate, right.to_predicate)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class OrNode < Treetop::Runtime::SyntaxNode
|
85
|
+
def left; elements[0] end
|
86
|
+
def right; elements[2] end
|
87
|
+
|
88
|
+
def to_predicate
|
89
|
+
Or.new(left.to_predicate, right.to_predicate)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class ParensNode < Treetop::Runtime::SyntaxNode
|
94
|
+
def inner; elements[2] end
|
95
|
+
def not?; elements[0].text_value=="!" end
|
96
|
+
|
97
|
+
def to_predicate
|
98
|
+
inner_predicate = inner.to_predicate
|
99
|
+
not? ? Not.new(inner_predicate) : inner_predicate
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
|
3
|
+
module Predicated
|
4
|
+
|
5
|
+
require_gem_version("nokogiri", "1.4.3")
|
6
|
+
|
7
|
+
class Predicate
|
8
|
+
def self.from_xml(xml_str)
|
9
|
+
NodeToPredicate.convert(Nokogiri::XML(xml_str).root)
|
10
|
+
end
|
11
|
+
|
12
|
+
module NodeToPredicate
|
13
|
+
OPERATION_TAG_TO_CLASS = {
|
14
|
+
"equal" => Equal,
|
15
|
+
"greaterThan" => GreaterThan,
|
16
|
+
"lessThan" => LessThan,
|
17
|
+
"greaterThanOrEqualTo" => GreaterThanOrEqualTo,
|
18
|
+
"lessThanOrEqualTo" => LessThanOrEqualTo
|
19
|
+
}
|
20
|
+
|
21
|
+
def self.convert(node)
|
22
|
+
|
23
|
+
node = next_non_text_node(node)
|
24
|
+
|
25
|
+
if node.name == "and"
|
26
|
+
left = next_non_text_node(node.children[0])
|
27
|
+
right = next_non_text_node(left.next)
|
28
|
+
And.new(convert(left), convert(right))
|
29
|
+
elsif node.name == "or"
|
30
|
+
left = next_non_text_node(node.children[0])
|
31
|
+
right = next_non_text_node(left.next)
|
32
|
+
Or.new(convert(left), convert(right))
|
33
|
+
elsif node.name == "not"
|
34
|
+
inner = next_non_text_node(node.children[0])
|
35
|
+
Not.new(convert(inner))
|
36
|
+
elsif operation_class=OPERATION_TAG_TO_CLASS[node.name]
|
37
|
+
left = next_non_text_node(node.children[0])
|
38
|
+
right = next_non_text_node(left.next)
|
39
|
+
operation_class.new(left.text, right.text)
|
40
|
+
else
|
41
|
+
raise DontKnowWhatToDoWithThisXmlTag.new(node.name)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.next_non_text_node(node)
|
46
|
+
while node.text?
|
47
|
+
node = node.next
|
48
|
+
end
|
49
|
+
node
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class DontKnowWhatToDoWithThisXmlTag < StandardError
|
55
|
+
def initialize(xml_tag)
|
56
|
+
super("don't know what to do with #{xml_tag}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Predicated
|
2
|
+
def self.require_gem_version(gem_name, minimum_version, require_name=gem_name)
|
3
|
+
unless Gem.available?(gem_name, Gem::Requirement.create(">= #{minimum_version}"))
|
4
|
+
raise %{
|
5
|
+
Gem: #{gem_name} >=#{minimum_version}
|
6
|
+
Does not appear to be installed. Please install it.
|
7
|
+
|
8
|
+
Predicated is built in a way that allows you to pick and
|
9
|
+
choose which features to use.
|
10
|
+
|
11
|
+
RubyGems has no way to specify optional dependencies,
|
12
|
+
therefore I've made the decision not to have Predicated
|
13
|
+
automatically depend into the various gems referenced
|
14
|
+
in from/to "extensions".
|
15
|
+
|
16
|
+
The cost here is that the gem install doesn't necessarily
|
17
|
+
"just work" for you out of the box. But in return you get
|
18
|
+
greater flexibility.
|
19
|
+
|
20
|
+
Notably, rails/arel unfortunately has a hard dependency
|
21
|
+
on Rails 3 activesupport, which requires ruby 1.8.7.
|
22
|
+
By making from/to dependencies optional, those with
|
23
|
+
no interest in arel can use Predicated in a wider
|
24
|
+
variety of environments.
|
25
|
+
|
26
|
+
For more discussion see:
|
27
|
+
http://stackoverflow.com/questions/2993335/rubygems-optional-dependencies
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
require require_name
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "predicated/gem_check"
|
2
|
+
|
3
|
+
module Predicated
|
4
|
+
|
5
|
+
def Predicate(&block)
|
6
|
+
result = nil
|
7
|
+
Module.new do
|
8
|
+
extend Shorthand
|
9
|
+
result = instance_eval(&block)
|
10
|
+
end
|
11
|
+
result
|
12
|
+
end
|
13
|
+
|
14
|
+
class Predicate; end #marker class
|
15
|
+
|
16
|
+
class Unary < Predicate
|
17
|
+
attr_accessor :inner
|
18
|
+
|
19
|
+
def initialize(inner)
|
20
|
+
@inner = inner
|
21
|
+
end
|
22
|
+
|
23
|
+
module FlipThroughMe
|
24
|
+
def each(ancestors=[], &block)
|
25
|
+
yield([self, ancestors])
|
26
|
+
ancestors_including_me = ancestors.dup + [self]
|
27
|
+
inner.each(ancestors_including_me) { |item| block.call(item) } if inner.is_a?(Enumerable)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
include FlipThroughMe
|
31
|
+
include Enumerable
|
32
|
+
|
33
|
+
module ValueEquality
|
34
|
+
def ==(other)
|
35
|
+
self.class == other.class &&
|
36
|
+
self.inner == other.inner
|
37
|
+
end
|
38
|
+
end
|
39
|
+
include ValueEquality
|
40
|
+
end
|
41
|
+
|
42
|
+
class Binary < Predicate
|
43
|
+
attr_accessor :left, :right
|
44
|
+
|
45
|
+
def initialize(left, right)
|
46
|
+
@left = left
|
47
|
+
@right = right
|
48
|
+
end
|
49
|
+
|
50
|
+
module FlipThroughMe
|
51
|
+
def each(ancestors=[], &block)
|
52
|
+
yield([self, ancestors])
|
53
|
+
ancestors_including_me = ancestors.dup + [self]
|
54
|
+
enumerate_side(@left, ancestors_including_me, &block)
|
55
|
+
enumerate_side(@right, ancestors_including_me, &block)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
def enumerate_side(thing, ancestors)
|
60
|
+
thing.each(ancestors) { |item| yield(item) } if thing.is_a?(Enumerable)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
include FlipThroughMe
|
64
|
+
include Enumerable
|
65
|
+
|
66
|
+
module ValueEquality
|
67
|
+
def ==(other)
|
68
|
+
self.class == other.class &&
|
69
|
+
self.left == other.left &&
|
70
|
+
self.right == other.right
|
71
|
+
end
|
72
|
+
end
|
73
|
+
include ValueEquality
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
class And < Binary; def self.shorthand; :And end end
|
80
|
+
class Or < Binary; def self.shorthand; :Or end end
|
81
|
+
class Not < Unary; def self.shorthand; :Not end end
|
82
|
+
|
83
|
+
|
84
|
+
class Operation < Binary; end
|
85
|
+
|
86
|
+
class Equal < Operation; def self.shorthand; :Eq end end
|
87
|
+
class LessThan < Operation; def self.shorthand; :Lt end end
|
88
|
+
class GreaterThan < Operation; def self.shorthand; :Gt end end
|
89
|
+
class GreaterThanOrEqualTo < Operation; def self.shorthand; :Gte end end
|
90
|
+
class LessThanOrEqualTo < Operation; def self.shorthand; :Lte end end
|
91
|
+
|
92
|
+
|
93
|
+
module Shorthand
|
94
|
+
def And(left, right) ::Predicated::And.new(left, right) end
|
95
|
+
def Or(left, right) ::Predicated::Or.new(left, right) end
|
96
|
+
def Not(inner) ::Predicated::Not.new(inner) end
|
97
|
+
|
98
|
+
def Eq(left, right) ::Predicated::Equal.new(left, right) end
|
99
|
+
def Lt(left, right) ::Predicated::LessThan.new(left, right) end
|
100
|
+
def Gt(left, right) ::Predicated::GreaterThan.new(left, right) end
|
101
|
+
def Lte(left, right) ::Predicated::LessThanOrEqualTo.new(left, right) end
|
102
|
+
def Gte(left, right) ::Predicated::GreaterThanOrEqualTo.new(left, right) end
|
103
|
+
end
|
104
|
+
|
105
|
+
ALL_PREDICATE_CLASSES = [
|
106
|
+
And, Or, Not,
|
107
|
+
Equal, LessThan, GreaterThan, LessThanOrEqualTo, GreaterThanOrEqualTo
|
108
|
+
]
|
109
|
+
end
|
110
|
+
|
111
|
+
require "predicated/print"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Predicated
|
2
|
+
module PrintSupport
|
3
|
+
def inspect(indent="")
|
4
|
+
indent + to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
def part_to_s(thing)
|
9
|
+
part_to_str(thing) {|thing| thing.to_s}
|
10
|
+
end
|
11
|
+
|
12
|
+
def part_inspect(thing, indent="")
|
13
|
+
part_to_str(thing, indent) {|thing| thing.inspect(indent)}
|
14
|
+
end
|
15
|
+
|
16
|
+
def part_to_str(thing, indent="")
|
17
|
+
if thing.is_a?(String)
|
18
|
+
"'#{thing}'"
|
19
|
+
elsif thing.is_a?(Numeric) || thing.is_a?(TrueClass) || thing.is_a?(FalseClass)
|
20
|
+
thing.to_s
|
21
|
+
elsif thing.is_a?(Binary)
|
22
|
+
yield(thing)
|
23
|
+
elsif thing.nil?
|
24
|
+
"nil"
|
25
|
+
else
|
26
|
+
"#{thing.class.name}{'#{thing.to_s}'}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Unary < Predicate
|
32
|
+
include PrintSupport
|
33
|
+
def to_s
|
34
|
+
"#{self.class.shorthand}(#{part_to_s(inner)})"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Binary < Predicate
|
39
|
+
include PrintSupport
|
40
|
+
def to_s
|
41
|
+
"#{self.class.shorthand}(#{part_to_s(left)},#{part_to_s(right)})"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module ContainerToString
|
46
|
+
def inspect(indent="")
|
47
|
+
next_indent = indent + " " + " "
|
48
|
+
|
49
|
+
str = "#{indent}#{self.class.shorthand}(\n"
|
50
|
+
str << "#{part_inspect(left, next_indent)},\n"
|
51
|
+
str << "#{part_inspect(right, next_indent)}\n"
|
52
|
+
str << "#{indent})"
|
53
|
+
str << "\n" if indent == ""
|
54
|
+
|
55
|
+
str
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class And; include ContainerToString end
|
60
|
+
class Or; include ContainerToString end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
|
2
|
+
class Object
|
3
|
+
# todo: make this definition conditional
|
4
|
+
# todo: move this to a monkey patch file
|
5
|
+
def singleton_class
|
6
|
+
class << self
|
7
|
+
self
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Predicated
|
13
|
+
module Selectable
|
14
|
+
# Make an Enumerable instance into a Selectable.
|
15
|
+
# This does for instances what "include Selectable" does for classes.
|
16
|
+
# todo: rename?
|
17
|
+
def self.bless_enumerable(enumerable, selectors)
|
18
|
+
enumerable.singleton_class.instance_eval do
|
19
|
+
include Selectable
|
20
|
+
selector selectors
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# merge several hashes into one, skipping nils
|
25
|
+
# todo: unit test
|
26
|
+
# todo: move onto Hash?
|
27
|
+
def self.merge_many(*hashes)
|
28
|
+
result = {}
|
29
|
+
hashes.compact.each do |hash|
|
30
|
+
result.merge! hash
|
31
|
+
end
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
SELECTORS = :@_predicated_selectors
|
36
|
+
|
37
|
+
def self.included(base)
|
38
|
+
base.extend ClassMethods
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
def selector(hash)
|
43
|
+
instance_variable_set(SELECTORS, Selectable.merge_many(instance_variable_get(SELECTORS), hash))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def selectors
|
48
|
+
class_selectors = self.class.instance_variable_get(SELECTORS)
|
49
|
+
singleton_selectors = self.singleton_class.instance_variable_get(SELECTORS)
|
50
|
+
instance_selectors = self.instance_variable_get(SELECTORS)
|
51
|
+
Selectable.merge_many(class_selectors, singleton_selectors, instance_selectors)
|
52
|
+
end
|
53
|
+
|
54
|
+
def select(*keys, &block)
|
55
|
+
if block_given?
|
56
|
+
super
|
57
|
+
else
|
58
|
+
key = keys.shift
|
59
|
+
result =
|
60
|
+
if key
|
61
|
+
selecting_proc = selectors[key]
|
62
|
+
raise "no selector found for '#{key}'. current selectors: [#{selectors.collect { |k, v| k.to_s }.join(",")}]" unless selecting_proc
|
63
|
+
memos_for(:select)[key] ||= begin
|
64
|
+
super(&selecting_proc)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
raise "select must be called with either a key or a block"
|
68
|
+
end
|
69
|
+
|
70
|
+
Selectable.bless_enumerable(result, selectors)
|
71
|
+
|
72
|
+
if keys.length >= 1
|
73
|
+
result.select(*keys, &block)
|
74
|
+
else
|
75
|
+
result
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def memos_for(group)
|
82
|
+
@_predicated_memos ||= {}
|
83
|
+
@_predicated_memos[group] ||= {}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
all_basic_selectors = {:all => proc{|predicate, enumerable|true}}.merge(
|
88
|
+
([Unary, Binary, Operation] + ALL_PREDICATE_CLASSES).inject({}) do |h, klass|
|
89
|
+
h[klass] = proc{|predicate, enumerable|predicate.is_a?(klass)}
|
90
|
+
h
|
91
|
+
end
|
92
|
+
)
|
93
|
+
|
94
|
+
ALL_PREDICATE_CLASSES.each do |klass|
|
95
|
+
klass.class_eval do
|
96
|
+
klass.send(:include, Selectable)
|
97
|
+
klass.selector all_basic_selectors
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|