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.
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
+