wrong 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/README.markdown +114 -25
  2. data/lib/predicated/Gemfile +15 -0
  3. data/lib/predicated/LICENSE +20 -0
  4. data/lib/predicated/README.markdown +191 -0
  5. data/lib/predicated/Rakefile +51 -0
  6. data/lib/predicated/lib/predicated.rb +4 -0
  7. data/lib/predicated/lib/predicated/autogen_call.rb +37 -0
  8. data/lib/predicated/lib/predicated/constrain.rb +66 -0
  9. data/lib/predicated/lib/predicated/evaluate.rb +94 -0
  10. data/lib/predicated/lib/predicated/from/callable_object.rb +108 -0
  11. data/lib/predicated/lib/predicated/from/json.rb +59 -0
  12. data/lib/predicated/lib/predicated/from/ruby_code_string.rb +73 -0
  13. data/lib/predicated/lib/predicated/from/url_part.rb +104 -0
  14. data/lib/predicated/lib/predicated/from/xml.rb +61 -0
  15. data/lib/predicated/lib/predicated/gem_check.rb +34 -0
  16. data/lib/predicated/lib/predicated/predicate.rb +111 -0
  17. data/lib/predicated/lib/predicated/print.rb +62 -0
  18. data/lib/predicated/lib/predicated/selectable.rb +102 -0
  19. data/lib/predicated/lib/predicated/simple_templated_predicate.rb +79 -0
  20. data/lib/predicated/lib/predicated/string_utils.rb +20 -0
  21. data/lib/predicated/lib/predicated/to/arel.rb +41 -0
  22. data/lib/predicated/lib/predicated/to/json.rb +48 -0
  23. data/lib/predicated/lib/predicated/to/sentence.rb +94 -0
  24. data/lib/predicated/lib/predicated/to/solr.rb +15 -0
  25. data/lib/predicated/lib/predicated/to/xml.rb +67 -0
  26. data/lib/predicated/lib/predicated/version.rb +3 -0
  27. data/lib/predicated/predicated.gemspec +22 -0
  28. data/lib/predicated/test/autogen_call_test.rb +40 -0
  29. data/lib/predicated/test/canonical_transform_cases.rb +63 -0
  30. data/lib/predicated/test/constrain_test.rb +86 -0
  31. data/lib/predicated/test/enumerable_test.rb +32 -0
  32. data/lib/predicated/test/equality_test.rb +32 -0
  33. data/lib/predicated/test/evaluate_test.rb +149 -0
  34. data/lib/predicated/test/from/callable_object_canonical_test.rb +43 -0
  35. data/lib/predicated/test/from/callable_object_test.rb +78 -0
  36. data/lib/predicated/test/from/json_test.rb +83 -0
  37. data/lib/predicated/test/from/ruby_code_string_canonical_test.rb +37 -0
  38. data/lib/predicated/test/from/ruby_code_string_test.rb +103 -0
  39. data/lib/predicated/test/from/url_part_parser_test.rb +123 -0
  40. data/lib/predicated/test/from/url_part_test.rb +48 -0
  41. data/lib/predicated/test/from/xml_test.rb +57 -0
  42. data/lib/predicated/test/json_conversion_test.rb +33 -0
  43. data/lib/predicated/test/print_test.rb +66 -0
  44. data/lib/predicated/test/selectable_test.rb +123 -0
  45. data/lib/predicated/test/simple_templated_predicate_test.rb +39 -0
  46. data/lib/predicated/test/suite.rb +2 -0
  47. data/lib/predicated/test/test_helper.rb +64 -0
  48. data/lib/predicated/test/test_helper_with_wrong.rb +6 -0
  49. data/lib/predicated/test/to/arel_test.rb +85 -0
  50. data/lib/predicated/test/to/json_test.rb +74 -0
  51. data/lib/predicated/test/to/sentence_test.rb +90 -0
  52. data/lib/predicated/test/to/solr_test.rb +39 -0
  53. data/lib/predicated/test/to/xml_test.rb +72 -0
  54. data/lib/predicated/test/xml_conversion_test.rb +34 -0
  55. data/lib/predicated/test_integration/arel_integration_test.rb +52 -0
  56. data/lib/predicated/test_integration/canonical_integration_cases.rb +66 -0
  57. data/lib/predicated/test_integration/schema.xml +83 -0
  58. data/lib/predicated/test_integration/solr_integration_test.rb +71 -0
  59. data/lib/predicated/test_integration/sqlite_db +0 -0
  60. data/lib/predicated/test_integration/suite.rb +2 -0
  61. data/lib/predicated/test_integration/usage_test.rb +252 -0
  62. data/lib/wrong.rb +3 -1
  63. data/lib/wrong/adapters/test_unit.rb +1 -3
  64. data/lib/wrong/assert.rb +81 -24
  65. data/lib/wrong/chunk.rb +145 -0
  66. data/lib/wrong/message/string_diff.rb +2 -4
  67. data/lib/wrong/message/test_context.rb +2 -2
  68. data/lib/wrong/version.rb +2 -2
  69. data/test/adapters/minitest_test.rb +16 -9
  70. data/test/adapters/test_unit_test.rb +1 -1
  71. data/test/assert_test.rb +90 -0
  72. data/test/catch_raise_test.rb +2 -2
  73. data/test/chunk_test.rb +236 -0
  74. data/test/failures_test.rb +109 -74
  75. data/test/message/array_diff_test.rb +35 -19
  76. data/test/message/string_diff_test.rb +39 -15
  77. data/test/message/test_context_text.rb +2 -2
  78. data/test/test_helper.rb +25 -7
  79. metadata +86 -33
  80. 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
+