lafcadio 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
@@ -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
- else
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