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,37 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
require "predicated/evaluate"
|
3
|
+
require "predicated/string_utils"
|
4
|
+
|
5
|
+
module Predicated
|
6
|
+
class AutogenCall < Call
|
7
|
+
def to_s
|
8
|
+
method_cameled = StringUtils.uppercamelize(method_sym.to_s)
|
9
|
+
|
10
|
+
if Predicated.const_defined?(:SimpleTemplatedShorthand) && left == Placeholder
|
11
|
+
"#{method_cameled}#{right_to_s}"
|
12
|
+
else
|
13
|
+
left_str = left_to_s
|
14
|
+
right_str = right_to_s
|
15
|
+
right_str = "," + right_str unless right_str.empty?
|
16
|
+
"#{method_cameled}(#{left_str}#{right_str})"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Shorthand
|
22
|
+
def method_missing(uppercase_cameled_method_sym, *args)
|
23
|
+
subject = args.shift
|
24
|
+
method_sym = StringUtils.underscore(uppercase_cameled_method_sym.to_s).to_sym
|
25
|
+
object = args
|
26
|
+
AutogenCall.new(subject, method_sym, (object.empty? ? [] : object))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module SimpleTemplatedShorthand
|
31
|
+
def method_missing(uppercase_cameled_method_sym, *args)
|
32
|
+
method_sym = StringUtils.underscore(uppercase_cameled_method_sym.to_s).to_sym
|
33
|
+
object = args
|
34
|
+
AutogenCall.new(Placeholder, method_sym, (object.empty? ? [] : object))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "predicated/selectable"
|
2
|
+
|
3
|
+
module Predicated
|
4
|
+
class Constraints
|
5
|
+
def initialize
|
6
|
+
@constraints = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(constraint)
|
10
|
+
@constraints << constraint
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def check(whole_predicate)
|
15
|
+
result = ConstraintCheckResult.new
|
16
|
+
|
17
|
+
@constraints.collect do |constraint|
|
18
|
+
whole_predicate.select(*constraint.selectors).collect do |predicate, ancestors|
|
19
|
+
if ! constraint.check(predicate, ancestors)
|
20
|
+
result.violation(constraint, predicate)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def ==(other)
|
29
|
+
@constraints == other.instance_variable_get("@constraints".to_sym)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Constraint
|
34
|
+
attr_reader :name, :selectors
|
35
|
+
def initialize(args)
|
36
|
+
@name = args[:name]
|
37
|
+
@selectors = args[:selectors] || [:all]
|
38
|
+
@check_that = args[:check_that]
|
39
|
+
end
|
40
|
+
|
41
|
+
def check(predicate, ancestors)
|
42
|
+
@check_that.call(predicate, ancestors)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
@name == other.name && @selectors == other.selectors
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ConstraintCheckResult
|
51
|
+
attr_reader :violations
|
52
|
+
def initialize
|
53
|
+
@violations = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def pass?
|
57
|
+
@violations.empty?
|
58
|
+
end
|
59
|
+
|
60
|
+
def violation(constraint, predicate)
|
61
|
+
@violations[constraint] ||= []
|
62
|
+
@violations[constraint] << predicate
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
|
3
|
+
module Predicated
|
4
|
+
|
5
|
+
|
6
|
+
class Operation < Binary
|
7
|
+
attr_reader :method_sym
|
8
|
+
|
9
|
+
def initialize(left, method_sym, right)
|
10
|
+
super(left, right)
|
11
|
+
@method_sym = method_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
def evaluate
|
15
|
+
right_values = right.nil? ? [nil] : right #1.9 problem where nils and varargs don't play nicely
|
16
|
+
left.send(@method_sym, *right_values)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
super && method_sym==other.method_sym
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
class Equal < Operation; def initialize(left, right); super(left, :==, right); end end
|
26
|
+
class LessThan < Operation; def initialize(left, right); super(left, :<, right); end end
|
27
|
+
class GreaterThan < Operation; def initialize(left, right); super(left, :>, right); end end
|
28
|
+
class LessThanOrEqualTo < Operation; def initialize(left, right); super(left, :<=, right); end end
|
29
|
+
class GreaterThanOrEqualTo < Operation; def initialize(left, right); super(left, :>=, right); end end
|
30
|
+
|
31
|
+
class Call < Operation
|
32
|
+
def self.shorthand
|
33
|
+
:Call
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(left, method_sym, right=[])
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"Call(#{left_to_s}.#{method_sym.to_s}#{right_to_s})"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def left_to_s
|
46
|
+
part_to_s(left)
|
47
|
+
end
|
48
|
+
|
49
|
+
def right_to_s
|
50
|
+
values = right.is_a?(::Enumerable) ? right : [right]
|
51
|
+
values.empty? ? "" :
|
52
|
+
"(" + values.collect{|arg|part_to_s(arg)}.join(",") + ")"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
Shorthand.module_eval(%{
|
56
|
+
def Call(left_object, method_sym, right_args=[])
|
57
|
+
::Predicated::Call.new(left_object, method_sym, right_args)
|
58
|
+
end
|
59
|
+
})
|
60
|
+
|
61
|
+
module Container
|
62
|
+
private
|
63
|
+
def boolean_or_evaluate(thing)
|
64
|
+
if thing.is_a?(FalseClass)
|
65
|
+
false
|
66
|
+
elsif thing.is_a?(TrueClass)
|
67
|
+
true
|
68
|
+
else
|
69
|
+
thing.evaluate
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class And
|
75
|
+
include Container
|
76
|
+
def evaluate
|
77
|
+
boolean_or_evaluate(left) && boolean_or_evaluate(right)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Or
|
82
|
+
include Container
|
83
|
+
def evaluate
|
84
|
+
boolean_or_evaluate(left) || boolean_or_evaluate(right)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Not
|
89
|
+
include Container
|
90
|
+
def evaluate
|
91
|
+
! boolean_or_evaluate(inner)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
require "predicated/from/ruby_code_string"
|
3
|
+
|
4
|
+
|
5
|
+
#raise %{
|
6
|
+
#
|
7
|
+
#This will never work in ruby 1.9.
|
8
|
+
#
|
9
|
+
#see http://blog.zenspider.com/2009/04/parsetree-eol.html
|
10
|
+
#
|
11
|
+
#} if RUBY_VERSION =~/^1.9/
|
12
|
+
|
13
|
+
raise %{
|
14
|
+
|
15
|
+
You appear to be using ruby 1.8.7 and you don't have
|
16
|
+
an INLINEDIR environment variable set to a valid directory.
|
17
|
+
|
18
|
+
ParseTree (used by "from_callable_object") uses RubyInline.
|
19
|
+
RubyInline requires that the INLINEDIR environment variable point
|
20
|
+
to a directory. The easiest thing to do is to just go
|
21
|
+
create a directory somewhere - let's say, ~/inlinedir,
|
22
|
+
and point the INLINEDIR at it. In bash this would be:
|
23
|
+
|
24
|
+
mkdir ~/inlinedir
|
25
|
+
export INLINEDIR=~/inlinedir
|
26
|
+
|
27
|
+
You'll probably want to put this in .bash_profile too.
|
28
|
+
|
29
|
+
Sorry for the inconvenience. I hope the value you'll
|
30
|
+
get out of "from_callable_object" makes it all worth it.
|
31
|
+
|
32
|
+
} if RUBY_VERSION=="1.8.7" && !ENV["INLINEDIR"]
|
33
|
+
#Procs and lambdas are "callable objects"
|
34
|
+
|
35
|
+
module Predicated
|
36
|
+
|
37
|
+
require_gem_version("ParseTree", "3.0.5", "parse_tree") if RUBY_VERSION < "1.9"
|
38
|
+
|
39
|
+
class Predicate
|
40
|
+
|
41
|
+
#hrm
|
42
|
+
def self.from_callable_object(context_or_callable_object=nil, context=nil, &block)
|
43
|
+
callable_object = nil
|
44
|
+
|
45
|
+
if context_or_callable_object.is_a?(Binding) || context_or_callable_object.nil?
|
46
|
+
context = context_or_callable_object
|
47
|
+
callable_object = block
|
48
|
+
else
|
49
|
+
callable_object = context_or_callable_object
|
50
|
+
end
|
51
|
+
|
52
|
+
context ||= callable_object.binding
|
53
|
+
|
54
|
+
from_ruby_code_string(TranslateToRubyString.convert(callable_object), context)
|
55
|
+
end
|
56
|
+
|
57
|
+
module TranslateToRubyString
|
58
|
+
#see http://stackoverflow.com/questions/199603/how-do-you-stringize-serialize-ruby-code
|
59
|
+
def self.convert(callable_object)
|
60
|
+
temp_class = Class.new
|
61
|
+
temp_class.class_eval do
|
62
|
+
define_method :serializable, &callable_object
|
63
|
+
end
|
64
|
+
ruby_code_string = Ruby2Ruby.translate(temp_class, :serializable)
|
65
|
+
ruby_code_string.sub(/^def serializable\n /, "").sub(/\nend$/, "")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#see http://gist.github.com/321038
|
70
|
+
# # Monkey-patch to have Ruby2Ruby#translate with r2r >= 1.2.3, from
|
71
|
+
# # http://seattlerb.rubyforge.org/svn/ruby2ruby/1.2.2/lib/ruby2ruby.rb
|
72
|
+
class ::Ruby2Ruby < ::SexpProcessor
|
73
|
+
def self.translate(klass_or_str, method = nil)
|
74
|
+
sexp = ParseTree.translate(klass_or_str, method)
|
75
|
+
unifier = Unifier.new
|
76
|
+
unifier.processors.each do |p|
|
77
|
+
p.unsupported.delete :cfunc # HACK
|
78
|
+
end
|
79
|
+
sexp = unifier.process(sexp)
|
80
|
+
self.new.process(sexp)
|
81
|
+
end
|
82
|
+
|
83
|
+
#sconover - 7/2010 - monkey-patch
|
84
|
+
#{1=>2}=={1=>2}
|
85
|
+
#The right side was having its braces cut off because of
|
86
|
+
#special handling of hashes within arglists within the seattlerb code.
|
87
|
+
#I tried to fork r2r and add a test, but a lot of other tests
|
88
|
+
#broke, and I just dont understand the test in ruby2ruby.
|
89
|
+
#So I'm emailing the author...
|
90
|
+
def process_hash(exp)
|
91
|
+
result = []
|
92
|
+
until exp.empty?
|
93
|
+
lhs = process(exp.shift)
|
94
|
+
rhs = exp.shift
|
95
|
+
t = rhs.first
|
96
|
+
rhs = process rhs
|
97
|
+
rhs = "(#{rhs})" unless [:lit, :str].include? t # TODO: verify better!
|
98
|
+
|
99
|
+
result << "#{lhs} => #{rhs}"
|
100
|
+
end
|
101
|
+
|
102
|
+
return "{ #{result.join(', ')} }"
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
|
3
|
+
module Predicated
|
4
|
+
|
5
|
+
require_gem_version("json", "1.1.9")
|
6
|
+
|
7
|
+
class Predicate
|
8
|
+
def self.from_json_str(json_str)
|
9
|
+
from_json_struct(JSON.parse(json_str))
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_json_struct(json_struct)
|
13
|
+
JsonStructToPredicate.convert(json_struct)
|
14
|
+
end
|
15
|
+
|
16
|
+
module JsonStructToPredicate
|
17
|
+
SIGN_TO_CLASS = {
|
18
|
+
"==" => Equal,
|
19
|
+
">" => GreaterThan,
|
20
|
+
"<" => LessThan,
|
21
|
+
">=" => GreaterThanOrEqualTo,
|
22
|
+
"<=" => LessThanOrEqualTo
|
23
|
+
}
|
24
|
+
|
25
|
+
def self.convert(json_struct)
|
26
|
+
if json_struct.is_a?(Array)
|
27
|
+
left, sign, right = json_struct
|
28
|
+
if operation_class=SIGN_TO_CLASS[sign]
|
29
|
+
operation_class.new(left, right)
|
30
|
+
else
|
31
|
+
raise DontKnowWhatToDoWithThisJson.new(json_struct)
|
32
|
+
end
|
33
|
+
elsif json_struct.is_a?(Hash)
|
34
|
+
if left_and_right=json_struct["and"]
|
35
|
+
left, right = left_and_right
|
36
|
+
And.new(convert(left), convert(right))
|
37
|
+
elsif left_and_right=json_struct["or"]
|
38
|
+
left, right = left_and_right
|
39
|
+
Or.new(convert(left), convert(right))
|
40
|
+
elsif inner=json_struct["not"]
|
41
|
+
Not.new(convert(inner))
|
42
|
+
else
|
43
|
+
raise DontKnowWhatToDoWithThisJson.new(json_struct)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
raise DontKnowWhatToDoWithThisJson.new(json_struct)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class DontKnowWhatToDoWithThisJson < StandardError
|
53
|
+
def initialize(json_struct)
|
54
|
+
super("don't know what to do with #{JSON.generate(json_struct)}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require "predicated/predicate"
|
2
|
+
require "predicated/evaluate"
|
3
|
+
|
4
|
+
module Predicated
|
5
|
+
|
6
|
+
require_gem_version("ruby_parser", "2.0.4")
|
7
|
+
require_gem_version("ruby2ruby", "1.2.4")
|
8
|
+
|
9
|
+
class Predicate
|
10
|
+
def self.from_ruby_code_string(ruby_predicate_string, context=binding())
|
11
|
+
sexp = RubyParser.new.process(ruby_predicate_string.strip)
|
12
|
+
SexpToPredicate.new(context).convert(sexp)
|
13
|
+
end
|
14
|
+
|
15
|
+
class SexpToPredicate
|
16
|
+
SIGN_TO_PREDICATE_CLASS = {
|
17
|
+
:== => Equal,
|
18
|
+
:> => GreaterThan,
|
19
|
+
:< => LessThan,
|
20
|
+
:>= => GreaterThanOrEqualTo,
|
21
|
+
:<= => LessThanOrEqualTo,
|
22
|
+
}
|
23
|
+
|
24
|
+
def initialize(context)
|
25
|
+
@context = context
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert(sexp)
|
29
|
+
first_element = sexp.first
|
30
|
+
if first_element == :block
|
31
|
+
#eval all the top lines and then treat the last one as a predicate
|
32
|
+
body_sexps = sexp.sexp_body.to_a
|
33
|
+
body_sexps.slice(0..-2).each do |upper_sexp|
|
34
|
+
eval_sexp(upper_sexp)
|
35
|
+
end
|
36
|
+
convert(body_sexps.last)
|
37
|
+
elsif first_element == :call
|
38
|
+
sym, left_sexp, method_sym, right_sexp = sexp
|
39
|
+
left = eval_sexp(left_sexp)
|
40
|
+
right = eval_sexp(right_sexp)
|
41
|
+
|
42
|
+
if operation_class=SIGN_TO_PREDICATE_CLASS[method_sym]
|
43
|
+
operation_class.new(left, right)
|
44
|
+
else
|
45
|
+
Call.new(left, method_sym, right)
|
46
|
+
end
|
47
|
+
elsif first_element == :and
|
48
|
+
sym, left, right = sexp
|
49
|
+
And.new(convert(left), convert(right))
|
50
|
+
elsif first_element == :or
|
51
|
+
sym, left, right = sexp
|
52
|
+
Or.new(convert(left), convert(right))
|
53
|
+
elsif first_element == :not
|
54
|
+
sym, inner = sexp
|
55
|
+
Not.new(convert(inner))
|
56
|
+
else
|
57
|
+
raise DontKnowWhatToDoWithThisSexpError.new(sexp)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def eval_sexp(sexp)
|
62
|
+
eval(Ruby2Ruby.new.process(sexp), @context)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
class DontKnowWhatToDoWithThisSexpError < StandardError
|
68
|
+
def initialize(sexp)
|
69
|
+
super("don't know what to do with #{sexp.inspect}")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|