ar_loader 0.0.6 → 0.0.8

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