datashift 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/README.markdown +13 -12
  2. data/Rakefile +8 -8
  3. data/VERSION +1 -5
  4. data/datashift.gemspec +12 -38
  5. data/lib/applications/jruby/jexcel_file.rb +23 -11
  6. data/lib/datashift/method_detail.rb +44 -5
  7. data/lib/datashift/method_dictionary.rb +210 -0
  8. data/lib/datashift/method_mapper.rb +25 -191
  9. data/lib/generators/excel_generator.rb +12 -11
  10. data/lib/helpers/spree_helper.rb +36 -12
  11. data/lib/loaders/excel_loader.rb +2 -1
  12. data/lib/loaders/loader_base.rb +37 -20
  13. data/lib/loaders/spree/image_loader.rb +35 -16
  14. data/lib/loaders/spree/product_loader.rb +27 -1
  15. data/spec/csv_loader_spec.rb +3 -3
  16. data/spec/excel_exporter_spec.rb +79 -0
  17. data/spec/excel_generator_spec.rb +3 -3
  18. data/spec/excel_loader_spec.rb +35 -16
  19. data/spec/fixtures/ProjectsMultiCategoriesHeaderLookup.xls +0 -0
  20. data/spec/fixtures/images/DEMO_001_ror_bag.jpeg +0 -0
  21. data/spec/fixtures/images/DEMO_002_Powerstation.jpg +0 -0
  22. data/spec/fixtures/images/DEMO_003_ror_mug.jpeg +0 -0
  23. data/spec/fixtures/images/DEMO_004_ror_ringer.jpeg +0 -0
  24. data/spec/fixtures/interact_models_db.sqlite +0 -0
  25. data/spec/fixtures/interact_spree_db.sqlite +0 -0
  26. data/spec/fixtures/spree/SpreeProductsWithImages.xls +0 -0
  27. data/spec/loader_spec.rb +4 -4
  28. data/spec/method_dictionary_spec.rb +243 -0
  29. data/spec/method_mapper_spec.rb +17 -213
  30. data/spec/spec_helper.rb +1 -0
  31. data/spec/spree_loader_spec.rb +18 -1
  32. data/spec/spree_method_mapping_spec.rb +4 -4
  33. metadata +14 -130
  34. data/Gemfile +0 -28
  35. 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
- bundle exec rake datashift:import:excel model=BlogPost input=BlogPostImport.xls verbose=true
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
- MethodMapper.find_operators( Blog )
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 MethodMapper's operators
74
- (i.e. the model's attributes and associations).
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
- MethodMapper.find_method_detail( Blog, "Blog Date" )
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
- MethodMapper.find_method_detail( Blog, "Blog Date" ).assign( Blog.new, "Sat Jul 23 2011" )
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
- Specific Rake tasks are also provided for Spree loading - currently supports Product with associations,
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
@@ -1,5 +1 @@
1
- 0.0.2
2
-
3
- module DataShift
4
- VERSION=0.0.2
5
- end
1
+ 0.1.0
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.2"
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"
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.2.gem",
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(start_column, record, connection_column)
221
- start_column += 1
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
- datum = record.send(connection_column.name)
227
-
228
- if(connection_column.sql_type =~ /date/) then
229
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue(datum.to_s)
230
- elsif connection_column.sql_type =~ /int/ then
231
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_NUMERIC).setCellValue(datum.to_i)
232
- else
233
- @row.createCell(column - 1, HSSFCell::CELL_TYPE_STRING).setCellValue( datum.to_s )
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
- # Store the raw (client supplied) name against the active record klass(model), operator and types
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
- # logger.info("WARNING nil value supplied for Column [#{@name}]") if(@current_value.nil?)
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