lafcadio 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
data/bin/lafcadio_schema CHANGED
@@ -1,36 +1,38 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'ubygems'
4
- require 'lafcadio/depend'
5
- require 'lafcadio/schema'
6
- require 'lafcadio/util'
7
4
  require 'getoptlong'
5
+ require 'lafcadio'
8
6
  include Lafcadio
9
7
 
8
+ $all_domain_classes = []
9
+ module Lafcadio
10
+ def DomainObject.inherited( subclass )
11
+ $all_domain_classes << subclass
12
+ end
13
+ end
14
+
15
+ ( config, dclass_name ) = [ nil, nil ]
10
16
  opts = GetoptLong.new(
11
- [ '--config', '-c', GetoptLong::REQUIRED_ARGUMENT ],
17
+ [ '--config', '-c', GetoptLong::OPTIONAL_ARGUMENT ],
12
18
  [ '--class', '-C', GetoptLong::OPTIONAL_ARGUMENT ]
13
19
  )
14
- if ( ( optArray = opts.get ) && ARGV.size >= 1 )
15
- configFile = optArray[1]
16
- LafcadioConfig.set_filename( configFile )
17
- class_opts = opts.get
18
- ARGV.each { |fileName|
19
- require "#{ fileName }"
20
- if class_opts
21
- className = class_opts.last
22
- else
23
- fileName =~ /(\w*)\.rb/
24
- className = $1
25
- end
26
- domainClass = Class.by_name( className )
27
- if domainClass
28
- statement = CreateTableStatement.new( domainClass )
29
- puts statement.to_sql
30
- else
31
- puts "No domain class in #{ fileName }"
32
- end
33
- }
20
+ opts.each do |opt, arg|
21
+ config = arg if opt == '--config'
22
+ dclass_name = arg if opt == '--class'
23
+ end
24
+ if ARGV.size >= 1
25
+ LafcadioConfig.set_filename config if config
26
+ ARGV.each { |fileName| require fileName }
27
+ if dclass_name
28
+ domain_classes = [ Class.by_name( dclass_name ) ]
29
+ else
30
+ domain_classes = $all_domain_classes
31
+ end
32
+ sql = domain_classes.map { |domain_class|
33
+ CreateTableStatement.new( domain_class ).to_sql
34
+ }.join( "\n\n" )
35
+ puts sql
34
36
  else
35
- puts "lafcadio_schema -c config domain1.rb {, domain2.rb, domain3.rb ... }"
36
- end
37
+ puts "lafcadio_schema [-C domain_class] file1.rb [file2.rb file3.rb ...]"
38
+ end
data/lib/lafcadio.rb CHANGED
@@ -1,5 +1,5 @@
1
- # Lafcadio is an object-relational mapping library for Ruby and MySQL. Its
2
- # design has a few aspects in mind:
1
+ # Lafcadio is an object-relational mapping library for Ruby. It currently
2
+ # supports MySQL and PostgreSQL. Its design has a few aspects in mind:
3
3
  # * The importance of unit-testing. Lafcadio includes a MockObjectStore which
4
4
  # can take the place of the ObjectStore for unit tests, so you can test
5
5
  # complex database-driven logic. Committing domain objects, running queries,
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.9.2"
19
+ Version = "0.9.3"
20
20
 
21
21
  require 'lafcadio/depend'
22
22
  require 'lafcadio/domain'
data/lib/lafcadio.rb~ CHANGED
@@ -16,7 +16,7 @@
16
16
  # http://lafcadio.rubyforge.org/tutorial.html.
17
17
 
18
18
  module Lafcadio
19
- Version = "0.9.1"
19
+ Version = "0.9.2"
20
20
 
21
21
  require 'lafcadio/depend'
22
22
  require 'lafcadio/domain'
@@ -1,6 +1,7 @@
1
1
  require 'extensions/module'
2
2
  require 'lafcadio/objectField'
3
3
  require 'lafcadio/util'
4
+ require 'monitor'
4
5
  require 'rexml/document'
5
6
 
6
7
  module Lafcadio
@@ -42,15 +43,13 @@ module Lafcadio
42
43
 
43
44
  def possible_field_attributes
44
45
  fieldAttr = []
45
- fieldAttr << FieldAttribute.new( 'size', FieldAttribute::INTEGER )
46
- fieldAttr << FieldAttribute.new( 'unique', FieldAttribute::BOOLEAN )
47
- fieldAttr << FieldAttribute.new( 'not_nil', FieldAttribute::BOOLEAN )
48
- fieldAttr << FieldAttribute.new( 'enum_type', FieldAttribute::ENUM,
49
- BooleanField )
50
- fieldAttr << FieldAttribute.new( 'enums', FieldAttribute::HASH )
51
- fieldAttr << FieldAttribute.new( 'range', FieldAttribute::ENUM,
52
- DateField )
53
- fieldAttr << FieldAttribute.new( 'large', FieldAttribute::BOOLEAN )
46
+ fieldAttr << FieldAttribute.new( 'size', :integer )
47
+ fieldAttr << FieldAttribute.new( 'unique', :boolean )
48
+ fieldAttr << FieldAttribute.new( 'not_nil', :boolean )
49
+ fieldAttr << FieldAttribute.new( 'enum_type', :enum, BooleanField )
50
+ fieldAttr << FieldAttribute.new( 'enums', :hash )
51
+ fieldAttr << FieldAttribute.new( 'range', :enum, DateField )
52
+ fieldAttr << FieldAttribute.new( 'large', :boolean )
54
53
  end
55
54
 
56
55
  def register_name( name )
@@ -69,11 +68,6 @@ module Lafcadio
69
68
  end
70
69
 
71
70
  class FieldAttribute
72
- INTEGER = 1
73
- BOOLEAN = 2
74
- ENUM = 3
75
- HASH = 4
76
-
77
71
  attr_reader :name, :value_class
78
72
 
79
73
  def initialize( name, value_class, objectFieldClass = nil )
@@ -84,7 +78,7 @@ module Lafcadio
84
78
  def maybe_set_field_attr( field, fieldElt )
85
79
  setterMethod = "#{ name }="
86
80
  if field.respond_to?( setterMethod )
87
- if value_class != FieldAttribute::HASH
81
+ if value_class != :hash
88
82
  if ( attrStr = fieldElt.attributes[name] )
89
83
  field.send( setterMethod, value_from_string( attrStr ) )
90
84
  end
@@ -107,12 +101,12 @@ module Lafcadio
107
101
  end
108
102
 
109
103
  def value_from_string( valueStr )
110
- if @value_class == INTEGER
104
+ if @value_class == :integer
111
105
  valueStr.to_i
112
- elsif @value_class == BOOLEAN
106
+ elsif @value_class == :boolean
113
107
  valueStr == 'y'
114
- elsif @value_class == ENUM
115
- eval "#{ @objectFieldClass.name }::#{ valueStr }"
108
+ elsif @value_class == :enum
109
+ eval ":#{ valueStr }"
116
110
  end
117
111
  end
118
112
  end
@@ -165,11 +159,11 @@ module Lafcadio
165
159
  # email 'email'
166
160
  # date 'birthday'
167
161
  # end
168
- # The class methods you can use are +blob+, +boolean+, +date+,
162
+ # The class methods you can use are +binary+, +boolean+, +date+,
169
163
  # +date_time+, +domain_object+, +email+, +enum+, +float+, +integer+,
170
- # +month+, +state+, +string+, +text_list+. These correspond to BlobField,
171
- # BooleanField, DateField, DateTimeField, DomainObjectField, EmailField,
172
- # EnumField, FloatField, IntegerField, MonthField, StateField,
164
+ # +month+, +state+, +string+, +text_list+. These correspond to
165
+ # BinaryField, BooleanField, DateField, DateTimeField, DomainObjectField,
166
+ # EmailField, EnumField, FloatField, IntegerField, MonthField, StateField,
173
167
  # StringField, and TextListField, respectively. Consult their individual
174
168
  # RDoc entries for more on how to parameterize them through the class
175
169
  # methods.
@@ -284,10 +278,7 @@ module Lafcadio
284
278
  # different levels.
285
279
  class DomainObject
286
280
  @@subclass_records = Hash.new do |h, k| h[k] = SubclassRecord.new( k ); end
287
-
288
- COMMIT_ADD = 1
289
- COMMIT_EDIT = 2
290
- COMMIT_DELETE = 3
281
+ @@subclass_records.extend MonitorMixin
291
282
 
292
283
  include DomainComparable
293
284
 
@@ -307,7 +298,9 @@ module Lafcadio
307
298
  # Returns every committed instance of the domain class.
308
299
  #
309
300
  # User.all # => all users
310
- def self.all; ObjectStore.get_object_store.all( self ); end
301
+ def self.all( opts = {} )
302
+ ObjectStore.get_object_store.all( self, opts )
303
+ end
311
304
 
312
305
  def self.all_fields # :nodoc:
313
306
  self_and_concrete_superclasses.map { |a_class|
@@ -316,7 +309,7 @@ module Lafcadio
316
309
  end
317
310
 
318
311
  def self.class_field(fieldName) #:nodoc:
319
- self.class_fields.find { |field| field.name == fieldName }
312
+ self.class_fields.find { |field| field.name == fieldName.to_s }
320
313
  end
321
314
 
322
315
  def self.class_fields #:nodoc:
@@ -381,10 +374,7 @@ module Lafcadio
381
374
  #
382
375
  # class LotsOfBooleans < Lafcadio::DomainObject
383
376
  # default_field_setup_hash(
384
- # Lafcadio::BooleanField,
385
- # {
386
- # 'enum_type' => Lafcadio::BooleanField::ENUMS_CAPITAL_YES_NO
387
- # }
377
+ # Lafcadio::BooleanField, { 'enum_type' => :capital_yes_no }
388
378
  # )
389
379
  # booleans 'this', 'that', 'the_other', 'and_another_one',
390
380
  # 'this_one_too'
@@ -470,7 +460,7 @@ module Lafcadio
470
460
  if arg.is_a? Fixnum
471
461
  ObjectStore.get_object_store.get( self, *args )
472
462
  else
473
- qry = Query.new( self, nil, { :group_functions => [ :count ] } )
463
+ qry = Query.new( self, :group_functions => [ :count ] )
474
464
  ObjectStore.get_object_store.group_query qry
475
465
  end
476
466
  else
@@ -557,6 +547,10 @@ module Lafcadio
557
547
  #
558
548
  # the_one_user = User.only
559
549
  def self.only; all.only; end
550
+
551
+ def self.postgres_pk_id_seq
552
+ "#{ table_name }_#{ sql_primary_key_name }_seq"
553
+ end
560
554
 
561
555
  def self.require_domain_file( typeString ) # :nodoc:
562
556
  typeString =~ /([^\:]*)$/
@@ -611,7 +605,7 @@ module Lafcadio
611
605
  end
612
606
 
613
607
  def self.subclass_record # :nodoc:
614
- @@subclass_records[self]
608
+ @@subclass_records.synchronize { @@subclass_records[self] }
615
609
  end
616
610
 
617
611
  def self.subclasses #:nodoc:
@@ -42,15 +42,13 @@ module Lafcadio
42
42
 
43
43
  def possible_field_attributes
44
44
  fieldAttr = []
45
- fieldAttr << FieldAttribute.new( 'size', FieldAttribute::INTEGER )
46
- fieldAttr << FieldAttribute.new( 'unique', FieldAttribute::BOOLEAN )
47
- fieldAttr << FieldAttribute.new( 'not_nil', FieldAttribute::BOOLEAN )
48
- fieldAttr << FieldAttribute.new( 'enum_type', FieldAttribute::ENUM,
49
- BooleanField )
50
- fieldAttr << FieldAttribute.new( 'enums', FieldAttribute::HASH )
51
- fieldAttr << FieldAttribute.new( 'range', FieldAttribute::ENUM,
52
- DateField )
53
- fieldAttr << FieldAttribute.new( 'large', FieldAttribute::BOOLEAN )
45
+ fieldAttr << FieldAttribute.new( 'size', :integer )
46
+ fieldAttr << FieldAttribute.new( 'unique', :boolean )
47
+ fieldAttr << FieldAttribute.new( 'not_nil', :boolean )
48
+ fieldAttr << FieldAttribute.new( 'enum_type', :enum, BooleanField )
49
+ fieldAttr << FieldAttribute.new( 'enums', :hash )
50
+ fieldAttr << FieldAttribute.new( 'range', :enum, DateField )
51
+ fieldAttr << FieldAttribute.new( 'large', :boolean )
54
52
  end
55
53
 
56
54
  def register_name( name )
@@ -69,11 +67,6 @@ module Lafcadio
69
67
  end
70
68
 
71
69
  class FieldAttribute
72
- INTEGER = 1
73
- BOOLEAN = 2
74
- ENUM = 3
75
- HASH = 4
76
-
77
70
  attr_reader :name, :value_class
78
71
 
79
72
  def initialize( name, value_class, objectFieldClass = nil )
@@ -84,7 +77,7 @@ module Lafcadio
84
77
  def maybe_set_field_attr( field, fieldElt )
85
78
  setterMethod = "#{ name }="
86
79
  if field.respond_to?( setterMethod )
87
- if value_class != FieldAttribute::HASH
80
+ if value_class != :hash
88
81
  if ( attrStr = fieldElt.attributes[name] )
89
82
  field.send( setterMethod, value_from_string( attrStr ) )
90
83
  end
@@ -107,12 +100,12 @@ module Lafcadio
107
100
  end
108
101
 
109
102
  def value_from_string( valueStr )
110
- if @value_class == INTEGER
103
+ if @value_class == :integer
111
104
  valueStr.to_i
112
- elsif @value_class == BOOLEAN
105
+ elsif @value_class == :boolean
113
106
  valueStr == 'y'
114
- elsif @value_class == ENUM
115
- eval "#{ @objectFieldClass.name }::#{ valueStr }"
107
+ elsif @value_class == :enum
108
+ eval ":#{ valueStr }"
116
109
  end
117
110
  end
118
111
  end
@@ -165,11 +158,11 @@ module Lafcadio
165
158
  # email 'email'
166
159
  # date 'birthday'
167
160
  # end
168
- # The class methods you can use are +blob+, +boolean+, +date+,
161
+ # The class methods you can use are +binary+, +boolean+, +date+,
169
162
  # +date_time+, +domain_object+, +email+, +enum+, +float+, +integer+,
170
- # +month+, +state+, +string+, +text_list+. These correspond to BlobField,
171
- # BooleanField, DateField, DateTimeField, DomainObjectField, EmailField,
172
- # EnumField, FloatField, IntegerField, MonthField, StateField,
163
+ # +month+, +state+, +string+, +text_list+. These correspond to
164
+ # BinaryField, BooleanField, DateField, DateTimeField, DomainObjectField,
165
+ # EmailField, EnumField, FloatField, IntegerField, MonthField, StateField,
173
166
  # StringField, and TextListField, respectively. Consult their individual
174
167
  # RDoc entries for more on how to parameterize them through the class
175
168
  # methods.
@@ -285,10 +278,6 @@ module Lafcadio
285
278
  class DomainObject
286
279
  @@subclass_records = Hash.new do |h, k| h[k] = SubclassRecord.new( k ); end
287
280
 
288
- COMMIT_ADD = 1
289
- COMMIT_EDIT = 2
290
- COMMIT_DELETE = 3
291
-
292
281
  include DomainComparable
293
282
 
294
283
  # Shortcut method for retrieving one domain object.
@@ -307,7 +296,9 @@ module Lafcadio
307
296
  # Returns every committed instance of the domain class.
308
297
  #
309
298
  # User.all # => all users
310
- def self.all; ObjectStore.get_object_store.all( self ); end
299
+ def self.all( opts = {} )
300
+ ObjectStore.get_object_store.all( self, opts )
301
+ end
311
302
 
312
303
  def self.all_fields # :nodoc:
313
304
  self_and_concrete_superclasses.map { |a_class|
@@ -316,7 +307,7 @@ module Lafcadio
316
307
  end
317
308
 
318
309
  def self.class_field(fieldName) #:nodoc:
319
- self.class_fields.find { |field| field.name == fieldName }
310
+ self.class_fields.find { |field| field.name == fieldName.to_s }
320
311
  end
321
312
 
322
313
  def self.class_fields #:nodoc:
@@ -381,10 +372,7 @@ module Lafcadio
381
372
  #
382
373
  # class LotsOfBooleans < Lafcadio::DomainObject
383
374
  # default_field_setup_hash(
384
- # Lafcadio::BooleanField,
385
- # {
386
- # 'enum_type' => Lafcadio::BooleanField::ENUMS_CAPITAL_YES_NO
387
- # }
375
+ # Lafcadio::BooleanField, { 'enum_type' => :capital_yes_no }
388
376
  # )
389
377
  # booleans 'this', 'that', 'the_other', 'and_another_one',
390
378
  # 'this_one_too'
@@ -470,7 +458,7 @@ module Lafcadio
470
458
  if arg.is_a? Fixnum
471
459
  ObjectStore.get_object_store.get( self, *args )
472
460
  else
473
- qry = Query.new( self, nil, { :group_functions => [ :count ] } )
461
+ qry = Query.new( self, :group_functions => [ :count ] )
474
462
  ObjectStore.get_object_store.group_query qry
475
463
  end
476
464
  else
@@ -557,6 +545,10 @@ module Lafcadio
557
545
  #
558
546
  # the_one_user = User.only
559
547
  def self.only; all.only; end
548
+
549
+ def self.postgres_pk_id_seq
550
+ "#{ table_name }_#{ sql_primary_key_name }_seq"
551
+ end
560
552
 
561
553
  def self.require_domain_file( typeString ) # :nodoc:
562
554
  typeString =~ /([^\:]*)$/
@@ -652,14 +644,15 @@ module Lafcadio
652
644
 
653
645
  def self.try_load_xml_parser # :nodoc:
654
646
  require 'lafcadio/domain'
655
- dirName = LafcadioConfig.new['classDefinitionDir']
656
- xmlFileName = self.basename + '.xml'
657
- xmlPath = File.join( dirName, xmlFileName )
658
- begin
659
- xml = File.open( xmlPath ) do |f| f.gets( nil ); end
660
- ClassDefinitionXmlParser.new( self, xml )
661
- rescue Errno::ENOENT
662
- # no xml file, so no @xmlParser
647
+ if ( dirName = LafcadioConfig.new['classDefinitionDir'] )
648
+ xmlFileName = self.basename + '.xml'
649
+ xmlPath = File.join( dirName, xmlFileName )
650
+ begin
651
+ xml = File.open( xmlPath ) do |f| f.gets( nil ); end
652
+ ClassDefinitionXmlParser.new( self, xml )
653
+ rescue Errno::ENOENT
654
+ # no xml file, so no @xmlParser
655
+ end
663
656
  end
664
657
  end
665
658
 
data/lib/lafcadio/mock.rb CHANGED
@@ -9,32 +9,27 @@ module Lafcadio
9
9
  @objects = {}
10
10
  @next_pk_ids = {}
11
11
  @queries = []
12
+ @transaction = nil
12
13
  end
13
14
 
14
15
  def all( domain_class )
15
16
  @objects[domain_class] ? @objects[domain_class].values : []
16
17
  end
17
18
 
18
- def select_dobjs(query)
19
- @queries << query
20
- domain_class = query.domain_class
21
- objects = []
22
- all( domain_class ).each { |dbObj|
23
- objects << dbObj if query.dobj_satisfies?( dbObj )
24
- }
25
- query.order_and_limit_collection objects
26
- end
27
-
28
19
  def commit(db_object)
29
20
  if db_object.pk_id and !db_object.pk_id.is_a?( Integer )
30
21
  raise ArgumentError
31
22
  end
32
- objects_by_domain_class = objects_by_domain_class db_object.domain_class
33
- if db_object.delete
34
- objects_by_domain_class.delete( db_object.pk_id )
23
+ if @transaction
24
+ @transaction << db_object
35
25
  else
36
- object_pk_id = pre_commit_pk_id( db_object )
37
- objects_by_domain_class[object_pk_id] = db_object
26
+ objects_by_domain_class = objects_by_domain_class db_object.domain_class
27
+ if db_object.delete
28
+ objects_by_domain_class.delete( db_object.pk_id )
29
+ else
30
+ object_pk_id = pre_commit_pk_id( db_object )
31
+ objects_by_domain_class[object_pk_id] = db_object
32
+ end
38
33
  end
39
34
  end
40
35
 
@@ -79,10 +74,37 @@ module Lafcadio
79
74
  @queries.select { |qry| qry.to_sql == sql }.size
80
75
  end
81
76
 
77
+ def select_dobjs(query)
78
+ @queries << query
79
+ domain_class = query.domain_class
80
+ objects = []
81
+ all( domain_class ).each { |dbObj|
82
+ objects << dbObj if query.dobj_satisfies?( dbObj )
83
+ }
84
+ query.order_and_limit_collection objects
85
+ end
86
+
82
87
  def set_next_pk_id( domain_class, npi )
83
88
  @next_pk_ids = {} unless @next_pk_ids
84
89
  @next_pk_ids[ domain_class ] = npi
85
90
  end
91
+
92
+ def transaction( action )
93
+ tr = MockDbBridge::Transaction.new
94
+ @transaction = tr
95
+ begin
96
+ action.call tr
97
+ @transaction = nil
98
+ tr.each do |dobj_to_commit| commit( dobj_to_commit ); end
99
+ rescue RollbackError; end
100
+ end
101
+
102
+ class Transaction < Array
103
+ def rollback; raise RollbackError; end
104
+ end
105
+
106
+ class RollbackError < StandardError #:nodoc:
107
+ end
86
108
  end
87
109
 
88
110
  # Externally, the MockObjectStore looks and acts exactly like the ObjectStore,