neo-viz 1.0.1
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/.gitignore +9 -0
- data/.livereload +20 -0
- data/.rspec +2 -0
- data/.rvmrc +3 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +120 -0
- data/Rakefile +12 -0
- data/bin/neo-viz +12 -0
- data/config.ru +35 -0
- data/lib/neo-viz.rb +185 -0
- data/lib/neo-viz/version.rb +6 -0
- data/neo-viz.gemspec +37 -0
- data/public/coffeescripts/app_context.coffee +89 -0
- data/public/coffeescripts/canvas_util.coffee +68 -0
- data/public/coffeescripts/event_broker.coffee +16 -0
- data/public/coffeescripts/filters.coffee +113 -0
- data/public/coffeescripts/main.coffee.erb +132 -0
- data/public/coffeescripts/neo4j.coffee +90 -0
- data/public/coffeescripts/renderer.coffee +141 -0
- data/public/coffeescripts/space.coffee +81 -0
- data/public/images/ajax-loader.gif +0 -0
- data/public/javascripts/data.js +45 -0
- data/public/javascripts/data2.js +1287 -0
- data/public/javascripts/main.sprockets.js +9 -0
- data/public/lib/arbor/arbor-tween.js +86 -0
- data/public/lib/arbor/arbor.js +67 -0
- data/public/lib/jQuery/jquery-1.6.1.min.js +18 -0
- data/public/lib/jasmine-1.1.0/MIT.LICENSE +20 -0
- data/public/lib/jasmine-1.1.0/jasmine-html.js +190 -0
- data/public/lib/jasmine-1.1.0/jasmine.css +166 -0
- data/public/lib/jasmine-1.1.0/jasmine.js +2476 -0
- data/public/lib/jasmine-1.1.0/jasmine_favicon.png +0 -0
- data/public/lib/stdlib/stdlib.js +115 -0
- data/public/lib/sylvester-0.1.3/CHANGELOG.txt +29 -0
- data/public/lib/sylvester-0.1.3/sylvester.js +1 -0
- data/public/lib/sylvester-0.1.3/sylvester.js.gz +0 -0
- data/public/lib/sylvester-0.1.3/sylvester.src.js +1254 -0
- data/public/scss/main.scss +152 -0
- data/public/scss/mixins.scss +37 -0
- data/spec/coffeescripts/canvas_util_spec.coffee +4 -0
- data/spec/coffeescripts/filters_spec.coffee +37 -0
- data/spec/coffeescripts/neo4j_spec.coffee +76 -0
- data/spec/neo_viz_spec.rb +48 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/struct_matcher.rb +117 -0
- data/views/_filters.haml +22 -0
- data/views/_partial.haml +39 -0
- data/views/embedded.haml +15 -0
- data/views/index.haml +19 -0
- data/views/jasmine_specs_runner.haml +50 -0
- metadata +236 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
@import 'mixins.scss';
|
2
|
+
|
3
|
+
$page-width: 760px;
|
4
|
+
$body-color-top: #ffe;
|
5
|
+
$body-color-bottom: #dfa;
|
6
|
+
|
7
|
+
$default-radius: .8em;
|
8
|
+
|
9
|
+
$header-font: normal 24px Verdana, sans-serif;
|
10
|
+
|
11
|
+
.clear { clear: both; }
|
12
|
+
|
13
|
+
.neoviz_body {
|
14
|
+
font-family: Century Gothic, sans-serif;
|
15
|
+
font-size: 14px;
|
16
|
+
line-height: 18px;
|
17
|
+
@include background-gradient($body-color-top, $body-color-bottom);
|
18
|
+
}
|
19
|
+
|
20
|
+
.neoviz_header {
|
21
|
+
p {
|
22
|
+
float: right;
|
23
|
+
width: 240px;
|
24
|
+
margin-right: 1em;
|
25
|
+
margin-top: 0;
|
26
|
+
padding: 1em;
|
27
|
+
@include sym-border-radius($default-radius);
|
28
|
+
color: white;
|
29
|
+
background: gray;
|
30
|
+
}
|
31
|
+
|
32
|
+
td{
|
33
|
+
vertical-align:top;
|
34
|
+
}
|
35
|
+
|
36
|
+
form {
|
37
|
+
float: left;
|
38
|
+
padding: 0.4em;
|
39
|
+
margin: 1em;
|
40
|
+
@include sym-border-radius($default-radius);
|
41
|
+
color: black;
|
42
|
+
border: gray solid 2px;
|
43
|
+
|
44
|
+
input[type=number] {
|
45
|
+
width: 3em;
|
46
|
+
}
|
47
|
+
textarea {
|
48
|
+
width: 20em;
|
49
|
+
height: 4em;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
#console {
|
55
|
+
width: 600px;
|
56
|
+
height: 100px;
|
57
|
+
}
|
58
|
+
|
59
|
+
#consoleOutputArea{
|
60
|
+
width: 600px;
|
61
|
+
height: 50px;
|
62
|
+
}
|
63
|
+
|
64
|
+
.viewport {
|
65
|
+
width: 960px;
|
66
|
+
height: 500px;
|
67
|
+
}
|
68
|
+
|
69
|
+
#container {
|
70
|
+
clear: both;
|
71
|
+
margin: 1em 1em 1em 1em;
|
72
|
+
border: 2px gray solid;
|
73
|
+
padding: 2em;
|
74
|
+
@include sym-border-radius($default-radius);
|
75
|
+
@include background-gradient($body-color-top, $body-color-bottom);
|
76
|
+
|
77
|
+
td {
|
78
|
+
vertical-align: top;
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
|
83
|
+
.details {
|
84
|
+
|
85
|
+
vertical-align: bottom;
|
86
|
+
|
87
|
+
table {
|
88
|
+
border: 2px solid gray;
|
89
|
+
border-spacing: 0;
|
90
|
+
|
91
|
+
tr {
|
92
|
+
margin: 0;
|
93
|
+
border-width: 0;
|
94
|
+
padding: 0;
|
95
|
+
|
96
|
+
&:nth-child(2n) { background-color: white; }
|
97
|
+
&:nth-child(2n+1) { background-color: lightgray; }
|
98
|
+
}
|
99
|
+
td {
|
100
|
+
border: 0 none black;
|
101
|
+
padding: 0.3em;
|
102
|
+
margin: 0;
|
103
|
+
|
104
|
+
&:first-child {
|
105
|
+
text-align: right;
|
106
|
+
padding-left: 1em;
|
107
|
+
font-weight: bold;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
.filters{
|
114
|
+
table {
|
115
|
+
border: 1px solid gray;
|
116
|
+
border-spacing: 0;
|
117
|
+
background-color: #eee;
|
118
|
+
|
119
|
+
tr {
|
120
|
+
margin: 0;
|
121
|
+
border-width: 0;
|
122
|
+
padding: 0;
|
123
|
+
|
124
|
+
}
|
125
|
+
td {
|
126
|
+
border: 0 none black;
|
127
|
+
padding: 0.3em;
|
128
|
+
margin: 0;
|
129
|
+
|
130
|
+
&:first-child {
|
131
|
+
text-align: left;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
.relationsFilter{
|
138
|
+
table {
|
139
|
+
tr {
|
140
|
+
&:nth-child(2n) { background-color: white; }
|
141
|
+
&:nth-child(2n+1) { background-color: lightgray; }
|
142
|
+
}
|
143
|
+
td {
|
144
|
+
|
145
|
+
&:first-child {
|
146
|
+
text-align: right;
|
147
|
+
padding-left: 1em;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
@mixin reset {
|
2
|
+
html, body, div, span, applet, object, iframe,
|
3
|
+
h1, h2, h3, h4, h5, h6, tr, th, td {
|
4
|
+
margin: 0;
|
5
|
+
padding: 0;
|
6
|
+
border: 0;
|
7
|
+
outline: 0;
|
8
|
+
vertical-align: baseline;
|
9
|
+
background: transparent;
|
10
|
+
}
|
11
|
+
table {
|
12
|
+
border-collapse: collapse;
|
13
|
+
border-spacing: 0;
|
14
|
+
}
|
15
|
+
|
16
|
+
}
|
17
|
+
|
18
|
+
@mixin border-radius($tl, $tr, $br, $bl) {
|
19
|
+
-webkit-border-radius: $tl $tr $br $bl;
|
20
|
+
-moz-border-radius: $tl $tr $br $bl;
|
21
|
+
border-radius: $tl $tr $br $bl;
|
22
|
+
}
|
23
|
+
|
24
|
+
@mixin sym-border-radius($r) {
|
25
|
+
@include border-radius($r, $r, $r, $r);
|
26
|
+
}
|
27
|
+
|
28
|
+
@mixin background-gradient($top, $bottom) {
|
29
|
+
background: -webkit-gradient(linear, left top, left bottom, from($top), to($bottom));
|
30
|
+
background: -webkit-linear-gradient(top, $top, $bottom);
|
31
|
+
background: -moz-linear-gradient(top, $top, $bottom);
|
32
|
+
background: -ms-linear-gradient(top, $top, $bottom);
|
33
|
+
background: -o-linear-gradient(top, $top, $bottom);
|
34
|
+
background: linear-gradient(top, $top, $bottom);
|
35
|
+
}
|
36
|
+
|
37
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe 'filters', ->
|
2
|
+
|
3
|
+
it 'can build hiddenNodeData for circular subgraph hidden by user', ->
|
4
|
+
# n0--->---n1-----<-----n3
|
5
|
+
# \-->--n2->--/
|
6
|
+
nodeData = [{"id":0},{"id":1},{"id":2},{"id":3}]
|
7
|
+
relData = [{"id":0, "start_node":0, "end_node":1, "data":{"rel_type":""}},
|
8
|
+
{"id":1, "start_node":1, "end_node":2, "data":{"rel_type":""}},
|
9
|
+
{"id":2, "start_node":2, "end_node":3, "data":{"rel_type":""}}
|
10
|
+
{"id":3, "start_node":3, "end_node":1, "data":{"rel_type":""}}]
|
11
|
+
graph = new Graph(nodeData, relData)
|
12
|
+
|
13
|
+
activatedNode = graph.load(0)
|
14
|
+
relsHiddenByUser = graph.relationships[0..0]
|
15
|
+
hiddenNodeData = test_buildHiddenNodeData(graph, activatedNode, relsHiddenByUser)
|
16
|
+
|
17
|
+
expect(hiddenNodeData.nodeIds.length).toEqual(3)
|
18
|
+
expect(hiddenNodeData.relIds.length).toEqual(4) #4: rels hidden by user should be included
|
19
|
+
|
20
|
+
it 'can build hiddenNodeData for 6 node mesh with one hidden rel', ->
|
21
|
+
nodeData = [{"id":0},{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]
|
22
|
+
relData = [{"id":0, "start_node":0, "end_node":1, "data":{"rel_type":""}},
|
23
|
+
{"id":1, "start_node":1, "end_node":2, "data":{"rel_type":""}},
|
24
|
+
{"id":2, "start_node":2, "end_node":3, "data":{"rel_type":""}},
|
25
|
+
{"id":3, "start_node":3, "end_node":0, "data":{"rel_type":""}},
|
26
|
+
{"id":4, "start_node":3, "end_node":4, "data":{"rel_type":""}},
|
27
|
+
{"id":5, "start_node":4, "end_node":5, "data":{"rel_type":""}},
|
28
|
+
{"id":6, "start_node":5, "end_node":0, "data":{"rel_type":""}}]
|
29
|
+
graph = new Graph(nodeData, relData)
|
30
|
+
|
31
|
+
activatedNode = graph.load(0)
|
32
|
+
relsHiddenByUser = graph.relationships[6..6]
|
33
|
+
hiddenNodeData = test_buildHiddenNodeData(graph, activatedNode, relsHiddenByUser)
|
34
|
+
|
35
|
+
expect(hiddenNodeData.nodeIds.length).toEqual(0)
|
36
|
+
expect(hiddenNodeData.relIds.length).toEqual(1)
|
37
|
+
expect(hiddenNodeData.relIds[0]).toEqual(6)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
describe 'neo4j', ->
|
2
|
+
describe 'Graph', ->
|
3
|
+
|
4
|
+
describe 'Node', ->
|
5
|
+
|
6
|
+
graph = null
|
7
|
+
node = null
|
8
|
+
|
9
|
+
beforeEach ->
|
10
|
+
nodeData = [{"id":0},{"id":1},{"id":2}]
|
11
|
+
relData = [{"id":0, "start_node":0, "end_node":1, "data":{"rel_type":"friend"}},
|
12
|
+
{"id":1, "start_node":1, "end_node":2, "data":{"rel_type":"enemy"}},
|
13
|
+
{"id":2, "start_node":2, "end_node":0, "data":{"rel_type":"brother"}},
|
14
|
+
{"id":3, "start_node":0, "end_node":2, "data":{"rel_type":"brother"}}]
|
15
|
+
graph = new Graph(nodeData, relData)
|
16
|
+
node = graph.load(0)
|
17
|
+
|
18
|
+
it 'has incoming relationships', ->
|
19
|
+
expect(node.incoming()[0].type).toEqual("brother")
|
20
|
+
|
21
|
+
it 'has outgoing relationships', ->
|
22
|
+
expect(node.outgoing()[0].type).toEqual("friend")
|
23
|
+
|
24
|
+
it 'has incoming relationships of certain type', ->
|
25
|
+
expect(node.incoming(["foo"]).length).toEqual(0)
|
26
|
+
expect(node.incoming(["brother"]).length).toEqual(1)
|
27
|
+
|
28
|
+
it 'has outgoing relationships of certain type', ->
|
29
|
+
expect(node.outgoing(["foo"]).length).toEqual(0)
|
30
|
+
expect(node.outgoing(["friend", "brother"]).length).toEqual(2)
|
31
|
+
|
32
|
+
describe 'Connectivity', ->
|
33
|
+
|
34
|
+
graph = null
|
35
|
+
|
36
|
+
beforeEach ->
|
37
|
+
nodeData = [{"id":0},{"id":1}]
|
38
|
+
relData = [{"id":0, "start_node":0, "end_node":1, "data":{"rel_type":"networks"}}]
|
39
|
+
graph = new Graph(nodeData, relData)
|
40
|
+
|
41
|
+
it 'knows two neighbors are connected', ->
|
42
|
+
expect(graph.areConnected(graph.load(0), graph.load(1))).toEqual(true)
|
43
|
+
|
44
|
+
it 'only considers active relations', ->
|
45
|
+
expect(graph.areConnected(graph.load(0), graph.load(1), [])).toEqual(false)
|
46
|
+
|
47
|
+
it 'knows circular triplet is connected when one relationship is inactive', ->
|
48
|
+
# n0-----<-----n2
|
49
|
+
# \-->--n1->--/
|
50
|
+
nodeData = [{"id":0},{"id":1},{"id":2}]
|
51
|
+
relData = [{"id":0, "start_node":0, "end_node":1, "data":{"rel_type":""}},
|
52
|
+
{"id":1, "start_node":1, "end_node":2, "data":{"rel_type":""}},
|
53
|
+
{"id":2, "start_node":2, "end_node":0, "data":{"rel_type":""}}]
|
54
|
+
graph = new Graph(nodeData, relData)
|
55
|
+
|
56
|
+
# deactivate connection between n0 and n1:
|
57
|
+
activeRels = graph.relationships[1..2]
|
58
|
+
|
59
|
+
expect(graph.areConnected(graph.load(1), graph.load(0), activeRels)).toEqual(true)
|
60
|
+
|
61
|
+
it 'knows a 6 node mesh is connected when one relationship is inactive', ->
|
62
|
+
nodeData = [{"id":0},{"id":1},{"id":2},{"id":3},{"id":4},{"id":5}]
|
63
|
+
relData = [{"id":0, "start_node":0, "end_node":1, "data":{"rel_type":""}},
|
64
|
+
{"id":1, "start_node":1, "end_node":2, "data":{"rel_type":""}},
|
65
|
+
{"id":2, "start_node":2, "end_node":3, "data":{"rel_type":""}},
|
66
|
+
{"id":3, "start_node":3, "end_node":0, "data":{"rel_type":""}},
|
67
|
+
{"id":4, "start_node":3, "end_node":4, "data":{"rel_type":""}},
|
68
|
+
{"id":5, "start_node":4, "end_node":5, "data":{"rel_type":""}},
|
69
|
+
{"id":6, "start_node":5, "end_node":0, "data":{"rel_type":""}}]
|
70
|
+
graph = new Graph(nodeData, relData)
|
71
|
+
|
72
|
+
# deactivate connection between n0 and n5:
|
73
|
+
activeRels = graph.relationships[0..5]
|
74
|
+
console.dir activeRels
|
75
|
+
expect(graph.areConnected(graph.load(0), graph.load(5), activeRels)).toEqual(true)
|
76
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Neo::Viz::App do
|
4
|
+
|
5
|
+
def app
|
6
|
+
Neo::Viz::App
|
7
|
+
end
|
8
|
+
|
9
|
+
specify "get / should return OK" do
|
10
|
+
get '/'
|
11
|
+
last_response.should be_redirect
|
12
|
+
end
|
13
|
+
|
14
|
+
specify 'get /node-count should return number of nodes' do
|
15
|
+
get '/node-count'
|
16
|
+
last_response.body.should == '155'
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'get /nodes/0' do
|
20
|
+
before do
|
21
|
+
get '/nodes/0'
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:body) { last_response.body }
|
25
|
+
|
26
|
+
it 'should return the root node' do
|
27
|
+
body.should struct_match({
|
28
|
+
:nodes => [
|
29
|
+
{ :id => 0, :data => {} },
|
30
|
+
{ :id => 5, :data => {} },
|
31
|
+
{ :id => 1, :data => {} },
|
32
|
+
{ :id => 2, :data => {} },
|
33
|
+
{ :id => 6, :data => {} },
|
34
|
+
{ :id => 4, :data => {} },
|
35
|
+
{ :id => 3, :data => {} }
|
36
|
+
],
|
37
|
+
:rels => [
|
38
|
+
{:id => 4, :start_node => 0, :end_node => 5, :data => { :rel_type => 'BirdiesBackend::User'}},
|
39
|
+
{},
|
40
|
+
{},
|
41
|
+
{},
|
42
|
+
{},
|
43
|
+
{:id => 2, :start_node => 0, :end_node => 3, :data => { :rel_type => 'BirdiesBackend::Tag'}}
|
44
|
+
]
|
45
|
+
})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/..'
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'rack/test'
|
5
|
+
require 'support/struct_matcher'
|
6
|
+
require 'neo-viz'
|
7
|
+
|
8
|
+
|
9
|
+
class Neo::Viz::App
|
10
|
+
set :environment, :test
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |c|
|
14
|
+
c.include Rack::Test::Methods
|
15
|
+
c.mock_with :rspec
|
16
|
+
c.include Matchers
|
17
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Matchers
|
4
|
+
# Compares two structures
|
5
|
+
#
|
6
|
+
# If the actual structure is a String it will be converted with JSON.parse
|
7
|
+
# If the expected structure is
|
8
|
+
# A Hash is compared by:
|
9
|
+
# * If the expected value of a key is nil, it must not be present in the actual
|
10
|
+
# * If the expected key is missing, it is allowed in the actual
|
11
|
+
# * Otherwise, the values are compared as if they were structures themselves.
|
12
|
+
# An Array is compared by:
|
13
|
+
# * If the expected value is an empty array, any actual is OK
|
14
|
+
# * Otherwise, the lengths AND the values are compared
|
15
|
+
# A Regexp is compare by =~
|
16
|
+
# All other values are compare by ==
|
17
|
+
#
|
18
|
+
class StructMatcher
|
19
|
+
|
20
|
+
def initialize(expected)
|
21
|
+
@expected = expected
|
22
|
+
@errors = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def matches?(actual)
|
26
|
+
if actual.kind_of? String
|
27
|
+
@actual = JSON.parse(actual)
|
28
|
+
else
|
29
|
+
@actual = actual
|
30
|
+
end
|
31
|
+
match_struct(@expected, @actual, '')
|
32
|
+
@errors.size == 0
|
33
|
+
end
|
34
|
+
|
35
|
+
def match_struct(expected, actual, prop)
|
36
|
+
if expected.kind_of? Hash
|
37
|
+
match_hash(expected, actual, prop)
|
38
|
+
elsif expected.kind_of? Array
|
39
|
+
match_array(expected, actual, prop)
|
40
|
+
elsif expected.kind_of? Regexp
|
41
|
+
match_regexp(expected, actual, prop)
|
42
|
+
elsif expected.kind_of? Class
|
43
|
+
match_class(expected, actual, prop)
|
44
|
+
else
|
45
|
+
match_equals(expected, actual, prop)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def match_hash(expected, actual, prop)
|
50
|
+
unless actual.kind_of? Hash
|
51
|
+
@errors << "(#{prop}) expected Hash, actual #{actual.class}"
|
52
|
+
return
|
53
|
+
end
|
54
|
+
expected.each do |expected_key, expected_value|
|
55
|
+
if expected_value.nil? #value nil means that the key should not exist
|
56
|
+
if actual.has_key? expected_key or actual.has_key? expected_key.to_s
|
57
|
+
@errors << "(#{prop}) expected #{expected_key} to not exists: #{actual[expected_key]}"
|
58
|
+
next
|
59
|
+
end
|
60
|
+
else
|
61
|
+
unless actual.has_key? expected_key or actual.has_key? expected_key.to_s
|
62
|
+
@errors << "(#{prop}) expected #{expected_key} to exists"
|
63
|
+
next
|
64
|
+
end
|
65
|
+
actual_value = actual[expected_key.to_s]
|
66
|
+
match_struct(expected_value, actual_value, "#{prop}-#{expected_key}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def match_array(expected, actual, prop)
|
73
|
+
unless actual.kind_of? Array
|
74
|
+
@errors << "(#{prop}) expected Array, actual #{actual.class}"
|
75
|
+
return
|
76
|
+
end
|
77
|
+
if actual.size != expected.size and expected.size > 0
|
78
|
+
@errors << "(#{prop}) expected size #{expected.size}, actual #{actual.size}"
|
79
|
+
end
|
80
|
+
min = [actual.size, expected.size].min
|
81
|
+
(0...min).each do |i|
|
82
|
+
match_struct(expected[i], actual[i], "#{prop}[#{i}]")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def match_regexp(expected, actual, prop)
|
87
|
+
unless actual.respond_to? :=~
|
88
|
+
@errors << "(#{prop}) cannot be matched with =~: expected #{expected.inspect}, actual #{actual.inspect}"
|
89
|
+
return
|
90
|
+
end
|
91
|
+
unless actual =~ expected
|
92
|
+
@errors << "(#{prop}) no match: expected #{expected.inspect}, actual #{actual.inspect}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def match_equals(expected, actual, prop)
|
97
|
+
unless actual == expected
|
98
|
+
@errors << "(#{prop}) not equal: expected #{expected.inspect}, actual #{actual.inspect}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def match_class(expected, actual, prop)
|
103
|
+
unless actual.kind_of? expected
|
104
|
+
@errors << "(#{prop}) not a kind of: expected #{expected.inspect}, actual #{actual.class.inspect}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def failure_message_for_should
|
109
|
+
"expected\n#{@actual.inspect}\nto match\n#{@expected.inspect}\nerrors\n#{@errors.join(%{\n})}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def struct_match(expected)
|
114
|
+
StructMatcher.new(expected)
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|