ditto 0.0.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/ditto +9 -2
- data/features/create_entities.feature +77 -29
- data/features/load_entity.feature +29 -9
- data/features/step_definitions/definition_steps.rb +31 -9
- data/features/store_entity.feature +138 -0
- data/features/support/hooks.rb +2 -0
- data/lib/ditto.rb +9 -2
- data/lib/ditto/dsl.rb +10 -3
- data/lib/ditto/entity.rb +123 -14
- data/lib/ditto/map.rb +39 -0
- data/lib/ditto/options.rb +44 -14
- data/lib/ditto/runner.rb +93 -4
- data/lib/ditto/version.rb +3 -0
- data/spec/entity_spec.rb +69 -0
- data/spec/options_spec.rb +59 -13
- metadata +61 -22
- data/CHANGELOG +0 -1
- data/README +0 -11
- data/ditto.gemspec +0 -16
- data/rakefile +0 -8
- data/samples/simple_object.xml +0 -23
- data/samples/simple_object.yaml +0 -21
- data/samples/simple_object_seq.yaml +0 -18
- data/samples/simple_object_sym.yaml +0 -19
data/bin/ditto
CHANGED
@@ -4,57 +4,66 @@ As a user of Ditto
|
|
4
4
|
I want to load data entity definitions
|
5
5
|
|
6
6
|
Scenario: Ditto parses a single correct entity
|
7
|
-
Given a file named "simple_entity.
|
7
|
+
Given a file named "simple_entity.ditto" with:
|
8
8
|
"""
|
9
|
-
require 'ditto'
|
10
|
-
|
11
9
|
entity :currency, {
|
12
10
|
code: [ :mandatory, :unique ],
|
13
11
|
description: nil,
|
14
12
|
exchange_rate: :mandatory,
|
15
13
|
is_designated: nil,
|
16
14
|
}
|
17
|
-
check!
|
18
15
|
"""
|
19
|
-
When I run "simple_entity.
|
16
|
+
When I run ditto on "simple_entity.ditto"
|
17
|
+
Then the exit code should be 0
|
18
|
+
And the stdout should be
|
19
|
+
"""
|
20
|
+
checked 1 entities, 0 relationships, 0 errors
|
21
|
+
|
22
|
+
"""
|
23
|
+
When I run ditto -v on "simple_entity.ditto"
|
20
24
|
Then the exit code should be 0
|
21
25
|
And the stdout should be
|
22
|
-
"""
|
23
|
-
|
26
|
+
"""
|
27
|
+
1 definition files loaded
|
28
|
+
checking entities...
|
24
29
|
currency
|
25
|
-
1 entities, 0 relationships, 0 errors
|
30
|
+
checked 1 entities, 0 relationships, 0 errors
|
26
31
|
|
27
32
|
"""
|
33
|
+
When I run ditto -vv on "simple_entity.ditto"
|
34
|
+
Then the exit code should be 0
|
35
|
+
And the stdout should be
|
36
|
+
"""
|
37
|
+
loading file: simple_entity.ditto
|
38
|
+
1 definition files loaded
|
39
|
+
checking entities...
|
40
|
+
currency
|
41
|
+
checked 1 entities, 0 relationships, 0 errors
|
28
42
|
|
29
|
-
Scenario: Ditto parses a faulty entity attribute
|
30
|
-
Given a file named "faulty_attribute.rb" with:
|
31
43
|
"""
|
32
|
-
require 'ditto'
|
33
44
|
|
45
|
+
Scenario: Ditto parses a faulty entity attribute
|
46
|
+
Given a file named "faulty_attribute.ditto" with:
|
47
|
+
"""
|
34
48
|
entity :currency, {
|
35
49
|
code: [ :mandatory, :unique, :radar ],
|
36
50
|
description: nil,
|
37
51
|
exchange_rate: :mandatory,
|
38
52
|
is_designated: nil,
|
39
53
|
}
|
40
|
-
check!
|
41
54
|
"""
|
42
|
-
When I run "faulty_attribute.
|
43
|
-
Then the exit code should be
|
55
|
+
When I run ditto on "faulty_attribute.ditto"
|
56
|
+
Then the exit code should be 2
|
44
57
|
And the stdout should be
|
45
|
-
"""
|
46
|
-
|
47
|
-
|
48
|
-
unknown properties: radar
|
49
|
-
1 entities, 0 relationships, 1 errors
|
58
|
+
"""
|
59
|
+
currency: unknown properties: 'radar'
|
60
|
+
checked 1 entities, 0 relationships, 1 errors
|
50
61
|
|
51
62
|
"""
|
52
63
|
|
53
64
|
Scenario: Ditto parses a correct entity relationship
|
54
|
-
Given a file named "simple_relationship.
|
65
|
+
Given a file named "simple_relationship.ditto" with:
|
55
66
|
"""
|
56
|
-
require 'ditto'
|
57
|
-
|
58
67
|
entity :currency, {
|
59
68
|
code: [ :mandatory, :unique ],
|
60
69
|
description: nil,
|
@@ -67,15 +76,54 @@ Scenario: Ditto parses a correct entity relationship
|
|
67
76
|
:description => nil,
|
68
77
|
:is_commodity => nil
|
69
78
|
}
|
70
|
-
check!
|
71
79
|
"""
|
72
|
-
When I run "simple_relationship.
|
80
|
+
When I run ditto on "simple_relationship.ditto"
|
73
81
|
Then the exit code should be 0
|
74
82
|
And the stdout should be
|
75
|
-
"""
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
83
|
+
"""
|
84
|
+
checked 2 entities, 1 relationships, 0 errors
|
85
|
+
|
86
|
+
"""
|
87
|
+
Scenario: Ditto parses a bad entity relationship
|
88
|
+
Given a file named "bad_relationship.ditto" with:
|
89
|
+
"""
|
90
|
+
entity :currency, {
|
91
|
+
code: [ :mandatory, :unique ],
|
92
|
+
description: nil,
|
93
|
+
exchange_rate: :mandatory,
|
94
|
+
is_designated: nil,
|
95
|
+
currency_band: :related
|
96
|
+
}
|
97
|
+
entity :currency_group, {
|
98
|
+
:code => [ :mandatory, :unique ],
|
99
|
+
:description => nil,
|
100
|
+
:is_commodity => nil
|
101
|
+
}
|
102
|
+
"""
|
103
|
+
When I run ditto on "bad_relationship.ditto"
|
104
|
+
Then the exit code should be 2
|
105
|
+
And the stdout should be
|
106
|
+
"""
|
107
|
+
currency: unknown relationship to currency_band
|
108
|
+
checked 2 entities, 1 relationships, 1 errors
|
109
|
+
|
110
|
+
"""
|
111
|
+
Scenario: Ditto detects duplicate entity definitions
|
112
|
+
Given a file named "simple_entity.ditto" with:
|
113
|
+
"""
|
114
|
+
entity :currency, {
|
115
|
+
code: [ :mandatory, :unique ],
|
116
|
+
description: nil,
|
117
|
+
exchange_rate: :mandatory,
|
118
|
+
is_designated: nil,
|
119
|
+
}
|
120
|
+
"""
|
121
|
+
When I run ditto with the file "simple_entity.ditto" twice
|
122
|
+
Then the exit code should be 2
|
123
|
+
And the stdout should be
|
124
|
+
"""
|
125
|
+
currency: duplicate definition of entity
|
126
|
+
(was previously defined at simple_entity.ditto:1)
|
127
|
+
checked 1 entities, 0 relationships, 1 errors
|
80
128
|
|
81
129
|
"""
|
@@ -4,11 +4,11 @@ As a user of Ditto
|
|
4
4
|
I want to load data definitions
|
5
5
|
|
6
6
|
Scenario: Ditto loads a simple entity
|
7
|
-
Given a file named "
|
7
|
+
Given a file named "simple_entity.yaml" with:
|
8
8
|
"""
|
9
9
|
version: 1.0.0
|
10
10
|
date: 2012-10-13
|
11
|
-
---
|
11
|
+
---
|
12
12
|
currency:
|
13
13
|
code: GBP
|
14
14
|
description: Pounds Sterling
|
@@ -20,14 +20,34 @@ Scenario: Ditto loads a simple entity
|
|
20
20
|
code: Europe
|
21
21
|
description: European Countries
|
22
22
|
is_commodity: false
|
23
|
-
version: 1.0.1
|
24
23
|
"""
|
25
|
-
When I run ditto on "simple_entity.
|
24
|
+
When I run ditto on "simple_entity.yaml"
|
25
|
+
Then the exit code should be 0
|
26
|
+
And the stdout should be
|
27
|
+
"""
|
28
|
+
validated 2 entities, 2 instances
|
29
|
+
|
30
|
+
"""
|
31
|
+
When I run ditto -v on "simple_entity.yaml"
|
26
32
|
Then the exit code should be 0
|
27
33
|
And the stdout should be
|
28
|
-
"""
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
"""
|
35
|
+
1 data files, 2 instances loaded, 0 errors
|
36
|
+
checking instances...
|
37
|
+
validated 2 entities, 2 instances
|
38
|
+
|
39
|
+
"""
|
40
|
+
When I run ditto -vv on "simple_entity.yaml"
|
41
|
+
Then the exit code should be 0
|
42
|
+
Then the stdout should be
|
43
|
+
"""
|
44
|
+
loading file: simple_entity.yaml
|
45
|
+
1 data files, 2 instances loaded, 0 errors
|
46
|
+
checking instances...
|
47
|
+
currency
|
48
|
+
{"code"=>"GBP", "description"=>"Pounds Sterling", "exchange_rate"=>1.5, "is_designated"=>false, "currency_group"=>"Europe"}
|
49
|
+
currency_group
|
50
|
+
{"code"=>"Europe", "description"=>"European Countries", "is_commodity"=>false}
|
51
|
+
validated 2 entities, 2 instances
|
52
|
+
|
33
53
|
"""
|
@@ -1,17 +1,9 @@
|
|
1
|
-
require 'tmpdir'
|
2
|
-
|
3
1
|
Given /^a file named "(.*?)" with:$/ do |arg1, string|
|
4
|
-
filename = "#{
|
2
|
+
filename = "#{@tmpdir}/#{arg1}"
|
5
3
|
File.open(filename,"w") { |f| f.write string }
|
6
4
|
@files[arg1] = filename
|
7
5
|
end
|
8
6
|
|
9
|
-
When /^I run "(.*?)"$/ do |arg1|
|
10
|
-
@out = `ruby -I lib #{@files[arg1]}`
|
11
|
-
raise "Exec failed!" if $?.success?.nil?
|
12
|
-
@rc = $?.exitstatus
|
13
|
-
end
|
14
|
-
|
15
7
|
Then /^the exit code should be (\d+)$/ do |arg1|
|
16
8
|
@rc.should == arg1.to_i
|
17
9
|
end
|
@@ -19,3 +11,33 @@ end
|
|
19
11
|
Then /^the stdout should be$/ do |string|
|
20
12
|
@out.should == string
|
21
13
|
end
|
14
|
+
|
15
|
+
When /^I run ditto on "(.*?)"$/ do |arg1|
|
16
|
+
@out = `ruby -I lib bin/ditto #{@files[arg1]}`
|
17
|
+
raise "Exec failed!" if $?.success?.nil?
|
18
|
+
@rc = $?.exitstatus
|
19
|
+
end
|
20
|
+
|
21
|
+
When /^I run ditto on two files "(.*?)", "(.*?)"$/ do |arg1,arg2|
|
22
|
+
@out = `ruby -I lib bin/ditto --droptables -d #{@files[arg1]} #{@files[arg2]}`
|
23
|
+
raise "Exec failed!" if $?.success?.nil?
|
24
|
+
@rc = $?.exitstatus
|
25
|
+
end
|
26
|
+
|
27
|
+
When /^I run ditto \-v on "(.*?)"$/ do |arg1|
|
28
|
+
@out = `ruby -I lib bin/ditto -v #{@files[arg1]}`
|
29
|
+
raise "Exec failed!" if $?.success?.nil?
|
30
|
+
@rc = $?.exitstatus
|
31
|
+
end
|
32
|
+
|
33
|
+
When /^I run ditto \-vv on "(.*?)"$/ do |arg1|
|
34
|
+
@out = `ruby -I lib bin/ditto -vv #{@files[arg1]}`
|
35
|
+
raise "Exec failed!" if $?.success?.nil?
|
36
|
+
@rc = $?.exitstatus
|
37
|
+
end
|
38
|
+
|
39
|
+
When /^I run ditto with the file "(.*?)" twice$/ do |arg1|
|
40
|
+
@out = `ruby -I lib bin/ditto #{@files[arg1]} #{@files[arg1]}`
|
41
|
+
raise "Exec failed!" if $?.success?.nil?
|
42
|
+
@rc = $?.exitstatus
|
43
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
Feature: Ditto stores entities
|
2
|
+
In order to create test data
|
3
|
+
As a user of Ditto
|
4
|
+
I want to load data
|
5
|
+
|
6
|
+
Scenario: Ditto detects missing mappings
|
7
|
+
Given a file named "missing_mapping.ditto" with:
|
8
|
+
"""
|
9
|
+
entity :currency, {
|
10
|
+
code: [ :mandatory, :unique ],
|
11
|
+
description: nil,
|
12
|
+
exchange_rate: :mandatory,
|
13
|
+
is_designated: nil,
|
14
|
+
}
|
15
|
+
"""
|
16
|
+
And a file named "missing_mapping.yaml" with:
|
17
|
+
"""
|
18
|
+
version: 1.0.0
|
19
|
+
date: 2012-10-13
|
20
|
+
---
|
21
|
+
currency:
|
22
|
+
code: GBP
|
23
|
+
description: Pounds Sterling
|
24
|
+
exchange_rate: 1.5
|
25
|
+
is_designated: false
|
26
|
+
currency_group: Europe
|
27
|
+
"""
|
28
|
+
When I run ditto on two files "missing_mapping.ditto", "missing_mapping.yaml"
|
29
|
+
Then the exit code should be 5
|
30
|
+
And the stdout should be
|
31
|
+
"""
|
32
|
+
checked 1 entities, 0 relationships, 0 errors
|
33
|
+
validated 1 entities, 1 instances
|
34
|
+
currency: no mapping
|
35
|
+
|
36
|
+
"""
|
37
|
+
|
38
|
+
Scenario: Ditto stores a single correct entity
|
39
|
+
Given a file named "simple_entity.ditto" with:
|
40
|
+
"""
|
41
|
+
entity :currency_group, {
|
42
|
+
:code => [ :mandatory, :unique ],
|
43
|
+
:description => nil,
|
44
|
+
:is_commodity => nil
|
45
|
+
}
|
46
|
+
require 'dm/currency_group'
|
47
|
+
require 'dm/currency'
|
48
|
+
|
49
|
+
add :currency_group, :version => '1.0.0' do |ditto|
|
50
|
+
CurrencyGroup.create(
|
51
|
+
:code => ditto.code,
|
52
|
+
:description => ditto.description,
|
53
|
+
:is_commodity => ditto.is_commodity
|
54
|
+
)
|
55
|
+
end
|
56
|
+
"""
|
57
|
+
And a file named "simple_entity.yaml" with:
|
58
|
+
"""
|
59
|
+
version: 1.0.0
|
60
|
+
date: 2012-10-13
|
61
|
+
---
|
62
|
+
currency_group:
|
63
|
+
code: Europe
|
64
|
+
description: European Countries
|
65
|
+
is_commodity: false
|
66
|
+
"""
|
67
|
+
When I run ditto on two files "simple_entity.ditto", "simple_entity.yaml"
|
68
|
+
Then the exit code should be 0
|
69
|
+
And the stdout should be
|
70
|
+
"""
|
71
|
+
checked 1 entities, 0 relationships, 0 errors
|
72
|
+
validated 1 entities, 1 instances
|
73
|
+
stored 1 entities, 1 instances
|
74
|
+
|
75
|
+
"""
|
76
|
+
|
77
|
+
Scenario: Ditto stores related entities
|
78
|
+
Given a file named "simple_relationship.ditto" with:
|
79
|
+
"""
|
80
|
+
entity :currency, {
|
81
|
+
code: [ :mandatory, :unique ],
|
82
|
+
description: nil,
|
83
|
+
exchange_rate: :mandatory,
|
84
|
+
is_designated: nil,
|
85
|
+
currency_group: :related
|
86
|
+
}
|
87
|
+
entity :currency_group, {
|
88
|
+
:code => [ :mandatory, :unique ],
|
89
|
+
:description => nil,
|
90
|
+
:is_commodity => nil
|
91
|
+
}
|
92
|
+
require 'dm/currency_group'
|
93
|
+
require 'dm/currency'
|
94
|
+
|
95
|
+
add :currency_group, :version => '1.0.0' do |ditto|
|
96
|
+
CurrencyGroup.create(
|
97
|
+
:code => ditto.code,
|
98
|
+
:description => ditto.description,
|
99
|
+
:is_commodity => ditto.is_commodity
|
100
|
+
)
|
101
|
+
end
|
102
|
+
add :currency, :version => '1.0.0' do |ditto|
|
103
|
+
cg = CurrencyGroup.get(ditto.currency_group)
|
104
|
+
cg.currencies.create(
|
105
|
+
:code => ditto.code,
|
106
|
+
:description => ditto.description,
|
107
|
+
:exchange_rate => ditto.exchange_rate,
|
108
|
+
:is_designated => ditto.is_designated
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
"""
|
113
|
+
And a file named "simple_relationship.yaml" with:
|
114
|
+
"""
|
115
|
+
version: 1.0.0
|
116
|
+
date: 2012-10-13
|
117
|
+
---
|
118
|
+
currency:
|
119
|
+
code: GBP
|
120
|
+
description: Pounds Sterling
|
121
|
+
exchange_rate: 1.5
|
122
|
+
is_designated: false
|
123
|
+
currency_group: Europe
|
124
|
+
---
|
125
|
+
currency_group:
|
126
|
+
code: Europe
|
127
|
+
description: European Countries
|
128
|
+
is_commodity: false
|
129
|
+
"""
|
130
|
+
When I run ditto on two files "simple_relationship.ditto", "simple_relationship.yaml"
|
131
|
+
Then the exit code should be 0
|
132
|
+
And the stdout should be
|
133
|
+
"""
|
134
|
+
checked 2 entities, 1 relationships, 0 errors
|
135
|
+
validated 2 entities, 2 instances
|
136
|
+
stored 2 entities, 2 instances
|
137
|
+
|
138
|
+
"""
|
data/features/support/hooks.rb
CHANGED
data/lib/ditto.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'ditto/version'
|
2
|
+
require 'ditto/options'
|
3
|
+
require 'ditto/entity'
|
4
|
+
require 'ditto/dsl'
|
3
5
|
include Ditto::DSL
|
6
|
+
module Ditto
|
7
|
+
def self.symbolize_keys h
|
8
|
+
h.inject({}) { |opts,(k,v)| opts[(k.to_sym rescue k) || k] = v; opts }
|
9
|
+
end
|
10
|
+
end
|
data/lib/ditto/dsl.rb
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
# Define the DSL for Ditto
|
2
2
|
#
|
3
|
+
require_relative 'entity'
|
4
|
+
require_relative 'map'
|
5
|
+
|
3
6
|
module Ditto
|
4
7
|
module DSL
|
8
|
+
def version (*args, &block)
|
9
|
+
Ditto::Entity.set_version(*args, &block)
|
10
|
+
end
|
5
11
|
def entity (*args, &block)
|
6
12
|
Ditto::Entity.define_entity(*args, &block)
|
7
13
|
end
|
8
|
-
def
|
9
|
-
|
10
|
-
exit(nerr)
|
14
|
+
def add (name, opts = {}, &block)
|
15
|
+
Ditto::Map.add name, opts, &block
|
11
16
|
end
|
12
17
|
end
|
13
18
|
end
|
19
|
+
|
20
|
+
include Ditto::DSL
|
data/lib/ditto/entity.rb
CHANGED
@@ -1,34 +1,143 @@
|
|
1
1
|
# Entity support for Ditto
|
2
|
+
# Note that Entities are not objects in the current implementation, so we don't
|
3
|
+
# use a Class, we use a Module.
|
2
4
|
#
|
3
5
|
module Ditto
|
4
|
-
|
6
|
+
module Entity
|
5
7
|
PROPS = [ :mandatory, :unique, :related ].freeze
|
6
|
-
@entities = {}
|
8
|
+
@entities = {} # Hash containing definition hashes for each entity
|
9
|
+
@definitions = {} # Hash remembering where entities were defined
|
10
|
+
@instances = Hash.new {|h,k| h[k] = []} # Arrays of instance hashes for each entity
|
11
|
+
@deps = {} # Arrays of dependencies for each entity
|
12
|
+
@errors = 0
|
13
|
+
|
14
|
+
def self.set_version version
|
15
|
+
@version = version
|
16
|
+
end
|
17
|
+
|
18
|
+
# Maps to the 'entity' DSL keyword
|
19
|
+
#
|
7
20
|
def self.define_entity name, details
|
8
|
-
|
9
|
-
|
10
|
-
|
21
|
+
src = Thread.current.backtrace[2].split(':')[0..1]
|
22
|
+
src[0] = File.basename(src[0]) unless Ditto::Options.instance.debug
|
23
|
+
error("entity details must be a hash!", name, src) unless details.kind_of? Hash
|
24
|
+
|
25
|
+
if @entities.has_key?(name.to_sym)
|
26
|
+
error "duplicate definition of entity", name, src,
|
27
|
+
"(was previously defined at #{@definitions[name.to_sym].join(':')})"
|
28
|
+
end
|
29
|
+
@entities[name.to_sym] = Ditto.symbolize_keys details
|
30
|
+
@definitions[name.to_sym] = src
|
11
31
|
end
|
12
|
-
|
32
|
+
|
33
|
+
# Check that the entities are well defined
|
34
|
+
# return true/false
|
35
|
+
#
|
36
|
+
def self.check_definitions verbose = 0
|
37
|
+
return true if @entities.size == 0
|
13
38
|
nrel = 0
|
14
|
-
nerr = 0
|
15
39
|
nkey = 0
|
16
|
-
puts "
|
40
|
+
puts "checking entities..." if verbose > 0
|
17
41
|
@entities.each do |name,fields|
|
18
|
-
puts name
|
42
|
+
puts name if verbose > 0
|
43
|
+
@deps[name] = []
|
19
44
|
fields.each do |field, props|
|
20
45
|
symprops = Array(props).select{|p| p.is_a? Symbol}
|
21
46
|
duffprops = symprops - PROPS
|
22
47
|
unless duffprops.empty?
|
23
|
-
|
24
|
-
nerr += 1
|
48
|
+
error("unknown properties: '#{duffprops.join(' ,')}'", name, @definitions[name])
|
25
49
|
end
|
26
50
|
nkey += symprops.count(:unique)
|
27
|
-
|
51
|
+
if symprops.include? :related
|
52
|
+
nrel += 1
|
53
|
+
if @entities.has_key? field
|
54
|
+
@deps[name] << field
|
55
|
+
else
|
56
|
+
error("unknown relationship to #{field}", name, @definitions[name])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
puts "checked #{@entities.size} entities, #{nrel} relationships, #{@errors} errors"
|
62
|
+
return error?
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.load_instance name, instance
|
66
|
+
@instances[name] << instance
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.validate_instances verbose = 0
|
70
|
+
return true if instance_count == 0
|
71
|
+
puts "checking instances..." if verbose > 0
|
72
|
+
ninst = nerr = 0
|
73
|
+
@instances.each do |entity_name,entities|
|
74
|
+
puts entity_name if verbose > 1
|
75
|
+
entities.each do |e|
|
76
|
+
puts e.inspect if verbose > 1
|
77
|
+
if Ditto::Entity.validate(entity_name, e)
|
78
|
+
ninst += 1
|
79
|
+
else
|
80
|
+
nerr += 1
|
81
|
+
end
|
28
82
|
end
|
29
83
|
end
|
30
|
-
puts "#{@
|
31
|
-
return nerr
|
84
|
+
puts "validated #{@instances.keys.size} entities, #{ninst} instances"
|
85
|
+
return (nerr == 0)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Compute dependencies recursively
|
89
|
+
#
|
90
|
+
def self.dep_list(name, list, dephash)
|
91
|
+
if deps = dephash[name]
|
92
|
+
dephash.delete name
|
93
|
+
deps.each do |dep|
|
94
|
+
self.dep_list dep, list, dephash
|
95
|
+
end
|
96
|
+
list << name unless list.include? name
|
97
|
+
else
|
98
|
+
return if list.include? name # we did it earlier
|
99
|
+
raise "Missing or circular dependency on #{name}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Before storing, sort the instances by dependency
|
104
|
+
#
|
105
|
+
def self.store_all
|
106
|
+
seq = []
|
107
|
+
deps = @deps.dup # shallow copy as deleting entries...
|
108
|
+
@deps.each_key do |entity|
|
109
|
+
self.dep_list entity, seq, deps
|
110
|
+
end
|
111
|
+
return false if Ditto::Map.check_maps(seq, @definitions) > 0
|
112
|
+
n = Ditto::Map.add_all seq, @instances
|
113
|
+
puts "stored #{@instances.keys.size} entities, #{n} instances"
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.validate name, instance
|
118
|
+
return true
|
119
|
+
return false unless @entities.has_key?(name)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.instance_count
|
123
|
+
return @instances.keys.size
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.entity_count
|
127
|
+
return @entities.keys.size
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def self.error?
|
132
|
+
return @errors == 0
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.error msg, name, src, extra = nil
|
136
|
+
print "#{name}: #{msg}"
|
137
|
+
print " at line #{src[1]} in file #{src[0]}" if !src and Ditto::Options.instance.debug
|
138
|
+
print "\n";
|
139
|
+
puts extra if extra
|
140
|
+
@errors += 1
|
32
141
|
end
|
33
142
|
end
|
34
143
|
end
|
data/lib/ditto/map.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Mapping support for Ditto
|
2
|
+
#
|
3
|
+
require 'ostruct'
|
4
|
+
require 'data_mapper'
|
5
|
+
|
6
|
+
module Ditto
|
7
|
+
module Map
|
8
|
+
@maps = {}
|
9
|
+
|
10
|
+
def self.add name, opts, &block
|
11
|
+
@maps[name] = block
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.check_maps seq, definitions
|
15
|
+
nerr = 0
|
16
|
+
seq.each do |entity|
|
17
|
+
unless @maps.has_key? entity
|
18
|
+
nerr += 1
|
19
|
+
Ditto::Entity.error("no mapping", entity, definitions[entity])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return nerr
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.add_all seq, data
|
26
|
+
ninst = 0
|
27
|
+
seq.each do |entity|
|
28
|
+
instances = data[entity]
|
29
|
+
instances.each do |ihash|
|
30
|
+
puts "adding #{entity} #{ihash.inspect}" if Ditto::Options.instance.verbose > 1
|
31
|
+
instance = OpenStruct.new ihash
|
32
|
+
@maps[entity].call instance
|
33
|
+
ninst += 1
|
34
|
+
end
|
35
|
+
end
|
36
|
+
return ninst
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/ditto/options.rb
CHANGED
@@ -1,33 +1,63 @@
|
|
1
1
|
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'singleton'
|
2
4
|
|
3
5
|
module Ditto
|
4
|
-
class Options
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
class Options < OpenStruct
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
DEFAULTS = {
|
10
|
+
:connstring => ENV['DATABASE_URL'] || 'nodb://a/b',
|
11
|
+
:modelpath => '.',
|
12
|
+
:debug => false,
|
13
|
+
:droptables => false,
|
14
|
+
:verbose => 0,
|
15
|
+
:loadpaths => nil
|
16
|
+
}
|
17
|
+
def initialize
|
18
|
+
super(DEFAULTS)
|
11
19
|
end
|
12
|
-
|
13
|
-
def parse
|
20
|
+
|
21
|
+
def parse argv
|
22
|
+
|
23
|
+
Ditto::Options::DEFAULTS.each do |k,v|
|
24
|
+
self.send "#{k}=".to_s, v
|
25
|
+
end
|
26
|
+
|
14
27
|
OptionParser.new do |opts|
|
15
|
-
opts.banner = "Usage: ditto [ options ]
|
16
|
-
opts.
|
17
|
-
|
28
|
+
opts.banner = "Usage: ditto [ options ] paths..."
|
29
|
+
opts.separator "Paths will be searched for .ditto files (definitions) and .yaml files (data)"
|
30
|
+
opts.on("-c STRING", "--connection", String, "Database connection string",
|
31
|
+
"Defaults to environment variable DATABASE_URL",
|
32
|
+
"Current default is #{DEFAULTS[:connstring]}") do |s|
|
33
|
+
self.connstring = s
|
34
|
+
end
|
35
|
+
opts.on("-m PATH", "--modelpath", String, "Path to load DataMapper models from (default #{DEFAULTS[:modelpath]})") do |m|
|
36
|
+
raise "Modelpath #{m} does not exist!" unless Dir.exist?(m)
|
37
|
+
self.modelpath = m
|
38
|
+
end
|
39
|
+
opts.on("-d", "--debug", "Debug") do |s|
|
40
|
+
self.debug = true
|
41
|
+
end
|
42
|
+
opts.on("-v", "--verbose", "Verbose (repeat for more)") do
|
43
|
+
self.verbose += 1
|
44
|
+
end
|
45
|
+
opts.on("--droptables", "Delete existing database tables!") do
|
46
|
+
self.droptables = true
|
18
47
|
end
|
19
|
-
opts.on("-h", "--help", "Show this message") do
|
48
|
+
opts.on("-?", "-h", "--help", "Show this message") do
|
20
49
|
puts opts
|
21
50
|
exit
|
22
51
|
end
|
23
52
|
begin
|
24
|
-
argv = ["-h"] if argv.empty?
|
25
53
|
opts.parse!(argv)
|
26
54
|
rescue OptionParser::ParseError => e
|
27
55
|
STDERR.puts e.message, "\n", opts
|
28
56
|
exit(-1)
|
29
57
|
end
|
30
58
|
end
|
59
|
+
self.loadpaths = argv
|
60
|
+
return self
|
31
61
|
end
|
32
62
|
end
|
33
63
|
end
|
data/lib/ditto/runner.rb
CHANGED
@@ -1,13 +1,102 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require 'yaml'
|
2
|
+
require 'ditto'
|
3
3
|
|
4
4
|
module Ditto
|
5
5
|
class Runner
|
6
6
|
def initialize(argv)
|
7
|
-
@
|
7
|
+
@opts = Options.instance.parse(argv)
|
8
|
+
$: << @opts.modelpath
|
8
9
|
end
|
10
|
+
|
9
11
|
def run
|
10
|
-
|
12
|
+
begin
|
13
|
+
return 1 unless load_definitions
|
14
|
+
return 2 unless Ditto::Entity.check_definitions(@opts.verbose)
|
15
|
+
return 3 unless load_data
|
16
|
+
return 4 unless Ditto::Entity.validate_instances(@opts.verbose)
|
17
|
+
return 5 unless store_data
|
18
|
+
return 0
|
19
|
+
rescue StandardError => e
|
20
|
+
STDERR.puts "\nERROR: #{e.to_s}"
|
21
|
+
STDERR.puts e.backtrace if @opts.debug
|
22
|
+
return 99
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load definitions from Ditto files
|
27
|
+
#
|
28
|
+
def load_definitions
|
29
|
+
nfiles = 0
|
30
|
+
nerr = 0
|
31
|
+
Dir.glob(@opts.loadpaths) do |f|
|
32
|
+
next unless File.extname(f) == '.ditto'
|
33
|
+
puts "loading file: #{File.basename(f)}" if @opts.verbose > 1
|
34
|
+
begin
|
35
|
+
load File.absolute_path(f)
|
36
|
+
nfiles += 1
|
37
|
+
rescue LoadError => le
|
38
|
+
loc = le.backtrace[2].split(':')
|
39
|
+
puts "Error: #{le.to_s} (line #{loc[1]} in #{loc[0]})\nDid you set --modelpath ?"
|
40
|
+
nerr += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
puts "#{nfiles} definition files loaded" if (nfiles > 0 and @opts.verbose > 0)
|
44
|
+
return nerr == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
# Load data from YAML files into the in-memory representation
|
48
|
+
# return true if all OK
|
49
|
+
#
|
50
|
+
def load_data
|
51
|
+
nfiles = nent = nerr = 0
|
52
|
+
Dir.glob(@opts.loadpaths) do |f|
|
53
|
+
next unless File.extname(f) == '.yaml'
|
54
|
+
nfiles += 1
|
55
|
+
puts "loading file: #{File.basename(f)}" if @opts.verbose > 1
|
56
|
+
header = nil
|
57
|
+
YAML::load_documents(File.open(f)) do |doc|
|
58
|
+
unless header
|
59
|
+
header = doc
|
60
|
+
puts "Header info is #{header.to_s}" if @opts.verbose > 2
|
61
|
+
next
|
62
|
+
end
|
63
|
+
e = doc.flatten
|
64
|
+
if e.size != 2
|
65
|
+
nerr += 1
|
66
|
+
puts "ERROR: entity instance has multiple keys"
|
67
|
+
next
|
68
|
+
end
|
69
|
+
Ditto::Entity.load_instance e[0].to_sym, e[1]
|
70
|
+
nent += 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
puts "#{nfiles} data files, #{nent} instances loaded, #{nerr} errors" if (nfiles > 0 and @opts.verbose > 0)
|
74
|
+
return nerr == 0
|
11
75
|
end
|
76
|
+
|
77
|
+
# Add the data to the database
|
78
|
+
#
|
79
|
+
def store_data
|
80
|
+
return true if Ditto::Entity.instance_count == 0
|
81
|
+
return true if Ditto::Entity.entity_count == 0
|
82
|
+
|
83
|
+
puts "Running against: #{@opts.connstring}" if @opts.verbose > 0
|
84
|
+
DataMapper::Logger.new(STDOUT, (@opts.verbose > 1) ? :debug : :info)
|
85
|
+
DataMapper.setup(:default, @opts.connstring)
|
86
|
+
DataMapper.finalize
|
87
|
+
begin
|
88
|
+
if @opts.droptables
|
89
|
+
DataMapper.auto_migrate!
|
90
|
+
else
|
91
|
+
DataMapper.auto_upgrade!
|
92
|
+
end
|
93
|
+
return Ditto::Entity.store_all
|
94
|
+
rescue StandardError => e
|
95
|
+
STDERR.puts "\nERROR: #{e.to_s}"
|
96
|
+
STDERR.puts e.backtrace if @opts.debug
|
97
|
+
return false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
12
101
|
end
|
13
102
|
end
|
data/spec/entity_spec.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../lib/ditto/entity'
|
2
|
+
|
3
|
+
module Ditto
|
4
|
+
describe Entity do
|
5
|
+
context "Checking dependencies" do
|
6
|
+
before (:each) do
|
7
|
+
@deps = {
|
8
|
+
:currency_group => [],
|
9
|
+
:currency => [:currency_group],
|
10
|
+
:country => [:currency],
|
11
|
+
:continent => [:currency, :country],
|
12
|
+
:circle => [:circle],
|
13
|
+
:bigcircle => [:loop],
|
14
|
+
:loop => [:detour, :country],
|
15
|
+
:detour => [:bigcircle],
|
16
|
+
:random => [:stealth],
|
17
|
+
}
|
18
|
+
end
|
19
|
+
context "Checking dep_list()" do
|
20
|
+
it "should return empty dependency" do
|
21
|
+
Ditto::Entity.dep_list(:currency_group, [], @deps).should == [:currency_group]
|
22
|
+
end
|
23
|
+
it "should return simple dependency" do
|
24
|
+
Ditto::Entity.dep_list(:currency, [], @deps).should == [:currency_group, :currency]
|
25
|
+
end
|
26
|
+
it "should return multilevel dependency" do
|
27
|
+
Ditto::Entity.dep_list(:country, [], @deps).should == [:currency_group, :currency, :country]
|
28
|
+
end
|
29
|
+
it "should return square dependency" do
|
30
|
+
Ditto::Entity.dep_list(:continent, [], @deps).should == [:currency_group, :currency, :country, :continent]
|
31
|
+
end
|
32
|
+
it "should detect missing dependency" do
|
33
|
+
expect {
|
34
|
+
Ditto::Entity.dep_list(:random, [], @deps)
|
35
|
+
}.to raise_error "Missing or circular dependency on stealth"
|
36
|
+
end
|
37
|
+
it "should detect circular dependency" do
|
38
|
+
expect {
|
39
|
+
Ditto::Entity.dep_list(:circle, [], @deps)
|
40
|
+
}.to raise_error "Missing or circular dependency on circle"
|
41
|
+
end
|
42
|
+
it "should detect bigcircular dependency" do
|
43
|
+
expect {
|
44
|
+
Ditto::Entity.dep_list(:bigcircle, [], @deps)
|
45
|
+
}.to raise_error "Missing or circular dependency on bigcircle"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context "as used in store_all()" do
|
49
|
+
it "should work" do
|
50
|
+
@deps = {
|
51
|
+
:currency_group => [],
|
52
|
+
:currency => [:currency_group],
|
53
|
+
}
|
54
|
+
seq = []
|
55
|
+
Ditto::Entity.dep_list(:currency_group, seq, @deps)
|
56
|
+
Ditto::Entity.dep_list(:currency, seq, @deps)
|
57
|
+
seq.should == [:currency_group, :currency]
|
58
|
+
end
|
59
|
+
it "should work with 3" do
|
60
|
+
seq = []
|
61
|
+
Ditto::Entity.dep_list(:currency_group, seq, @deps)
|
62
|
+
Ditto::Entity.dep_list(:currency, seq, @deps)
|
63
|
+
Ditto::Entity.dep_list(:country, seq, @deps)
|
64
|
+
seq.should == [:currency_group, :currency, :country]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/options_spec.rb
CHANGED
@@ -2,29 +2,75 @@ require_relative '../lib/ditto/options'
|
|
2
2
|
|
3
3
|
module Ditto
|
4
4
|
describe Options do
|
5
|
-
context "specifying no
|
6
|
-
it "should return
|
7
|
-
opts = Ditto::Options.
|
8
|
-
|
5
|
+
context "specifying no options" do
|
6
|
+
it "should return defaults" do
|
7
|
+
opts = Ditto::Options.instance
|
8
|
+
opts.parse([])
|
9
|
+
opts.connstring.should == Ditto::Options::DEFAULTS[:connstring]
|
10
|
+
opts.modelpath.should == Ditto::Options::DEFAULTS[:modelpath]
|
11
|
+
opts.verbose.should == 0
|
12
|
+
opts.debug.should be_false
|
13
|
+
opts.droptables.should be_false
|
14
|
+
opts.loadpaths.size.should == 0
|
15
|
+
end
|
16
|
+
it "should return single path" do
|
17
|
+
opts = Ditto::Options.instance.parse(["testfile"])
|
18
|
+
opts.connstring.should == Ditto::Options::DEFAULTS[:connstring]
|
19
|
+
opts.modelpath.should == Ditto::Options::DEFAULTS[:modelpath]
|
20
|
+
opts.verbose.should == 0
|
21
|
+
opts.debug.should be_false
|
22
|
+
opts.droptables.should be_false
|
23
|
+
opts.loadpaths.should == ["testfile"]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
context "specifying a model path" do
|
27
|
+
it "should return it" do
|
28
|
+
mpath = "spec"
|
29
|
+
opts = Ditto::Options.instance.parse(["-m", "#{mpath}", "afile"])
|
30
|
+
opts.modelpath.should == mpath
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context "specifying a model path that doesn't exist" do
|
34
|
+
it "should fail" do
|
35
|
+
mpath = "zz/zz/zz/zz"
|
36
|
+
expect {
|
37
|
+
opts = Ditto::Options.instance.parse(["-m", "#{mpath}", "afile"])
|
38
|
+
}.to raise_error(RuntimeError, "Modelpath #{mpath} does not exist!")
|
9
39
|
end
|
10
40
|
end
|
11
|
-
context "specifying a
|
41
|
+
context "specifying a connection string" do
|
12
42
|
it "should return it" do
|
13
43
|
mydb = "username/password@//myserver:1521/my.service.com"
|
14
|
-
opts = Ditto::Options.
|
44
|
+
opts = Ditto::Options.instance.parse(["-c", "#{mydb}", "afile"])
|
15
45
|
opts.connstring.should == mydb
|
16
46
|
end
|
17
47
|
end
|
18
|
-
context "specifying
|
19
|
-
it "should return the
|
20
|
-
opts = Ditto::Options.
|
21
|
-
opts.
|
48
|
+
context "specifying paths and no connection string" do
|
49
|
+
it "should return the paths" do
|
50
|
+
opts = Ditto::Options.instance.parse(["file1", "path1"])
|
51
|
+
opts.loadpaths.should == ["file1", "path1"]
|
22
52
|
end
|
23
53
|
end
|
24
|
-
context "specifying files and a
|
54
|
+
context "specifying files and a connection string" do
|
25
55
|
it "should return the files" do
|
26
|
-
opts = Ditto::Options.
|
27
|
-
opts.
|
56
|
+
opts = Ditto::Options.instance.parse(["-c", "u/p@//serv:1521/mydb", "file1", "path1"])
|
57
|
+
opts.loadpaths.should == ["file1", "path1"]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
context "specifying droptables" do
|
61
|
+
it "should set the options" do
|
62
|
+
opts = Ditto::Options.instance.parse(["--droptables", "file1", "path1"])
|
63
|
+
opts.droptables.should be_true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
context "specifying verbose" do
|
67
|
+
it "should set verbose flag" do
|
68
|
+
opts = Ditto::Options.instance.parse(["-v", "file1"])
|
69
|
+
opts.verbose.should == 1
|
70
|
+
end
|
71
|
+
it "should set verbose flag twice" do
|
72
|
+
opts = Ditto::Options.instance.parse(["-vv", "file1"])
|
73
|
+
opts.verbose.should == 2
|
28
74
|
end
|
29
75
|
end
|
30
76
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ditto
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,6 +11,22 @@ bindir: bin
|
|
11
11
|
cert_chain: []
|
12
12
|
date: 2012-10-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: data_mapper
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.2.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.2.0
|
14
30
|
- !ruby/object:Gem::Dependency
|
15
31
|
name: rspec
|
16
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -18,7 +34,7 @@ dependencies:
|
|
18
34
|
requirements:
|
19
35
|
- - ! '>='
|
20
36
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
37
|
+
version: '2.11'
|
22
38
|
type: :development
|
23
39
|
prerelease: false
|
24
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,53 +42,69 @@ dependencies:
|
|
26
42
|
requirements:
|
27
43
|
- - ! '>='
|
28
44
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
45
|
+
version: '2.11'
|
30
46
|
description: ! 'Database Independent Test Objects (Ditto)
|
31
47
|
|
32
48
|
|
33
49
|
Ditto defines a simple DSL that allows test data to be expressed in a database independent
|
34
|
-
format.
|
35
|
-
|
50
|
+
format.
|
51
|
+
|
52
|
+
When the underlying tables are remapped the QA team don''t have to go and change
|
53
|
+
all the historical test cases.
|
36
54
|
|
37
55
|
|
38
|
-
There are
|
56
|
+
There are three parts, all of them versioned:
|
39
57
|
|
40
58
|
1. The data declarations
|
41
59
|
|
42
60
|
2. The mappings to the underlying database
|
43
61
|
|
62
|
+
3. The data itself
|
63
|
+
|
44
64
|
|
45
65
|
Idea is to replace an XML dialect with something simpler. See sample at simple_object.xml
|
46
66
|
|
47
67
|
|
68
|
+
Notes on DSL
|
69
|
+
|
70
|
+
------------
|
71
|
+
|
72
|
+
|
73
|
+
The entity declaration style uses verb and hash only, without a block.
|
74
|
+
|
75
|
+
Contrast this with mapping style which uses verb, params and then block. Of course
|
76
|
+
|
77
|
+
mappings need code and so the block usage is natural. This could also be done
|
78
|
+
|
79
|
+
for the declarations, very similar to the way the gem ''datamapper'' does it.
|
80
|
+
|
81
|
+
|
82
|
+
|
48
83
|
Nick Townsend, Oct 2012
|
49
84
|
|
50
85
|
'
|
51
86
|
email: nick.townsend@mac.com
|
52
|
-
executables:
|
87
|
+
executables:
|
88
|
+
- ditto
|
53
89
|
extensions: []
|
54
90
|
extra_rdoc_files: []
|
55
91
|
files:
|
92
|
+
- lib/ditto/dsl.rb
|
93
|
+
- lib/ditto/options.rb
|
94
|
+
- lib/ditto/entity.rb
|
95
|
+
- lib/ditto/version.rb
|
96
|
+
- lib/ditto/map.rb
|
97
|
+
- lib/ditto/runner.rb
|
98
|
+
- lib/ditto.rb
|
56
99
|
- bin/ditto
|
57
|
-
- CHANGELOG
|
58
|
-
- ditto.gemspec
|
59
100
|
- features/create_entities.feature
|
60
101
|
- features/load_entity.feature
|
61
102
|
- features/step_definitions/definition_steps.rb
|
103
|
+
- features/store_entity.feature
|
62
104
|
- features/support/hooks.rb
|
63
|
-
-
|
64
|
-
- lib/ditto/entity.rb
|
65
|
-
- lib/ditto/options.rb
|
66
|
-
- lib/ditto/runner.rb
|
67
|
-
- lib/ditto.rb
|
68
|
-
- rakefile
|
69
|
-
- README
|
70
|
-
- samples/simple_object.xml
|
71
|
-
- samples/simple_object.yaml
|
72
|
-
- samples/simple_object_seq.yaml
|
73
|
-
- samples/simple_object_sym.yaml
|
105
|
+
- spec/entity_spec.rb
|
74
106
|
- spec/options_spec.rb
|
75
|
-
homepage:
|
107
|
+
homepage: https://github.com/townsen/ditto
|
76
108
|
licenses: []
|
77
109
|
post_install_message:
|
78
110
|
rdoc_options: []
|
@@ -96,4 +128,11 @@ rubygems_version: 1.8.24
|
|
96
128
|
signing_key:
|
97
129
|
specification_version: 3
|
98
130
|
summary: Database independent test objects
|
99
|
-
test_files:
|
131
|
+
test_files:
|
132
|
+
- features/create_entities.feature
|
133
|
+
- features/load_entity.feature
|
134
|
+
- features/step_definitions/definition_steps.rb
|
135
|
+
- features/store_entity.feature
|
136
|
+
- features/support/hooks.rb
|
137
|
+
- spec/entity_spec.rb
|
138
|
+
- spec/options_spec.rb
|
data/CHANGELOG
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
Version 0.0.1: Initial attempts
|
data/README
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
Database Independent Test Objects (Ditto)
|
2
|
-
|
3
|
-
Ditto defines a simple DSL that allows test data to be expressed in a database independent format. When the underlying tables are remapped the QA team don't have to go and change all the historical test cases.
|
4
|
-
|
5
|
-
There are two parts, both of them versioned:
|
6
|
-
1. The data declarations
|
7
|
-
2. The mappings to the underlying database
|
8
|
-
|
9
|
-
Idea is to replace an XML dialect with something simpler. See sample at simple_object.xml
|
10
|
-
|
11
|
-
Nick Townsend, Oct 2012
|
data/ditto.gemspec
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = 'ditto'
|
3
|
-
s.version = '0.0.1'
|
4
|
-
s.date = '2012-10-12'
|
5
|
-
s.summary = "Database independent test objects"
|
6
|
-
s.description = File.read(File.join(File.dirname(__FILE__), 'README'))
|
7
|
-
s.authors = ["Nick Townsend"]
|
8
|
-
s.email = 'nick.townsend@mac.com'
|
9
|
-
s.platform = Gem::Platform::RUBY
|
10
|
-
s.required_ruby_version = '>=1.9'
|
11
|
-
s.files = Dir['**/**']
|
12
|
-
s.homepage = 'http://rubygems.org/gems/ditto'
|
13
|
-
# s.add_runtime_dependency "daemons", ["= 1.1.0"]
|
14
|
-
s.add_development_dependency "rspec", [">= 0"]
|
15
|
-
s.has_rdoc = false
|
16
|
-
end
|
data/rakefile
DELETED
data/samples/simple_object.xml
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
---------------------------------------- DEFINITIONS ----------------------------------------
|
2
|
-
<object_classes>
|
3
|
-
<object name='currency'>
|
4
|
-
<property name='code' mandatory='Y' is_key='Y' />
|
5
|
-
<property name='description'/>
|
6
|
-
<property name='exchange_rate' mandatory='Y' />
|
7
|
-
<property name='is_designated'/>
|
8
|
-
<relation name='currency_group' relation_object='currency_group' relation_type='unique'/>
|
9
|
-
</object>
|
10
|
-
<object name='currency_group'>
|
11
|
-
<property name='code' mandatory='Y' is_key='Y'/>
|
12
|
-
<property name='description'/>
|
13
|
-
<property name='is_commodity'/>
|
14
|
-
</object>
|
15
|
-
</object_classes>
|
16
|
-
|
17
|
-
---------------------------------------- DATA ----------------------------------------
|
18
|
-
<object_instances>
|
19
|
-
<currency code ='GBP' description='Great Britain Pound' exchange_rate='1.5' is_designated='false'>
|
20
|
-
<currency_group code='Europe' />
|
21
|
-
</currency>
|
22
|
-
<currency_group code='Europe' description='European countries' is_commodity='false'/>
|
23
|
-
</object_instances>
|
data/samples/simple_object.yaml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# This format has multiple YAML streams:
|
2
|
-
# The first contains version info, date and author etc.
|
3
|
-
# The subsequent are hashes, each being an entity with key the entity name
|
4
|
-
# Note that strings are used as keys and not symbols for user clarity
|
5
|
-
#
|
6
|
-
version: 1.0.0
|
7
|
-
date: 2012-10-13
|
8
|
-
author: Nick Townsend
|
9
|
-
---
|
10
|
-
currency:
|
11
|
-
code: GBP
|
12
|
-
description: Pounds Sterling
|
13
|
-
exchange_rate: 1.5
|
14
|
-
is_designated: false
|
15
|
-
currency_group: Europe
|
16
|
-
---
|
17
|
-
currency_group:
|
18
|
-
code: Europe
|
19
|
-
description: European Countries
|
20
|
-
is_commodity: false
|
21
|
-
version: 1.0.1
|
@@ -1,18 +0,0 @@
|
|
1
|
-
# This format has two streams, the first version info etc.
|
2
|
-
# The second is a sequence of hashes, each has being an entity
|
3
|
-
#
|
4
|
-
:version: 1.0.0
|
5
|
-
---
|
6
|
-
-
|
7
|
-
:currency:
|
8
|
-
:code: GBP
|
9
|
-
:description: Pounds Sterling
|
10
|
-
:exchange_rate: 1.5
|
11
|
-
:is_designated: false
|
12
|
-
:currency_group: Europe
|
13
|
-
-
|
14
|
-
:currency_group:
|
15
|
-
:code: Europe
|
16
|
-
:description: European Countries
|
17
|
-
:is_commodity: false
|
18
|
-
:version: 1.0.1
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# This format has multiple streams, the first version info etc.
|
2
|
-
# The subsequent are hashes, each has being an entity
|
3
|
-
# Symbols are used throughout
|
4
|
-
#
|
5
|
-
:version: 1.0.0
|
6
|
-
:date: 2012-10-13
|
7
|
-
---
|
8
|
-
:currency:
|
9
|
-
:code: GBP
|
10
|
-
:description: Pounds Sterling
|
11
|
-
:exchange_rate: 1.5
|
12
|
-
:is_designated: false
|
13
|
-
:currency_group: Europe
|
14
|
-
---
|
15
|
-
:currency_group:
|
16
|
-
:code: Europe
|
17
|
-
:description: European Countries
|
18
|
-
:is_commodity: false
|
19
|
-
:version: 1.0.1
|