ar_loader 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/LICENSE +9 -9
  2. data/README.markdown +268 -221
  3. data/Rakefile +76 -76
  4. data/lib/VERSION +1 -1
  5. data/lib/ar_loader.rb +87 -66
  6. data/lib/ar_loader/exceptions.rb +2 -0
  7. data/lib/{engine → ar_loader}/file_definitions.rb +353 -353
  8. data/lib/{engine → ar_loader}/mapping_file_definitions.rb +87 -87
  9. data/lib/ar_loader/method_detail.rb +257 -0
  10. data/lib/ar_loader/method_mapper.rb +213 -0
  11. data/lib/helpers/jruby/jexcel_file.rb +187 -0
  12. data/lib/{engine → helpers/jruby}/word.rb +79 -70
  13. data/lib/helpers/spree_helper.rb +85 -0
  14. data/lib/loaders/csv_loader.rb +87 -0
  15. data/lib/loaders/excel_loader.rb +132 -0
  16. data/lib/loaders/loader_base.rb +205 -73
  17. data/lib/loaders/spree/image_loader.rb +45 -41
  18. data/lib/loaders/spree/product_loader.rb +140 -91
  19. data/lib/to_b.rb +24 -24
  20. data/spec/csv_loader_spec.rb +27 -0
  21. data/spec/database.yml +19 -6
  22. data/spec/db/migrate/20110803201325_create_test_bed.rb +78 -0
  23. data/spec/excel_loader_spec.rb +113 -98
  24. data/spec/fixtures/BadAssociationName.xls +0 -0
  25. data/spec/fixtures/DemoNegativeTesting.xls +0 -0
  26. data/spec/fixtures/DemoTestModelAssoc.xls +0 -0
  27. data/spec/fixtures/ProjectsMultiCategories.xls +0 -0
  28. data/spec/fixtures/SimpleProjects.xls +0 -0
  29. data/spec/fixtures/SpreeProducts.xls +0 -0
  30. data/spec/fixtures/SpreeZoneExample.csv +5 -0
  31. data/spec/fixtures/SpreeZoneExample.xls +0 -0
  32. data/spec/loader_spec.rb +116 -0
  33. data/spec/logs/test.log +5000 -0
  34. data/spec/method_mapper_spec.rb +222 -0
  35. data/spec/models.rb +55 -0
  36. data/spec/spec_helper.rb +85 -18
  37. data/spec/spree_loader_spec.rb +223 -157
  38. data/tasks/config/seed_fu_product_template.erb +15 -15
  39. data/tasks/config/tidy_config.txt +12 -12
  40. data/tasks/db_tasks.rake +64 -64
  41. data/tasks/excel_loader.rake +63 -113
  42. data/tasks/file_tasks.rake +36 -37
  43. data/tasks/loader.rake +45 -0
  44. data/tasks/spree/image_load.rake +108 -107
  45. data/tasks/spree/product_loader.rake +49 -107
  46. data/tasks/word_to_seedfu.rake +166 -166
  47. metadata +66 -61
  48. data/lib/engine/jruby/jexcel_file.rb +0 -182
  49. data/lib/engine/jruby/method_mapper_excel.rb +0 -44
  50. data/lib/engine/method_detail.rb +0 -140
  51. data/lib/engine/method_mapper.rb +0 -157
  52. data/lib/engine/method_mapper_csv.rb +0 -28
  53. data/spec/db/migrate/20110803201325_create_testbed.rb +0 -25
@@ -1,88 +1,88 @@
1
- # This class provides a value map (hash) from a text mapping file
2
- #
3
- # The map file is a text file of delimeted key -> values pairs
4
- #
5
- # SUPPORTED FILE FORMATS:
6
- #
7
- # 2 column e.g. a,b
8
- # creates a simple hash {a => b)
9
- #
10
- # 3 column e.g. a,b,c
11
- # a,b becomes the key, c is the vaule
12
- # creates a hash { [a,b] => c }
13
- #
14
- # 4 column e.g. a,b,c,d
15
- # a,b becomes the key, c,d the value
16
- # creates a hash { [a,b] => [c,d] }
17
- #
18
- # TODO allow mapping file to be an xml file
19
- #
20
- class ValueMapFromFile < Hash
21
-
22
- def intialize(file_path, delim = ',')
23
- @delegate_to = {}
24
- @delim = delim
25
- load_map(file_path)
26
- end
27
-
28
- def load_map(file_path = nil, delim = ',')
29
- @file = file_path unless file_path.nil?
30
- @delim = delim
31
-
32
- raise BadConfigError.new("Can not read map file: #{@file}") unless File.readable?(@file)
33
-
34
- File.open(@file).each_line do |line|
35
- next unless(line && line.chomp!)
36
-
37
- values = line.split(@delim)
38
-
39
- case values.nitems
40
- when 2: self.store(values[0], values[1])
41
- when 3: self.store([values[0], values[1]], values[2])
42
- when 4: self.store([values[0], values[1]],[values[2], values[3]])
43
- else
44
- raise BadConfigError.new("Bad key,value row in #{@file}: #{values.nitems} number of columns not supported")
45
- end
46
- end
47
-
48
- return self
49
- end
50
- end
51
-
52
-
53
- # Expects file of format [TradeType,LDN_TradeId,HUB_TradeId,LDN_AssetId,HUB_AssetId,LDN_StrutureId,HUB_StructureId,LDN_ProductType,HUB_ProductType]
54
- # Convets in to and araya containing rows [LDN_TradeId, LDN_AssetId, HUB_TradeId, HUB_AssetId]
55
- class AssetMapFromFile < Array
56
-
57
- def intialize(file_path, delim = ',')
58
- @delegate_to = {}
59
- @delim = delim
60
- load_map(file_path)
61
- end
62
-
63
- def load_map(file_path = nil, delim = ',')
64
- @file = file_path unless file_path.nil?
65
- @delim = delim
66
-
67
- raise BadConfigError.new("Can not read asset map file: #{@file}") unless File.readable?(@file)
68
-
69
- File.open(@file).each_line do |line|
70
- next unless(line && line.chomp!)
71
- # skip the header row
72
- next if line.include?('TradeType')
73
-
74
- values = line.split(@delim)
75
-
76
- self.push(Array[values[1], values[3], values[2], values[4]])
77
- end
78
-
79
- return self
80
- end
81
-
82
- def write_map(file_path = nil, delim = ',')
83
- mapfile = File.open( file_path, 'w')
84
-
85
- self.each{|row| mapfile.write(row.join(delim)+"\n")}
86
- end
87
-
1
+ # This class provides a value map (hash) from a text mapping file
2
+ #
3
+ # The map file is a text file of delimeted key -> values pairs
4
+ #
5
+ # SUPPORTED FILE FORMATS:
6
+ #
7
+ # 2 column e.g. a,b
8
+ # creates a simple hash {a => b)
9
+ #
10
+ # 3 column e.g. a,b,c
11
+ # a,b becomes the key, c is the vaule
12
+ # creates a hash { [a,b] => c }
13
+ #
14
+ # 4 column e.g. a,b,c,d
15
+ # a,b becomes the key, c,d the value
16
+ # creates a hash { [a,b] => [c,d] }
17
+ #
18
+ # TODO allow mapping file to be an xml file
19
+ #
20
+ class ValueMapFromFile < Hash
21
+
22
+ def intialize(file_path, delim = ',')
23
+ @delegate_to = {}
24
+ @delim = delim
25
+ load_map(file_path)
26
+ end
27
+
28
+ def load_map(file_path = nil, delim = ',')
29
+ @file = file_path unless file_path.nil?
30
+ @delim = delim
31
+
32
+ raise BadConfigError.new("Can not read map file: #{@file}") unless File.readable?(@file)
33
+
34
+ File.open(@file).each_line do |line|
35
+ next unless(line && line.chomp!)
36
+
37
+ values = line.split(@delim)
38
+
39
+ case values.nitems
40
+ when 2: self.store(values[0], values[1])
41
+ when 3: self.store([values[0], values[1]], values[2])
42
+ when 4: self.store([values[0], values[1]],[values[2], values[3]])
43
+ else
44
+ raise BadConfigError.new("Bad key,value row in #{@file}: #{values.nitems} number of columns not supported")
45
+ end
46
+ end
47
+
48
+ return self
49
+ end
50
+ end
51
+
52
+
53
+ # Expects file of format [TradeType,LDN_TradeId,HUB_TradeId,LDN_AssetId,HUB_AssetId,LDN_StrutureId,HUB_StructureId,LDN_ProductType,HUB_ProductType]
54
+ # Convets in to and araya containing rows [LDN_TradeId, LDN_AssetId, HUB_TradeId, HUB_AssetId]
55
+ class AssetMapFromFile < Array
56
+
57
+ def intialize(file_path, delim = ',')
58
+ @delegate_to = {}
59
+ @delim = delim
60
+ load_map(file_path)
61
+ end
62
+
63
+ def load_map(file_path = nil, delim = ',')
64
+ @file = file_path unless file_path.nil?
65
+ @delim = delim
66
+
67
+ raise BadConfigError.new("Can not read asset map file: #{@file}") unless File.readable?(@file)
68
+
69
+ File.open(@file).each_line do |line|
70
+ next unless(line && line.chomp!)
71
+ # skip the header row
72
+ next if line.include?('TradeType')
73
+
74
+ values = line.split(@delim)
75
+
76
+ self.push(Array[values[1], values[3], values[2], values[4]])
77
+ end
78
+
79
+ return self
80
+ end
81
+
82
+ def write_map(file_path = nil, delim = ',')
83
+ mapfile = File.open( file_path, 'w')
84
+
85
+ self.each{|row| mapfile.write(row.join(delim)+"\n")}
86
+ end
87
+
88
88
  end
@@ -0,0 +1,257 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ # License:: MIT
5
+ #
6
+ # Details:: This class provides info and access to the individual population methods
7
+ # on an AR model. Populated by, and coupled with MethodMapper,
8
+ # which does the model interrogation work and stores sets of MethodDetails.
9
+ #
10
+ # Enables 'loaders' to iterate over the MethodMapper results set,
11
+ # and assign values to AR object, without knowing anything about that receiving object.
12
+ #
13
+ require 'to_b'
14
+
15
+ module ARLoader
16
+
17
+ class MethodDetail
18
+
19
+ def self.type_enum
20
+ @type_enum ||= Set[:assignment, :belongs_to, :has_one, :has_many]
21
+ @type_enum
22
+ end
23
+
24
+ # When looking up an association, try each of these in turn till a match
25
+ # i.e find_by_name .. find_by_title and so on, lastly try the raw id
26
+ @@insistent_find_by_list ||= [:name, :title, :id]
27
+
28
+ @@default_values = {}
29
+ @@prefixes = {}
30
+
31
+
32
+ # Name is the raw, client supplied name
33
+ attr_reader :name, :col_type, :current_value
34
+
35
+ attr_reader :operator, :operator_type
36
+
37
+ # Store the raw (client supplied) name against the active record klass(model), operator and types
38
+ def initialize(client_name, klass, operator, type, col_types = {} )
39
+ @klass, @name = klass, client_name
40
+
41
+ if( MethodDetail::type_enum.member?(type.to_sym) )
42
+ @operator_type = type
43
+ else
44
+ raise "Bad operator Type #{type} passed to Method Detail"
45
+ end
46
+
47
+ @operator = operator
48
+
49
+ # Note : Not all assignments will currently have a column type, for example
50
+ # those that are derived from a delegate_belongs_to
51
+ @col_type = col_types[operator]
52
+ end
53
+
54
+
55
+ # Return the actual operator's name for supplied method type
56
+ # where type one of :assignment, :has_one, :belongs_to, :has_many etc
57
+ def operator_for( type )
58
+ return operator if(@operator_type == type)
59
+ nil
60
+ end
61
+
62
+ def operator?(name)
63
+ operator == name
64
+ end
65
+
66
+ # Return the operator's expected class name, if can be derived, else nil
67
+ def operator_class_name()
68
+ @operator_class_name ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
69
+ begin
70
+ Kernel.const_get(operator.classify)
71
+ operator.classify
72
+ rescue; ""; end
73
+
74
+ elsif(@col_type)
75
+ @col_type.type.to_s.classify
76
+ else
77
+ ""
78
+ end
79
+
80
+ @operator_class_name
81
+ end
82
+
83
+ # Return the operator's expected class, if can be derived, else nil
84
+ def operator_class()
85
+ @operator_class ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
86
+ begin
87
+ Kernel.const_get(operator.classify)
88
+ rescue; ""; end
89
+
90
+ elsif(@col_type)
91
+ begin
92
+ Kernel.const_get(@col_type.type.to_s.classify)
93
+ rescue; nil; end
94
+ else
95
+ nil
96
+ end
97
+
98
+ @operator_class
99
+ end
100
+
101
+
102
+
103
+ def validate_value(value)
104
+
105
+ return @@default_values[@name] if(@@default_values[@name])
106
+
107
+ return "#{@@prefixes[@name]}#{value}" if(@@prefixes[@name])
108
+
109
+ value
110
+ end
111
+
112
+ def assign(record, value )
113
+
114
+ @current_value = validate_value(value)
115
+
116
+ puts "WARNING nil value supplied for Column [#{@name}]" if(@current_value.nil?)
117
+
118
+ if( operator_for(:belongs_to) )
119
+
120
+ #puts "DEBUG : BELONGS_TO : #{@name} : #{operator} - Lookup #{@current_value} in DB"
121
+ insistent_belongs_to(record, @current_value)
122
+
123
+ elsif( operator_for(:has_many) )
124
+
125
+ #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
126
+ if(value.is_a?(Array) || value.is_a?(operator_class))
127
+ record.send(operator) << value
128
+ else
129
+ puts "ERROR #{value.class} - Not expected type for has_many #{operator} - cannot assign"
130
+ # TODO - Not expected type - maybe try to look it up somehow ?"
131
+ #insistent_has_many(record, @current_value)
132
+ end
133
+
134
+ elsif( operator_for(:has_one) )
135
+
136
+ #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
137
+ if(value.is_a?(operator_class))
138
+ record.send(operator + '=', value)
139
+ else
140
+ puts "ERROR #{value.class} - Not expected type for has_one #{operator} - cannot assign"
141
+ # TODO - Not expected type - maybe try to look it up somehow ?"
142
+ #insistent_has_many(record, @current_value)
143
+ end
144
+
145
+ elsif( operator_for(:assignment) && @col_type )
146
+ #puts "DEBUG : COl TYPE defined for #{@name} : #{@assignment} => #{@current_value} #{@col_type.inspect}"
147
+ #puts "DEBUG : COl TYPE CAST: #{@current_value} => #{@col_type.type_cast( @current_value ).inspect}"
148
+ record.send( operator + '=' , @col_type.type_cast( @current_value ) )
149
+
150
+ elsif( operator_for(:assignment) )
151
+ #puts "DEBUG : Brute force assignment of value #{@current_value} supplied for Column [#{@name}]"
152
+ # brute force case for assignments without a column type (which enables us to do correct type_cast)
153
+ # so in this case, attempt straightforward assignment then if that fails, basic ops such as to_s, to_i, to_f etc
154
+ insistent_assignment(record, @current_value)
155
+ end
156
+ end
157
+
158
+ def self.set_default_value( name, value )
159
+ @@default_values[name] = value
160
+ end
161
+
162
+ def self.default_value(name)
163
+ @@default_values[name]
164
+ end
165
+
166
+ def self.set_prefix( name, value )
167
+ @@prefixes[name] = value
168
+ end
169
+
170
+ def self.prefix_value(name)
171
+ @@prefixes[name]
172
+ end
173
+
174
+ def pp
175
+ "#{@name} => #{operator}"
176
+ end
177
+
178
+
179
+ def self.insistent_method_list
180
+ @insistent_method_list ||= [:to_s, :to_i, :to_f, :to_b]
181
+ @insistent_method_list
182
+ end
183
+
184
+ private
185
+
186
+ # Attempt to find the associated object via id, name, title ....
187
+ def insistent_belongs_to( record, value )
188
+
189
+ if( value.class == operator_class)
190
+ record.send(operator) << value
191
+ else
192
+
193
+ @@insistent_find_by_list.each do |x|
194
+ begin
195
+ next unless operator_class.respond_to?( "find_by_#{x}" )
196
+ item = operator_class.send( "find_by_#{x}", value)
197
+ if(item)
198
+ record.send(operator + '=', item)
199
+ break
200
+ end
201
+ rescue => e
202
+ puts "ERROR: #{e.inspect}"
203
+ if(x == MethodDetail::insistent_method_list.last)
204
+ raise "I'm sorry I have failed to assign [#{value}] to #{@assignment}" unless value.nil?
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ # Attempt to find the associated object via id, name, title ....
212
+ def insistent_has_many( record, value )
213
+
214
+ if( value.class == operator_class)
215
+ record.send(operator) << value
216
+ else
217
+ @@insistent_find_by_list.each do |x|
218
+ begin
219
+ item = operator_class.send( "find_by_#{x}", value)
220
+ if(item)
221
+ record.send(operator) << item
222
+ break
223
+ end
224
+ rescue => e
225
+ puts "ERROR: #{e.inspect}"
226
+ if(x == MethodDetail::insistent_method_list.last)
227
+ raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ def insistent_assignment( record, value )
235
+ #puts "DEBUG: RECORD CLASS #{record.class}"
236
+ op = operator + '='
237
+
238
+ begin
239
+ record.send(op, value)
240
+ rescue => e
241
+ MethodDetail::insistent_method_list.each do |f|
242
+ begin
243
+ record.send(op, value.send( f) )
244
+ break
245
+ rescue => e
246
+ #puts "DEBUG: insistent_assignment: #{e.inspect}"
247
+ if f == MethodDetail::insistent_method_list.last
248
+ puts "I'm sorry I have failed to assign [#{value}] to #{operator}"
249
+ raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
256
+
257
+ end
@@ -0,0 +1,213 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ # License:: MIT
5
+ #
6
+ # Details:: A base class that stores details of all possible associations on AR classes and,
7
+ # given user supplied class and name, attempts to find correct attribute/association.
8
+ #
9
+ # Derived classes define where the user supplied list of names originates from.
10
+ #
11
+ # Example usage, load from a spreadsheet where the column names are only
12
+ # an approximation of the actual associations. Given a column heading of
13
+ # 'Product Properties' on class Product, find_method_detail() would search AR model,
14
+ # and return details of real has_many association 'product_properties'.
15
+ #
16
+ # This real association can then be used to send spreadsheet row data to the AR object.
17
+ #
18
+ require 'method_detail'
19
+
20
+ module ARLoader
21
+
22
+ class MethodMapper
23
+
24
+ attr_accessor :header_row, :headers
25
+ attr_accessor :method_details, :missing_methods
26
+
27
+ @@has_many = Hash.new
28
+ @@belongs_to = Hash.new
29
+ @@assignments = Hash.new
30
+ @@column_types = Hash.new
31
+
32
+ def initialize
33
+ @method_details = []
34
+ @headers = []
35
+ end
36
+
37
+ # Build complete picture of the methods whose names listed in method_list
38
+ # Handles method names as defined by a user or in file headers where names may
39
+ # not be exactly as required e.g handles capitalisation, white space, _ etc
40
+
41
+ def populate_methods( klass, method_list )
42
+ @method_details, @missing_methods = [], []
43
+
44
+ method_list.each do |x|
45
+ md = MethodMapper::find_method_detail( klass, x )
46
+ md ? @method_details << md : @missing_methods << x
47
+ end
48
+ #@method_details.compact! .. currently we may neeed to map via the index on @method_details so don't remove nils for now
49
+ @method_details
50
+ end
51
+
52
+ # The raw client supplied names
53
+ def method_names()
54
+ @method_details.collect( &:name )
55
+ end
56
+
57
+ # The true operator names discovered from model
58
+ def operator_names()
59
+ @method_details.collect( &:operator )
60
+ end
61
+
62
+ # Returns true if discovered methods contain every operator in mandatory_list
63
+ def contains_mandatory?( mandatory_list )
64
+ puts operator_names.inspect
65
+ [mandatory_list - operator_names].flatten.empty?
66
+ end
67
+
68
+ def missing_mandatory( mandatory_list )
69
+ [mandatory_list - operator_names].flatten
70
+ end
71
+
72
+ # Create picture of the operators for assignment available on an AR model,
73
+ # including via associations (which provide both << and = )
74
+ #
75
+ def self.find_operators(klass, options = {} )
76
+
77
+ # Find the has_many associations which can be populated via <<
78
+ if( options[:reload] || @@has_many[klass].nil? )
79
+ @@has_many[klass] = klass.reflect_on_all_associations(:has_many).map { |i| i.name.to_s }
80
+ klass.reflect_on_all_associations(:has_and_belongs_to_many).inject(@@has_many[klass]) { |x,i| x << i.name.to_s }
81
+ end
82
+ # puts "DEBUG: Has Many Associations:", @@has_many[klass].inspect
83
+
84
+ # Find the belongs_to associations which can be populated via Model.belongs_to_name = OtherArModelObject
85
+ if( options[:reload] || @@belongs_to[klass].nil? )
86
+ @@belongs_to[klass] = klass.reflect_on_all_associations(:belongs_to).map { |i| i.name.to_s }
87
+ end
88
+
89
+ #puts "Belongs To Associations:", @@belongs_to[klass].inspect
90
+
91
+ # Find the has_one associations which can be populated via Model.has_one_name = OtherArModelObject
92
+ if( options[:reload] || self.has_one[klass].nil? )
93
+ self.has_one[klass] = klass.reflect_on_all_associations(:has_one).map { |i| i.name.to_s }
94
+ end
95
+
96
+ #puts "has_one Associations:", self.has_one[klass].inspect
97
+
98
+ # Find the model's column associations which can be populated via = value
99
+ if( options[:reload] || @@assignments[klass].nil? )
100
+
101
+ @@assignments[klass] = (klass.column_names + klass.instance_methods.grep(/=/).map{|i| i.gsub(/=/, '')})
102
+ @@assignments[klass] -= @@has_many[klass] if(@@has_many[klass])
103
+ @@assignments[klass] -= @@belongs_to[klass] if(@@belongs_to[klass])
104
+ @@assignments[klass] -= self.has_one[klass] if(self.has_one[klass])
105
+
106
+ @@assignments[klass].uniq!
107
+
108
+ @@assignments[klass].each do |assign|
109
+ @@column_types[klass] ||= {}
110
+ found = klass.columns.find{ |col| col.name == assign }
111
+ @@column_types[klass].merge!( found.name => found) if found
112
+ end
113
+ end
114
+ end
115
+
116
+ # Find the proper format of name, appropriate call + column type for a given name.
117
+ # e.g Given users entry in spread sheet check for pluralization, missing underscores etc
118
+ #
119
+ # If not nil, returned method can be used directly in for example klass.new.send( call, .... )
120
+ #
121
+ def self.find_method_detail( klass, external_name )
122
+ assign = nil
123
+
124
+ name = external_name.to_s
125
+
126
+ # TODO - check out regexp to do this work better plus Inflections ??
127
+ # Want to be able to handle any of ["Count On hand", 'count_on_hand', "Count OnHand", "COUNT ONHand" etc]
128
+ [
129
+ name,
130
+ name.tableize,
131
+ name.gsub(' ', '_'),
132
+ name.gsub(' ', '_').downcase,
133
+ name.gsub(/(\s+)/, '_').downcase,
134
+ name.gsub(' ', ''),
135
+ name.gsub(' ', '').downcase,
136
+ name.gsub(' ', '_').underscore].each do |n|
137
+
138
+ assign = (assignments_for(klass).include?(n)) ? n : nil
139
+
140
+ return MethodDetail.new(name, klass, assign, :assignment, @@column_types[klass]) if(assign)
141
+
142
+ assign = (has_one_for(klass).include?(n)) ? n : nil
143
+
144
+ return MethodDetail.new(name, klass, assign, :has_one, @@column_types[klass]) if(assign)
145
+
146
+ assign = (has_many_for(klass).include?(n)) ? n : nil
147
+
148
+ return MethodDetail.new(name, klass, assign, :has_many, @@column_types[klass]) if(assign)
149
+
150
+ assign = (belongs_to_for(klass).include?(n)) ? n : nil
151
+
152
+ return MethodDetail.new(name, klass, assign, :belongs_to, @@column_types[klass]) if(assign)
153
+
154
+ end
155
+
156
+ nil
157
+ end
158
+
159
+ def self.clear
160
+ @@belongs_to.clear
161
+ @@has_many.clear
162
+ @@assignments.clear
163
+ @@column_types.clear
164
+ self.has_one.clear
165
+ end
166
+
167
+ def self.column_key(klass, column)
168
+ "#{klass.name}:#{column}"
169
+ end
170
+
171
+ # TODO - remove use of class variables - not good Ruby design
172
+ def self.belongs_to
173
+ @@belongs_to
174
+ end
175
+
176
+ def self.has_many
177
+ @@has_many
178
+ end
179
+
180
+ def self.has_one
181
+ @has_one ||= {}
182
+ @has_one
183
+ end
184
+
185
+ def self.assignments
186
+ @@assignments
187
+ end
188
+ def self.column_types
189
+ @@column_types
190
+ end
191
+
192
+
193
+ def self.belongs_to_for(klass)
194
+ @@belongs_to[klass] || []
195
+ end
196
+ def self.has_many_for(klass)
197
+ @@has_many[klass] || []
198
+ end
199
+
200
+ def self.has_one_for(klass)
201
+ self.has_one[klass] || []
202
+ end
203
+
204
+ def self.assignments_for(klass)
205
+ @@assignments[klass] || []
206
+ end
207
+ def self.column_type_for(klass, column)
208
+ @@column_types[klass] ? @@column_types[klass][column] : []
209
+ end
210
+
211
+ end
212
+
213
+ end