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.
- data/.document +5 -5
- data/LICENSE.txt +26 -26
- data/README.markdown +305 -303
- data/README.rdoc +19 -19
- data/Rakefile +93 -93
- data/VERSION +1 -1
- data/datashift-0.1.0.gem +0 -0
- data/datashift.gemspec +152 -136
- data/lib/applications/jruby/jexcel_file.rb +408 -408
- data/lib/applications/jruby/word.rb +79 -79
- data/lib/datashift.rb +152 -152
- data/lib/datashift/exceptions.rb +11 -11
- data/lib/datashift/file_definitions.rb +353 -353
- data/lib/datashift/mapping_file_definitions.rb +87 -87
- data/lib/datashift/method_detail.rb +275 -275
- data/lib/datashift/method_dictionary.rb +209 -209
- data/lib/datashift/method_mapper.rb +90 -90
- data/lib/generators/csv_generator.rb +36 -36
- data/lib/generators/excel_generator.rb +122 -122
- data/lib/generators/generator_base.rb +13 -13
- data/lib/helpers/core_ext/to_b.rb +24 -24
- data/lib/helpers/spree_helper.rb +153 -155
- data/lib/java/poi-3.7/LICENSE +507 -507
- data/lib/java/poi-3.7/NOTICE +21 -21
- data/lib/java/poi-3.7/RELEASE_NOTES.txt +115 -115
- data/lib/loaders/csv_loader.rb +98 -98
- data/lib/loaders/excel_loader.rb +155 -155
- data/lib/loaders/loader_base.rb +420 -420
- data/lib/loaders/spreadsheet_loader.rb +136 -136
- data/lib/loaders/spree/image_loader.rb +63 -64
- data/lib/loaders/spree/product_loader.rb +248 -250
- data/public/spree/products/large/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/large/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/large/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/mini/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/mini/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/mini/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/original/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/original/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/original/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/product/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/product/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/product/DEMO_003_ror_mug.jpeg +0 -0
- data/public/spree/products/small/DEMO_001_ror_bag.jpeg +0 -0
- data/public/spree/products/small/DEMO_002_Powerstation.jpg +0 -0
- data/public/spree/products/small/DEMO_003_ror_mug.jpeg +0 -0
- data/spec/csv_loader_spec.rb +30 -30
- data/spec/datashift_spec.rb +26 -26
- data/spec/db/migrate/20110803201325_create_test_bed.rb +85 -85
- data/spec/excel_exporter_spec.rb +78 -78
- data/spec/excel_generator_spec.rb +78 -78
- data/spec/excel_loader_spec.rb +223 -223
- data/spec/file_definitions.rb +141 -141
- data/spec/fixtures/ProjectsDefaults.yml +29 -29
- data/spec/fixtures/config/database.yml +27 -24
- data/spec/fixtures/datashift_Spree_db.sqlite +0 -0
- data/spec/fixtures/interact_models_db.sqlite +0 -0
- data/spec/fixtures/negative/SpreeProdMiss1Mandatory.csv +4 -4
- data/spec/fixtures/negative/SpreeProdMissManyMandatory.csv +4 -4
- data/spec/fixtures/spree/SpreeProducts.csv +4 -4
- data/spec/fixtures/spree/SpreeProductsMultiColumn.csv +4 -4
- data/spec/fixtures/spree/SpreeProductsSimple.csv +4 -4
- data/spec/fixtures/spree/SpreeProductsWithImages.csv +4 -0
- data/spec/fixtures/spree/SpreeZoneExample.csv +5 -5
- data/spec/fixtures/test_model_defs.rb +57 -57
- data/spec/loader_spec.rb +120 -120
- data/spec/method_dictionary_spec.rb +242 -242
- data/spec/method_mapper_spec.rb +41 -41
- data/spec/spec_helper.rb +116 -116
- data/spec/spree_generator_spec.rb +64 -64
- data/spec/spree_loader_spec.rb +324 -327
- data/spec/spree_method_mapping_spec.rb +214 -214
- data/tasks/config/seed_fu_product_template.erb +15 -15
- data/tasks/config/tidy_config.txt +12 -12
- data/tasks/db_tasks.rake +65 -65
- data/tasks/excel_generator.rake +78 -78
- data/tasks/file_tasks.rake +36 -36
- data/tasks/import/csv.rake +49 -49
- data/tasks/import/excel.rake +71 -71
- data/tasks/spree/image_load.rake +108 -108
- data/tasks/spree/product_loader.rake +43 -43
- data/tasks/word_to_seedfu.rake +166 -166
- data/test/helper.rb +18 -18
- data/test/test_interact.rb +7 -7
- metadata +22 -3
- 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
|