datashift 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +13 -12
- data/Rakefile +8 -8
- data/VERSION +1 -5
- data/datashift.gemspec +12 -38
- data/lib/applications/jruby/jexcel_file.rb +23 -11
- data/lib/datashift/method_detail.rb +44 -5
- data/lib/datashift/method_dictionary.rb +210 -0
- data/lib/datashift/method_mapper.rb +25 -191
- data/lib/generators/excel_generator.rb +12 -11
- data/lib/helpers/spree_helper.rb +36 -12
- data/lib/loaders/excel_loader.rb +2 -1
- data/lib/loaders/loader_base.rb +37 -20
- data/lib/loaders/spree/image_loader.rb +35 -16
- data/lib/loaders/spree/product_loader.rb +27 -1
- data/spec/csv_loader_spec.rb +3 -3
- data/spec/excel_exporter_spec.rb +79 -0
- data/spec/excel_generator_spec.rb +3 -3
- data/spec/excel_loader_spec.rb +35 -16
- data/spec/fixtures/ProjectsMultiCategoriesHeaderLookup.xls +0 -0
- data/spec/fixtures/images/DEMO_001_ror_bag.jpeg +0 -0
- data/spec/fixtures/images/DEMO_002_Powerstation.jpg +0 -0
- data/spec/fixtures/images/DEMO_003_ror_mug.jpeg +0 -0
- data/spec/fixtures/images/DEMO_004_ror_ringer.jpeg +0 -0
- data/spec/fixtures/interact_models_db.sqlite +0 -0
- data/spec/fixtures/interact_spree_db.sqlite +0 -0
- data/spec/fixtures/spree/SpreeProductsWithImages.xls +0 -0
- data/spec/loader_spec.rb +4 -4
- data/spec/method_dictionary_spec.rb +243 -0
- data/spec/method_mapper_spec.rb +17 -213
- data/spec/spec_helper.rb +1 -0
- data/spec/spree_loader_spec.rb +18 -1
- data/spec/spree_method_mapping_spec.rb +4 -4
- metadata +14 -130
- data/Gemfile +0 -28
- data/spec/fixtures/.~lock.ProjectsSingleCategories.xls# +0 -1
data/README.markdown
CHANGED
@@ -54,7 +54,11 @@ Provides high level rake tasks for importing data via ActiveRecord models into a
|
|
54
54
|
from various sources, currently csv or .xls files (Excel/Open Office)
|
55
55
|
|
56
56
|
|
57
|
-
|
57
|
+
jruby -S rake datashift:import:excel model=BlogPost input=BlogPostImport.xls verbose=true
|
58
|
+
|
59
|
+
Provides high level rake tasks for exporting data to various sources, currently .xls files (Excel/Open Office)
|
60
|
+
|
61
|
+
jruby -S rake datashift:export:excel model=BlogPost result=BlogExport.xls
|
58
62
|
|
59
63
|
|
60
64
|
The library can be easily extended with Loaders to deal with non trivial cases required to exchange data between
|
@@ -63,19 +67,19 @@ The library can be easily extended with Loaders to deal with non trivial cases r
|
|
63
67
|
Spree loaders are an example, these illustrate over riding processing for specific columns with
|
64
68
|
complicated lookup requirements.
|
65
69
|
|
66
|
-
A core feature of DataShift is the MethodMapper, which provides features for collecting
|
70
|
+
A core feature of DataShift is the MethodDictionary and MethodMapper, which provides features for collecting
|
67
71
|
reflection information from ActiveRecord models (all different associations, including join tables with many-to-many relationships).
|
68
72
|
|
69
73
|
A full picture of all possible operations on a class can be created very easily, for example ona Blog model :
|
70
74
|
|
71
|
-
|
75
|
+
MethodDictionary.find_operators( Blog )
|
72
76
|
|
73
|
-
This then allows Import/Export to be achieved, by mapping the file's header and column data to
|
74
|
-
|
77
|
+
This then allows Import/Export to be achieved, by mapping the file's header and column data to the found operators
|
78
|
+
i.e. the methods to set data on model's attributes and associations.
|
75
79
|
|
76
80
|
Here we retrieve the method details for a column name from a file, "Blog Date"
|
77
81
|
|
78
|
-
|
82
|
+
MethodDictionary.find_method_detail( Blog, "Blog Date" )
|
79
83
|
|
80
84
|
Loaders can use this method lookup functionality, to find the correct association for a column heading,
|
81
85
|
and populate AR object with row data.
|
@@ -88,7 +92,7 @@ and whenever possible, under the bonnet the data will be cast to correct DB type
|
|
88
92
|
|
89
93
|
Here we show how a column name from a file, "Blog Date", can be mapped to Assign a stringified date, to the blog_date column, on a new Blog object :
|
90
94
|
|
91
|
-
|
95
|
+
MethodDictionary.find_method_detail( Blog, "Blog Date" ).assign( Blog.new, "Sat Jul 23 2011" )
|
92
96
|
|
93
97
|
Because it's based on reflection against the model, can build complex relationships between tables during import/export,
|
94
98
|
and extending data files with new columns need not require any additional Ruby coding.
|
@@ -173,15 +177,12 @@ This data can be exported directly to CSV or Excel/OpenOffice spreadsheets.
|
|
173
177
|
|
174
178
|
- *Spree Rake Tasks*
|
175
179
|
|
176
|
-
|
177
|
-
and Image loading.
|
180
|
+
Spree's product associations are non trivial so specific Rake tasks are also provided for loading Spree Producta
|
181
|
+
with all associations and Image loading.
|
178
182
|
|
179
183
|
jruby -S rake datashift:spree:products input=C:\MyProducts.xls
|
180
184
|
|
181
185
|
|
182
|
-
**Product loading from Excel files specifically requires JRuby (But not Excel or OLE)**.
|
183
|
-
|
184
|
-
|
185
186
|
- *Seamless Spree Image loading can be achieved by ensuring SKU or class Name features in Image filename.
|
186
187
|
|
187
188
|
Lookup is performed either via the SKU being prepended to the image name, or by the image name being equal to the **name attribute** of the klass in question.
|
data/Rakefile
CHANGED
@@ -22,14 +22,14 @@
|
|
22
22
|
## encoding: utf-8
|
23
23
|
|
24
24
|
require 'rubygems'
|
25
|
-
require 'bundler'
|
26
|
-
begin
|
27
|
-
Bundler.setup(:default, :development)
|
28
|
-
rescue Bundler::BundlerError => e
|
29
|
-
$stderr.puts e.message
|
30
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
31
|
-
exit e.status_code
|
32
|
-
end
|
25
|
+
#require 'bundler'
|
26
|
+
#begin
|
27
|
+
# Bundler.setup(:default, :development)
|
28
|
+
#rescue Bundler::BundlerError => e
|
29
|
+
# $stderr.puts e.message
|
30
|
+
# $stderr.puts "Run `bundle install` to install missing gems"
|
31
|
+
# exit e.status_code
|
32
|
+
#end
|
33
33
|
require 'rake'
|
34
34
|
|
35
35
|
require 'lib/datashift'
|
data/VERSION
CHANGED
data/datashift.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "datashift"
|
8
|
-
s.version = "0.0
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thomas Statter"]
|
12
|
-
s.date = "2012-02-
|
12
|
+
s.date = "2012-02-27"
|
13
13
|
s.description = "A suite of tools to move data between ActiveRecord models,databases,applications like Excel/Open Office, files and projects including Spree"
|
14
14
|
s.email = "rubygems@autotelik.co.uk"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -19,13 +19,12 @@ Gem::Specification.new do |s|
|
|
19
19
|
]
|
20
20
|
s.files = [
|
21
21
|
".document",
|
22
|
-
"Gemfile",
|
23
22
|
"LICENSE.txt",
|
24
23
|
"README.markdown",
|
25
24
|
"README.rdoc",
|
26
25
|
"Rakefile",
|
27
26
|
"VERSION",
|
28
|
-
"datashift-0.0.
|
27
|
+
"datashift-0.1.0.gem",
|
29
28
|
"datashift.gemspec",
|
30
29
|
"lib/applications/jruby/jexcel_file.rb",
|
31
30
|
"lib/applications/jruby/word.rb",
|
@@ -34,6 +33,7 @@ Gem::Specification.new do |s|
|
|
34
33
|
"lib/datashift/file_definitions.rb",
|
35
34
|
"lib/datashift/mapping_file_definitions.rb",
|
36
35
|
"lib/datashift/method_detail.rb",
|
36
|
+
"lib/datashift/method_dictionary.rb",
|
37
37
|
"lib/datashift/method_mapper.rb",
|
38
38
|
"lib/generators/csv_generator.rb",
|
39
39
|
"lib/generators/excel_generator.rb",
|
@@ -64,17 +64,22 @@ Gem::Specification.new do |s|
|
|
64
64
|
"spec/csv_loader_spec.rb",
|
65
65
|
"spec/datashift_spec.rb",
|
66
66
|
"spec/db/migrate/20110803201325_create_test_bed.rb",
|
67
|
+
"spec/excel_exporter_spec.rb",
|
67
68
|
"spec/excel_generator_spec.rb",
|
68
69
|
"spec/excel_loader_spec.rb",
|
69
70
|
"spec/file_definitions.rb",
|
70
|
-
"spec/fixtures/.~lock.ProjectsSingleCategories.xls#",
|
71
71
|
"spec/fixtures/BadAssociationName.xls",
|
72
72
|
"spec/fixtures/DemoNegativeTesting.xls",
|
73
73
|
"spec/fixtures/ProjectsDefaults.yml",
|
74
74
|
"spec/fixtures/ProjectsMultiCategories.xls",
|
75
|
+
"spec/fixtures/ProjectsMultiCategoriesHeaderLookup.xls",
|
75
76
|
"spec/fixtures/ProjectsSingleCategories.xls",
|
76
77
|
"spec/fixtures/SimpleProjects.xls",
|
77
78
|
"spec/fixtures/config/database.yml",
|
79
|
+
"spec/fixtures/images/DEMO_001_ror_bag.jpeg",
|
80
|
+
"spec/fixtures/images/DEMO_002_Powerstation.jpg",
|
81
|
+
"spec/fixtures/images/DEMO_003_ror_mug.jpeg",
|
82
|
+
"spec/fixtures/images/DEMO_004_ror_ringer.jpeg",
|
78
83
|
"spec/fixtures/interact_models_db.sqlite",
|
79
84
|
"spec/fixtures/interact_spree_db.sqlite",
|
80
85
|
"spec/fixtures/negative/SpreeProdMiss1Mandatory.csv",
|
@@ -89,10 +94,12 @@ Gem::Specification.new do |s|
|
|
89
94
|
"spec/fixtures/spree/SpreeProductsMultiColumn.xls",
|
90
95
|
"spec/fixtures/spree/SpreeProductsSimple.csv",
|
91
96
|
"spec/fixtures/spree/SpreeProductsSimple.xls",
|
97
|
+
"spec/fixtures/spree/SpreeProductsWithImages.xls",
|
92
98
|
"spec/fixtures/spree/SpreeZoneExample.csv",
|
93
99
|
"spec/fixtures/spree/SpreeZoneExample.xls",
|
94
100
|
"spec/fixtures/test_model_defs.rb",
|
95
101
|
"spec/loader_spec.rb",
|
102
|
+
"spec/method_dictionary_spec.rb",
|
96
103
|
"spec/method_mapper_spec.rb",
|
97
104
|
"spec/spec_helper.rb",
|
98
105
|
"spec/spree_generator_spec.rb",
|
@@ -121,42 +128,9 @@ Gem::Specification.new do |s|
|
|
121
128
|
s.specification_version = 3
|
122
129
|
|
123
130
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
124
|
-
s.add_development_dependency(%q<rails>, ["= 3.1.3"])
|
125
|
-
s.add_development_dependency(%q<activerecord>, ["= 3.1.3"])
|
126
|
-
s.add_development_dependency(%q<activesupport>, ["= 3.1.3"])
|
127
|
-
s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
|
128
|
-
s.add_development_dependency(%q<activerecord-jdbcsqlite3-adapter>, [">= 0"])
|
129
|
-
s.add_development_dependency(%q<spree>, ["= 0.70.3"])
|
130
|
-
s.add_development_dependency(%q<rspec>, [">= 0"])
|
131
|
-
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
132
|
-
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
133
|
-
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
134
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
135
131
|
else
|
136
|
-
s.add_dependency(%q<rails>, ["= 3.1.3"])
|
137
|
-
s.add_dependency(%q<activerecord>, ["= 3.1.3"])
|
138
|
-
s.add_dependency(%q<activesupport>, ["= 3.1.3"])
|
139
|
-
s.add_dependency(%q<jruby-openssl>, [">= 0"])
|
140
|
-
s.add_dependency(%q<activerecord-jdbcsqlite3-adapter>, [">= 0"])
|
141
|
-
s.add_dependency(%q<spree>, ["= 0.70.3"])
|
142
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
143
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
144
|
-
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
145
|
-
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
146
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
147
132
|
end
|
148
133
|
else
|
149
|
-
s.add_dependency(%q<rails>, ["= 3.1.3"])
|
150
|
-
s.add_dependency(%q<activerecord>, ["= 3.1.3"])
|
151
|
-
s.add_dependency(%q<activesupport>, ["= 3.1.3"])
|
152
|
-
s.add_dependency(%q<jruby-openssl>, [">= 0"])
|
153
|
-
s.add_dependency(%q<activerecord-jdbcsqlite3-adapter>, [">= 0"])
|
154
|
-
s.add_dependency(%q<spree>, ["= 0.70.3"])
|
155
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
156
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
157
|
-
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
158
|
-
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
159
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
160
134
|
end
|
161
135
|
end
|
162
136
|
|
@@ -212,25 +212,37 @@ if(DataShift::Guards::jruby?)
|
|
212
212
|
end
|
213
213
|
|
214
214
|
# Save data from an AR record to the current row, based on the record's columns [c1,c2,c3]
|
215
|
-
|
215
|
+
# Returns the number of the final column written to
|
216
216
|
def ar_to_xls_row(start_column, record)
|
217
217
|
return unless( record.is_a?(ActiveRecord::Base))
|
218
218
|
|
219
|
+
column = start_column
|
219
220
|
record.class.columns.each do |connection_column|
|
220
|
-
ar_to_xls_cell(
|
221
|
-
|
221
|
+
ar_to_xls_cell(column, record, connection_column)
|
222
|
+
column += 1
|
222
223
|
end
|
224
|
+
column
|
223
225
|
end
|
224
226
|
|
225
227
|
def ar_to_xls_cell(column, record, connection_column)
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
228
|
+
begin
|
229
|
+
datum = record.send(connection_column.name)
|
230
|
+
|
231
|
+
if(connection_column.sql_type =~ /date/)
|
232
|
+
@row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue(datum.to_s)
|
233
|
+
|
234
|
+
elsif(connection_column.type == :boolean || connection_column.sql_type =~ /tinyint/)
|
235
|
+
@row.createCell(column - 1, HSSFCell::CELL_TYPE_BOOLEAN).setCellValue(datum)
|
236
|
+
|
237
|
+
elsif(connection_column.sql_type =~ /int/)
|
238
|
+
@row.createCell(column - 1, HSSFCell::CELL_TYPE_NUMERIC).setCellValue(datum.to_i)
|
239
|
+
else
|
240
|
+
@row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue( datum.to_s )
|
241
|
+
end
|
242
|
+
|
243
|
+
rescue => e
|
244
|
+
puts "Failed to export #{datum} from #{connection_column.inspect} to column #{column}"
|
245
|
+
puts e
|
234
246
|
end
|
235
247
|
end
|
236
248
|
|
@@ -14,6 +14,38 @@ require 'to_b'
|
|
14
14
|
|
15
15
|
module DataShift
|
16
16
|
|
17
|
+
# Stores MethodDetails for a class mapped by type
|
18
|
+
class MethodDetailsManager
|
19
|
+
|
20
|
+
attr_reader :method_details
|
21
|
+
|
22
|
+
def initialize( klass )
|
23
|
+
@parent_class = klass
|
24
|
+
@method_details = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def add(method_details)
|
28
|
+
@method_details[method_details.operator_type] ||= {}
|
29
|
+
@method_details[method_details.operator_type][method_details.name] = method_details
|
30
|
+
end
|
31
|
+
|
32
|
+
def <<(method_details)
|
33
|
+
add(method_details)
|
34
|
+
end
|
35
|
+
|
36
|
+
def find(name, type)
|
37
|
+
method_details = get(type)
|
38
|
+
|
39
|
+
method_details ? method_details[name] : nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# type is expected to be one of MethodDetail::type_enum
|
43
|
+
def get( type )
|
44
|
+
@method_details[type]
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
17
49
|
class MethodDetail
|
18
50
|
|
19
51
|
def self.type_enum
|
@@ -30,14 +62,21 @@ module DataShift
|
|
30
62
|
|
31
63
|
attr_reader :operator, :operator_type
|
32
64
|
|
33
|
-
#
|
65
|
+
# TODO make it a list/primary keys
|
66
|
+
attr_accessor :find_by_operator
|
67
|
+
|
68
|
+
# Store the raw (client supplied) name against the active record klass(model).
|
69
|
+
# Operator is the associated method call on klass,
|
70
|
+
# so client name maybe Price but true operator is price
|
71
|
+
#
|
34
72
|
# col_types can typically be derived from klass.columns - set of ActiveRecord::ConnectionAdapters::Column
|
35
73
|
|
36
|
-
def initialize(client_name, klass, operator, type, col_types = {} )
|
74
|
+
def initialize(client_name, klass, operator, type, col_types = {}, find_by_operator = nil )
|
37
75
|
@klass, @name = klass, client_name
|
76
|
+
@find_by_operator = find_by_operator
|
38
77
|
|
39
78
|
if( MethodDetail::type_enum.member?(type.to_sym) )
|
40
|
-
@operator_type = type
|
79
|
+
@operator_type = type.to_sym
|
41
80
|
else
|
42
81
|
raise "Bad operator Type #{type} passed to Method Detail"
|
43
82
|
end
|
@@ -57,7 +96,7 @@ module DataShift
|
|
57
96
|
# Return the actual operator's name for supplied method type
|
58
97
|
# where type one of :assignment, :has_one, :belongs_to, :has_many etc
|
59
98
|
def operator_for( type )
|
60
|
-
return operator if(@operator_type == type)
|
99
|
+
return operator if(@operator_type == type.to_sym)
|
61
100
|
nil
|
62
101
|
end
|
63
102
|
|
@@ -105,7 +144,7 @@ module DataShift
|
|
105
144
|
|
106
145
|
@current_value = value
|
107
146
|
|
108
|
-
|
147
|
+
# logger.info("WARNING nil value supplied for Column [#{@name}]") if(@current_value.nil?)
|
109
148
|
|
110
149
|
if( operator_for(:belongs_to) )
|
111
150
|
|
@@ -0,0 +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
|
+
|
210
|
+
end
|