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,210 +1,210 @@
1
- # Copyright:: (c) Autotelik Media Ltd 2012
2
- # Author :: Tom Statter
3
- # Date :: Aug 2010
4
- # License:: MIT
5
- #
6
- # Details:: A cache type class that stores details of all possible associations on AR classes.
7
- #
8
- require 'method_detail'
9
-
10
- module DataShift
11
-
12
- class MethodDictionary
13
-
14
- def initialize
15
- end
16
-
17
-
18
- # Create simple picture of all the operator names for assignment available on an AR model,
19
- # grouped by type of association (includes belongs_to and has_many which provides both << and = )
20
- # Options:
21
- # :reload => clear caches and re-perform lookup
22
- # :instance_methods => if true include instance method type assignment operators as well as model's pure columns
23
- #
24
- def self.find_operators(klass, options = {} )
25
-
26
- # Find the has_many associations which can be populated via <<
27
- if( options[:reload] || has_many[klass].nil? )
28
- has_many[klass] = klass.reflect_on_all_associations(:has_many).map { |i| i.name.to_s }
29
- klass.reflect_on_all_associations(:has_and_belongs_to_many).inject(has_many[klass]) { |x,i| x << i.name.to_s }
30
- end
31
- # puts "DEBUG: Has Many Associations:", has_many[klass].inspect
32
-
33
- # Find the belongs_to associations which can be populated via Model.belongs_to_name = OtherArModelObject
34
- if( options[:reload] || belongs_to[klass].nil? )
35
- belongs_to[klass] = klass.reflect_on_all_associations(:belongs_to).map { |i| i.name.to_s }
36
- end
37
-
38
- #puts "Belongs To Associations:", belongs_to[klass].inspect
39
-
40
- # Find the has_one associations which can be populated via Model.has_one_name = OtherArModelObject
41
- if( options[:reload] || self.has_one[klass].nil? )
42
- has_one[klass] = klass.reflect_on_all_associations(:has_one).map { |i| i.name.to_s }
43
- end
44
-
45
- #puts "has_one Associations:", self.has_one[klass].inspect
46
-
47
- # Find the model's column associations which can be populated via xxxxxx= value
48
- # Note, not all reflections return method names in same style so we convert all to
49
- # the raw form i.e without the '=' for consistency
50
- if( options[:reload] || assignments[klass].nil? )
51
-
52
- assignments[klass] = klass.column_names
53
-
54
- if(options[:instance_methods] == true)
55
- setters = klass.instance_methods.grep(/\w+=/).collect {|x| x.to_s }
56
-
57
- if(klass.respond_to? :defined_activerecord_methods)
58
- setters = setters - klass.defined_activerecord_methods.to_a
59
- end
60
-
61
- # get into same format as other names
62
- assignments[klass] += setters.map{|i| i.gsub(/=/, '')}
63
- end
64
-
65
- assignments[klass] -= has_many[klass] if(has_many[klass])
66
- assignments[klass] -= belongs_to[klass] if(belongs_to[klass])
67
- assignments[klass] -= self.has_one[klass] if(self.has_one[klass])
68
-
69
- assignments[klass].uniq!
70
-
71
- assignments[klass].each do |assign|
72
- column_types[klass] ||= {}
73
- column_def = klass.columns.find{ |col| col.name == assign }
74
- column_types[klass].merge!( assign => column_def) if column_def
75
- end
76
- end
77
- end
78
-
79
-
80
- # Build a thorough and usable picture of the operators by building dictionary of our MethodDetail
81
- # objects which can be used to import/export data to objects of type 'klass'
82
- #
83
- def self.build_method_details( klass )
84
- method_details_mgr = MethodDetailsManager.new( klass )
85
-
86
- assignments_for(klass).each do |n|
87
- method_details_mgr << MethodDetail.new(n, klass, n, :assignment, column_types[klass])
88
- end
89
-
90
- has_one_for(klass).each do |n|
91
- method_details_mgr << MethodDetail.new(n, klass, n, :has_one)
92
- end
93
-
94
- has_many_for(klass).each do |n|
95
- method_details_mgr << MethodDetail.new(n, klass, n, :has_many)
96
- end
97
-
98
- belongs_to_for(klass).each do |n|
99
- method_details_mgr << MethodDetail.new(n, klass, n, :belongs_to)
100
- end
101
-
102
- method_details_mgrs[klass] = method_details_mgr
103
-
104
- end
105
-
106
- # Find the proper format of name, appropriate call + column type for a given name.
107
- # e.g Given users entry in spread sheet check for pluralization, missing underscores etc
108
- #
109
- # If not nil, returned method can be used directly in for example klass.new.send( call, .... )
110
- #
111
- def self.find_method_detail( klass, external_name )
112
- operator = nil
113
-
114
- # TODO - should we raise error to warn find_operators never called ?
115
- md_mgr = method_details_mgrs[klass] || MethodDetailsManager.new( klass )
116
-
117
- name = external_name.to_s
118
-
119
- # TODO - check out regexp to do this work better plus Inflections ??
120
- # Want to be able to handle any of ["Count On hand", 'count_on_hand', "Count OnHand", "COUNT ONHand" etc]
121
- [
122
- name,
123
- name.tableize,
124
- name.gsub(' ', '_'),
125
- name.gsub(' ', '_').downcase,
126
- name.gsub(/(\s+)/, '_').downcase,
127
- name.gsub(' ', ''),
128
- name.gsub(' ', '').downcase,
129
- name.gsub(' ', '_').underscore].each do |n|
130
-
131
- # Try each association type, returning first that contains matching operator with name n
132
-
133
- MethodDetail::type_enum.each do |t|
134
- method_detail = md_mgr.find(n, t)
135
- return method_detail if(method_detail)
136
- end
137
-
138
- end
139
-
140
- nil
141
- end
142
-
143
- def self.clear
144
- belongs_to.clear
145
- has_many.clear
146
- assignments.clear
147
- column_types.clear
148
- has_one.clear
149
- method_details_mgrs.clear
150
- end
151
-
152
- def self.column_key(klass, column)
153
- "#{klass.name}:#{column}"
154
- end
155
-
156
- def self.method_details_mgrs
157
- @method_details_mgrs ||= {}
158
- @method_details_mgrs
159
- end
160
-
161
- # TODO - remove use of class variables - not good Ruby design
162
- def self.belongs_to
163
- @belongs_to ||={}
164
- @belongs_to
165
- end
166
-
167
- def self.has_many
168
- @has_many ||= {}
169
- @has_many
170
- end
171
-
172
- def self.has_one
173
- @has_one ||= {}
174
- @has_one
175
- end
176
-
177
- def self.assignments
178
- @assignments ||= {}
179
- @assignments
180
- end
181
-
182
- def self.column_types
183
- @column_types ||= {}
184
- @column_types
185
- end
186
-
187
-
188
- def self.belongs_to_for(klass)
189
- belongs_to[klass] || []
190
- end
191
-
192
- def self.has_many_for(klass)
193
- has_many[klass] || []
194
- end
195
-
196
- def self.has_one_for(klass)
197
- has_one[klass] || []
198
- end
199
-
200
- def self.assignments_for(klass)
201
- assignments[klass] || []
202
- end
203
-
204
- def self.column_type_for(klass, column)
205
- column_types[klass] ? column_types[klass][column] : []
206
- end
207
-
208
- end
209
-
1
+ # Copyright:: (c) Autotelik Media Ltd 2012
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ # License:: MIT
5
+ #
6
+ # Details:: A cache type class that stores details of all possible associations on AR classes.
7
+ #
8
+ require 'method_detail'
9
+
10
+ module DataShift
11
+
12
+ class MethodDictionary
13
+
14
+ def initialize
15
+ end
16
+
17
+
18
+ # Create simple picture of all the operator names for assignment available on an AR model,
19
+ # grouped by type of association (includes belongs_to and has_many which provides both << and = )
20
+ # Options:
21
+ # :reload => clear caches and re-perform lookup
22
+ # :instance_methods => if true include instance method type assignment operators as well as model's pure columns
23
+ #
24
+ def self.find_operators(klass, options = {} )
25
+
26
+ # Find the has_many associations which can be populated via <<
27
+ if( options[:reload] || has_many[klass].nil? )
28
+ has_many[klass] = klass.reflect_on_all_associations(:has_many).map { |i| i.name.to_s }
29
+ klass.reflect_on_all_associations(:has_and_belongs_to_many).inject(has_many[klass]) { |x,i| x << i.name.to_s }
30
+ end
31
+ # puts "DEBUG: Has Many Associations:", has_many[klass].inspect
32
+
33
+ # Find the belongs_to associations which can be populated via Model.belongs_to_name = OtherArModelObject
34
+ if( options[:reload] || belongs_to[klass].nil? )
35
+ belongs_to[klass] = klass.reflect_on_all_associations(:belongs_to).map { |i| i.name.to_s }
36
+ end
37
+
38
+ #puts "Belongs To Associations:", belongs_to[klass].inspect
39
+
40
+ # Find the has_one associations which can be populated via Model.has_one_name = OtherArModelObject
41
+ if( options[:reload] || self.has_one[klass].nil? )
42
+ has_one[klass] = klass.reflect_on_all_associations(:has_one).map { |i| i.name.to_s }
43
+ end
44
+
45
+ #puts "has_one Associations:", self.has_one[klass].inspect
46
+
47
+ # Find the model's column associations which can be populated via xxxxxx= value
48
+ # Note, not all reflections return method names in same style so we convert all to
49
+ # the raw form i.e without the '=' for consistency
50
+ if( options[:reload] || assignments[klass].nil? )
51
+
52
+ assignments[klass] = klass.column_names
53
+
54
+ if(options[:instance_methods] == true)
55
+ setters = klass.instance_methods.grep(/\w+=/).collect {|x| x.to_s }
56
+
57
+ if(klass.respond_to? :defined_activerecord_methods)
58
+ setters = setters - klass.defined_activerecord_methods.to_a
59
+ end
60
+
61
+ # get into same format as other names
62
+ assignments[klass] += setters.map{|i| i.gsub(/=/, '')}
63
+ end
64
+
65
+ assignments[klass] -= has_many[klass] if(has_many[klass])
66
+ assignments[klass] -= belongs_to[klass] if(belongs_to[klass])
67
+ assignments[klass] -= self.has_one[klass] if(self.has_one[klass])
68
+
69
+ assignments[klass].uniq!
70
+
71
+ assignments[klass].each do |assign|
72
+ column_types[klass] ||= {}
73
+ column_def = klass.columns.find{ |col| col.name == assign }
74
+ column_types[klass].merge!( assign => column_def) if column_def
75
+ end
76
+ end
77
+ end
78
+
79
+
80
+ # Build a thorough and usable picture of the operators by building dictionary of our MethodDetail
81
+ # objects which can be used to import/export data to objects of type 'klass'
82
+ #
83
+ def self.build_method_details( klass )
84
+ method_details_mgr = MethodDetailsManager.new( klass )
85
+
86
+ assignments_for(klass).each do |n|
87
+ method_details_mgr << MethodDetail.new(n, klass, n, :assignment, column_types[klass])
88
+ end
89
+
90
+ has_one_for(klass).each do |n|
91
+ method_details_mgr << MethodDetail.new(n, klass, n, :has_one)
92
+ end
93
+
94
+ has_many_for(klass).each do |n|
95
+ method_details_mgr << MethodDetail.new(n, klass, n, :has_many)
96
+ end
97
+
98
+ belongs_to_for(klass).each do |n|
99
+ method_details_mgr << MethodDetail.new(n, klass, n, :belongs_to)
100
+ end
101
+
102
+ method_details_mgrs[klass] = method_details_mgr
103
+
104
+ end
105
+
106
+ # Find the proper format of name, appropriate call + column type for a given name.
107
+ # e.g Given users entry in spread sheet check for pluralization, missing underscores etc
108
+ #
109
+ # If not nil, returned method can be used directly in for example klass.new.send( call, .... )
110
+ #
111
+ def self.find_method_detail( klass, external_name )
112
+ operator = nil
113
+
114
+ # TODO - should we raise error to warn find_operators never called ?
115
+ md_mgr = method_details_mgrs[klass] || MethodDetailsManager.new( klass )
116
+
117
+ name = external_name.to_s
118
+
119
+ # TODO - check out regexp to do this work better plus Inflections ??
120
+ # Want to be able to handle any of ["Count On hand", 'count_on_hand', "Count OnHand", "COUNT ONHand" etc]
121
+ [
122
+ name,
123
+ name.tableize,
124
+ name.gsub(' ', '_'),
125
+ name.gsub(' ', '_').downcase,
126
+ name.gsub(/(\s+)/, '_').downcase,
127
+ name.gsub(' ', ''),
128
+ name.gsub(' ', '').downcase,
129
+ name.gsub(' ', '_').underscore].each do |n|
130
+
131
+ # Try each association type, returning first that contains matching operator with name n
132
+
133
+ MethodDetail::type_enum.each do |t|
134
+ method_detail = md_mgr.find(n, t)
135
+ return method_detail if(method_detail)
136
+ end
137
+
138
+ end
139
+
140
+ nil
141
+ end
142
+
143
+ def self.clear
144
+ belongs_to.clear
145
+ has_many.clear
146
+ assignments.clear
147
+ column_types.clear
148
+ has_one.clear
149
+ method_details_mgrs.clear
150
+ end
151
+
152
+ def self.column_key(klass, column)
153
+ "#{klass.name}:#{column}"
154
+ end
155
+
156
+ def self.method_details_mgrs
157
+ @method_details_mgrs ||= {}
158
+ @method_details_mgrs
159
+ end
160
+
161
+ # TODO - remove use of class variables - not good Ruby design
162
+ def self.belongs_to
163
+ @belongs_to ||={}
164
+ @belongs_to
165
+ end
166
+
167
+ def self.has_many
168
+ @has_many ||= {}
169
+ @has_many
170
+ end
171
+
172
+ def self.has_one
173
+ @has_one ||= {}
174
+ @has_one
175
+ end
176
+
177
+ def self.assignments
178
+ @assignments ||= {}
179
+ @assignments
180
+ end
181
+
182
+ def self.column_types
183
+ @column_types ||= {}
184
+ @column_types
185
+ end
186
+
187
+
188
+ def self.belongs_to_for(klass)
189
+ belongs_to[klass] || []
190
+ end
191
+
192
+ def self.has_many_for(klass)
193
+ has_many[klass] || []
194
+ end
195
+
196
+ def self.has_one_for(klass)
197
+ has_one[klass] || []
198
+ end
199
+
200
+ def self.assignments_for(klass)
201
+ assignments[klass] || []
202
+ end
203
+
204
+ def self.column_type_for(klass, column)
205
+ column_types[klass] ? column_types[klass][column] : []
206
+ end
207
+
208
+ end
209
+
210
210
  end
@@ -1,91 +1,91 @@
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
- require 'method_dictionary'
20
-
21
- module DataShift
22
-
23
- class MethodMapper
24
-
25
- attr_accessor :header_row, :headers
26
- attr_accessor :method_details, :missing_methods
27
-
28
-
29
- # As well as just the column name, support embedding find operators for that column
30
- # in the heading .. i.e Column header => 'BlogPosts:user_id'
31
- # ... association has many BlogPosts selected via find_by_user_id
32
- #
33
- def self.column_delim
34
- @column_delim ||= ':'
35
- @column_delim
36
- end
37
-
38
- def self.set_column_delim(x) @column_delim = x; end
39
-
40
-
41
- def initialize
42
- @method_details = []
43
- @headers = []
44
- end
45
-
46
- # Build complete picture of the methods whose names listed in method_list
47
- # Handles method names as defined by a user or in file headers where names may
48
- # not be exactly as required e.g handles capitalisation, white space, _ etc
49
- # Returns: Array of matching method_details
50
- #
51
- def map_inbound_to_methods( klass, method_list )
52
-
53
- @method_details, @missing_methods = [], []
54
-
55
- method_list.each do |name|
56
- x, lookup = name.split(MethodMapper::column_delim)
57
- md = MethodDictionary::find_method_detail( klass, x )
58
-
59
- # TODO be nice if we could cheeck that the assoc on klass responds to the specified
60
- # lookup key now (nice n early)
61
- # active_record_helper = "find_by_#{lookup}"
62
-
63
- md.find_by_operator = lookup if(lookup) # TODO and klass.x.respond_to?(active_record_helper))
64
- md ? @method_details << md : @missing_methods << x
65
- end
66
- #@method_details.compact! .. currently we may need to map via the index on @method_details so don't remove nils for now
67
- @method_details
68
- end
69
-
70
- # The raw client supplied names
71
- def method_names()
72
- @method_details.collect( &:name )
73
- end
74
-
75
- # The true operator names discovered from model
76
- def operator_names()
77
- @method_details.collect( &:operator )
78
- end
79
-
80
- # Returns true if discovered methods contain every operator in mandatory_list
81
- def contains_mandatory?( mandatory_list )
82
- [ [*mandatory_list] - operator_names].flatten.empty?
83
- end
84
-
85
- def missing_mandatory( mandatory_list )
86
- [ [*mandatory_list] - operator_names].flatten
87
- end
88
-
89
- end
90
-
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
+ require 'method_dictionary'
20
+
21
+ module DataShift
22
+
23
+ class MethodMapper
24
+
25
+ attr_accessor :header_row, :headers
26
+ attr_accessor :method_details, :missing_methods
27
+
28
+
29
+ # As well as just the column name, support embedding find operators for that column
30
+ # in the heading .. i.e Column header => 'BlogPosts:user_id'
31
+ # ... association has many BlogPosts selected via find_by_user_id
32
+ #
33
+ def self.column_delim
34
+ @column_delim ||= ':'
35
+ @column_delim
36
+ end
37
+
38
+ def self.set_column_delim(x) @column_delim = x; end
39
+
40
+
41
+ def initialize
42
+ @method_details = []
43
+ @headers = []
44
+ end
45
+
46
+ # Build complete picture of the methods whose names listed in method_list
47
+ # Handles method names as defined by a user or in file headers where names may
48
+ # not be exactly as required e.g handles capitalisation, white space, _ etc
49
+ # Returns: Array of matching method_details
50
+ #
51
+ def map_inbound_to_methods( klass, method_list )
52
+
53
+ @method_details, @missing_methods = [], []
54
+
55
+ method_list.each do |name|
56
+ x, lookup = name.split(MethodMapper::column_delim)
57
+ md = MethodDictionary::find_method_detail( klass, x )
58
+
59
+ # TODO be nice if we could cheeck that the assoc on klass responds to the specified
60
+ # lookup key now (nice n early)
61
+ # active_record_helper = "find_by_#{lookup}"
62
+
63
+ md.find_by_operator = lookup if(lookup) # TODO and klass.x.respond_to?(active_record_helper))
64
+ md ? @method_details << md : @missing_methods << x
65
+ end
66
+ #@method_details.compact! .. currently we may need to map via the index on @method_details so don't remove nils for now
67
+ @method_details
68
+ end
69
+
70
+ # The raw client supplied names
71
+ def method_names()
72
+ @method_details.collect( &:name )
73
+ end
74
+
75
+ # The true operator names discovered from model
76
+ def operator_names()
77
+ @method_details.collect( &:operator )
78
+ end
79
+
80
+ # Returns true if discovered methods contain every operator in mandatory_list
81
+ def contains_mandatory?( mandatory_list )
82
+ [ [*mandatory_list] - operator_names].flatten.empty?
83
+ end
84
+
85
+ def missing_mandatory( mandatory_list )
86
+ [ [*mandatory_list] - operator_names].flatten
87
+ end
88
+
89
+ end
90
+
91
91
  end