ar_loader 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/LICENSE +9 -9
  2. data/README.markdown +268 -221
  3. data/Rakefile +76 -76
  4. data/lib/VERSION +1 -1
  5. data/lib/ar_loader.rb +87 -66
  6. data/lib/ar_loader/exceptions.rb +2 -0
  7. data/lib/{engine → ar_loader}/file_definitions.rb +353 -353
  8. data/lib/{engine → ar_loader}/mapping_file_definitions.rb +87 -87
  9. data/lib/ar_loader/method_detail.rb +257 -0
  10. data/lib/ar_loader/method_mapper.rb +213 -0
  11. data/lib/helpers/jruby/jexcel_file.rb +187 -0
  12. data/lib/{engine → helpers/jruby}/word.rb +79 -70
  13. data/lib/helpers/spree_helper.rb +85 -0
  14. data/lib/loaders/csv_loader.rb +87 -0
  15. data/lib/loaders/excel_loader.rb +132 -0
  16. data/lib/loaders/loader_base.rb +205 -73
  17. data/lib/loaders/spree/image_loader.rb +45 -41
  18. data/lib/loaders/spree/product_loader.rb +140 -91
  19. data/lib/to_b.rb +24 -24
  20. data/spec/csv_loader_spec.rb +27 -0
  21. data/spec/database.yml +19 -6
  22. data/spec/db/migrate/20110803201325_create_test_bed.rb +78 -0
  23. data/spec/excel_loader_spec.rb +113 -98
  24. data/spec/fixtures/BadAssociationName.xls +0 -0
  25. data/spec/fixtures/DemoNegativeTesting.xls +0 -0
  26. data/spec/fixtures/DemoTestModelAssoc.xls +0 -0
  27. data/spec/fixtures/ProjectsMultiCategories.xls +0 -0
  28. data/spec/fixtures/SimpleProjects.xls +0 -0
  29. data/spec/fixtures/SpreeProducts.xls +0 -0
  30. data/spec/fixtures/SpreeZoneExample.csv +5 -0
  31. data/spec/fixtures/SpreeZoneExample.xls +0 -0
  32. data/spec/loader_spec.rb +116 -0
  33. data/spec/logs/test.log +5000 -0
  34. data/spec/method_mapper_spec.rb +222 -0
  35. data/spec/models.rb +55 -0
  36. data/spec/spec_helper.rb +85 -18
  37. data/spec/spree_loader_spec.rb +223 -157
  38. data/tasks/config/seed_fu_product_template.erb +15 -15
  39. data/tasks/config/tidy_config.txt +12 -12
  40. data/tasks/db_tasks.rake +64 -64
  41. data/tasks/excel_loader.rake +63 -113
  42. data/tasks/file_tasks.rake +36 -37
  43. data/tasks/loader.rake +45 -0
  44. data/tasks/spree/image_load.rake +108 -107
  45. data/tasks/spree/product_loader.rake +49 -107
  46. data/tasks/word_to_seedfu.rake +166 -166
  47. metadata +66 -61
  48. data/lib/engine/jruby/jexcel_file.rb +0 -182
  49. data/lib/engine/jruby/method_mapper_excel.rb +0 -44
  50. data/lib/engine/method_detail.rb +0 -140
  51. data/lib/engine/method_mapper.rb +0 -157
  52. data/lib/engine/method_mapper_csv.rb +0 -28
  53. data/spec/db/migrate/20110803201325_create_testbed.rb +0 -25
@@ -1,92 +1,141 @@
1
- # Copyright:: (c) Autotelik Media Ltd 2010
2
- # Author :: Tom Statter
3
- # Date :: Aug 2010
4
- # License:: MIT ?
5
- #
6
- # Details:: Specific over-rides/additions to support Spree Products
7
- #
8
- require 'loader_base'
9
-
10
- class ProductLoader < LoaderBase
11
-
12
- def initialize(klass = Product, product = nil)
13
- super( klass, product )
14
- raise "Failed to create Product for loading" unless @load_object
15
- end
16
-
17
- # What process a value string from a column, assigning value(s) to correct association on Product.
18
- # Method map represents a column from a file and it's correlated Product association.
19
- # Value string which may contain multiple values for a collection association.
20
- # Product to assign that value to.
21
- def process( method_map, value)
22
- #puts "INFO: PRODUCT LOADER processing #{@load_object}"
23
- @value = value
24
-
25
- #puts "DEBUG : process #{method_map.inspect} : #{value.inspect}"
26
- # Special case for OptionTypes as it's two stage process
27
- # First add the possible option_types to Product, then we are able
28
- # to define Variants on those options.
29
-
30
- if(method_map.name == 'option_types' && @value)
31
-
32
- option_types = @value.split(@@multi_assoc_delim)
33
- option_types.each do |ostr|
34
- oname, value_str = ostr.split(@@name_value_delim)
35
- option_type = OptionType.find_or_create_by_name(oname)
36
- unless option_type
37
- puts "WARNING: OptionType #{oname} NOT found - Not set Product"
38
- next
39
- end
40
-
41
- @load_object.option_types << option_type unless @load_object.option_types.include?(option_type)
42
-
43
- # Now get the value(s) for the option e.g red,blue,green for OptType 'colour'
44
- ovalues = value_str.split(',')
45
- ovalues.each_with_index do |ovname, i|
46
- ovname.strip!
47
- ov = OptionValue.find_by_name(ovname)
48
- if ov
49
- object = Variant.new( :sku => "#{@load_object.sku}_#{i}", :price => @load_object.price, :available_on => @load_object.available_on)
50
- #puts "DEBUG: Create New Variant: #{object.inspect}"
51
- object.option_values << ov
52
- @load_object.variants << object
53
- else
54
- puts "WARNING: Option #{ovname} NOT FOUND - No Variant created"
55
- end
56
- end
57
- end
58
-
59
- # Special case for ProductProperties since it can have additional value applied.
60
- # A list of Properties with a optional Value - supplied in form :
61
- # Property:value|Property2:value|Property3:value
62
- #
63
- elsif(method_map.name == 'product_properties' && @value)
64
-
65
- property_list = @value.split(@@multi_assoc_delim)
66
-
67
- property_list.each do |pstr|
68
- pname, pvalue = pstr.split(@@name_value_delim)
69
- property = Property.find_by_name(pname)
70
- unless property
71
- puts "WARNING: Property #{pname} NOT found - Not set Product"
72
- next
73
- end
74
- @load_object.product_properties << ProductProperty.create( :property => property, :value => pvalue)
75
- end
76
-
77
- elsif(method_map.name == 'count_on_hand' && @load_object.variants.size > 0 &&
78
- @value.is_a?(String) && @value.include?(@@multi_assoc_delim))
79
- # Check if we processed Option Types and assign count per option
80
- values = @value.split(@@multi_assoc_delim)
81
- if(@load_object.variants.size == values.size)
82
- @load_object.variants.each_with_index {|v, i| v.count_on_hand == values[i] }
83
- else
84
- puts "WARNING: Count on hand entries does not match number of Variants"
85
- end
86
-
87
- else
88
- super(method_map, value)
89
- end
90
-
91
- end
1
+ # Copyright:: (c) Autotelik Media Ltd 2010
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2010
4
+ # License:: MIT ?
5
+ #
6
+ # Details:: Specific over-rides/additions to support Spree Products
7
+ #
8
+ require 'loader_base'
9
+ require 'excel_loader'
10
+
11
+
12
+ module ARLoader
13
+
14
+ class ProductLoader < ExcelLoader
15
+
16
+ def initialize(product = nil)
17
+ super( Product, product )
18
+ raise "Failed to create Product for loading" unless @load_object
19
+ end
20
+
21
+ # What process a value string from a column, assigning value(s) to correct association on Product.
22
+ # Method map represents a column from a file and it's correlated Product association.
23
+ # Value string which may contain multiple values for a collection association.
24
+ # Product to assign that value to.
25
+ def process( method_detail, value)
26
+
27
+ @current_value = value
28
+ @current_method_detail = method_detail
29
+
30
+ #puts "DEBUG: process #{method_detail.inspect} : #{current_value.inspect}"
31
+ # Special case for OptionTypes as it's two stage process
32
+ # First add the possible option_types to Product, then we are able
33
+ # to define Variants on those options values.
34
+
35
+ if(method_detail.operator?('option_types') && current_value)
36
+
37
+ #puts "DEBUG: Special Case: VARIANTS: #{method_detail.inspect} : #{current_value.inspect}"
38
+
39
+ # TODO smart column ordering to ensure always valid by time we get to associations
40
+ save if(load_object.valid? && load_object.new_record?)
41
+
42
+ option_types = current_value.split(@@multi_assoc_delim)
43
+
44
+ option_types.each do |ostr|
45
+ oname, value_str = ostr.split(@@name_value_delim)
46
+ option_type = OptionType.find_or_create_by_name(oname)
47
+ unless option_type
48
+ puts "WARNING: OptionType #{oname} NOT found - Not set Product"
49
+ next
50
+ end
51
+
52
+ @load_object.option_types << option_type unless @load_object.option_types.include?(option_type)
53
+
54
+ # Now get the value(s) for the option e.g red,blue,green for OptType 'colour'
55
+ ovalues = value_str.split(',')
56
+
57
+ ovalues.each_with_index do |ovname, i|
58
+ ovname.strip!
59
+ ov = OptionValue.find_or_create_by_name(ovname)
60
+ if ov
61
+ object = Variant.new( :sku => "#{@load_object.sku}_#{i}", :price => @load_object.price, :available_on => @load_object.available_on)
62
+ #puts "DEBUG: Create New Variant: #{object.inspect}"
63
+ object.option_values << ov
64
+ @load_object.variants << object
65
+ else
66
+ puts "WARNING: Option #{ovname} NOT FOUND - No Variant created"
67
+ end
68
+ end
69
+ end
70
+
71
+ elsif(method_detail.operator?('taxons') && current_value)
72
+
73
+ # TODO smart column ordering to ensure always valid by time we get to associations
74
+ save if(load_object.valid? && load_object.new_record?)
75
+
76
+ name_list = current_value.split(@@multi_assoc_delim)
77
+
78
+ taxons = name_list.collect do |t|
79
+
80
+ taxon = Taxon.find_by_name(t)
81
+
82
+ unless taxon
83
+ parent = Taxonomy.find_by_name(t)
84
+
85
+ #puts "DEBUG: Taxonomy #{parent} for #{t}"
86
+ begin
87
+ parent = Taxonomy.create!( :name => t ) unless parent
88
+
89
+ taxon = Taxon.create(:name => t, :taxonomy_id => parent.id)
90
+ rescue => e
91
+ e.backtrace
92
+ e.inspect
93
+ puts "ERROR : Cannot assign Taxon ['#{t}'] to Product ['#{load_object.name}']"
94
+ next
95
+ end
96
+ end
97
+ taxon
98
+ end
99
+
100
+ taxons.compact!
101
+
102
+ @load_object.taxons << taxons unless(taxons.empty?)
103
+
104
+ # Special case for ProductProperties since it can have additional value applied.
105
+ # A list of Properties with a optional Value - supplied in form :
106
+ # Property:value|Property2:value|Property3:value
107
+ #
108
+ elsif(method_detail.operator?('product_properties') && current_value)
109
+
110
+ # TODO smart column ordering to ensure always valid by time we get to associations
111
+ save if(load_object.valid? && load_object.new_record?)
112
+
113
+ property_list = current_value.split(@@multi_assoc_delim)
114
+
115
+ property_list.each do |pstr|
116
+ pname, pvalue = pstr.split(@@name_value_delim)
117
+ property = Property.find_by_name(pname)
118
+ unless property
119
+ puts "WARNING: Property #{pname} NOT found - Not set Product"
120
+ next
121
+ end
122
+ @load_object.product_properties << ProductProperty.create( :property => property, :value => pvalue)
123
+ end
124
+
125
+ elsif(method_detail.operator?('count_on_hand') && @load_object.variants.size > 0 && current_value.is_a?(String) && current_value.include?(@@multi_assoc_delim))
126
+ # Check if we processed Option Types and assign count per option
127
+ values = current_value.split(@@multi_assoc_delim)
128
+ if(@load_object.variants.size == values.size)
129
+ @load_object.variants.each_with_index {|v, i| v.count_on_hand == values[i] }
130
+ else
131
+ puts "WARNING: Count on hand entries does not match number of Variants"
132
+ end
133
+
134
+ else
135
+ super(method_detail, value)
136
+ end
137
+
138
+ end
139
+ end
140
+
92
141
  end
data/lib/to_b.rb CHANGED
@@ -1,24 +1,24 @@
1
- class Object
2
- def to_b
3
- case self
4
- when true, false: self
5
- when nil: false
6
- else
7
- to_i != 0
8
- end
9
- end
10
- end
11
-
12
- class String
13
- TRUE_REGEXP = /^(yes|true|on|t|1|\-1)$/i.freeze
14
- FALSE_REGEXP = /^(no|false|off|f|0)$/i.freeze
15
-
16
- def to_b
17
- case self
18
- when TRUE_REGEXP: true
19
- when FALSE_REGEXP: false
20
- else
21
- to_i != 0
22
- end
23
- end
24
- end
1
+ class Object
2
+ def to_b
3
+ case self
4
+ when true, false: self
5
+ when nil: false
6
+ else
7
+ to_i != 0
8
+ end
9
+ end
10
+ end
11
+
12
+ class String
13
+ TRUE_REGEXP = /^(yes|true|on|t|1|\-1)$/i.freeze
14
+ FALSE_REGEXP = /^(no|false|off|f|0)$/i.freeze
15
+
16
+ def to_b
17
+ case self
18
+ when TRUE_REGEXP: true
19
+ when FALSE_REGEXP: false
20
+ else
21
+ to_i != 0
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2011
4
+ # License:: MIT
5
+ #
6
+ # Details:: Specs for Excel aspect of Active Record Loader
7
+ #
8
+ require File.dirname(__FILE__) + '/spec_helper'
9
+
10
+ require 'erb'
11
+ require 'excel_loader'
12
+
13
+ describe 'CSV Loader' do
14
+
15
+ before(:all) do
16
+ db_connect( 'test_file' ) # , test_memory, test_mysql
17
+ migrate_up
18
+ @klazz = Project
19
+ end
20
+
21
+ before(:each) do
22
+ MethodMapper.clear
23
+ MethodMapper.find_operators( @klazz )
24
+ MethodMapper.find_operators( @assoc_klazz )
25
+ end
26
+
27
+ end
data/spec/database.yml CHANGED
@@ -1,6 +1,19 @@
1
- <% adapter = 'jdbcsqlite3' %>
2
-
3
- test:
4
- adapter: <%= adapter %>
5
- encoding: utf8
6
- database: :memory
1
+ <% adapter = 'sqlite3' %>
2
+ <% adapter = 'jdbcsqlite3' if(Guards::jruby? ) %>
3
+
4
+ test_mysql:
5
+ adapter: jdbcmysql
6
+ database: ar_loader_test
7
+ username: test
8
+ password: test
9
+ host: localhost
10
+
11
+ test_memory:
12
+ adapter: <%= adapter %>
13
+ database: :memory
14
+
15
+ test_file:
16
+ adapter: <%= adapter %>
17
+ database: 'ar_loader_test'
18
+ encoding: utf8
19
+ host: localhost
@@ -0,0 +1,78 @@
1
+ class CreateTestBed < ActiveRecord::Migration
2
+
3
+ def self.up
4
+
5
+ # has many :milestones
6
+ create_table :projects do |t|
7
+ t.string :title
8
+ t.string :value_as_string
9
+ t.text :value_as_text
10
+ t.boolean :value_as_boolean, :default => false
11
+ t.datetime :value_as_datetime, :default => nil
12
+ t.integer :value_as_integer, :default => 0
13
+ t.decimal :value_as_double, :precision => 8, :scale => 2, :default => 0.0
14
+ t.timestamps
15
+ end
16
+
17
+ # belongs_to :project, project => has_many
18
+ create_table :milestones do |t|
19
+ t.string :name
20
+ t.datetime :datetime, :default => nil
21
+ t.decimal :cost, :precision => 8, :scale => 2, :default => 0.0
22
+ t.references :project
23
+ t.timestamps
24
+ end
25
+
26
+ # belongs_to :project, project => has_one
27
+ create_table :owners do |t|
28
+ t.string :name
29
+ t.references :project
30
+ t.timestamps
31
+ end
32
+
33
+ # has_belongs_to_many :project
34
+ create_table :categories do |t|
35
+ t.string :reference
36
+ t.timestamps
37
+ end
38
+
39
+ # testing has_belongs_to_many (hence no id)
40
+ create_table :categories_projects, :id => false do |t|
41
+ t.references :category
42
+ t.references :project
43
+ end
44
+
45
+ create_table :versions do |t|
46
+ t.string :name
47
+ t.timestamps
48
+ end
49
+
50
+ # testing project has_many release + versions :through
51
+ create_table :loader_releases do |t|
52
+ t.string :name
53
+ t.references :project
54
+ t.references :version
55
+ t.timestamps
56
+ end
57
+
58
+
59
+ create_table :long_and_complex_table_linked_to_versions do |t|
60
+ t.references :version
61
+ end
62
+
63
+ create_table :empties do |t|
64
+ end
65
+
66
+ end
67
+
68
+ def self.down
69
+ drop_table :projects
70
+ drop_table :categories
71
+ drop_table :loader_releases
72
+ drop_table :versions
73
+ drop_table :categories_projectss
74
+ drop_table :milestones
75
+ drop_table :long_and_complex_table_linked_to_versions
76
+ drop_table :empties
77
+ end
78
+ end
@@ -1,99 +1,114 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
2
-
3
- require 'erb'
4
- require File.dirname(__FILE__) + '/../lib/ar_loader'
5
-
6
- # We are not setup as a Rails project so need to mimic an active record database setup so
7
- # we have some AR models top test against. Create an in memory database from scratch.
8
- #
9
- def db_connect( env = 'test')
10
-
11
- configuration = {:rails_env => env }
12
-
13
- # Some active record stuff seems to rely on the RAILS_ENV being set ?
14
-
15
- ENV['RAILS_ENV'] = configuration[:rails_env]
16
-
17
- configuration[:database_configuration] = YAML::load(ERB.new(IO.read( File.dirname(__FILE__) + '/database.yml')).result)
18
- db = configuration[:database_configuration][ configuration[:rails_env] ]
19
-
20
- puts "Setting DB Config - #{db.inspect}"
21
- ActiveRecord::Base.configurations = db
22
-
23
- puts "Connecting to DB"
24
- ActiveRecord::Base.establish_connection( db )
25
-
26
- puts "Connected to DB Config - #{configuration[:rails_env]}"
27
- end
28
-
29
- def create_in_memory_database
30
- ActiveRecord::Migrator.up('db/migrate')
31
- end
32
-
33
-
34
- class TestModel < ActiveRecord::Base
35
- has_many :TestAssociationModel
36
- end
37
-
38
- class TestAssociationModel < ActiveRecord::Base
39
- belongs_to :test_model
40
- end
41
-
42
- describe 'ExcelLoader' do
43
-
44
- before do
45
- db_connect
46
- create_in_memory_database
47
- @klazz = TestModel
48
- MethodMapper.clear
49
- end
50
-
51
- it "should populate operators for a given AR model" do
52
- MethodMapper.find_operators( @klazz )
53
-
54
- MethodMapper.has_many.should_not be_empty
55
- MethodMapper.assignments.should_not be_empty
56
-
57
- hmf = MethodMapper.has_many_for(@klazz)
58
- arf = MethodMapper.assignments_for(@klazz)
59
-
60
- (hmf & arf).should_not be_empty # Associations provide << or =
61
-
62
- MethodMapper.column_types.should be_is_a(Hash)
63
- MethodMapper.column_types.should_not be_empty
64
-
65
- end
66
-
67
- it "should populate operators respecting unique option" do
68
- MethodMapper.find_operators( @klazz, :unique => true )
69
-
70
- hmf = MethodMapper.has_many_for(@klazz)
71
- arf = MethodMapper.assignments_for(@klazz)
72
-
73
- (hmf & arf).should be_empty
74
- end
75
-
76
- it "should populate assignment method and col type for different forms of a column name" do
77
-
78
- MethodMapper.find_operators( @klazz )
79
- end
80
-
81
- it "should populate both methods for different forms of an association name" do
82
-
83
- MethodMapper.find_operators( @klazz )
84
- end
85
-
86
- it "should not populate anything when non existent column name" do
87
- MethodMapper.find_operators( @klazz )
88
- end
89
-
90
- it "should enable correct assignment and sending of a value to AR model" do
91
- MethodMapper.find_operators( @klazz )
92
- end
93
-
94
- it "should enable correct assignment and sending of association to AR model" do
95
- MethodMapper.find_operators( @klazz )
96
- end
97
-
98
-
1
+ # Copyright:: (c) Autotelik Media Ltd 2011
2
+ # Author :: Tom Statter
3
+ # Date :: Aug 2011
4
+ # License:: MIT
5
+ #
6
+ # Details:: Specs for Excel aspect of Active Record Loader
7
+ #
8
+ require File.dirname(__FILE__) + '/spec_helper'
9
+
10
+ require 'erb'
11
+ require 'excel_loader'
12
+
13
+ include ARLoader
14
+
15
+ describe 'Excel Loader' do
16
+
17
+ before(:all) do
18
+ db_connect( 'test_file' ) # , test_memory, test_mysql
19
+
20
+ db_clear() # todo read up about proper transactional fixtures
21
+
22
+
23
+ @klazz = Project
24
+ @assoc_klazz = Category
25
+ end
26
+
27
+ before(:each) do
28
+
29
+ Project.delete_all
30
+
31
+ %w{category_001 category_002 category_003}.each do |cat|
32
+ @assoc_klazz.find_or_create_by_reference(cat)
33
+ end
34
+
35
+
36
+ MethodMapper.clear
37
+ MethodMapper.find_operators( @klazz )
38
+ MethodMapper.find_operators( @assoc_klazz )
39
+ end
40
+
41
+ it "should be able to create a new excel loader and load object" do
42
+ loader = ExcelLoader.new( @klazz)
43
+
44
+ loader.load_object.should_not be_nil
45
+ loader.load_object.should be_is_a(@klazz)
46
+ loader.load_object.new_record?.should be_true
47
+ end
48
+
49
+ it "should process a simple .xls spreedsheet" do
50
+
51
+ loader = ExcelLoader.new(@klazz)
52
+
53
+ count = @klazz.count
54
+ loader.load( $fixture_path + '/SimpleProjects.xls')
55
+
56
+ loader.loaded_count.should == (@klazz.count - count)
57
+
58
+ end
59
+
60
+ it "should process associations in .xls spreedsheet" do
61
+
62
+ @klazz.find_by_title('001').should be_nil
63
+ count = @klazz.count
64
+
65
+ loader = ExcelLoader.new(@klazz)
66
+
67
+ loader.load( $fixture_path + '/DemoTestModelAssoc.xls')
68
+
69
+ loader.loaded_count.should be > 3
70
+ loader.loaded_count.should == (@klazz.count - count)
71
+
72
+ {'001' => 2, '002' => 1, '003' => 3, '004' => 0 }.each do|title, expected|
73
+ project = @klazz.find_by_title(title)
74
+
75
+ project.should_not be_nil
76
+ puts "#{project.inspect} [#{project.categories.size}]"
77
+
78
+ project.should have(expected).categories
79
+ end
80
+ end
81
+
82
+ it "should process multiple associations in excel spreedsheet" do
83
+
84
+ loader = ExcelLoader.new(Project)
85
+
86
+ count = Project.count
87
+ loader.load( $fixture_path + '/ProjectsMultiCategories.xls')
88
+
89
+ loader.loaded_count.should == (Project.count - count)
90
+
91
+ {'004' => 3, '005' => 1, '006' => 0, '007' => 1 }.each do|title, expected|
92
+ project = @klazz.find_by_title(title)
93
+
94
+ project.should_not be_nil
95
+ puts "#{project.inspect} [#{project.categories.size}]"
96
+
97
+ project.should have(expected).categories
98
+ end
99
+
100
+ end
101
+
102
+ it "should not process badly defined excel spreedsheet" do
103
+ loader = ExcelLoader.new(Project)
104
+ expect {loader.load( $fixture_path + '/BadAssociationName.xls')}.to raise_error(MappingDefinitionError)
105
+ end
106
+
107
+ #@pending
108
+ it "should raise an error when mandatory columns missing" do
109
+ #loader = ExcelLoader.new(Project)
110
+ #required = [:x, :. :z]
111
+ #expect {loader.load( $fixture_path + '/BadAssociationName.xls', { :mandatopry:=> required}) }.to raise_error(????????)
112
+ end
113
+
99
114
  end