activefacts-api 0.9.2 → 0.9.3

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/.travis.yml CHANGED
@@ -6,4 +6,4 @@ rvm:
6
6
  - jruby-18mode
7
7
  - jruby-19mode
8
8
  - rbx-18mode
9
- - rbx-19mode
9
+ - rbx-19mode
data/Gemfile CHANGED
@@ -5,6 +5,9 @@ gem 'rake', :group => [:development, :test]
5
5
  group :development do
6
6
  gem 'jeweler'
7
7
  gem 'rspec', '~>2.6.0'
8
+ gem 'ruby-debug', :platforms => [:mri_18]
9
+ gem 'debugger', :platforms => [:mri_19]
10
+ gem 'pry', :platforms => [:jruby, :rbx]
8
11
  end
9
12
 
10
13
  group :test do
data/Rakefile CHANGED
@@ -31,22 +31,41 @@ require 'rspec/core'
31
31
  require 'rspec/core/rake_task'
32
32
  require 'rdoc/task'
33
33
 
34
+ gem "rspec", :require => "spec/rake/spectask"
35
+
34
36
  task :default => :spec
35
37
 
36
38
  desc "Run Rspec tests"
37
- RSpec::Core::RakeTask.new(:spec)
39
+ RSpec::Core::RakeTask.new(:spec) do |t|
40
+ t.rspec_opts = %w{-f d}
41
+ end
42
+
43
+ namespace :spec do
44
+ namespace :rubies do
45
+ SUPPORTED_RUBIES = %w{ 1.8.7 1.9.2 1.9.3 jruby-1.7.0 rbx }
46
+
47
+ desc "Run Rspec tests on all supported rubies"
48
+ task :all_tasks => [:install_gems, :exec]
49
+
50
+ desc "Run `bundle install` on all rubies"
51
+ task :install_gems do
52
+ sh %{ rvm #{SUPPORTED_RUBIES.join(',')} exec bundle install }
53
+ end
54
+
55
+ desc "Run `bundle exec rake` on all rubies"
56
+ task :exec do
57
+ sh %{ rvm #{SUPPORTED_RUBIES.join(',')} exec bundle exec rake spec }
58
+ end
59
+ end
60
+ end
38
61
 
39
62
  desc "Run RSpec tests and produce coverage files (results viewable in coverage/index.html)"
40
63
  RSpec::Core::RakeTask.new(:coverage) do |spec|
41
64
  if RUBY_VERSION < '1.9'
42
- spec.rcov_opts = [
43
- '--exclude', 'spec',
44
- '--exclude', 'lib/activefacts/tracer.rb',
45
- '--exclude', 'gem/*'
46
- ]
65
+ spec.rcov_opts = %{ --exclude spec --exclude lib/activefacts/tracer.rb --exclude gem/* }
47
66
  spec.rcov = true
48
67
  else
49
- spec.rspec_opts = ['--require', 'simplecov_helper']
68
+ spec.rspec_opts = %w{ --require simplecov_helper }
50
69
  end
51
70
  end
52
71
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.2
1
+ 0.9.3
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "activefacts-api"
8
- s.version = "0.9.2"
8
+ s.version = "0.9.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Clifford Heath"]
12
- s.date = "2012-10-17"
12
+ s.date = "2012-11-01"
13
13
  s.description = "\nThe ActiveFacts API is a Ruby DSL for managing constellations of elementary facts.\nEach fact is either existential (a value or an entity), characteristic (boolean) or\nbinary relational (A rel B). Relational facts are consistently co-referenced, so you\ncan traverse them efficiently in any direction. Each constellation maintains constraints\nover the fact population.\n"
14
14
  s.email = "clifford.heath@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
31
31
  "lib/activefacts/api/constellation.rb",
32
32
  "lib/activefacts/api/entity.rb",
33
33
  "lib/activefacts/api/exceptions.rb",
34
+ "lib/activefacts/api/guid.rb",
34
35
  "lib/activefacts/api/instance.rb",
35
36
  "lib/activefacts/api/instance_index.rb",
36
37
  "lib/activefacts/api/numeric.rb",
@@ -54,6 +55,8 @@ Gem::Specification.new do |s|
54
55
  "spec/object_type/entity_type/entity_type_spec.rb",
55
56
  "spec/object_type/entity_type/multipart_identification_spec.rb",
56
57
  "spec/object_type/value_type/autocounter_spec.rb",
58
+ "spec/object_type/value_type/date_time_spec.rb",
59
+ "spec/object_type/value_type/guid_spec.rb",
57
60
  "spec/object_type/value_type/numeric_spec.rb",
58
61
  "spec/object_type/value_type/value_type_spec.rb",
59
62
  "spec/simplecov_helper.rb",
@@ -72,6 +75,9 @@ Gem::Specification.new do |s|
72
75
  s.add_development_dependency(%q<rake>, [">= 0"])
73
76
  s.add_development_dependency(%q<jeweler>, [">= 0"])
74
77
  s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
78
+ s.add_development_dependency(%q<ruby-debug>, [">= 0"])
79
+ s.add_development_dependency(%q<debugger>, [">= 0"])
80
+ s.add_development_dependency(%q<pry>, [">= 0"])
75
81
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
76
82
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
77
83
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -80,6 +86,9 @@ Gem::Specification.new do |s|
80
86
  s.add_dependency(%q<rake>, [">= 0"])
81
87
  s.add_dependency(%q<jeweler>, [">= 0"])
82
88
  s.add_dependency(%q<rspec>, ["~> 2.6.0"])
89
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
90
+ s.add_dependency(%q<debugger>, [">= 0"])
91
+ s.add_dependency(%q<pry>, [">= 0"])
83
92
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
84
93
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
85
94
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -89,6 +98,9 @@ Gem::Specification.new do |s|
89
98
  s.add_dependency(%q<rake>, [">= 0"])
90
99
  s.add_dependency(%q<jeweler>, [">= 0"])
91
100
  s.add_dependency(%q<rspec>, ["~> 2.6.0"])
101
+ s.add_dependency(%q<ruby-debug>, [">= 0"])
102
+ s.add_dependency(%q<debugger>, [">= 0"])
103
+ s.add_dependency(%q<pry>, [">= 0"])
92
104
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
93
105
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
94
106
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -43,7 +43,9 @@ module ActiveFacts
43
43
  def initialize(vocabulary)
44
44
  @vocabulary = vocabulary
45
45
  @instances = Hash.new do |h,k|
46
- raise "A constellation over #{@vocabulary.name} can only index instances of object_types in that vocabulary, not #{k.inspect}" unless k.is_a?(Class) and k.modspace == vocabulary
46
+ unless k.is_a?(Class) and k.modspace == vocabulary
47
+ raise "A constellation over #{@vocabulary.name} can only index instances of classes in that vocabulary, not #{k.inspect}"
48
+ end
47
49
  h[k] = InstanceIndex.new(self, k)
48
50
  end
49
51
  end
@@ -66,27 +68,6 @@ module ActiveFacts
66
68
  self
67
69
  end
68
70
 
69
- =begin
70
- def assert *args
71
- case
72
- when args.size >= 1
73
- args.each do |arg|
74
- assert arg
75
- end
76
- when args[0].is_a?(Hash)
77
- args[0].each do |key, value|
78
- klass_name = key.is_a?(Symbol) ? key.to_s.camelcase : key.to_s
79
- klass = vocabulary.const_get(klass_name)
80
- send(klass_name).assert(*Array(value))
81
- end
82
- else
83
- args.each do |arg|
84
- assert(arg)
85
- end
86
- end
87
- end
88
- =end
89
-
90
71
  # Constellations verbalise all members of all classes in alphabetical order, showing
91
72
  # non-identifying role values as well
92
73
  def verbalise
@@ -137,8 +118,8 @@ module ActiveFacts
137
118
  # With no parameters, return the collection of all instances of that object_type.
138
119
  # With parameters, assert an instance of the object_type identified by the values passed as args.
139
120
  def method_missing(m, *args, &b)
140
- if klass = @vocabulary.const_get(m) and klass.is_a?(Class) and klass.respond_to?(:assert_instance)
141
-
121
+ klass = @vocabulary.const_get(m)
122
+ if klass and klass.is_a?(Class) and klass.respond_to?(:assert_instance)
142
123
  (class << self; self; end).
143
124
  send(:define_method, sym = m.to_sym) do |*args|
144
125
  instance_index = @instances[klass]
@@ -58,10 +58,41 @@ module ActiveFacts
58
58
  # Assign the identifying roles in order. Any other roles will be assigned by our caller
59
59
  klass.identifying_role_names.zip(args).each do |role_name, value|
60
60
  role = self.class.roles(role_name)
61
- send(role.setter, value)
61
+ begin
62
+ send(role.setter, value)
63
+ rescue NoMethodError => e
64
+ raise settable_roles_exception(e, role_name)
65
+ end
62
66
  end
63
67
  end
64
68
 
69
+ def settable_roles_exception e, role_name
70
+ n = e.class.new(
71
+ "#{self.class} has no setter for #{role_name}.\n" +
72
+ "Settable roles are #{settable_roles*', '}.\n" +
73
+ (if self.class.vocabulary.delayed.empty?
74
+ ''
75
+ else
76
+ "This could be because the following expected object types are still not defined: #{self.class.vocabulary.delayed.keys.sort*', '}\n"
77
+ end
78
+ )
79
+ )
80
+ n.set_backtrace(e.backtrace)
81
+ n
82
+ end
83
+
84
+ def settable_roles
85
+ ([self.class]+self.class.supertypes_transitive).
86
+ map do |k|
87
+ k.roles.
88
+ map do |name, role|
89
+ role.unique ? name : nil
90
+ end.
91
+ compact
92
+ end.
93
+ flatten
94
+ end
95
+
65
96
  def inspect #:nodoc:
66
97
  inc = constellation ? " in #{constellation.inspect}" : ""
67
98
  # REVISIT: Where there are one-to-one roles, this cycles
@@ -196,7 +227,7 @@ module ActiveFacts
196
227
  end
197
228
  if arg == nil # But not false
198
229
  if role.mandatory
199
- raise "You must provide a #{role.counterpart.object_type.name} to identify a #{basename}"
230
+ raise MissingMandatoryRoleValueException.new(self, role)
200
231
  end
201
232
  else
202
233
  role.counterpart_object_type.identifying_role_values(*arg)
@@ -246,13 +277,17 @@ module ActiveFacts
246
277
  elsif !arg
247
278
  value = role_key = nil
248
279
  else
280
+ =begin
281
+ # REVISIT; These next few lines are bogus; they generate TypeError exceptions which are caught and ignored
282
+ # A new approach will follow shortly which won't @create_instances that won't be needed
249
283
  if role.counterpart.object_type.is_entity_type
250
284
  add = !constellation.send(role.counterpart.object_type.basename.to_sym).include?([arg])
251
285
  else
252
286
  add = !constellation.send(role.counterpart.object_type.basename.to_sym).include?(arg)
253
287
  end
288
+ =end
254
289
  value, role_key = role.counterpart.object_type.assert_instance(constellation, Array(arg))
255
- @created_instances << [role.counterpart, value] if add
290
+ # @created_instances << [role.counterpart, value] if add
256
291
  end
257
292
  key << role_key
258
293
  value
@@ -6,7 +6,34 @@
6
6
 
7
7
  module ActiveFacts
8
8
  module API
9
- class DuplicateIdentifyingValueException < StandardError
9
+ class ActiveFactsException < StandardError
10
+ end
11
+
12
+ class ActiveFactsSchemaException < ActiveFactsException
13
+ end
14
+
15
+ class ActiveFactsRuntimeException < ActiveFactsException
16
+ end
17
+
18
+ class CrossVocabularyRoleException < ActiveFactsSchemaException
19
+ def initialize klass, vocabulary
20
+ super "#{klass} must be an object type in #{vocabulary.name}"
21
+ end
22
+ end
23
+
24
+ class RoleNotDefinedException < ActiveFactsRuntimeException
25
+ def initialize klass, role_name
26
+ super "Role #{klass.basename}.#{role_name} is not defined"
27
+ end
28
+ end
29
+
30
+ class MissingMandatoryRoleValueException < ActiveFactsRuntimeException
31
+ def initialize klass, role
32
+ super "A #{role.counterpart.object_type.basename} is required to satisfy the #{role.name.inspect} role of #{klass.basename}"
33
+ end
34
+ end
35
+
36
+ class DuplicateIdentifyingValueException < ActiveFactsRuntimeException
10
37
  def initialize(desc)
11
38
  super("Illegal attempt to assert #{desc[:class].basename} having identifying value" +
12
39
  " (#{desc[:role].name} is #{desc[:value].verbalise})," +
@@ -0,0 +1,57 @@
1
+ require 'delegate'
2
+ require 'date'
3
+ require 'securerandom'
4
+
5
+ unless defined? SecureRandom.uuid
6
+ # I think this only applies to 1.8.6 (and JRuby/Rubinius in 1.8 mode) now:
7
+ def SecureRandom.uuid
8
+ hex(16).sub(/(........)(....)(....)(....)(............)/,'\1-\2-\3-\4-\5')
9
+ end
10
+ end
11
+
12
+ # The Guid class is what it says on the packet, but you can assert a :new one.
13
+ class Guid
14
+ def initialize(i = :new)
15
+ if i == :new
16
+ @value = SecureRandom.uuid.freeze
17
+ elsif (v = i.to_s).length == 36 and !(v !~ /[^0-9a-f]/i)
18
+ @value = v.clone.freeze
19
+ else
20
+ raise "Illegal non-Guid value #{i.inspect} given for Guid"
21
+ end
22
+ end
23
+
24
+ def to_s
25
+ @value
26
+ end
27
+
28
+ # if the value is unassigned, it equal?(:new).
29
+ def equal? value
30
+ @value == value
31
+ end
32
+
33
+ def inspect
34
+ "\#<Guid #{@value}>"
35
+ end
36
+
37
+ def hash #:nodoc:
38
+ @value.hash
39
+ end
40
+
41
+ def eql?(o) #:nodoc:
42
+ to_s.eql?(o.to_s)
43
+ end
44
+
45
+ # def self.inherited(other) #:nodoc:
46
+ # def other.identifying_role_values(*args)
47
+ # return nil if args == [:new] # A new object has no identifying_role_values
48
+ # if args.size == 1
49
+ # return args[0] if args[0].is_a?(AutoCounter)
50
+ # return args[0].send(self.basename.snakecase.to_sym) if args[0].respond_to?(self.basename.snakecase.to_sym)
51
+ # end
52
+ # return new(*args)
53
+ # end
54
+ # super
55
+ # end
56
+
57
+ end
@@ -44,7 +44,17 @@ module ActiveFacts
44
44
  if args.size == 1 && args[0].is_a?(@klass)
45
45
  key = args[0].identifying_role_values
46
46
  else
47
- key = @klass.identifying_role_values(*args) rescue nil
47
+ begin
48
+ key = @klass.identifying_role_values(*args)
49
+ rescue TypeError => e
50
+ # This happens (and should not) during assert_instance when checking
51
+ # for new asserts of identifying values that might get rolled back
52
+ # when the assert fails (for example because of an implied subtyping change)
53
+ key = nil
54
+ rescue ActiveFactsRuntimeException => e
55
+ # This is currently only known to happen during a retract()
56
+ key = nil
57
+ end
48
58
  end
49
59
 
50
60
  @hash[key]
@@ -100,6 +100,7 @@ class ::Date
100
100
  elsif (a.size == 1 && a[0].is_a?(String))
101
101
  parse(a[0])
102
102
  else
103
+ a = [] if a == [nil]
103
104
  civil(*a, &b)
104
105
  end
105
106
  end
@@ -29,13 +29,17 @@ module ActiveFacts
29
29
  unless role = @roles[role_name.to_sym]
30
30
  role = nil
31
31
  supertypes.each do |supertype|
32
- r = supertype.roles(role_name) rescue nil
33
- next unless r
34
- role = r
32
+ begin
33
+ role = supertype.roles(role_name)
34
+ rescue RoleNotDefinedException
35
+ next
36
+ end
35
37
  break
36
38
  end
37
39
  end
38
- raise "Role #{basename}.#{role_name} is not defined" unless role
40
+ unless role
41
+ raise RoleNotDefinedException.new(self, role_name)
42
+ end
39
43
  role
40
44
  else
41
45
  nil
@@ -132,7 +136,7 @@ module ActiveFacts
132
136
  # REVISIT: Need to check all superclass roles recursively, unless we hit a common supertype
133
137
  realise_supertypes(object_type, all_supertypes)
134
138
  end
135
- [(superclass.vocabulary && superclass rescue nil), *@supertypes].compact
139
+ [(superclass.respond_to?(:vocabulary) ? superclass : nil), *@supertypes].compact
136
140
  end
137
141
  end
138
142
 
@@ -140,12 +144,12 @@ module ActiveFacts
140
144
  def supertypes_transitive
141
145
  class_eval do
142
146
  supertypes = []
143
- supertypes << superclass if Module === (superclass.vocabulary rescue nil)
147
+ v = superclass.respond_to?(:vocabulary) ? superclass.vocabulary : nil
148
+ supertypes << superclass if v.kind_of?(Module)
144
149
  supertypes += (@supertypes ||= [])
145
150
  sts = supertypes.inject([]) do |a, t|
146
151
  next if a.include?(t)
147
- a += [t]
148
- a += t.supertypes_transitive rescue []
152
+ a += [t] + t.supertypes_transitive
149
153
  end.uniq
150
154
  sts # The local variable unconfuses rcov
151
155
  end
@@ -236,7 +240,7 @@ module ActiveFacts
236
240
  class_eval do
237
241
  define_method role.getter do |*a|
238
242
  raise "Parameters passed to #{self.class.name}\##{role.name}" if a.size > 0
239
- instance_variable_get(role.variable) rescue nil
243
+ instance_variable_get(role.variable)
240
244
  end
241
245
  end
242
246
  end
@@ -247,7 +251,7 @@ module ActiveFacts
247
251
  class_eval do
248
252
  define_method role.setter do |value|
249
253
 
250
- old = instance_variable_get(role.variable) rescue nil
254
+ old = instance_variable_get(role.variable)
251
255
  return true if old.equal?(value) # Occurs when another instance having the same value is assigned
252
256
 
253
257
  value = role.adapt(@constellation, value) if value
@@ -288,7 +292,7 @@ module ActiveFacts
288
292
  role_var = role.variable
289
293
 
290
294
  # Get old value, and jump out early if it's unchanged:
291
- old = instance_variable_get(role_var) rescue nil
295
+ old = instance_variable_get(role_var)
292
296
  return value if old.equal?(value) # Occurs during one_to_one assignment, for example
293
297
 
294
298
  value = role.adapt(constellation, value) if value
@@ -324,10 +328,9 @@ module ActiveFacts
324
328
  def define_many_to_one_accessor(role)
325
329
  class_eval do
326
330
  define_method role.getter do
327
- unless (r = instance_variable_get(role_var = role.variable) rescue nil)
328
- r = instance_variable_set(role_var, RoleValues.new)
329
- end
330
- r
331
+ role_var = role.variable
332
+ instance_variable_get(role_var) or
333
+ instance_variable_set(role_var, RoleValues.new)
331
334
  end
332
335
  end
333
336
  end
@@ -388,10 +391,12 @@ module ActiveFacts
388
391
  end
389
392
 
390
393
  # resolve the Symbol to a Class now if possible:
391
- resolved = vocabulary.object_type(related) rescue nil
394
+ resolved = vocabulary.object_type(related)
392
395
  related = resolved if resolved
393
396
  if related.is_a?(Class)
394
- raise "#{related} must be an object type in #{vocabulary.name}" unless related.respond_to?(:vocabulary) and related.vocabulary == self.vocabulary
397
+ unless related.respond_to?(:vocabulary) and related.vocabulary == self.vocabulary
398
+ raise CrossVocabularyRoleException.new(related, vocabulary)
399
+ end
395
400
  end
396
401
 
397
402
  if options.delete(:mandatory) == true
@@ -101,7 +101,7 @@ module ActiveFacts
101
101
  role = self
102
102
  klass.class_eval do
103
103
  role_accessor_name = "#{role.name}_role"
104
- unless (method(role_accessor_name) rescue nil)
104
+ unless respond_to?(role_accessor_name)
105
105
  (class << self; self; end).
106
106
  send(:define_method, role_accessor_name) do
107
107
  role
@@ -9,6 +9,7 @@
9
9
  #
10
10
  require 'date'
11
11
  require 'activefacts/api/numeric'
12
+ require 'activefacts/api/guid'
12
13
 
13
14
  module ActiveFacts
14
15
  module API
@@ -23,7 +24,7 @@ module ActiveFacts
23
24
  end
24
25
  # Add the methods that convert our classes into ObjectType types:
25
26
 
26
- ValueClasses = [String, Date, DateTime, Time, Int, Real, AutoCounter, Decimal]
27
+ ValueClasses = [String, Date, DateTime, Time, Int, Real, AutoCounter, Decimal, Guid]
27
28
  ValueClasses.each{|c|
28
29
  c.send :extend, ActiveFacts::API::ValueClass
29
30
  }
@@ -9,7 +9,7 @@
9
9
  # We use this to define open-ended ranges.
10
10
  begin
11
11
  Object.const_get("Infinity")
12
- rescue
12
+ rescue NameError
13
13
  Infinity = 1.0/0.0
14
14
  end
15
15
 
@@ -21,7 +21,9 @@ module ActiveFacts
21
21
  return @object_type unless name
22
22
 
23
23
  if name.is_a? Class
24
- raise "#{name} must be an object type in #{self.name}" unless name.vocabulary == self
24
+ unless name.respond_to?(:vocabulary) and name.vocabulary == self
25
+ raise CrossVocabularyRoleException.new(name, self)
26
+ end
25
27
  return name
26
28
  end
27
29
 
@@ -31,7 +33,7 @@ module ActiveFacts
31
33
  c
32
34
  else
33
35
  begin
34
- const_get("#{self.name}::#{camel}")
36
+ const_get(camel)
35
37
  rescue NameError
36
38
  nil
37
39
  end
@@ -64,22 +66,26 @@ module ActiveFacts
64
66
  end
65
67
 
66
68
  def __delay(object_type_name, args, &block) #:nodoc:
67
- @delayed ||= Hash.new { |h,k| h[k] = [] }
68
- @delayed[object_type_name] << [args, block]
69
+ delayed[object_type_name] << [args, block]
69
70
  end
70
71
 
71
72
  # __bind raises an error if the named class doesn't exist yet.
72
73
  def __bind(object_type_name) #:nodoc:
73
74
  object_type = const_get(object_type_name)
74
- if (@delayed && @delayed.include?(object_type_name))
75
- d = @delayed[object_type_name]
75
+ if (delayed.include?(object_type_name))
76
+ d = delayed[object_type_name]
76
77
  d.each{|(a,b)|
77
78
  b.call(object_type, *a)
78
79
  }
79
- @delayed.delete(object_type_name)
80
+ delayed.delete(object_type_name)
80
81
  end
81
82
  end
82
83
 
84
+ def delayed
85
+ @delayed ||= Hash.new { |h,k| h[k] = [] }
86
+ @delayed
87
+ end
88
+
83
89
  end
84
90
  end
85
91
  end
@@ -26,14 +26,26 @@ module ActiveFacts
26
26
  }
27
27
  end
28
28
  if @keys[:debug]
29
- ['pry', 'debugger', 'ruby-debug'].each do |debugger|
29
+ errors = []
30
+ success = false
31
+ [ENV["DEBUG_PREFERENCE"]].compact+
32
+ [
33
+ 'pry',
34
+ 'debugger',
35
+ 'ruby-debug'
36
+ ].each do |debugger|
30
37
  begin
31
38
  require debugger
32
39
  puts "Loaded "+debugger
40
+ success = true
33
41
  break
34
- rescue LoadError
42
+ rescue LoadError => e
43
+ errors << e
35
44
  end
36
45
  end
46
+ unless success
47
+ puts "Can't load any debugger, failed on:\n#{errors.inspect}"
48
+ end
37
49
  ::Debugger.start rescue nil
38
50
  end
39
51
  end
@@ -8,7 +8,7 @@ describe "A Constellation instance" do
8
8
  Object.send :remove_const, :Mod if Object.const_defined?("Mod")
9
9
  module Mod
10
10
  @base_types = [
11
- Int, Real, AutoCounter, String, Date, DateTime
11
+ Int, Real, AutoCounter, String, Date, DateTime, Decimal, Guid
12
12
  ]
13
13
 
14
14
  # Create a value type and a subtype of that value type for each base type:
@@ -9,7 +9,7 @@ describe "An instance of every type of ObjectType" do
9
9
  module Mod
10
10
  # These are the base value types we're going to test:
11
11
  @base_types = [
12
- Int, Real, AutoCounter, String, Date, DateTime, Decimal
12
+ Int, Real, AutoCounter, String, Date, DateTime, Decimal, Guid
13
13
  ]
14
14
 
15
15
  # Construct the names of the roles they play:
@@ -74,7 +74,13 @@ describe "An instance of every type of ObjectType" do
74
74
  one_to_one :test_by_#{base_type.name.snakecase}
75
75
  end
76
76
  END
77
- Mod.module_eval code
77
+
78
+ begin
79
+ Mod.module_eval code
80
+ rescue Exception => e
81
+ puts "Failure: #{e}"
82
+ puts "Failed on: <<END\n#{code}\nEND"
83
+ end
78
84
  end
79
85
  end
80
86
 
@@ -87,6 +93,7 @@ describe "An instance of every type of ObjectType" do
87
93
  @date = [2008, 04, 19]
88
94
  @date_time = [2008, 04, 19, 10, 28, 14]
89
95
  @decimal = BigDecimal.new('98765432109876543210')
96
+ @guid = '01234567-89ab-cdef-0123-456789abcdef'
90
97
 
91
98
  # Value Type instances
92
99
  @int_value = Mod::IntVal.new(1)
@@ -101,6 +108,7 @@ describe "An instance of every type of ObjectType" do
101
108
  @date_time_value = Mod::DateTimeVal.new d # 2008, 04, 20, 10, 28, 14
102
109
  # This next isn't in the same pattern; it makes a Decimal from a BigDecimal rather than a String (coverage reasons)
103
110
  @decimal_value = Mod::DecimalVal.new(BigDecimal.new('98765432109876543210'))
111
+ @guid_value = Mod::GuidVal.new(:new)
104
112
 
105
113
  # Value SubType instances
106
114
  @int_sub_value = Mod::IntSubVal.new(4)
@@ -112,6 +120,7 @@ describe "An instance of every type of ObjectType" do
112
120
  @date_time_sub_value = Mod::DateTimeSubVal.new(::DateTime.civil(2008, 04, 26, 10, 28, 14))
113
121
  # This next isn't in the same pattern; it makes a Decimal from a BigNum rather than a String (coverage reasons)
114
122
  @decimal_sub_value = Mod::DecimalSubVal.new(98765432109876543210)
123
+ @guid_sub_value = Mod::GuidSubVal.new(:new)
115
124
 
116
125
  # Entities identified by Value Type, SubType and Entity-by-value-type instances
117
126
  @test_by_int = Mod::TestByInt.new(2)
@@ -125,6 +134,7 @@ describe "An instance of every type of ObjectType" do
125
134
  @test_by_date_time = Mod::TestByDateTime.new([[2008,04,28,10,28,15]])
126
135
  #@test_by_date_time = Mod::TestByDateTime.new(DateTime.new(2008,04,28,10,28,15))
127
136
  @test_by_decimal = Mod::TestByDecimal.new('98765432109876543210')
137
+ @test_by_guid = Mod::TestByGuid.new('01234567-89ab-cdef-0123-456789abcdef')
128
138
 
129
139
  @test_by_int_sub = Mod::TestByIntSub.new(2)
130
140
  @test_by_real_sub = Mod::TestByRealSub.new(5.0)
@@ -134,6 +144,7 @@ describe "An instance of every type of ObjectType" do
134
144
  @test_by_date_sub = Mod::TestByDateSub.new(Date.new(2008,04,27))
135
145
  @test_by_date_time_sub = Mod::TestByDateTimeSub.new(2008,04,29,10,28,15)
136
146
  @test_by_decimal_sub = Mod::TestByDecimalSub.new('98765432109876543210')
147
+ @test_by_guid_sub = Mod::TestByGuidSub.new('01234567-89ab-cdef-0123-456789abcdef')
137
148
 
138
149
  @test_by_int_entity = Mod::TestByIntEntity.new(@test_by_int)
139
150
  @test_by_real_entity = Mod::TestByRealEntity.new(@test_by_real)
@@ -143,6 +154,7 @@ describe "An instance of every type of ObjectType" do
143
154
  @test_by_date_entity = Mod::TestByDateEntity.new(@test_by_date)
144
155
  @test_by_date_time_entity = Mod::TestByDateTimeEntity.new(@test_by_date_time)
145
156
  @test_by_decimal_entity = Mod::TestByDecimalEntity.new(@test_by_decimal)
157
+ @test_by_guid_entity = Mod::TestByGuidEntity.new(@test_by_guid)
146
158
 
147
159
  # Entity subtypes
148
160
  @test_sub_by_int = Mod::TestSubByInt.new(2)
@@ -153,45 +165,54 @@ describe "An instance of every type of ObjectType" do
153
165
  @test_sub_by_date = Mod::TestSubByDate.new(Date.new(2008,04,28))
154
166
  @test_sub_by_date_time = Mod::TestSubByDateTime.new(2008,04,28,10,28,15)
155
167
  @test_sub_by_decimal = Mod::TestSubByDecimal.new('98765432109876543210')
168
+ @test_sub_by_guid = Mod::TestSubByGuid.new('01234567-89ab-cdef-0123-456789abcdef')
156
169
 
157
170
  # These arrays get zipped together in various ways. Keep them aligned.
158
171
  @values = [
159
172
  @int, @real, @auto_counter, @new_auto_counter,
160
- @string, @date, @date_time, @decimal
173
+ @string, @date, @date_time, @decimal, @guid
161
174
  ]
162
175
  @classes = [
163
176
  Int, Real, AutoCounter, AutoCounter,
164
- String, Date, DateTime, Decimal
177
+ String, Date, DateTime, Decimal, Guid
165
178
  ]
166
179
  @value_types = [
167
180
  Mod::IntVal, Mod::RealVal, Mod::AutoCounterVal, Mod::AutoCounterVal,
168
181
  Mod::StringVal, Mod::DateVal, Mod::DateTimeVal, Mod::DecimalVal,
182
+ Mod::GuidVal,
169
183
  Mod::IntSubVal, Mod::RealSubVal, Mod::AutoCounterSubVal, Mod::AutoCounterSubVal,
170
184
  Mod::StringSubVal, Mod::DateSubVal, Mod::DateTimeSubVal, Mod::DecimalSubVal,
185
+ Mod::GuidSubVal,
171
186
  ]
172
187
  @value_instances = [
173
188
  @int_value, @real_value, @auto_counter_value, @new_auto_counter_value,
174
- @string_value, @date_value, @date_time_value, @decimal_value,
189
+ @string_value, @date_value, @date_time_value, @decimal_value, @guid_value,
175
190
  @int_sub_value, @real_sub_value, @auto_counter_sub_value, @auto_counter_sub_value_new,
176
- @string_sub_value, @date_sub_value, @date_time_sub_value, @decimal_sub_value,
191
+ @string_sub_value, @date_sub_value, @date_time_sub_value, @decimal_sub_value, @guid_sub_value,
177
192
  @int_value, @real_value, @auto_counter_value, @new_auto_counter_value,
178
- @string_value, @date_value, @date_time_value, @decimal_value,
193
+ @string_value, @date_value, @date_time_value, @decimal_value, @guid_value,
179
194
  ]
180
195
  @entity_types = [
181
196
  Mod::TestByInt, Mod::TestByReal, Mod::TestByAutoCounter, Mod::TestByAutoCounter,
182
197
  Mod::TestByString, Mod::TestByDate, Mod::TestByDateTime, Mod::TestByDecimal,
198
+ Mod::TestByGuid,
183
199
  Mod::TestByIntSub, Mod::TestByRealSub, Mod::TestByAutoCounterSub, Mod::TestByAutoCounterSub,
184
200
  Mod::TestByStringSub, Mod::TestByDateSub, Mod::TestByDateTimeSub, Mod::TestByDecimalSub,
201
+ Mod::TestByGuidSub,
185
202
  Mod::TestSubByInt, Mod::TestSubByReal, Mod::TestSubByAutoCounter, Mod::TestSubByAutoCounter,
186
203
  Mod::TestSubByString, Mod::TestSubByDate, Mod::TestSubByDateTime, Mod::TestByDecimalEntity,
204
+ Mod::TestByGuidEntity,
187
205
  ]
188
206
  @entities = [
189
207
  @test_by_int, @test_by_real, @test_by_auto_counter, @test_by_auto_counter_new,
190
208
  @test_by_string, @test_by_date, @test_by_date_time, @test_by_decimal,
209
+ @test_by_guid,
191
210
  @test_by_int_sub, @test_by_real_sub, @test_by_auto_counter_sub, @test_by_auto_counter_new_sub,
192
211
  @test_by_string_sub, @test_by_date_sub, @test_by_date_time_sub, @test_by_decimal_sub,
212
+ @test_by_guid_sub,
193
213
  @test_sub_by_int, @test_sub_by_real, @test_sub_by_auto_counter, @test_sub_by_auto_counter_new,
194
214
  @test_sub_by_string, @test_sub_by_date, @test_sub_by_date_time, @test_sub_by_decimal,
215
+ @test_sub_by_guid,
195
216
  ]
196
217
  @entities_by_entity = [
197
218
  @test_by_int_entity,
@@ -202,28 +223,35 @@ describe "An instance of every type of ObjectType" do
202
223
  @test_by_date_entity,
203
224
  @test_by_date_time_entity,
204
225
  @test_by_decimal_entity,
226
+ @test_by_guid_entity,
205
227
  ]
206
228
  @entities_by_entity_types = [
207
229
  Mod::TestByIntEntity, Mod::TestByRealEntity, Mod::TestByAutoCounterEntity, Mod::TestByAutoCounterEntity,
208
230
  Mod::TestByStringEntity, Mod::TestByDateEntity, Mod::TestByDateTimeEntity, Mod::TestByDecimalEntity,
231
+ Mod::TestByGuidEntity,
209
232
  ]
210
233
  @test_role_names = [
211
234
  :int_value, :real_value, :auto_counter_value, :auto_counter_value,
212
235
  :string_value, :date_value, :date_time_value, :decimal_value,
236
+ :guid_value,
213
237
  :int_sub_value, :real_sub_value, :auto_counter_sub_value, :auto_counter_sub_value,
214
238
  :string_sub_value, :date_sub_value, :date_time_sub_value, :decimal_sub_value,
239
+ :guid_sub_value,
215
240
  :int_value, :real_value, :auto_counter_value, :auto_counter_value,
216
241
  :string_value, :date_value, :date_time_value, :decimal_value,
242
+ :guid_value,
217
243
  ]
218
244
  @role_values = [
219
245
  3, 4.0, 5, 6,
220
246
  "three", Date.new(2008,4,21), DateTime.new(2008,4,22,10,28,16),
221
- '98765432109876543210'
247
+ '98765432109876543210',
248
+ '01234567-89ab-cdef-0123-456789abcdef'
222
249
  ]
223
250
  @role_alternate_values = [
224
251
  4, 5.0, 6, 7,
225
252
  "four", Date.new(2009,4,21), DateTime.new(2009,4,22,10,28,16),
226
- '98765432109876543211'
253
+ '98765432109876543211',
254
+ '01234567-89ab-cdef-0123-456789abcdef'
227
255
  ]
228
256
  @subtype_role_instances = [
229
257
  Mod::IntSubVal.new(6), Mod::RealSubVal.new(6.0),
@@ -231,6 +259,7 @@ describe "An instance of every type of ObjectType" do
231
259
  Mod::StringSubVal.new("seven"),
232
260
  Mod::DateSubVal.new(2008,4,29), Mod::DateTimeSubVal.new(2008,4,30,10,28,16),
233
261
  Mod::DecimalSubVal.new('98765432109876543210'),
262
+ Mod::DecimalSubVal.new('01234567-89ab-cdef-0123-456789abcdef'),
234
263
  ]
235
264
  end
236
265
 
@@ -3,9 +3,9 @@
3
3
  # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
4
  #
5
5
 
6
- VALUE_TYPES = Int, Real, AutoCounter, String, Date, DateTime, Decimal
7
- RAW_VALUES = [2, 3.0, 4, "5", Date.new(2008, 04, 20), DateTime.new(2008, 04, 20, 10, 28, 14)]
8
- ALT_VALUES = [3, 4.0, 5, "6", Date.new(2009, 04, 20), DateTime.new(2009, 04, 20, 10, 28, 14)]
6
+ VALUE_TYPES = Int, Real, AutoCounter, String, Date, DateTime, Decimal, Guid
7
+ RAW_VALUES = [2, 3.0, 4, "5", Date.new(2008, 04, 20), DateTime.new(2008, 04, 20, 10, 28, 14), '43210.98765', '01234567-89ab-cdef-0123-456789abcdef']
8
+ ALT_VALUES = [3, 4.0, 5, "6", Date.new(2009, 04, 20), DateTime.new(2009, 04, 20, 10, 28, 14), '56789.01234', 'fedcba987654-3210-fedc-ba98-76543210']
9
9
  VALUE_SUB_FOR_VALUE = {}
10
10
  VALUES_FOR_TYPE = VALUE_TYPES.zip(RAW_VALUES, ALT_VALUES).inject({}) do |h, (vt, v1, v2)|
11
11
  next h unless v1 and v2
@@ -186,7 +186,7 @@ describe "Object type role values" do
186
186
  it "should allow nullifying and reassigning a #{object_type_name} entity's identifying role value" do
187
187
  object = @constellation.send(object_type_name, *object_identifying_parameters(object_type_name, values[0]))
188
188
  object.class.identifying_roles.each do |identifying_role|
189
- next if identifying_role.name == :counter
189
+ next if [:counter, :id_guid_val].include?(identifying_role.name)
190
190
  assigned = object.send(:"#{identifying_role.name}=", nil)
191
191
  assigned.should be_nil
192
192
  object.send(:"#{identifying_role.name}=", values[1])
@@ -54,6 +54,48 @@ describe "Roles" do
54
54
  role.counterpart.object_type.should == Mod::Name
55
55
  end
56
56
 
57
+ it "should prevent association of a role name with an object_type from the wrong module" do
58
+ lambda {
59
+ module Mod2
60
+ class Unrelated
61
+ end
62
+ end
63
+
64
+ module Mod
65
+ class Existing1 < String
66
+ value_type
67
+ has_one :unrelated, :class => Mod2::Unrelated
68
+ end
69
+ end
70
+ }.should raise_error(ActiveFacts::API::CrossVocabularyRoleException)
71
+ end
72
+
73
+ it "should prevent association of a role name with a non-object_type" do
74
+ lambda {
75
+ module Mod
76
+ class NonObject
77
+ end
78
+ class Existing1 < String
79
+ value_type
80
+ has_one :non_object, :class => NonObject
81
+ end
82
+ end
83
+ }.should raise_error(ActiveFacts::API::CrossVocabularyRoleException)
84
+ end
85
+
86
+ it "should prevent association of a role name with an implied non-object_type" do
87
+ lambda {
88
+ module Mod
89
+ class NonObject
90
+ end
91
+ class Existing1 < String
92
+ value_type
93
+ has_one :non_object
94
+ end
95
+ end
96
+ }.should raise_error(ActiveFacts::API::CrossVocabularyRoleException)
97
+ end
98
+
57
99
  it "should provide value type metadata" do
58
100
  Mod::Name.length.should == 40
59
101
  Mod::Name.scale.should == 0
@@ -67,6 +67,7 @@ describe "identity" do
67
67
  @p2.should be_nil
68
68
  @c1.Name.values.should =~ [@juliar, @tony]
69
69
  @c1.Name.keys.should =~ [@juliar, @tony]
70
+ pending "This functionality was poorly implemented and has been temporarily switched off"
70
71
  @c1.TFN.keys.should =~ [123]
71
72
  @c1.Person.values.should =~ [@p1, @p3]
72
73
  @c1.Person.keys.should =~ [[@juliar],[@tony]]
@@ -0,0 +1,38 @@
1
+ #
2
+ # ActiveFacts tests: Value instances in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+ require 'activefacts/api'
6
+
7
+ describe Date do
8
+ it "should construct with no arguments" do
9
+ proc {
10
+ @d = Date.new()
11
+ }.should_not raise_error
12
+ @d.year.should == -4712
13
+ end
14
+
15
+ it "should construct with a nil argument" do
16
+ proc {
17
+ @d = Date.new(nil)
18
+ }.should_not raise_error
19
+ @d.year.should == -4712
20
+ end
21
+
22
+ it "should construct with a full arguments" do
23
+ proc {
24
+ @d = Date.civil(2012, 10, 31)
25
+ }.should_not raise_error
26
+ @d.to_s.should == "2012-10-31"
27
+ end
28
+
29
+ =begin
30
+ it "should be encodable in JSON" do
31
+ proc {
32
+ @d = Date.new(2012, 10, 31)
33
+ @d.to_json.should == "REVISIT"
34
+ }.should_not raise_error
35
+ end
36
+ =end
37
+
38
+ end
@@ -0,0 +1,71 @@
1
+ #
2
+ # ActiveFacts tests: Value instances in the Runtime API
3
+ # Copyright (c) 2008 Clifford Heath. Read the LICENSE file.
4
+ #
5
+
6
+ describe "Guid Value Type instances" do
7
+ before :each do
8
+ Object.send :remove_const, :Mod if Object.const_defined?("Mod")
9
+ module Mod
10
+ class ThingId < Guid
11
+ value_type
12
+ end
13
+ class Thing
14
+ identified_by :thing_id
15
+ one_to_one :thing_id
16
+ end
17
+ class Ordinal < Int
18
+ value_type
19
+ end
20
+ class ThingFacet
21
+ identified_by :thing, :ordinal
22
+ has_one :thing
23
+ has_one :ordinal
24
+ end
25
+ end
26
+ @constellation = ActiveFacts::API::Constellation.new(Mod)
27
+ @thing = Mod::Thing.new(:new)
28
+ @thing_id = Mod::ThingId.new
29
+ end
30
+
31
+ it "should respond to verbalise" do
32
+ @thing_id.respond_to?(:verbalise).should be_true
33
+ end
34
+
35
+ it "should verbalise correctly" do
36
+ @thing_id.verbalise.should =~ /ThingId '[-0-9a-f]{36}'/i
37
+ end
38
+
39
+ it "should respond to constellation" do
40
+ @thing_id.respond_to?(:constellation).should be_true
41
+ end
42
+
43
+ it "should respond to its roles" do
44
+ @thing_id.respond_to?(:thing).should be_true
45
+ end
46
+
47
+ it "should allow prevent invalid role assignment" do
48
+ lambda {
49
+ @thing.thing_id = "foo"
50
+ }.should raise_error
51
+ end
52
+
53
+ it "should allow an existing guid to be re-used" do
54
+ @new_thing = Mod::Thing.new(@thing_id)
55
+ @new_thing.thing_id.should == @thing_id
56
+ end
57
+
58
+ it "should return the ValueType in response to .class()" do
59
+ @thing_id.class.vocabulary.should == Mod
60
+ end
61
+
62
+ it "should allow an existing guid-identified object to be re-used" do
63
+ thing = @constellation.Thing(:new)
64
+ facets = []
65
+ facets << @constellation.ThingFacet(thing, 0)
66
+ facets << @constellation.ThingFacet(thing, 1)
67
+ facets[0].thing.should be_eql(facets[1].thing)
68
+ facets[0].thing.thing_id.should be_eql(facets[1].thing.thing_id)
69
+ end
70
+
71
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activefacts-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-17 00:00:00.000000000 Z
12
+ date: 2012-11-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -59,6 +59,54 @@ dependencies:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
61
  version: 2.6.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: ruby-debug
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: debugger
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: pry
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
62
110
  - !ruby/object:Gem::Dependency
63
111
  name: rspec
64
112
  requirement: !ruby/object:Gem::Requirement
@@ -159,6 +207,7 @@ files:
159
207
  - lib/activefacts/api/constellation.rb
160
208
  - lib/activefacts/api/entity.rb
161
209
  - lib/activefacts/api/exceptions.rb
210
+ - lib/activefacts/api/guid.rb
162
211
  - lib/activefacts/api/instance.rb
163
212
  - lib/activefacts/api/instance_index.rb
164
213
  - lib/activefacts/api/numeric.rb
@@ -182,6 +231,8 @@ files:
182
231
  - spec/object_type/entity_type/entity_type_spec.rb
183
232
  - spec/object_type/entity_type/multipart_identification_spec.rb
184
233
  - spec/object_type/value_type/autocounter_spec.rb
234
+ - spec/object_type/value_type/date_time_spec.rb
235
+ - spec/object_type/value_type/guid_spec.rb
185
236
  - spec/object_type/value_type/numeric_spec.rb
186
237
  - spec/object_type/value_type/value_type_spec.rb
187
238
  - spec/simplecov_helper.rb
@@ -202,7 +253,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
202
253
  version: '0'
203
254
  segments:
204
255
  - 0
205
- hash: -2189448494771312824
256
+ hash: 1877620921052571193
206
257
  required_rubygems_version: !ruby/object:Gem::Requirement
207
258
  none: false
208
259
  requirements: