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