Class: Ditto::Entity

Inherits:
Object
  • Object
show all
Defined in:
lib/ditto/entity.rb

Constant Summary collapse

PROPS =
[ :mandatory, :unique, :related ].freeze
@@entities =

Entities are stored in a hash keyed by entity name The contents are an array of Entity objects one for each version

Hash.new {|h,k| h[k] = []}
@@instances =

Instances are stored in a hash keyed by entity name The contents are a hash (keyed by version) of arrays of instance hashes

Hash.new {|h,k| h[k] = Hash.new{|i,j|i[j] = []}}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, version, fields, methods) ⇒ Entity

Maps to the ‘entity’ DSL keyword Stack depth is just empirically determined! TODO WRITE A TEST!

Raises:



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/ditto/entity.rb', line 26

def initialize name, version, fields, methods
  src = Thread.current.backtrace[3].split(':')[0..1]

  @name = name.to_sym
  @version = Gem::Version.new(version) rescue raise(Error.new(src), "#{name}: #{$!.message}")
  @methods = methods
  @loc = src
  @deps = []

  if @@entities.has_key?(@name) and @@entities[@name].any?{|e| e.version == @version}
	loc = @@entities[@name].select{|e| e.version == @version}.first.loc
	raise Error.new(src), "#{name}: previously defined at #{loc.join(':')}"
  end
  raise Error.new(src), "#{name}: entity fields must be a hash!" unless fields.kind_of? Hash

  @fields = Ditto.symbolize_keys fields

  nkey = 0
  @fields.each do |field, props|
	symprops = Array(props).select{|p| p.is_a? Symbol}
	duffprops = symprops - PROPS
	unless duffprops.empty?
	  raise Error.new(src), "#{name}: unknown properties: '#{duffprops.join(' ,')}'"
	end
	nkey += symprops.count(:unique)
	@deps << field if symprops.include? :related
  end

  @@entities[@name] << self
end

Instance Attribute Details

#depsObject (readonly)

Returns the value of attribute deps.



21
22
23
# File 'lib/ditto/entity.rb', line 21

def deps
  @deps
end

#fieldsObject (readonly)

Returns the value of attribute fields.



21
22
23
# File 'lib/ditto/entity.rb', line 21

def fields
  @fields
end

#locObject (readonly)

Returns the value of attribute loc.



21
22
23
# File 'lib/ditto/entity.rb', line 21

def loc
  @loc
end

#methodsObject (readonly)

Returns the value of attribute methods.



21
22
23
# File 'lib/ditto/entity.rb', line 21

def methods
  @methods
end

#nameObject (readonly)

Returns the value of attribute name.



21
22
23
# File 'lib/ditto/entity.rb', line 21

def name
  @name
end

#versionObject (readonly)

Returns the value of attribute version.



21
22
23
# File 'lib/ditto/entity.rb', line 21

def version
  @version
end

Class Method Details

.check_dm_versions(requirements) ⇒ Object



155
156
157
158
159
160
# File 'lib/ditto/entity.rb', line 155

def self.check_dm_versions requirements
  requirements.all? do |klass, req|
	loadedver = Gem::Version.new(eval "#{klass}::VERSION")
	req.satisfied_by?(loadedver)
  end
end

.dep_list(name, list, done) ⇒ Object

Compute dependencies recursively build a list in dependency sequence



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/ditto/entity.rb', line 165

def self.dep_list(name, list, done)
  raise "circular dependency: #{name}" if done.include? name
  raise "missing dependency: #{name}" unless @@entities.has_key? name
  deps = @@entities[name].flat_map {|e| e.deps}.uniq
  done.push name
  deps.each do |depname|
	self.dep_list depname, list, done
  end
  done.pop
  list << name unless list.include? name
end

.find_entity_map(name, dataversion, type) ⇒ Object

Given the input data version get the right entity We assume all entity versions with same major work and take the highest

Raises:



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/ditto/entity.rb', line 138

def self.find_entity_map name, dataversion, type
  list = @@entities[name].sort{|a,b| b.version <=> a.version}
  majorversion = dataversion.to_s.slice /^\d+\./
  entityreq = Gem::Requirement.new("~>#{majorversion}0")
  entity = list.find {|e| entityreq.satisfied_by? e.version }
  raise Error.new(),"#{name}: can't find entity matching dataversion #{dataversion.to_s}" unless entity

  # Search for the most recent map that matches the database
  # datamapper objects

  map = entity.methods.reverse.find do |m|
	m[0] == type and self.check_dm_versions(m[1])
  end
  raise Error.new(),"#{name}: can't find #{type} map that matches current database" unless map
  return map
end

.load_entities(filepath, verbose = 0) ⇒ Object

Load entities required by the instances



103
104
105
106
107
108
109
110
111
112
# File 'lib/ditto/entity.rb', line 103

def self.load_entities filepath, verbose = 0
  puts "loading entities..." if verbose > 0
  @@instances.each_key do |name|
	file = File.expand_path("#{name}.ditto", filepath)
	puts "loading #{file}" if verbose > 1
	self.load_from_file File.expand_path("#{name}.ditto", filepath)
	puts "#{name}: #{@@entities[name].inspect}" if verbose > 2
  end
  puts "loaded #{@@entities.size} entities" if verbose > 0
end

.load_from_file(f) ⇒ Object

Errors discovered in here will be load/syntax errors Stack depth is just empirically determined! TODO WRITE A TEST!



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ditto/entity.rb', line 60

def self.load_from_file f
  begin
	load File.absolute_path(f)
	return true
  rescue Ditto::Error
	raise
  rescue SyntaxError => se # Includes the failing location
	raise Error.new(), "#{se.class}: #{se.to_s}"
  rescue StandardError => le
	loc = le.backtrace[0].split(':')
	raise Error.new(loc), "#{f}: #{le.class}: #{le.to_s}"
  end
end

.load_instances(f, verbose = 0) ⇒ Object

Load data from YAML files into the in-memory representation return number of instances loaded if all OK



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/ditto/entity.rb', line 77

def self.load_instances f, verbose = 0
  header = nil
  version = nil
  nent = 0
  YAML::load_documents(File.open(f)) do |doc|
	unless header
	  header = Ditto.symbolize_keys doc
	  version = Gem::Version.new(header[:version])
	  puts (verbose > 1) ? " (version: #{version.to_s})" : ""
	  puts "header #{header.to_s}" if verbose > 2
	  next
	end
	raise Error.new(), "instance has no version header" if version.nil?
	e = doc.flatten
	raise Error.new(), "instance malformed" if e.size != 2
	name = e[0].to_sym
	fields = Ditto.symbolize_keys e[1]
	puts "#{name}: #{fields.inspect}" if verbose > 2
	@@instances[name][version] << fields
	nent += 1
  end
  return nent
end

.resetObject



16
17
18
19
# File 'lib/ditto/entity.rb', line 16

def self.reset
  @@entities.clear
  @@instances.clear
end

.store_all(verbose = 0) ⇒ Object

Store the instances in dependency order



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/ditto/entity.rb', line 116

def self.store_all verbose = 0
  seq = []
  @@entities.each_key { |name| self.dep_list(name, seq, []) }
  puts "dependency sequence: #{seq.inspect}" if verbose > 2
  nsto = 0
  seq.each do |name|
	@@instances[name].each do |version, instances|
	  map = self.find_entity_map name, version, :add
	  instances.each do |instance|
 puts "store #{name}: #{instance.inspect}" if verbose > 2
 map[2].call OpenStruct.new(instance)
 nsto += 1
	  end
	end
  end
  puts "#{nsto} instances stored" if verbose > 0
end