datashift 0.1.0 → 0.2.1

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 (86) hide show
  1. data/.document +5 -5
  2. data/LICENSE.txt +26 -26
  3. data/README.markdown +305 -303
  4. data/README.rdoc +19 -19
  5. data/Rakefile +93 -93
  6. data/VERSION +1 -1
  7. data/datashift-0.1.0.gem +0 -0
  8. data/datashift.gemspec +152 -136
  9. data/lib/applications/jruby/jexcel_file.rb +408 -408
  10. data/lib/applications/jruby/word.rb +79 -79
  11. data/lib/datashift.rb +152 -152
  12. data/lib/datashift/exceptions.rb +11 -11
  13. data/lib/datashift/file_definitions.rb +353 -353
  14. data/lib/datashift/mapping_file_definitions.rb +87 -87
  15. data/lib/datashift/method_detail.rb +275 -275
  16. data/lib/datashift/method_dictionary.rb +209 -209
  17. data/lib/datashift/method_mapper.rb +90 -90
  18. data/lib/generators/csv_generator.rb +36 -36
  19. data/lib/generators/excel_generator.rb +122 -122
  20. data/lib/generators/generator_base.rb +13 -13
  21. data/lib/helpers/core_ext/to_b.rb +24 -24
  22. data/lib/helpers/spree_helper.rb +153 -155
  23. data/lib/java/poi-3.7/LICENSE +507 -507
  24. data/lib/java/poi-3.7/NOTICE +21 -21
  25. data/lib/java/poi-3.7/RELEASE_NOTES.txt +115 -115
  26. data/lib/loaders/csv_loader.rb +98 -98
  27. data/lib/loaders/excel_loader.rb +155 -155
  28. data/lib/loaders/loader_base.rb +420 -420
  29. data/lib/loaders/spreadsheet_loader.rb +136 -136
  30. data/lib/loaders/spree/image_loader.rb +63 -64
  31. data/lib/loaders/spree/product_loader.rb +248 -250
  32. data/public/spree/products/large/DEMO_001_ror_bag.jpeg +0 -0
  33. data/public/spree/products/large/DEMO_002_Powerstation.jpg +0 -0
  34. data/public/spree/products/large/DEMO_003_ror_mug.jpeg +0 -0
  35. data/public/spree/products/mini/DEMO_001_ror_bag.jpeg +0 -0
  36. data/public/spree/products/mini/DEMO_002_Powerstation.jpg +0 -0
  37. data/public/spree/products/mini/DEMO_003_ror_mug.jpeg +0 -0
  38. data/public/spree/products/original/DEMO_001_ror_bag.jpeg +0 -0
  39. data/public/spree/products/original/DEMO_002_Powerstation.jpg +0 -0
  40. data/public/spree/products/original/DEMO_003_ror_mug.jpeg +0 -0
  41. data/public/spree/products/product/DEMO_001_ror_bag.jpeg +0 -0
  42. data/public/spree/products/product/DEMO_002_Powerstation.jpg +0 -0
  43. data/public/spree/products/product/DEMO_003_ror_mug.jpeg +0 -0
  44. data/public/spree/products/small/DEMO_001_ror_bag.jpeg +0 -0
  45. data/public/spree/products/small/DEMO_002_Powerstation.jpg +0 -0
  46. data/public/spree/products/small/DEMO_003_ror_mug.jpeg +0 -0
  47. data/spec/csv_loader_spec.rb +30 -30
  48. data/spec/datashift_spec.rb +26 -26
  49. data/spec/db/migrate/20110803201325_create_test_bed.rb +85 -85
  50. data/spec/excel_exporter_spec.rb +78 -78
  51. data/spec/excel_generator_spec.rb +78 -78
  52. data/spec/excel_loader_spec.rb +223 -223
  53. data/spec/file_definitions.rb +141 -141
  54. data/spec/fixtures/ProjectsDefaults.yml +29 -29
  55. data/spec/fixtures/config/database.yml +27 -24
  56. data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
  57. data/spec/fixtures/interact_models_db.sqlite +0 -0
  58. data/spec/fixtures/negative/SpreeProdMiss1Mandatory.csv +4 -4
  59. data/spec/fixtures/negative/SpreeProdMissManyMandatory.csv +4 -4
  60. data/spec/fixtures/spree/SpreeProducts.csv +4 -4
  61. data/spec/fixtures/spree/SpreeProductsMultiColumn.csv +4 -4
  62. data/spec/fixtures/spree/SpreeProductsSimple.csv +4 -4
  63. data/spec/fixtures/spree/SpreeProductsWithImages.csv +4 -0
  64. data/spec/fixtures/spree/SpreeZoneExample.csv +5 -5
  65. data/spec/fixtures/test_model_defs.rb +57 -57
  66. data/spec/loader_spec.rb +120 -120
  67. data/spec/method_dictionary_spec.rb +242 -242
  68. data/spec/method_mapper_spec.rb +41 -41
  69. data/spec/spec_helper.rb +116 -116
  70. data/spec/spree_generator_spec.rb +64 -64
  71. data/spec/spree_loader_spec.rb +324 -327
  72. data/spec/spree_method_mapping_spec.rb +214 -214
  73. data/tasks/config/seed_fu_product_template.erb +15 -15
  74. data/tasks/config/tidy_config.txt +12 -12
  75. data/tasks/db_tasks.rake +65 -65
  76. data/tasks/excel_generator.rake +78 -78
  77. data/tasks/file_tasks.rake +36 -36
  78. data/tasks/import/csv.rake +49 -49
  79. data/tasks/import/excel.rake +71 -71
  80. data/tasks/spree/image_load.rake +108 -108
  81. data/tasks/spree/product_loader.rake +43 -43
  82. data/tasks/word_to_seedfu.rake +166 -166
  83. data/test/helper.rb +18 -18
  84. data/test/test_interact.rb +7 -7
  85. metadata +22 -3
  86. data/spec/fixtures/interact_spree_db.sqlite +0 -0
@@ -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 ArgumentError.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 then self.store(values[0], values[1])
41
- when 3 then self.store([values[0], values[1]], values[2])
42
- when 4 then self.store([values[0], values[1]],[values[2], values[3]])
43
- else
44
- raise ArgumentError.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 ArgumentError.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 ArgumentError.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 then self.store(values[0], values[1])
41
+ when 3 then self.store([values[0], values[1]], values[2])
42
+ when 4 then self.store([values[0], values[1]],[values[2], values[3]])
43
+ else
44
+ raise ArgumentError.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 ArgumentError.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
@@ -1,276 +1,276 @@
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 DataShift
16
-
17
- # Stores MethodDetails for a class mapped by type
18
- class MethodDetailsManager
19
-
20
- attr_reader :method_details
21
-
22
- def initialize( klass )
23
- @parent_class = klass
24
- @method_details = {}
25
- end
26
-
27
- def add(method_details)
28
- @method_details[method_details.operator_type] ||= {}
29
- @method_details[method_details.operator_type][method_details.name] = method_details
30
- end
31
-
32
- def <<(method_details)
33
- add(method_details)
34
- end
35
-
36
- def find(name, type)
37
- method_details = get(type)
38
-
39
- method_details ? method_details[name] : nil
40
- end
41
-
42
- # type is expected to be one of MethodDetail::type_enum
43
- def get( type )
44
- @method_details[type]
45
- end
46
-
47
- end
48
-
49
- class MethodDetail
50
-
51
- def self.type_enum
52
- @type_enum ||= Set[:assignment, :belongs_to, :has_one, :has_many]
53
- @type_enum
54
- end
55
-
56
- # When looking up an association, try each of these in turn till a match
57
- # i.e find_by_name .. find_by_title and so on, lastly try the raw id
58
- @@insistent_find_by_list ||= [:name, :title, :id]
59
-
60
- # Name is the raw, client supplied name
61
- attr_reader :name, :col_type, :current_value
62
-
63
- attr_reader :operator, :operator_type
64
-
65
- # TODO make it a list/primary keys
66
- attr_accessor :find_by_operator
67
-
68
- # Store the raw (client supplied) name against the active record klass(model).
69
- # Operator is the associated method call on klass,
70
- # so client name maybe Price but true operator is price
71
- #
72
- # col_types can typically be derived from klass.columns - set of ActiveRecord::ConnectionAdapters::Column
73
-
74
- def initialize(client_name, klass, operator, type, col_types = {}, find_by_operator = nil )
75
- @klass, @name = klass, client_name
76
- @find_by_operator = find_by_operator
77
-
78
- if( MethodDetail::type_enum.member?(type.to_sym) )
79
- @operator_type = type.to_sym
80
- else
81
- raise "Bad operator Type #{type} passed to Method Detail"
82
- end
83
-
84
- @operator = operator
85
-
86
- # Note : Not all assignments will currently have a column type, for example
87
- # those that are derived from a delegate_belongs_to
88
- if(col_types.empty?)
89
- @col_type = klass.columns.find{ |col| col.name == operator }
90
- else
91
- @col_type = col_types[operator]
92
- end
93
- end
94
-
95
-
96
- # Return the actual operator's name for supplied method type
97
- # where type one of :assignment, :has_one, :belongs_to, :has_many etc
98
- def operator_for( type )
99
- return operator if(@operator_type == type.to_sym)
100
- nil
101
- end
102
-
103
- def operator?(name)
104
- operator == name
105
- end
106
-
107
- # Return the operator's expected class name, if can be derived, else nil
108
- def operator_class_name()
109
- @operator_class_name ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
110
- begin
111
- Kernel.const_get(operator.classify)
112
- operator.classify
113
- rescue; ""; end
114
-
115
- elsif(@col_type)
116
- @col_type.type.to_s.classify
117
- else
118
- ""
119
- end
120
-
121
- @operator_class_name
122
- end
123
-
124
- # Return the operator's expected class, if can be derived, else nil
125
- def operator_class()
126
- @operator_class ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
127
- begin
128
- Kernel.const_get(operator.classify)
129
- rescue; ""; end
130
-
131
- elsif(@col_type)
132
- begin
133
- Kernel.const_get(@col_type.type.to_s.classify)
134
- rescue; nil; end
135
- else
136
- nil
137
- end
138
-
139
- @operator_class
140
- end
141
-
142
-
143
- def assign(record, value )
144
-
145
- @current_value = value
146
-
147
- # logger.info("WARNING nil value supplied for Column [#{@name}]") if(@current_value.nil?)
148
-
149
- if( operator_for(:belongs_to) )
150
-
151
- #puts "DEBUG : BELONGS_TO : #{@name} : #{operator} - Lookup #{@current_value} in DB"
152
- insistent_belongs_to(record, @current_value)
153
-
154
- elsif( operator_for(:has_many) )
155
-
156
- #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
157
- if(value.is_a?(Array) || value.is_a?(operator_class))
158
- record.send(operator) << value
159
- else
160
- puts "ERROR #{value.class} - Not expected type for has_many #{operator} - cannot assign"
161
- # TODO - Not expected type - maybe try to look it up somehow ?"
162
- #insistent_has_many(record, @current_value)
163
- end
164
-
165
- elsif( operator_for(:has_one) )
166
-
167
- #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
168
- if(value.is_a?(operator_class))
169
- record.send(operator + '=', value)
170
- else
171
- puts "ERROR #{value.class} - Not expected type for has_one #{operator} - cannot assign"
172
- # TODO - Not expected type - maybe try to look it up somehow ?"
173
- #insistent_has_many(record, @current_value)
174
- end
175
-
176
- elsif( operator_for(:assignment) && @col_type )
177
- #puts "DEBUG : COl TYPE defined for #{@name} : #{@assignment} => #{@current_value} #{@col_type.type}"
178
- #puts "DEBUG : COl TYPE CAST: #{@current_value} => #{@col_type.type_cast( @current_value ).inspect}"
179
- record.send( operator + '=' , @col_type.type_cast( @current_value ) )
180
-
181
- #puts "DEBUG : MethodDetails Assignment RESULT: #{record.send(operator)}"
182
-
183
- elsif( operator_for(:assignment) )
184
- #puts "DEBUG : Brute force assignment of value #{@current_value} supplied for Column [#{@name}]"
185
- # brute force case for assignments without a column type (which enables us to do correct type_cast)
186
- # so in this case, attempt straightforward assignment then if that fails, basic ops such as to_s, to_i, to_f etc
187
- insistent_assignment(record, @current_value)
188
- else
189
- puts "WARNING: No operator found for assignment on #{self.inspect} for Column [#{@name}]"
190
- end
191
- end
192
-
193
- def pp
194
- "#{@name} => #{operator}"
195
- end
196
-
197
-
198
- def self.insistent_method_list
199
- @insistent_method_list ||= [:to_s, :to_i, :to_f, :to_b]
200
- @insistent_method_list
201
- end
202
-
203
- private
204
-
205
- # Attempt to find the associated object via id, name, title ....
206
- def insistent_belongs_to( record, value )
207
-
208
- if( value.class == operator_class)
209
- record.send(operator) << value
210
- else
211
-
212
- @@insistent_find_by_list.each do |x|
213
- begin
214
- next unless operator_class.respond_to?( "find_by_#{x}" )
215
- item = operator_class.send( "find_by_#{x}", value)
216
- if(item)
217
- record.send(operator + '=', item)
218
- break
219
- end
220
- rescue => e
221
- puts "ERROR: #{e.inspect}"
222
- if(x == MethodDetail::insistent_method_list.last)
223
- raise "I'm sorry I have failed to assign [#{value}] to #{@assignment}" unless value.nil?
224
- end
225
- end
226
- end
227
- end
228
- end
229
-
230
- # Attempt to find the associated object via id, name, title ....
231
- def insistent_has_many( record, value )
232
-
233
- if( value.class == operator_class)
234
- record.send(operator) << value
235
- else
236
- @@insistent_find_by_list.each do |x|
237
- begin
238
- item = operator_class.send( "find_by_#{x}", value)
239
- if(item)
240
- record.send(operator) << item
241
- break
242
- end
243
- rescue => e
244
- puts "ERROR: #{e.inspect}"
245
- if(x == MethodDetail::insistent_method_list.last)
246
- raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
247
- end
248
- end
249
- end
250
- end
251
- end
252
-
253
- def insistent_assignment( record, value )
254
- #puts "DEBUG: RECORD CLASS #{record.class}"
255
- op = operator + '='
256
-
257
- begin
258
- record.send(op, value)
259
- rescue => e
260
- MethodDetail::insistent_method_list.each do |f|
261
- begin
262
- record.send(op, value.send( f) )
263
- break
264
- rescue => e
265
- #puts "DEBUG: insistent_assignment: #{e.inspect}"
266
- if f == MethodDetail::insistent_method_list.last
267
- puts "I'm sorry I have failed to assign [#{value}] to #{operator}"
268
- raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
269
- end
270
- end
271
- end
272
- end
273
- end
274
- end
275
-
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 DataShift
16
+
17
+ # Stores MethodDetails for a class mapped by type
18
+ class MethodDetailsManager
19
+
20
+ attr_reader :method_details
21
+
22
+ def initialize( klass )
23
+ @parent_class = klass
24
+ @method_details = {}
25
+ end
26
+
27
+ def add(method_details)
28
+ @method_details[method_details.operator_type] ||= {}
29
+ @method_details[method_details.operator_type][method_details.name] = method_details
30
+ end
31
+
32
+ def <<(method_details)
33
+ add(method_details)
34
+ end
35
+
36
+ def find(name, type)
37
+ method_details = get(type)
38
+
39
+ method_details ? method_details[name] : nil
40
+ end
41
+
42
+ # type is expected to be one of MethodDetail::type_enum
43
+ def get( type )
44
+ @method_details[type]
45
+ end
46
+
47
+ end
48
+
49
+ class MethodDetail
50
+
51
+ def self.type_enum
52
+ @type_enum ||= Set[:assignment, :belongs_to, :has_one, :has_many]
53
+ @type_enum
54
+ end
55
+
56
+ # When looking up an association, try each of these in turn till a match
57
+ # i.e find_by_name .. find_by_title and so on, lastly try the raw id
58
+ @@insistent_find_by_list ||= [:name, :title, :id]
59
+
60
+ # Name is the raw, client supplied name
61
+ attr_reader :name, :col_type, :current_value
62
+
63
+ attr_reader :operator, :operator_type
64
+
65
+ # TODO make it a list/primary keys
66
+ attr_accessor :find_by_operator
67
+
68
+ # Store the raw (client supplied) name against the active record klass(model).
69
+ # Operator is the associated method call on klass,
70
+ # so client name maybe Price but true operator is price
71
+ #
72
+ # col_types can typically be derived from klass.columns - set of ActiveRecord::ConnectionAdapters::Column
73
+
74
+ def initialize(client_name, klass, operator, type, col_types = {}, find_by_operator = nil )
75
+ @klass, @name = klass, client_name
76
+ @find_by_operator = find_by_operator
77
+
78
+ if( MethodDetail::type_enum.member?(type.to_sym) )
79
+ @operator_type = type.to_sym
80
+ else
81
+ raise "Bad operator Type #{type} passed to Method Detail"
82
+ end
83
+
84
+ @operator = operator
85
+
86
+ # Note : Not all assignments will currently have a column type, for example
87
+ # those that are derived from a delegate_belongs_to
88
+ if(col_types.empty?)
89
+ @col_type = klass.columns.find{ |col| col.name == operator }
90
+ else
91
+ @col_type = col_types[operator]
92
+ end
93
+ end
94
+
95
+
96
+ # Return the actual operator's name for supplied method type
97
+ # where type one of :assignment, :has_one, :belongs_to, :has_many etc
98
+ def operator_for( type )
99
+ return operator if(@operator_type == type.to_sym)
100
+ nil
101
+ end
102
+
103
+ def operator?(name)
104
+ operator == name
105
+ end
106
+
107
+ # Return the operator's expected class name, if can be derived, else nil
108
+ def operator_class_name()
109
+ @operator_class_name ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
110
+ begin
111
+ Kernel.const_get(operator.classify)
112
+ operator.classify
113
+ rescue; ""; end
114
+
115
+ elsif(@col_type)
116
+ @col_type.type.to_s.classify
117
+ else
118
+ ""
119
+ end
120
+
121
+ @operator_class_name
122
+ end
123
+
124
+ # Return the operator's expected class, if can be derived, else nil
125
+ def operator_class()
126
+ @operator_class ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
127
+ begin
128
+ Kernel.const_get(operator.classify)
129
+ rescue; ""; end
130
+
131
+ elsif(@col_type)
132
+ begin
133
+ Kernel.const_get(@col_type.type.to_s.classify)
134
+ rescue; nil; end
135
+ else
136
+ nil
137
+ end
138
+
139
+ @operator_class
140
+ end
141
+
142
+
143
+ def assign(record, value )
144
+
145
+ @current_value = value
146
+
147
+ # logger.info("WARNING nil value supplied for Column [#{@name}]") if(@current_value.nil?)
148
+
149
+ if( operator_for(:belongs_to) )
150
+
151
+ #puts "DEBUG : BELONGS_TO : #{@name} : #{operator} - Lookup #{@current_value} in DB"
152
+ insistent_belongs_to(record, @current_value)
153
+
154
+ elsif( operator_for(:has_many) )
155
+
156
+ #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
157
+ if(value.is_a?(Array) || value.is_a?(operator_class))
158
+ record.send(operator) << value
159
+ else
160
+ puts "ERROR #{value.class} - Not expected type for has_many #{operator} - cannot assign"
161
+ # TODO - Not expected type - maybe try to look it up somehow ?"
162
+ #insistent_has_many(record, @current_value)
163
+ end
164
+
165
+ elsif( operator_for(:has_one) )
166
+
167
+ #puts "DEBUG : HAS_MANY : #{@name} : #{operator}(#{operator_class}) - Lookup #{@current_value} in DB"
168
+ if(value.is_a?(operator_class))
169
+ record.send(operator + '=', value)
170
+ else
171
+ puts "ERROR #{value.class} - Not expected type for has_one #{operator} - cannot assign"
172
+ # TODO - Not expected type - maybe try to look it up somehow ?"
173
+ #insistent_has_many(record, @current_value)
174
+ end
175
+
176
+ elsif( operator_for(:assignment) && @col_type )
177
+ #puts "DEBUG : COl TYPE defined for #{@name} : #{@assignment} => #{@current_value} #{@col_type.type}"
178
+ #puts "DEBUG : COl TYPE CAST: #{@current_value} => #{@col_type.type_cast( @current_value ).inspect}"
179
+ record.send( operator + '=' , @col_type.type_cast( @current_value ) )
180
+
181
+ #puts "DEBUG : MethodDetails Assignment RESULT: #{record.send(operator)}"
182
+
183
+ elsif( operator_for(:assignment) )
184
+ #puts "DEBUG : Brute force assignment of value #{@current_value} supplied for Column [#{@name}]"
185
+ # brute force case for assignments without a column type (which enables us to do correct type_cast)
186
+ # so in this case, attempt straightforward assignment then if that fails, basic ops such as to_s, to_i, to_f etc
187
+ insistent_assignment(record, @current_value)
188
+ else
189
+ puts "WARNING: No operator found for assignment on #{self.inspect} for Column [#{@name}]"
190
+ end
191
+ end
192
+
193
+ def pp
194
+ "#{@name} => #{operator}"
195
+ end
196
+
197
+
198
+ def self.insistent_method_list
199
+ @insistent_method_list ||= [:to_s, :to_i, :to_f, :to_b]
200
+ @insistent_method_list
201
+ end
202
+
203
+ private
204
+
205
+ # Attempt to find the associated object via id, name, title ....
206
+ def insistent_belongs_to( record, value )
207
+
208
+ if( value.class == operator_class)
209
+ record.send(operator) << value
210
+ else
211
+
212
+ @@insistent_find_by_list.each do |x|
213
+ begin
214
+ next unless operator_class.respond_to?( "find_by_#{x}" )
215
+ item = operator_class.send( "find_by_#{x}", value)
216
+ if(item)
217
+ record.send(operator + '=', item)
218
+ break
219
+ end
220
+ rescue => e
221
+ puts "ERROR: #{e.inspect}"
222
+ if(x == MethodDetail::insistent_method_list.last)
223
+ raise "I'm sorry I have failed to assign [#{value}] to #{@assignment}" unless value.nil?
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+
230
+ # Attempt to find the associated object via id, name, title ....
231
+ def insistent_has_many( record, value )
232
+
233
+ if( value.class == operator_class)
234
+ record.send(operator) << value
235
+ else
236
+ @@insistent_find_by_list.each do |x|
237
+ begin
238
+ item = operator_class.send( "find_by_#{x}", value)
239
+ if(item)
240
+ record.send(operator) << item
241
+ break
242
+ end
243
+ rescue => e
244
+ puts "ERROR: #{e.inspect}"
245
+ if(x == MethodDetail::insistent_method_list.last)
246
+ raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ def insistent_assignment( record, value )
254
+ #puts "DEBUG: RECORD CLASS #{record.class}"
255
+ op = operator + '='
256
+
257
+ begin
258
+ record.send(op, value)
259
+ rescue => e
260
+ MethodDetail::insistent_method_list.each do |f|
261
+ begin
262
+ record.send(op, value.send( f) )
263
+ break
264
+ rescue => e
265
+ #puts "DEBUG: insistent_assignment: #{e.inspect}"
266
+ if f == MethodDetail::insistent_method_list.last
267
+ puts "I'm sorry I have failed to assign [#{value}] to #{operator}"
268
+ raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
269
+ end
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end
275
+
276
276
  end