lafcadio 0.8.3 → 0.9.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.
- data/lib/lafcadio.rb +1 -1
- data/lib/lafcadio.rb~ +1 -1
- data/lib/lafcadio/dateTime.rb~ +93 -0
- data/lib/lafcadio/depend.rb~ +8 -0
- data/lib/lafcadio/domain.rb +127 -120
- data/lib/lafcadio/domain.rb~ +48 -40
- data/lib/lafcadio/mock.rb +9 -12
- data/lib/lafcadio/mock.rb~ +110 -0
- data/lib/lafcadio/objectField.rb +6 -6
- data/lib/lafcadio/objectField.rb~ +564 -0
- data/lib/lafcadio/objectStore.rb +45 -24
- data/lib/lafcadio/objectStore.rb.~1.64.~ +766 -0
- data/lib/lafcadio/objectStore.rb~ +57 -26
- data/lib/lafcadio/query.rb +57 -22
- data/lib/lafcadio/query.rb~ +48 -17
- data/lib/lafcadio/schema.rb~ +56 -0
- data/lib/lafcadio/test.rb +218 -0
- data/lib/lafcadio/test.rb~ +25 -0
- data/lib/lafcadio/test/testconfig.dat~ +13 -0
- data/lib/lafcadio/util.rb +3 -2
- data/lib/lafcadio/util.rb~ +104 -0
- metadata +111 -97
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'lafcadio/objectField'
|
2
|
+
|
3
|
+
module Lafcadio
|
4
|
+
class CreateTableStatement #:nodoc:
|
5
|
+
@@simple_field_clauses = {
|
6
|
+
FloatField => 'float', DateField => 'date', BooleanField => 'bool',
|
7
|
+
TimeStampField => 'timestamp', TimeField => 'datetime'
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize( domain_class )
|
11
|
+
@domain_class = domain_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def definition_terms( field )
|
15
|
+
definitionTerms = []
|
16
|
+
definitionTerms << field.db_field_name
|
17
|
+
definitionTerms << type_clause( field )
|
18
|
+
definitionTerms << 'not null' if field.not_null
|
19
|
+
definitionTerms.join( ' ' )
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_sql
|
23
|
+
createDefinitions = []
|
24
|
+
createDefinitions << "#{ @domain_class.sql_primary_key_name } " +
|
25
|
+
"int not null auto_increment"
|
26
|
+
createDefinitions << "primary key (#{ @domain_class.sql_primary_key_name })"
|
27
|
+
@domain_class.class_fields.each { |field|
|
28
|
+
createDefinitions << definition_terms( field )
|
29
|
+
}
|
30
|
+
<<-SQL
|
31
|
+
create table #{ @domain_class.table_name } (
|
32
|
+
#{ createDefinitions.join(",\n ") }
|
33
|
+
);
|
34
|
+
SQL
|
35
|
+
end
|
36
|
+
|
37
|
+
def type_clause( field )
|
38
|
+
if ( type_clause = @@simple_field_clauses[field.class] )
|
39
|
+
type_clause
|
40
|
+
elsif ( field.class <= EnumField )
|
41
|
+
singleQuotedValues = field.enums.keys.collect! { |enumValue|
|
42
|
+
"'#{ enumValue }'"
|
43
|
+
}
|
44
|
+
"enum( #{ singleQuotedValues.join( ', ' ) } )"
|
45
|
+
elsif ( field.class <= StringField || field.class <= TextListField )
|
46
|
+
'varchar(255)'
|
47
|
+
elsif ( field.class <= DomainObjectField || field.class <= IntegerField )
|
48
|
+
'int'
|
49
|
+
elsif ( field.class <= FloatField )
|
50
|
+
'float(10, 2)'
|
51
|
+
elsif ( field.class <= BlobField )
|
52
|
+
'blob'
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/lafcadio/test.rb
CHANGED
@@ -23,3 +23,221 @@ class LafcadioTestCase < Test::Unit::TestCase
|
|
23
23
|
|
24
24
|
def default_test; end
|
25
25
|
end
|
26
|
+
|
27
|
+
module Lafcadio
|
28
|
+
def BooleanField.mock_value; true; end
|
29
|
+
|
30
|
+
def DateField.mock_value; Date.today; end
|
31
|
+
|
32
|
+
def DateTimeField.mock_value; Time.now; end
|
33
|
+
|
34
|
+
module DomainMock
|
35
|
+
Version = '0.1.0'
|
36
|
+
|
37
|
+
def self.included( includer )
|
38
|
+
def includer.setup_procs
|
39
|
+
unless defined? @@all_setup_procs
|
40
|
+
@@all_setup_procs = Hash.new { |hash, domain_class|
|
41
|
+
hash[domain_class] = []
|
42
|
+
}
|
43
|
+
end
|
44
|
+
@@all_setup_procs[self]
|
45
|
+
end
|
46
|
+
def includer.setup_mock_dobjs( *domain_classes_or_symbol_names )
|
47
|
+
domain_classes = DomainClassSymbolMapper.new
|
48
|
+
domain_classes_or_symbol_names.each { |domain_class_or_symbol_name|
|
49
|
+
domain_classes.process( domain_class_or_symbol_name )
|
50
|
+
}
|
51
|
+
domain_classes.finish
|
52
|
+
domain_classes.each { |my_domain_class, my_symbol_name|
|
53
|
+
proc = Proc.new { |test_case|
|
54
|
+
test_case.instance_variable_set(
|
55
|
+
my_symbol_name, my_domain_class.default_mock
|
56
|
+
)
|
57
|
+
}
|
58
|
+
setup_procs << proc
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def commit_domain_object( domain_class, non_default_values = {} )
|
64
|
+
self.class.mock_domain_files.each do |file| require file; end
|
65
|
+
dobj = domain_class.send( :custom_mock, non_default_values )
|
66
|
+
dobj.commit
|
67
|
+
end
|
68
|
+
|
69
|
+
def method_missing( sym, *args )
|
70
|
+
method_name = sym.id2name
|
71
|
+
if method_name =~ /^custom_mock_(.*)/
|
72
|
+
domain_class = Module.by_name( $1.underscore_to_camel_case )
|
73
|
+
commit_domain_object( domain_class, *args )
|
74
|
+
elsif method_name =~ /^default_mock_(.*)/
|
75
|
+
Module.by_name( $1.underscore_to_camel_case ).default_mock
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def setup
|
82
|
+
self.class.setup_procs.each { |proc| proc.call( self ) }
|
83
|
+
end
|
84
|
+
|
85
|
+
class DomainClassSymbolMapper < Hash
|
86
|
+
def initialize; @last_domain_class = nil; end
|
87
|
+
|
88
|
+
def default_symbol_name( domain_class )
|
89
|
+
"@#{ domain_class.name.camel_case_to_underscore }"
|
90
|
+
end
|
91
|
+
|
92
|
+
def finish
|
93
|
+
if @last_domain_class
|
94
|
+
self[@last_domain_class] = default_symbol_name( @last_domain_class )
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def process( domain_class_or_symbol_name )
|
99
|
+
if domain_class_or_symbol_name.class == Class
|
100
|
+
if @last_domain_class
|
101
|
+
self[@last_domain_class] = default_symbol_name( @last_domain_class )
|
102
|
+
end
|
103
|
+
@last_domain_class = domain_class_or_symbol_name
|
104
|
+
else
|
105
|
+
self[@last_domain_class] = domain_class_or_symbol_name
|
106
|
+
@last_domain_class = nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class DomainObject
|
113
|
+
@@default_mock_available = Hash.new true
|
114
|
+
@@default_arg_directives = Hash.new { |hash, a_class|
|
115
|
+
default_args = {}
|
116
|
+
a_class.all_fields.each do |field|
|
117
|
+
default_args[field.name] = field.default_mock_value
|
118
|
+
end
|
119
|
+
hash[a_class] = default_args
|
120
|
+
}
|
121
|
+
|
122
|
+
def self.commit_mock( args, caller = nil )
|
123
|
+
dobj = self.new( args )
|
124
|
+
link_fields = all_fields.select { |field| field.is_a? DomainObjectField }
|
125
|
+
link_fields.each do |field|
|
126
|
+
val = dobj.send( field.name )
|
127
|
+
if val and val.pk_id == 1
|
128
|
+
linked_type = field.linked_type
|
129
|
+
begin
|
130
|
+
ObjectStore.get_object_store.get( linked_type, 1 )
|
131
|
+
rescue DomainObjectNotFoundError
|
132
|
+
unless linked_type == caller
|
133
|
+
linked_type.send( 'default_mock', self )
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
dobj.commit
|
139
|
+
dobj
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.default_args
|
143
|
+
default_args = {}
|
144
|
+
@@default_arg_directives[self].each do |name, value_or_proc|
|
145
|
+
if value_or_proc.is_a? Proc
|
146
|
+
default_args[name] = value_or_proc.call
|
147
|
+
else
|
148
|
+
default_args[name] = value_or_proc
|
149
|
+
end
|
150
|
+
end
|
151
|
+
default_args
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.custom_mock( custom_args = nil )
|
155
|
+
dobj_args = default_args
|
156
|
+
object_store = ObjectStore.get_object_store
|
157
|
+
dbb = object_store.get_db_bridge
|
158
|
+
dbb.set_next_pk_id( self, 2 ) if dbb.next_pk_id( self ) == 1
|
159
|
+
dobj_args['pk_id'] = nil
|
160
|
+
dobj_args = dobj_args.merge( custom_args ) if custom_args.is_a?( Hash )
|
161
|
+
commit_mock( dobj_args )
|
162
|
+
end
|
163
|
+
|
164
|
+
def self.default_mock( calling_class = nil )
|
165
|
+
if @@default_mock_available[self]
|
166
|
+
begin
|
167
|
+
dobj = ObjectStore.get_object_store.get( self, 1 )
|
168
|
+
dobj
|
169
|
+
rescue DomainObjectNotFoundError
|
170
|
+
dbb = ObjectStore.get_object_store.get_db_bridge
|
171
|
+
dbb.set_next_pk_id( self, 1 ) if dbb.next_pk_id( self ) > 1
|
172
|
+
commit_mock( default_args, calling_class )
|
173
|
+
end
|
174
|
+
else
|
175
|
+
raise( TypeError, self.name + ".default_mock not allowed", caller )
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.default_mock_available( is_avail )
|
180
|
+
@@default_mock_available[self] = is_avail
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.mock_value( field_sym, value )
|
184
|
+
@@default_arg_directives[self][field_sym.id2name] = value
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.mock_values( hash )
|
188
|
+
hash.each do |field_sym, value| mock_value( field_sym, value ); end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class DomainObjectField < ObjectField
|
193
|
+
def default_mock_value; DomainObjectProxy.new( linked_type, 1 ); end
|
194
|
+
end
|
195
|
+
|
196
|
+
def EmailField.mock_value; 'john.doe@email.com'; end
|
197
|
+
|
198
|
+
def FloatField.mock_value; 0.0; end
|
199
|
+
|
200
|
+
def IntegerField.mock_value; 1; end
|
201
|
+
|
202
|
+
class MockDbBridge
|
203
|
+
unless instance_methods.include?( 'orig_get_pk_id_before_committing' )
|
204
|
+
alias_method(
|
205
|
+
:orig_get_pk_id_before_committing, :get_pk_id_before_committing
|
206
|
+
)
|
207
|
+
|
208
|
+
def get_pk_id_before_committing( domain_object )
|
209
|
+
@next_pk_ids = {} unless @next_pk_ids
|
210
|
+
orig_pk_id = orig_get_pk_id_before_committing domain_object
|
211
|
+
if (next_pk_id = @next_pk_ids[domain_object.domain_class])
|
212
|
+
@last_pk_id_inserted = next_pk_id
|
213
|
+
@next_pk_ids[domain_object.domain_class] = nil
|
214
|
+
next_pk_id
|
215
|
+
else
|
216
|
+
orig_pk_id
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def next_pk_id( domain_class )
|
222
|
+
dobjs = get_objects_by_domain_class( domain_class ).values
|
223
|
+
dobjs.inject( 0 ) { |memo, obj| memo > obj.pk_id ? memo : obj.pk_id } + 1
|
224
|
+
end
|
225
|
+
|
226
|
+
def set_next_pk_id( domain_class, npi )
|
227
|
+
@next_pk_ids = {} unless @next_pk_ids
|
228
|
+
@next_pk_ids[ domain_class ] = npi
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class ObjectField
|
233
|
+
attr_writer :mock_value
|
234
|
+
|
235
|
+
def default_mock_value; self.class.mock_value; end
|
236
|
+
end
|
237
|
+
|
238
|
+
def PrimaryKeyField.mock_value; nil; end
|
239
|
+
|
240
|
+
def StringField.mock_value; 'test text'; end
|
241
|
+
|
242
|
+
def TextListField.mock_value; %w( a b c ); end
|
243
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'lafcadio/depend'
|
2
|
+
require 'lafcadio/mock'
|
3
|
+
require 'lafcadio/util'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
# A test case that sets up a number of mock services. In writing an application
|
7
|
+
# that uses Lafcadio you may find it convenient to inherit from this class.
|
8
|
+
class LafcadioTestCase < Test::Unit::TestCase
|
9
|
+
include Lafcadio
|
10
|
+
|
11
|
+
def setup
|
12
|
+
context = ContextualService::Context.instance
|
13
|
+
context.flush
|
14
|
+
@mockObjectStore = MockObjectStore.new
|
15
|
+
ObjectStore.set_object_store @mockObjectStore
|
16
|
+
LafcadioConfig.set_values(
|
17
|
+
'classDefinitionDir' => '../test/testData', 'dbhost' => 'localhost',
|
18
|
+
'dbname' => 'test', 'dbpassword' => 'password', 'dbuser' => 'test',
|
19
|
+
'domainFiles' => %w( ../test/mock/domain ),
|
20
|
+
'logdir' => '../test/testOutput/', 'logSql' => 'n'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_test; end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
dbuser:test
|
2
|
+
dbpassword:password
|
3
|
+
dbname:test
|
4
|
+
dbhost:localhost
|
5
|
+
adminEmail:john.doe@email.com
|
6
|
+
siteName:Site Name
|
7
|
+
url:http://test.url
|
8
|
+
classpath:lafcadio/
|
9
|
+
logdir:../test/testOutput/
|
10
|
+
domainDirs:../test/mock/domain/
|
11
|
+
domainFiles:../test/mock/domain.rb
|
12
|
+
logSql:n
|
13
|
+
classDefinitionDir:../test/testData
|
data/lib/lafcadio/util.rb
CHANGED
@@ -2,7 +2,7 @@ require 'delegate'
|
|
2
2
|
require 'lafcadio/depend'
|
3
3
|
require 'singleton'
|
4
4
|
|
5
|
-
module Lafcadio
|
5
|
+
module Lafcadio
|
6
6
|
# LafcadioConfig is a Hash that takes its data from the config file. You'll
|
7
7
|
# have to set the location of that file before using it: Use
|
8
8
|
# LafcadioConfig.set_filename.
|
@@ -14,6 +14,7 @@ module Lafcadio
|
|
14
14
|
# dbname:lafcadio_test
|
15
15
|
# dbhost:localhost
|
16
16
|
class LafcadioConfig < Hash
|
17
|
+
@@filename = nil
|
17
18
|
@@value_hash = nil
|
18
19
|
|
19
20
|
def self.set_filename(filename); @@filename = filename; end
|
@@ -23,7 +24,7 @@ module Lafcadio
|
|
23
24
|
def initialize
|
24
25
|
if @@value_hash
|
25
26
|
@@value_hash.each { |key, value| self[key] = value }
|
26
|
-
|
27
|
+
elsif @@filename
|
27
28
|
File.new( @@filename ).each_line { |line|
|
28
29
|
line.chomp =~ /^(.*?):(.*)$/
|
29
30
|
self[$1] = $2
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'lafcadio/depend'
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
module Lafcadio
|
6
|
+
# LafcadioConfig is a Hash that takes its data from the config file. You'll
|
7
|
+
# have to set the location of that file before using it: Use
|
8
|
+
# LafcadioConfig.set_filename.
|
9
|
+
#
|
10
|
+
# LafcadioConfig expects its data to be colon-delimited, one key-value pair
|
11
|
+
# to a line. For example:
|
12
|
+
# dbuser:user
|
13
|
+
# dbpassword:password
|
14
|
+
# dbname:lafcadio_test
|
15
|
+
# dbhost:localhost
|
16
|
+
class LafcadioConfig < Hash
|
17
|
+
@@filename = nil
|
18
|
+
@@value_hash = nil
|
19
|
+
|
20
|
+
def self.set_filename(filename); @@filename = filename; end
|
21
|
+
|
22
|
+
def self.set_values( value_hash ); @@value_hash = value_hash; end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
#puts "initialize " + @@filename.inspect
|
26
|
+
if @@value_hash
|
27
|
+
@@value_hash.each { |key, value| self[key] = value }
|
28
|
+
# elsif @@filename
|
29
|
+
else
|
30
|
+
#puts caller.inspect
|
31
|
+
File.new( @@filename ).each_line { |line|
|
32
|
+
line.chomp =~ /^(.*?):(.*)$/
|
33
|
+
self[$1] = $2
|
34
|
+
}
|
35
|
+
# else
|
36
|
+
# raise
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class MissingError < RuntimeError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class String
|
46
|
+
unless method_defined?( :camel_case_to_underscore )
|
47
|
+
|
48
|
+
# Returns the underscored version of a camel-case string.
|
49
|
+
def camel_case_to_underscore
|
50
|
+
( gsub( /(.)([A-Z])/ ) { $1 + '_' + $2.downcase } ).downcase
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the number of times that <tt>regexp</tt> occurs in the string.
|
56
|
+
def count_occurrences(regexp)
|
57
|
+
count = 0
|
58
|
+
str = self.clone
|
59
|
+
while str =~ regexp
|
60
|
+
count += 1
|
61
|
+
str = $'
|
62
|
+
end
|
63
|
+
count
|
64
|
+
end
|
65
|
+
|
66
|
+
# Decapitalizes the first letter of the string, or decapitalizes the
|
67
|
+
# entire string if it's all capitals.
|
68
|
+
#
|
69
|
+
# 'InternalClient'.decapitalize -> "internalClient"
|
70
|
+
# 'SKU'.decapitalize -> "sku"
|
71
|
+
def decapitalize
|
72
|
+
string = clone
|
73
|
+
firstLetter = string[0..0].downcase
|
74
|
+
string = firstLetter + string[1..string.length]
|
75
|
+
newString = ""
|
76
|
+
while string =~ /([A-Z])([^a-z]|$)/
|
77
|
+
newString += $`
|
78
|
+
newString += $1.downcase
|
79
|
+
string = $2 + $'
|
80
|
+
end
|
81
|
+
newString += string
|
82
|
+
newString
|
83
|
+
end
|
84
|
+
|
85
|
+
# Left-pads a string with +fillChar+ up to +size+ size.
|
86
|
+
#
|
87
|
+
# "a".pad( 10, "+") -> "+++++++++a"
|
88
|
+
def pad(size, fillChar)
|
89
|
+
string = clone
|
90
|
+
while string.length < size
|
91
|
+
string = fillChar + string
|
92
|
+
end
|
93
|
+
string
|
94
|
+
end
|
95
|
+
|
96
|
+
unless method_defined?( :underscore_to_camel_case )
|
97
|
+
|
98
|
+
# Returns the camel-case equivalent of an underscore-style string.
|
99
|
+
def underscore_to_camel_case
|
100
|
+
capitalize.gsub( /_([a-zA-Z0-9]+)/ ) { |s| s[1,s.size - 1].capitalize }
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|