wrong 0.1.0 → 0.2.0
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.
- 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
|