datashift 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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