lafcadio 0.8.3 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|