ditto 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/bin/ditto +1 -1
  2. data/features/create_entities.feature +1 -0
  3. data/features/load_entity.feature +1 -0
  4. data/features/store_entity.feature +5 -4
  5. data/lib/ditto.rb +1 -0
  6. data/lib/ditto/dsl.rb +14 -9
  7. data/lib/ditto/entity.rb +118 -106
  8. data/lib/ditto/error.rb +19 -0
  9. data/lib/ditto/options.rb +45 -13
  10. data/lib/ditto/runner.rb +37 -56
  11. data/lib/ditto/version.rb +1 -1
  12. data/spec/entity_spec.rb +83 -59
  13. data/spec/fixtures/badmap.ditto +18 -0
  14. data/spec/fixtures/badver.ditto +17 -0
  15. data/spec/fixtures/bigcircle.ditto +17 -0
  16. data/spec/fixtures/circle.ditto +16 -0
  17. data/spec/fixtures/continent.ditto +16 -0
  18. data/spec/fixtures/country.ditto +16 -0
  19. data/spec/fixtures/currency.ditto +30 -0
  20. data/spec/fixtures/currency_group.ditto +17 -0
  21. data/spec/fixtures/dot.ditto +17 -0
  22. data/spec/fixtures/dupversion.ditto +32 -0
  23. data/spec/fixtures/good.ditto +17 -0
  24. data/spec/fixtures/multiversion.ditto +32 -0
  25. data/spec/fixtures/nocomma.ditto +16 -0
  26. data/spec/fixtures/nomethod.ditto +18 -0
  27. data/spec/fixtures/random.ditto +17 -0
  28. data/spec/fixtures/syntax.ditto +19 -0
  29. data/spec/options_spec.rb +43 -25
  30. data/test/data/simple_object.xml +23 -0
  31. data/test/data/simple_object.yaml +39 -0
  32. data/test/ditto/currency.ditto +30 -0
  33. data/test/ditto/currency_group.ditto +17 -0
  34. data/test/dm/currency-1.0.0.dm +13 -0
  35. data/test/dm/currency_group-1.0.0.dm +10 -0
  36. data/test/dm/datamart-1.0 +7 -0
  37. data/test/dm/nested +2 -0
  38. data/test/dmtest.rb +41 -0
  39. data/test/sample.test +2 -0
  40. metadata +59 -42
  41. data/lib/ditto/map.rb +0 -39
@@ -1,85 +1,67 @@
1
1
  require 'yaml'
2
2
  require 'ditto'
3
+ require 'data_mapper'
3
4
 
4
5
  module Ditto
5
6
  class Runner
6
7
  def initialize(argv)
7
- @opts = Options.instance.parse(argv)
8
- $: << @opts.modelpath
8
+ @opts = Options.new(argv)
9
9
  end
10
10
 
11
11
  def run
12
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
13
+ if @opts.check
14
+ puts "Checking..." if @opts.verbose > 0
15
+ load_files @opts.loadfiles
16
+ exit
17
+ end
18
+ load_files @opts.loadfiles, '.yaml'
19
+ raise "dittomart file must be specified!" unless @opts.dittomart
20
+ puts "dittomart file: #{@opts.dittomart}" if @opts.verbose > 0
21
+ load_files @opts.martfiles, '.dm'
22
+ Ditto::Entity.load_entities @opts.entitydir, @opts.verbose
23
+ store_data
18
24
  return 0
25
+ rescue Ditto::Error => de
26
+ STDERR.puts de.message(@opts.debug)
19
27
  rescue StandardError => e
20
- STDERR.puts "\nERROR: #{e.to_s}"
28
+ STDERR.puts "\n#{e.class}: #{e.message}"
21
29
  STDERR.puts e.backtrace if @opts.debug
22
- return 99
23
30
  end
31
+ return 99
24
32
  end
25
33
 
26
- # Load definitions from Ditto files
34
+ # Load any filetype depending on it's extension
27
35
  #
28
- def load_definitions
36
+ def load_files list, type = nil
29
37
  nfiles = 0
30
38
  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'
39
+ list.each do |f|
40
+ ext = File.extname(f)
41
+ next if type and ext != type
42
+ puts "loading #{f}" if @opts.verbose > 0
54
43
  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
44
+ case ext
45
+ when '.yaml'
46
+ nent = Entity.load_instances f, @opts.verbose
47
+ puts "#{nent} instances loaded" if @opts.verbose > 0
48
+ when '.ditto'
49
+ Ditto::Entity.load_from_file f
50
+ when '.dm'
51
+ begin
52
+ load File.absolute_path(f)
53
+ rescue ScriptError, StandardError => le
54
+ loc = le.backtrace[2].split(':')
55
+ puts "Error: #{le.to_s} (line #{loc[1]} in #{loc[0]})"
68
56
  end
69
- Ditto::Entity.load_instance e[0].to_sym, e[1]
70
- nent += 1
71
57
  end
72
58
  end
73
- puts "#{nfiles} data files, #{nent} instances loaded, #{nerr} errors" if (nfiles > 0 and @opts.verbose > 0)
74
- return nerr == 0
59
+ puts "#{nfiles} files loaded" if @opts.verbose > 0
75
60
  end
76
61
 
77
62
  # Add the data to the database
78
63
  #
79
64
  def store_data
80
- return true if Ditto::Entity.instance_count == 0
81
- return true if Ditto::Entity.entity_count == 0
82
-
83
65
  puts "Running against: #{@opts.connstring}" if @opts.verbose > 0
84
66
  DataMapper::Logger.new(STDOUT, (@opts.verbose > 1) ? :debug : :info)
85
67
  DataMapper.setup(:default, @opts.connstring)
@@ -90,13 +72,12 @@ module Ditto
90
72
  else
91
73
  DataMapper.auto_upgrade!
92
74
  end
93
- return Ditto::Entity.store_all
75
+ return Ditto::Entity.store_all @opts.verbose
94
76
  rescue StandardError => e
95
77
  STDERR.puts "\nERROR: #{e.to_s}"
96
78
  STDERR.puts e.backtrace if @opts.debug
97
79
  return false
98
80
  end
99
81
  end
100
-
101
82
  end
102
83
  end
@@ -1,3 +1,3 @@
1
1
  module Ditto
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.0'
3
3
  end
@@ -2,67 +2,91 @@ require_relative '../lib/ditto/entity'
2
2
 
3
3
  module Ditto
4
4
  describe Entity do
5
+ context "When loading a ditto file" do
6
+ before(:each) do
7
+ Ditto::Entity.reset
8
+ end
9
+ it "should parse correct format" do
10
+ Ditto::Entity.load_from_file("spec/fixtures/good.ditto").should be_true
11
+ end
12
+ it "should parse correct multiple version format" do
13
+ Ditto::Entity.load_from_file("spec/fixtures/multiversion.ditto").should be_true
14
+ end
15
+ it "should not load duplicate multiple version format" do
16
+ expect {
17
+ Ditto::Entity.load_from_file("spec/fixtures/dupversion.ditto").should be_true
18
+ }.to raise_error Ditto::Error, /previously defined/
19
+ end
20
+ it "should not load the same one twice" do
21
+ expect {
22
+ Ditto::Entity.load_from_file("spec/fixtures/good.ditto").should be_true
23
+ Ditto::Entity.load_from_file("spec/fixtures/good.ditto").should be_true
24
+ }.to raise_error Ditto::Error, /previously defined/
25
+ end
26
+ it "should parse a syntax error" do
27
+ expect {
28
+ Ditto::Entity.load_from_file("spec/fixtures/syntax.ditto")
29
+ }.to raise_error Ditto::Error, /SyntaxError/
30
+ end
31
+ it "should identify a comma error" do
32
+ expect {
33
+ Ditto::Entity.load_from_file("spec/fixtures/nocomma.ditto")
34
+ }.to raise_error Ditto::Error, /missing add/
35
+ end
36
+ it "should identify an invalid mapping" do
37
+ expect {
38
+ Ditto::Entity.load_from_file("spec/fixtures/badmap.ditto")
39
+ }.to raise_error Ditto::Error, /missing block/
40
+ end
41
+ it "should parse a method typo" do
42
+ expect {
43
+ Ditto::Entity.load_from_file("spec/fixtures/nomethod.ditto")
44
+ }.to raise_error Ditto::Error, /NameError/
45
+ end
46
+ it "should identify a bad version" do
47
+ expect {
48
+ Ditto::Entity.load_from_file("spec/fixtures/badver.ditto")
49
+ }.to raise_error Ditto::Error, /Malformed version/
50
+ end
51
+ end
5
52
  context "Checking dependencies" do
6
53
  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
54
+ Ditto::Entity.reset
55
+ Ditto::Entity.load_from_file("spec/fixtures/currency.ditto")
56
+ Ditto::Entity.load_from_file("spec/fixtures/currency_group.ditto")
57
+ Ditto::Entity.load_from_file("spec/fixtures/country.ditto")
58
+ Ditto::Entity.load_from_file("spec/fixtures/continent.ditto")
59
+ Ditto::Entity.load_from_file("spec/fixtures/random.ditto")
60
+ Ditto::Entity.load_from_file("spec/fixtures/circle.ditto")
61
+ Ditto::Entity.load_from_file("spec/fixtures/bigcircle.ditto")
62
+ Ditto::Entity.load_from_file("spec/fixtures/dot.ditto")
63
+ end
64
+ it "should return empty dependency" do
65
+ Ditto::Entity.dep_list(:currency_group, [], []).should == [:currency_group]
66
+ end
67
+ it "should return simple dependency" do
68
+ Ditto::Entity.dep_list(:currency, [], []).should == [:currency_group, :currency]
69
+ end
70
+ it "should return multilevel dependency" do
71
+ Ditto::Entity.dep_list(:country, [], []).should == [:currency_group, :currency, :country]
72
+ end
73
+ it "should return square dependency" do
74
+ Ditto::Entity.dep_list(:continent, [], []).should == [:currency_group, :currency, :country, :continent]
75
+ end
76
+ it "should detect missing dependency" do
77
+ expect {
78
+ Ditto::Entity.dep_list(:random, [], [])
79
+ }.to raise_error "missing dependency: stealth"
80
+ end
81
+ it "should detect circular dependency" do
82
+ expect {
83
+ Ditto::Entity.dep_list(:circle, [], [])
84
+ }.to raise_error "circular dependency: circle"
85
+ end
86
+ it "should detect bigcircular dependency" do
87
+ expect {
88
+ Ditto::Entity.dep_list(:bigcircle, [], [])
89
+ }.to raise_error "circular dependency: bigcircle"
66
90
  end
67
91
  end
68
92
  end
@@ -0,0 +1,18 @@
1
+ # Ditto entity definitions and mappings for currency_group
2
+ # Use do/end instead of {} and watch the mapping disappear!
3
+ #
4
+
5
+ entity :currency_group, '1.0.0', {
6
+ :code => [ :mandatory, :unique ],
7
+ :description => nil,
8
+ :is_commodity => nil
9
+ },
10
+ add(CurrencyGroup: '=1.0.0') do |ditto|
11
+ CurrencyGroup.create(
12
+ :code => ditto.code,
13
+ :description => ditto.description,
14
+ :is_commodity => ditto.is_commodity
15
+ )
16
+ end
17
+
18
+ # vim: set ft=ruby:
@@ -0,0 +1,17 @@
1
+ # Bad version number
2
+ #
3
+
4
+ entity :currency_group, '1.0.a.', {
5
+ :code => [ :mandatory, :unique ],
6
+ :description => nil,
7
+ :is_commodity => nil
8
+ },
9
+ add(CurrencyGroup: '=1.0.0') { |ditto|
10
+ CurrencyGroup.create(
11
+ :code => ditto.code,
12
+ :description => ditto.description,
13
+ :is_commodity => ditto.is_commodity
14
+ )
15
+ }
16
+
17
+ # vim: set ft=ruby:
@@ -0,0 +1,17 @@
1
+ # Ditto entity definitions and mappings for bigcircle
2
+ # A simple circular dependency with dot
3
+ #
4
+
5
+ entity :bigcircle, '1.0.0', {
6
+ code: [ :mandatory, :unique ],
7
+ description: nil,
8
+ dot: :related
9
+ },
10
+ add(Bigcircle: '~>1.0') { |ditto|
11
+ Bigcircle.create(
12
+ :code => ditto.code,
13
+ :description => ditto.description,
14
+ :dot => ditto.dot
15
+ )
16
+ }
17
+ # vim: set ft=ruby:
@@ -0,0 +1,16 @@
1
+ # Ditto entity definitions and mappings for circular reference
2
+ #
3
+
4
+ entity :circle, '1.0.0', {
5
+ code: [ :mandatory, :unique ],
6
+ description: nil,
7
+ circle: :related
8
+ },
9
+ add(Circle: '~>1.0') { |ditto|
10
+ Circle.create(
11
+ :code => ditto.code,
12
+ :description => ditto.description,
13
+ :circle => ditto.circle
14
+ )
15
+ }
16
+ # vim: set ft=ruby:
@@ -0,0 +1,16 @@
1
+ # Ditto entity definitions and mappings for continent
2
+ #
3
+
4
+ entity :continent, '1.0.0', {
5
+ code: [ :mandatory, :unique ],
6
+ description: nil,
7
+ country: :related
8
+ },
9
+ add(Continent: '~>1.0') { |ditto|
10
+ Continent.create(
11
+ :code => ditto.code,
12
+ :description => ditto.description,
13
+ :country => ditto.country
14
+ )
15
+ }
16
+ # vim: set ft=ruby:
@@ -0,0 +1,16 @@
1
+ # Ditto entity definitions and mappings for country
2
+ #
3
+
4
+ entity :country, '1.0.0', {
5
+ code: [ :mandatory, :unique ],
6
+ description: nil,
7
+ currency: :related
8
+ },
9
+ add(Country: '~>1.0') { |ditto|
10
+ Country.create(
11
+ :code => ditto.code,
12
+ :description => ditto.description,
13
+ :currency => ditto.currency
14
+ )
15
+ }
16
+ # vim: set ft=ruby:
@@ -0,0 +1,30 @@
1
+ # Ditto entity definitions and mappings for currency
2
+ #
3
+
4
+ entity :currency, '1.0.0', {
5
+ code: [ :mandatory, :unique ],
6
+ description: nil,
7
+ exchange_rate: :mandatory,
8
+ is_designated: nil,
9
+ currency_group: :related
10
+ },
11
+ add(Currency: '~>1.0', CurrencyGroup: '~>1.0') { |ditto|
12
+ cg = CurrencyGroup.get(ditto.currency_group)
13
+ cg.currencies.create(
14
+ :code => ditto.code,
15
+ :description => ditto.description,
16
+ :exchange_rate => ditto.exchange_rate,
17
+ :is_designated => ditto.is_designated
18
+ )
19
+ }
20
+ add(Currency: '~>2.0', CurrencyGroup: '~>2.0') { |ditto|
21
+ cg = CurrencyGroup.get(ditto.currency_group)
22
+ cg.currencies.create(
23
+ :code => ditto.code,
24
+ :description => ditto.description,
25
+ :index => 'ghost',
26
+ :exchange_rate => ditto.exchange_rate,
27
+ :is_designated => ditto.is_designated
28
+ )
29
+ }
30
+ # vim: set ft=ruby:
@@ -0,0 +1,17 @@
1
+ # Ditto entity definitions and mappings for currency_group
2
+ #
3
+
4
+ entity :currency_group, '1.0.0', {
5
+ :code => [ :mandatory, :unique ],
6
+ :description => nil,
7
+ :is_commodity => nil
8
+ },
9
+ add(CurrencyGroup: '=1.0.0') { |ditto|
10
+ CurrencyGroup.create(
11
+ :code => ditto.code,
12
+ :description => ditto.description,
13
+ :is_commodity => ditto.is_commodity
14
+ )
15
+ }
16
+
17
+ # vim: set ft=ruby: