datashift 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/.document +5 -5
  2. data/LICENSE.txt +26 -26
  3. data/README.markdown +326 -305
  4. data/README.rdoc +19 -19
  5. data/Rakefile +86 -93
  6. data/VERSION +1 -1
  7. data/datashift.gemspec +163 -152
  8. data/lib/applications/jruby/jexcel_file.rb +410 -408
  9. data/lib/applications/jruby/word.rb +79 -79
  10. data/lib/datashift.rb +183 -152
  11. data/lib/datashift/exceptions.rb +11 -11
  12. data/lib/datashift/file_definitions.rb +353 -353
  13. data/lib/datashift/mapping_file_definitions.rb +87 -87
  14. data/lib/datashift/method_detail.rb +293 -275
  15. data/lib/datashift/method_dictionary.rb +208 -209
  16. data/lib/datashift/method_mapper.rb +90 -90
  17. data/lib/datashift/model_mapper.rb +27 -0
  18. data/lib/exporters/csv_exporter.rb +36 -0
  19. data/lib/exporters/excel_exporter.rb +116 -0
  20. data/lib/exporters/exporter_base.rb +15 -0
  21. data/lib/generators/csv_generator.rb +36 -36
  22. data/lib/generators/excel_generator.rb +106 -122
  23. data/lib/generators/generator_base.rb +13 -13
  24. data/lib/helpers/core_ext/to_b.rb +24 -24
  25. data/lib/helpers/rake_utils.rb +42 -0
  26. data/lib/helpers/spree_helper.rb +194 -153
  27. data/lib/java/poi-3.7/LICENSE +507 -507
  28. data/lib/java/poi-3.7/NOTICE +21 -21
  29. data/lib/java/poi-3.7/RELEASE_NOTES.txt +115 -115
  30. data/lib/loaders/csv_loader.rb +98 -98
  31. data/lib/loaders/excel_loader.rb +155 -155
  32. data/lib/loaders/loader_base.rb +420 -420
  33. data/lib/loaders/spreadsheet_loader.rb +136 -136
  34. data/lib/loaders/spree/image_loader.rb +67 -63
  35. data/lib/loaders/spree/product_loader.rb +289 -248
  36. data/lib/thor/generate_excel.thor +54 -0
  37. data/sandbox/app/controllers/application_controller.rb +3 -0
  38. data/sandbox/config/application.rb +43 -0
  39. data/sandbox/config/database.yml +34 -0
  40. data/sandbox/config/environment.rb +7 -0
  41. data/sandbox/config/environments/development.rb +30 -0
  42. data/spec/csv_loader_spec.rb +30 -30
  43. data/spec/datashift_spec.rb +26 -26
  44. data/spec/db/migrate/20110803201325_create_test_bed.rb +85 -85
  45. data/spec/excel_exporter_spec.rb +78 -78
  46. data/spec/excel_generator_spec.rb +78 -78
  47. data/spec/excel_loader_spec.rb +223 -223
  48. data/spec/file_definitions.rb +141 -141
  49. data/spec/fixtures/ProjectsDefaults.yml +29 -29
  50. data/spec/fixtures/config/database.yml +27 -27
  51. data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
  52. data/spec/fixtures/datashift_test_models_db.sqlite +0 -0
  53. data/spec/fixtures/negative/SpreeProdMiss1Mandatory.csv +4 -4
  54. data/spec/fixtures/negative/SpreeProdMissManyMandatory.csv +4 -4
  55. data/spec/fixtures/spree/SpreeProducts.csv +4 -4
  56. data/spec/fixtures/spree/SpreeProducts.xls +0 -0
  57. data/spec/fixtures/spree/SpreeProductsMultiColumn.csv +4 -4
  58. data/spec/fixtures/spree/SpreeProductsMultiColumn.xls +0 -0
  59. data/spec/fixtures/spree/SpreeProductsSimple.csv +4 -4
  60. data/spec/fixtures/spree/SpreeProductsWithImages.csv +4 -4
  61. data/spec/fixtures/spree/SpreeZoneExample.csv +5 -5
  62. data/spec/fixtures/test_model_defs.rb +57 -57
  63. data/spec/loader_spec.rb +120 -120
  64. data/spec/method_dictionary_spec.rb +242 -242
  65. data/spec/method_mapper_spec.rb +41 -41
  66. data/spec/spec_helper.rb +154 -116
  67. data/spec/spree_exporter_spec.rb +67 -0
  68. data/spec/spree_generator_spec.rb +77 -64
  69. data/spec/spree_loader_spec.rb +363 -324
  70. data/spec/spree_method_mapping_spec.rb +218 -214
  71. data/tasks/config/seed_fu_product_template.erb +15 -15
  72. data/tasks/config/tidy_config.txt +12 -12
  73. data/tasks/{excel_generator.rake → export/excel_generator.rake} +101 -78
  74. data/tasks/file_tasks.rake +36 -36
  75. data/tasks/import/csv.rake +50 -49
  76. data/tasks/import/excel.rake +74 -71
  77. data/tasks/spree/image_load.rake +108 -108
  78. data/tasks/spree/product_loader.rake +43 -43
  79. data/tasks/word_to_seedfu.rake +166 -166
  80. data/test/helper.rb +18 -18
  81. data/test/test_interact.rb +7 -7
  82. metadata +16 -8
  83. data/datashift-0.1.0.gem +0 -0
  84. data/tasks/db_tasks.rake +0 -66
@@ -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,294 @@
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
+ @method_details_list = {}
26
+ end
27
+
28
+ def add(method_details)
29
+ @method_details[method_details.operator_type] ||= {}
30
+ @method_details_list[method_details.operator_type] ||= []
31
+
32
+ @method_details[method_details.operator_type][method_details.name] = method_details
33
+ @method_details_list[method_details.operator_type] << method_details
34
+ @method_details_list[method_details.operator_type].uniq!
35
+ end
36
+
37
+ def <<(method_details)
38
+ add(method_details)
39
+ end
40
+
41
+ def find(name, type)
42
+ method_details = get(type)
43
+
44
+ method_details ? method_details[name] : nil
45
+ end
46
+
47
+ # type is expected to be one of MethodDetail::supportedtype_enum
48
+ def get( type )
49
+ @method_details[type]
50
+ end
51
+
52
+ def get_list( type )
53
+ @method_details_list[type]
54
+ end
55
+
56
+ end
57
+
58
+ class MethodDetail
59
+
60
+ def self.supported_types_enum
61
+ @type_enum ||= Set[:assignment, :belongs_to, :has_one, :has_many]
62
+ @type_enum
63
+ end
64
+
65
+ # When looking up an association, try each of these in turn till a match
66
+ # i.e find_by_name .. find_by_title and so on, lastly try the raw id
67
+ @@insistent_find_by_list ||= [:name, :title, :id]
68
+
69
+ # Name is the raw, client supplied name
70
+ attr_reader :name, :col_type, :current_value
71
+
72
+ attr_reader :operator, :operator_type
73
+
74
+ # TODO make it a list/primary keys
75
+ attr_accessor :find_by_operator
76
+
77
+ # Store the raw (client supplied) name against the active record klass(model).
78
+ # Operator is the associated method call on klass,
79
+ # so client name maybe Price but true operator is price
80
+ #
81
+ # col_types can typically be derived from klass.columns - set of ActiveRecord::ConnectionAdapters::Column
82
+
83
+ def initialize(client_name, klass, operator, type, col_types = {}, find_by_operator = nil )
84
+ @klass, @name = klass, client_name
85
+ @find_by_operator = find_by_operator
86
+
87
+ if( MethodDetail::supported_types_enum.member?(type.to_sym) )
88
+ @operator_type = type.to_sym
89
+ else
90
+ raise "Bad operator Type #{type} passed to Method Detail"
91
+ end
92
+
93
+ @operator = operator
94
+
95
+ # Note : Not all assignments will currently have a column type, for example
96
+ # those that are derived from a delegate_belongs_to
97
+ if(col_types.empty?)
98
+ @col_type = klass.columns.find{ |col| col.name == operator }
99
+ else
100
+ @col_type = col_types[operator]
101
+ end
102
+ end
103
+
104
+
105
+ # Return the actual operator's name for supplied method type
106
+ # where type one of :assignment, :has_one, :belongs_to, :has_many etc
107
+ def operator_for( type )
108
+ return operator if(@operator_type == type.to_sym)
109
+ nil
110
+ end
111
+
112
+ def operator?(name)
113
+ operator == name
114
+ end
115
+
116
+ # Return the operator's expected class name, if can be derived, else nil
117
+ def operator_class_name()
118
+ @operator_class_name ||= if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
119
+ begin
120
+ Kernel.const_get(operator.classify)
121
+ operator.classify
122
+ rescue; ""; end
123
+
124
+ elsif(@col_type)
125
+ @col_type.type.to_s.classify
126
+ else
127
+ ""
128
+ end
129
+
130
+ @operator_class_name
131
+ end
132
+
133
+ # Return the operator's expected class, if can be derived, else nil
134
+ def operator_class()
135
+ @operator_class ||= get_operator_class()
136
+ @operator_class
137
+ end
138
+
139
+ def assign(record, value )
140
+
141
+ @current_value = value
142
+
143
+ # logger.info("WARNING nil value supplied for Column [#{@name}]") if(@current_value.nil?)
144
+
145
+ if( operator_for(:belongs_to) )
146
+
147
+ #puts "DEBUG : BELONGS_TO : #{@name} : #{operator} - Lookup #{@current_value} in DB"
148
+ insistent_belongs_to(record, @current_value)
149
+
150
+ elsif( operator_for(:has_many) )
151
+
152
+ puts "DEBUG : VALUE TYPE [#{value.class.name.include?(operator.classify)}] [#{ModelMapper.class_from_string(value.class.name)}]" unless(value.is_a?(Array))
153
+
154
+ # The include? check is best I can come up with right now .. to handle module/namespaces
155
+ # TODO - can we determine the real class type of an association
156
+ # e.g given a association taxons, which operator.classify gives us Taxon, but actually it's Spree::Taxon
157
+ # so how do we get from 'taxons' to Spree::Taxons ? .. check if further info in reflect_on_all_associations
158
+
159
+ if(value.is_a?(Array) || value.class.name.include?(operator.classify))
160
+ record.send(operator) << value
161
+ else
162
+ puts "ERROR #{value.class} - Not expected type for has_many #{operator} - cannot assign"
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
+
275
+ private
276
+ # Return the operator's expected class, if can be derived, else nil
277
+ def get_operator_class()
278
+ if(operator_for(:has_many) || operator_for(:belongs_to) || operator_for(:has_one))
279
+ begin
280
+ Kernel.const_get(operator.classify)
281
+ rescue; nil; end
282
+
283
+ elsif(@col_type)
284
+ begin
285
+ Kernel.const_get(@col_type.type.to_s.classify)
286
+ rescue; nil; end
287
+ else
288
+ nil
289
+ end
290
+ end
291
+
292
+ end
293
+
276
294
  end