ditto 0.0.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|