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.
@@ -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