ditto 0.5.0 → 0.6.0

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