ar_loader 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +9 -0
- data/README.markdown +211 -0
- data/Rakefile +76 -0
- data/lib/VERSION +1 -0
- data/lib/ar_loader.rb +53 -0
- data/lib/engine/file_definitions.rb +353 -0
- data/lib/engine/jruby/jexcel_file.rb +182 -0
- data/lib/engine/jruby/method_mapper_excel.rb +44 -0
- data/lib/engine/mapping_file_definitions.rb +88 -0
- data/lib/engine/method_detail.rb +139 -0
- data/lib/engine/method_mapper.rb +157 -0
- data/lib/engine/method_mapper_csv.rb +28 -0
- data/lib/engine/word.rb +70 -0
- data/lib/java/poi-3.2-FINAL-20081019.jar +0 -0
- data/lib/java/poi-3.6.jar +0 -0
- data/lib/java/poi-contrib-3.6-20091214.jar +0 -0
- data/lib/java/poi-examples-3.6-20091214.jar +0 -0
- data/lib/java/poi-ooxml-3.6-20091214.jar +0 -0
- data/lib/java/poi-ooxml-schemas-3.6-20091214.jar +0 -0
- data/lib/java/poi-scratchpad-3.6-20091214.jar +0 -0
- data/lib/loaders/loader_base.rb +61 -0
- data/lib/loaders/spree/image_loader.rb +47 -0
- data/lib/loaders/spree/product_loader.rb +93 -0
- data/lib/to_b.rb +24 -0
- data/spec/excel_loader_spec.rb +138 -0
- data/spec/spec_helper.rb +37 -0
- data/tasks/db_tasks.rake +65 -0
- data/tasks/excel_loader.rake +101 -0
- data/tasks/file_tasks.rake +38 -0
- data/tasks/seed_fu_product_template.erb +15 -0
- data/tasks/spree/image_load.rake +103 -0
- data/tasks/spree/product_loader.rake +107 -0
- data/tasks/tidy_config.txt +13 -0
- data/tasks/word_to_seedfu.rake +167 -0
- metadata +90 -0
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Aug 2010
|
4
|
+
# License:: MIT
|
5
|
+
#
|
6
|
+
class LoaderBase
|
7
|
+
|
8
|
+
attr_accessor :load_object, :value
|
9
|
+
|
10
|
+
# Enable single column (association) to contain multiple name/value sets in default form :
|
11
|
+
# Name1:value1, value2|Name2:value1, value2, value3|Name3:value1, value2
|
12
|
+
#
|
13
|
+
# E.G.
|
14
|
+
# Row for association could have a name (Size/Colour/Sex) with a set of values,
|
15
|
+
# and this combination can be expressed multiple times :
|
16
|
+
# Size:small,medium,large|Colour:red, green|Sex:Female
|
17
|
+
|
18
|
+
@@name_value_delim = ':'
|
19
|
+
@@multi_value_delim = ','
|
20
|
+
@@multi_assoc_delim = '|'
|
21
|
+
|
22
|
+
def self.set_name_value_delim(x) @@name_value_delim = x; end
|
23
|
+
def self.set_multi_value_delim(x) @@multi_value_delim = x; end
|
24
|
+
def self.set_multi_assoc_delim(x) @@multi_assoc_delim = x; end
|
25
|
+
|
26
|
+
def initialize(object)
|
27
|
+
@load_object = object
|
28
|
+
end
|
29
|
+
|
30
|
+
# What process a value string from a column.
|
31
|
+
# Assigning value(s) to correct association on @load_object.
|
32
|
+
# Method map represents a column from a file and it's correlated AR associations.
|
33
|
+
# Value string which may contain multiple values for a collection association.
|
34
|
+
#
|
35
|
+
def process(method_map, value)
|
36
|
+
@value = value
|
37
|
+
|
38
|
+
if(method_map.has_many && method_map.has_many_class && @value)
|
39
|
+
# The Generic handler for Associations
|
40
|
+
# The actual class of the association so we can find_or_create on it
|
41
|
+
assoc_class = method_map.has_many_class
|
42
|
+
|
43
|
+
puts "Processing Association: #{assoc_class} : #{@value}"
|
44
|
+
|
45
|
+
@value.split(@@multi_assoc_delim).collect do |lookup|
|
46
|
+
# TODO - Don't just rely on 'name' but try different finds as per MethodMappe::insistent_belongs_to ..
|
47
|
+
x = assoc_class.find(:first, :conditions => ['lower(name) LIKE ?', "%#{lookup.downcase}%"])
|
48
|
+
unless x
|
49
|
+
puts "WARNING: #{lookup} in #{assoc_class} NOT found - Not added to #{@load_object.class}"
|
50
|
+
next
|
51
|
+
end
|
52
|
+
@load_object.send( method_map.has_many ) << x
|
53
|
+
@load_object.save
|
54
|
+
end
|
55
|
+
else
|
56
|
+
# Nice n simple straight assignment to a column variable
|
57
|
+
method_map.assign(@load_object, @value) unless method_map.has_many
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Jan 2011
|
4
|
+
# License:: MIT. Free, Open Source.
|
5
|
+
#
|
6
|
+
require 'loader_base'
|
7
|
+
|
8
|
+
class ImageLoader < LoaderBase
|
9
|
+
|
10
|
+
def initialize(image = nil)
|
11
|
+
obj = image || Image.create
|
12
|
+
super( obj )
|
13
|
+
raise "Failed to create Image for loading" unless @load_object
|
14
|
+
end
|
15
|
+
|
16
|
+
def refresh
|
17
|
+
@load_object = Image.create
|
18
|
+
end
|
19
|
+
|
20
|
+
# Note the Spree Image model sets default storage path to
|
21
|
+
# => :path => ":rails_root/public/assets/products/:id/:style/:basename.:extension"
|
22
|
+
|
23
|
+
def process( image_path, record = nil)
|
24
|
+
|
25
|
+
unless File.exists?(image_path)
|
26
|
+
puts "ERROR : Invalid Path"
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
alt = (record and record.respond_to? :name) ? record.name : ""
|
31
|
+
|
32
|
+
@load_object.alt = alt
|
33
|
+
|
34
|
+
begin
|
35
|
+
@load_object.attachment = File.new(image_path, "r")
|
36
|
+
rescue => e
|
37
|
+
puts e.inspect
|
38
|
+
puts "ERROR : Failed to read image #{image_path}"
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
@load_object.attachment.reprocess!
|
43
|
+
@load_object.viewable = record if record
|
44
|
+
|
45
|
+
puts @load_object.save ? "Success: Uploaded Image: #{@load_object.inspect}" : "ERROR : Problem saving to DB Image: #{@load_object}"
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,93 @@
|
|
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 general loader to
|
7
|
+
# support Spree Products
|
8
|
+
#
|
9
|
+
require 'loader_base'
|
10
|
+
|
11
|
+
class ProductLoader < LoaderBase
|
12
|
+
|
13
|
+
def initialize(product = nil)
|
14
|
+
prod = product || Product.create
|
15
|
+
super( prod )
|
16
|
+
raise "Failed to create Product for loading" unless @load_object
|
17
|
+
end
|
18
|
+
|
19
|
+
# What process a value string from a column, assigning value(s) to correct association on Product.
|
20
|
+
# Method map represents a column from a file and it's correlated Product association.
|
21
|
+
# Value string which may contain multiple values for a collection association.
|
22
|
+
# Product to assign that value to.
|
23
|
+
def process( method_map, value)
|
24
|
+
@value = value
|
25
|
+
|
26
|
+
#puts "DEBUG : process #{method_map.inspect} : #{value.inspect}"
|
27
|
+
# Special case for OptionTypes as it's two stage process
|
28
|
+
# First add the possible option_types to Product, then we are able
|
29
|
+
# to define Variants on those options.
|
30
|
+
|
31
|
+
if(method_map.name == 'option_types' && @value)
|
32
|
+
|
33
|
+
option_types = @value.split(@@multi_assoc_delim)
|
34
|
+
option_types.each do |ostr|
|
35
|
+
oname, value_str = ostr.split(@@name_value_delim)
|
36
|
+
option_type = OptionType.find_or_create_by_name(oname)
|
37
|
+
unless option_type
|
38
|
+
puts "WARNING: OptionType #{oname} NOT found - Not set Product"
|
39
|
+
next
|
40
|
+
end
|
41
|
+
|
42
|
+
@load_object.option_types << option_type unless @load_object.option_types.include?(option_type)
|
43
|
+
|
44
|
+
# Now get the value(s) for the option e.g red,blue,green for OptType 'colour'
|
45
|
+
ovalues = value_str.split(',')
|
46
|
+
ovalues.each_with_index do |ovname, i|
|
47
|
+
ovname.strip!
|
48
|
+
ov = OptionValue.find_by_name(ovname)
|
49
|
+
if ov
|
50
|
+
object = Variant.new( :sku => "#{@load_object.sku}_#{i}", :price => @load_object.price, :available_on => @load_object.available_on)
|
51
|
+
#puts "DEBUG: Create New Variant: #{object.inspect}"
|
52
|
+
object.option_values << ov
|
53
|
+
@load_object.variants << object
|
54
|
+
else
|
55
|
+
puts "WARNING: Option #{ovname} NOT FOUND - No Variant created"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Special case for ProductProperties since it can have additional value applied.
|
61
|
+
# A list of Properties with a optional Value - supplied in form :
|
62
|
+
# Property:value|Property2:value|Property3:value
|
63
|
+
#
|
64
|
+
elsif(method_map.name == 'product_properties' && @value)
|
65
|
+
|
66
|
+
property_list = @value.split(@@multi_assoc_delim)
|
67
|
+
|
68
|
+
property_list.each do |pstr|
|
69
|
+
pname, pvalue = pstr.split(@@name_value_delim)
|
70
|
+
property = Property.find_by_name(pname)
|
71
|
+
unless property
|
72
|
+
puts "WARNING: Property #{pname} NOT found - Not set Product"
|
73
|
+
next
|
74
|
+
end
|
75
|
+
@load_object.product_properties << ProductProperty.create( :property => property, :value => pvalue)
|
76
|
+
end
|
77
|
+
|
78
|
+
elsif(method_map.name == 'count_on_hand' && @load_object.variants.size > 0 &&
|
79
|
+
@value.is_a?(String) && @value.include?(@@multi_assoc_delim))
|
80
|
+
# Check if we processed Option Types and assign count per option
|
81
|
+
values = @value.split(@@multi_assoc_delim)
|
82
|
+
if(@load_object.variants.size == values.size)
|
83
|
+
@load_object.variants.each_with_index {|v, i| v.count_on_hand == values[i] }
|
84
|
+
else
|
85
|
+
puts "WARNING: Count on hand entries does not match number of Variants"
|
86
|
+
end
|
87
|
+
|
88
|
+
else
|
89
|
+
super(method_map, value)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
data/lib/to_b.rb
ADDED
@@ -0,0 +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
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe 'ExcelLoader' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@klazz = Product
|
7
|
+
MethodMapper.clear
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should populate operators for a given AR model" do
|
11
|
+
MethodMapper.find_operators( @klazz )
|
12
|
+
|
13
|
+
MethodMapper.has_many.should_not be_empty
|
14
|
+
MethodMapper.assignments.should_not be_empty
|
15
|
+
|
16
|
+
hmf = MethodMapper.has_many_for(@klazz)
|
17
|
+
arf = MethodMapper.assignments_for(@klazz)
|
18
|
+
|
19
|
+
(hmf & arf).should_not be_empty # Associations provide << or =
|
20
|
+
|
21
|
+
hmf.should include('properties')
|
22
|
+
arf.should include('count_on_hand') # example of a column
|
23
|
+
arf.should include('cost_price') # example of delegated assignment (available through Variant)
|
24
|
+
|
25
|
+
MethodMapper.column_types.should be_is_a(Hash)
|
26
|
+
MethodMapper.column_types.should_not be_empty
|
27
|
+
|
28
|
+
MethodMapper.column_type_for(@klazz, 'count_on_hand').should_not be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should populate operators respecting unique option" do
|
32
|
+
MethodMapper.find_operators( @klazz, :unique => true )
|
33
|
+
|
34
|
+
hmf = MethodMapper.has_many_for(@klazz)
|
35
|
+
arf = MethodMapper.assignments_for(@klazz)
|
36
|
+
|
37
|
+
(hmf & arf).should be_empty
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should populate assignment method and col type for different forms of a column name" do
|
41
|
+
|
42
|
+
MethodMapper.find_operators( @klazz )
|
43
|
+
|
44
|
+
["Count On hand", 'count_on_hand', "Count OnHand", "COUNT ONHand"].each do |format|
|
45
|
+
mmap = MethodMapper.determine_calls( @klazz, format )
|
46
|
+
|
47
|
+
mmap.class.should == MethodDetail
|
48
|
+
|
49
|
+
mmap.assignment.should == 'count_on_hand='
|
50
|
+
mmap.has_many.should be_nil
|
51
|
+
|
52
|
+
mmap.col_type.should_not be_nil
|
53
|
+
mmap.col_type.name.should == 'count_on_hand'
|
54
|
+
mmap.col_type.default.should == 0
|
55
|
+
mmap.col_type.sql_type.should == 'int(10)'
|
56
|
+
mmap.col_type.type.should == :integer
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should populate both methods for different forms of an association name" do
|
61
|
+
|
62
|
+
MethodMapper.find_operators( @klazz )
|
63
|
+
["product_option_types", "product option types", 'product Option_types', "ProductOptionTypes", "Product_Option_Types"].each do |format|
|
64
|
+
mmap = MethodMapper.determine_calls( @klazz, format )
|
65
|
+
|
66
|
+
mmap.assignment.should == 'product_option_types='
|
67
|
+
mmap.has_many.should == 'product_option_types'
|
68
|
+
|
69
|
+
mmap.col_type.should be_nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
it "should not populate anything when non existent column name" do
|
75
|
+
["On sale", 'on_sale'].each do |format|
|
76
|
+
mmap = MethodMapper.determine_calls( @klazz, format )
|
77
|
+
|
78
|
+
mmap.class.should == MethodDetail
|
79
|
+
mmap.assignment.should be_nil
|
80
|
+
mmap.has_many.should be_nil
|
81
|
+
mmap.col_type.should be_nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should enable correct assignment and sending of a value to AR model" do
|
86
|
+
|
87
|
+
MethodMapper.find_operators( @klazz )
|
88
|
+
|
89
|
+
mmap = MethodMapper.determine_calls( @klazz, 'count on hand' )
|
90
|
+
mmap.assignment.should == 'count_on_hand='
|
91
|
+
|
92
|
+
x = @klazz.new
|
93
|
+
|
94
|
+
x.should be_new_record
|
95
|
+
|
96
|
+
x.send( mmap.assignment, 2 )
|
97
|
+
x.count_on_hand.should == 2
|
98
|
+
x.on_hand.should == 2 # helper method I know looks at same thing
|
99
|
+
|
100
|
+
mmap = MethodMapper.determine_calls( @klazz, 'SKU' )
|
101
|
+
mmap.assignment.should == 'sku='
|
102
|
+
x.send( mmap.assignment, 'TEST_SK 001' )
|
103
|
+
x.sku.should == 'TEST_SK 001'
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should enable correct assignment and sending of association to AR model" do
|
107
|
+
|
108
|
+
MethodMapper.find_operators( @klazz )
|
109
|
+
|
110
|
+
mmap = MethodMapper.determine_calls( @klazz, 'taxons' )
|
111
|
+
mmap.has_many.should == 'taxons'
|
112
|
+
|
113
|
+
x = @klazz.new
|
114
|
+
|
115
|
+
# NEW ASSOCIATION ASSIGNMENT v.,mn
|
116
|
+
x.send( mmap.has_many ) << Taxon.new
|
117
|
+
x.taxons.size.should == 1
|
118
|
+
|
119
|
+
x.send( mmap.has_many ) << [Taxon.new, Taxon.new]
|
120
|
+
x.taxons.size.should == 3
|
121
|
+
|
122
|
+
# EXISTING ASSOCIATIONS
|
123
|
+
x = Product.find :first
|
124
|
+
|
125
|
+
t = Taxonomy.find_or_create_by_name( 'BlahSpecTest' )
|
126
|
+
|
127
|
+
if x
|
128
|
+
sz = x.taxons.size
|
129
|
+
x.send(mmap.has_many) << t.root
|
130
|
+
x.taxons.size.should == sz + 1
|
131
|
+
else
|
132
|
+
puts "WARNING : Test not run could not find any Test Products"
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
unless defined? SPREE_ROOT
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
case
|
4
|
+
when ENV["SPREE_ENV_FILE"]
|
5
|
+
require ENV["SPREE_ENV_FILE"]
|
6
|
+
when File.dirname(__FILE__) =~ %r{vendor/SPREE/vendor/extensions}
|
7
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../../")}/config/environment"
|
8
|
+
else
|
9
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../")}/config/environment"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
require "#{SPREE_ROOT}/spec/spec_helper"
|
13
|
+
|
14
|
+
if File.directory?(File.dirname(__FILE__) + "/scenarios")
|
15
|
+
Scenario.load_paths.unshift File.dirname(__FILE__) + "/scenarios"
|
16
|
+
end
|
17
|
+
if File.directory?(File.dirname(__FILE__) + "/matchers")
|
18
|
+
Dir[File.dirname(__FILE__) + "/matchers/*.rb"].each {|file| require file }
|
19
|
+
end
|
20
|
+
|
21
|
+
Spec::Runner.configure do |config|
|
22
|
+
# config.use_transactional_fixtures = true
|
23
|
+
# config.use_instantiated_fixtures = false
|
24
|
+
# config.fixture_path = RAILS_ROOT + '/spec/fixtures'
|
25
|
+
|
26
|
+
# You can declare fixtures for each behaviour like this:
|
27
|
+
# describe "...." do
|
28
|
+
# fixtures :table_a, :table_b
|
29
|
+
#
|
30
|
+
# Alternatively, if you prefer to declare them only once, you can
|
31
|
+
# do so here, like so ...
|
32
|
+
#
|
33
|
+
# config.global_fixtures = :table_a, :table_b
|
34
|
+
#
|
35
|
+
# If you declare global fixtures, be aware that they will be declared
|
36
|
+
# for all of your examples, even those that don't use them.
|
37
|
+
end
|
data/tasks/db_tasks.rake
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Author :: Tom Statter
|
2
|
+
# Date :: Mar 2011
|
3
|
+
#
|
4
|
+
# License:: The MIT License (Free and OpenSource)
|
5
|
+
#
|
6
|
+
# About:: Additional Rake tasks useful when testing seeding DB via ARLoader
|
7
|
+
#
|
8
|
+
namespace :autotelik do
|
9
|
+
|
10
|
+
namespace :db do
|
11
|
+
|
12
|
+
SYSTEM_TABLE_EXCLUSION_LIST = ['schema_migrations']
|
13
|
+
|
14
|
+
desc "Purge the current database"
|
15
|
+
task :purge, :exclude_system_tables, :needs => [:environment] do |t, args|
|
16
|
+
require 'highline/import'
|
17
|
+
|
18
|
+
if(RAILS_ENV == 'production')
|
19
|
+
agree("WARNING: In Production database, REALLY PURGE ? [y]:")
|
20
|
+
end
|
21
|
+
|
22
|
+
config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
|
23
|
+
case config['adapter']
|
24
|
+
when "mysql", "jdbcmysql"
|
25
|
+
ActiveRecord::Base.establish_connection(config)
|
26
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
27
|
+
next if(args[:exclude_system_tables] && SYSTEM_TABLE_EXCLUSION_LIST.include?(table) )
|
28
|
+
puts "purging table: #{table}"
|
29
|
+
ActiveRecord::Base.connection.execute("TRUNCATE #{table}")
|
30
|
+
end
|
31
|
+
when "sqlite","sqlite3"
|
32
|
+
dbfile = config["database"] || config["dbfile"]
|
33
|
+
File.delete(dbfile) if File.exist?(dbfile)
|
34
|
+
when "sqlserver"
|
35
|
+
dropfkscript = "#{config["host"]}.#{config["database"]}.DP1".gsub(/\\/,'-')
|
36
|
+
`osql -E -S #{config["host"]} -d #{config["database"]} -i db\\#{dropfkscript}`
|
37
|
+
`osql -E -S #{config["host"]} -d #{config["database"]} -i db\\#{RAILS_ENV}_structure.sql`
|
38
|
+
when "oci", "oracle"
|
39
|
+
ActiveRecord::Base.establish_connection(config)
|
40
|
+
ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
|
41
|
+
ActiveRecord::Base.connection.execute(ddl)
|
42
|
+
end
|
43
|
+
when "firebird"
|
44
|
+
ActiveRecord::Base.establish_connection(config)
|
45
|
+
ActiveRecord::Base.connection.recreate_database!
|
46
|
+
else
|
47
|
+
raise "Task not supported by '#{config["adapter"]}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "Clear database and optional directories such as assets, then run db:seed"
|
52
|
+
task :seed_again, :assets, :needs => [:environment] do |t, args|
|
53
|
+
|
54
|
+
Rake::Task['autotelik:db:purge'].invoke( true ) # i.e ENV['exclude_system_tables'] = true
|
55
|
+
|
56
|
+
if(args[:assets])
|
57
|
+
assets = "#{Rails.root}/public/assets"
|
58
|
+
FileUtils::rm_rf(assets) if(File.exists?(assets))
|
59
|
+
end
|
60
|
+
|
61
|
+
Rake::Task['db:seed'].invoke
|
62
|
+
end
|
63
|
+
|
64
|
+
end # db
|
65
|
+
end # autotelik
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Feb 2011
|
4
|
+
# License:: TBD. Free, Open Source. MIT ?
|
5
|
+
#
|
6
|
+
# REQUIRES: JRuby
|
7
|
+
#
|
8
|
+
# Usage from rake : jruby -S rake excel_loader input=<file.xls>
|
9
|
+
#
|
10
|
+
# e.g. => jruby -S rake excel_load input=vendor\extensions\autotelik\fixtures\ExampleInfoWeb.xls
|
11
|
+
# => jruby -S rake excel_load input=C:\MyProducts.xls verbose=true
|
12
|
+
#
|
13
|
+
namespace :autotelik do
|
14
|
+
|
15
|
+
desc "Populate AR model's table with data stored in Excel"
|
16
|
+
task :excel_load, :klass, :input, :verbose, :sku_prefix, :needs => :environment do |t, args|
|
17
|
+
|
18
|
+
raise "USAGE: jruby -S rake excel_load input=excel_file.xls" unless args[:input]
|
19
|
+
raise "ERROR: Cannot process without AR Model - please supply model=<Class>" unless args[:class]
|
20
|
+
raise "ERROR: Could not find file #{args[:input]}" unless File.exists?(args[:input])
|
21
|
+
|
22
|
+
klass = Kernal.const_get(args[:model])
|
23
|
+
raise "ERROR: No such AR Model found - please check model=<Class>" unless(klass)
|
24
|
+
|
25
|
+
require 'product_loader'
|
26
|
+
require 'method_mapper_excel'
|
27
|
+
|
28
|
+
args[:class]
|
29
|
+
|
30
|
+
@method_mapper = MethodMapperExcel.new(args[:input], Product)
|
31
|
+
|
32
|
+
@excel = @method_mapper.excel
|
33
|
+
|
34
|
+
if(args[:verbose])
|
35
|
+
puts "Loading from Excel file: #{args[:input]}"
|
36
|
+
puts "Processing #{@excel.num_rows} rows"
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO create YAML configuration file to drive mandatory columns
|
40
|
+
#
|
41
|
+
# TODO create YAML configuration file to drive defaults etc
|
42
|
+
|
43
|
+
# Process spreadsheet and create model instances
|
44
|
+
|
45
|
+
method_names = @method_mapper.method_names
|
46
|
+
|
47
|
+
Product.transaction do
|
48
|
+
@products = []
|
49
|
+
|
50
|
+
(1..@excel.num_rows).collect do |row|
|
51
|
+
|
52
|
+
product_data_row = @excel.sheet.getRow(row)
|
53
|
+
break if product_data_row.nil?
|
54
|
+
|
55
|
+
# Excel num_rows seems to return all 'visible' rows so,
|
56
|
+
# we have to manually detect when actual data ends and all the empty rows start
|
57
|
+
contains_data = required_methods.find { |mthd| ! product_data_row.getCell(method_names.index(mthd)).to_s.empty? }
|
58
|
+
break unless contains_data
|
59
|
+
|
60
|
+
@assoc_classes = {}
|
61
|
+
|
62
|
+
loader = ProductLoader.new()
|
63
|
+
|
64
|
+
# TODO - Smart sorting of column processing order ....
|
65
|
+
# Does not currently ensure mandatory columns (for valid?) processed first but model needs saving
|
66
|
+
# before associations can be processed so user should ensure mandatory columns are prior to associations
|
67
|
+
|
68
|
+
@method_mapper.methods.each_with_index do |method_map, col|
|
69
|
+
|
70
|
+
loader.process(method_map, @excel.value(product_data_row, col))
|
71
|
+
begin
|
72
|
+
loader.load_object.save if( loader.load_object.valid? && loader.load_object.new_record? )
|
73
|
+
rescue
|
74
|
+
raise "Error processing Product"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
product = loader.load_object
|
79
|
+
|
80
|
+
product.available_on ||= Time.now.to_s(:db)
|
81
|
+
|
82
|
+
# TODO - handle when it's not valid ?
|
83
|
+
# Process rest and dump out an exception list of Products
|
84
|
+
#unless(product.valid?)
|
85
|
+
#end
|
86
|
+
|
87
|
+
puts "SAVING ROW #{row} : #{product.inspect}" if args[:verbose]
|
88
|
+
|
89
|
+
unless(product.save)
|
90
|
+
puts product.errors.inspect
|
91
|
+
puts product.errors.full_messages.inspect
|
92
|
+
raise "Error Saving Product: #{product.sku} :#{product.name}"
|
93
|
+
else
|
94
|
+
@products << product
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end # TRANSACTION
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# Copyright:: (c) Autotelik Media Ltd 2011
|
2
|
+
# Author :: Tom Statter
|
3
|
+
# Date :: Feb 2011
|
4
|
+
# License:: MIT. Free, Open Source.
|
5
|
+
#
|
6
|
+
# Usage:: rake autotelik:file_rename input=/blah image_load input=path_to_images
|
7
|
+
#
|
8
|
+
|
9
|
+
namespace :autotelik do
|
10
|
+
|
11
|
+
desc "copy or mv a folder of files, consistently renaming in the process"
|
12
|
+
task :file_rename, :input, :offset, :prefix, :width, :commit, :mv do |t, args|
|
13
|
+
raise "USAGE: rake file_rename input='C:\blah' [offset=n prefix='str' width=n]" unless args[:input] && File.exists?(args[:input])
|
14
|
+
width = args[:width] || 2
|
15
|
+
|
16
|
+
action = args[:mv] ? 'mv' : 'cp'
|
17
|
+
|
18
|
+
cache = args[:input]
|
19
|
+
|
20
|
+
if(File.exists?(cache) )
|
21
|
+
puts "Renaming files from #{cache}"
|
22
|
+
Dir.glob(File.join(cache, "*")) do |name|
|
23
|
+
path, base_name = File.split(name)
|
24
|
+
id = base_name.slice!(/\w+/)
|
25
|
+
|
26
|
+
id = id.to_i + args[:offset].to_i if(args[:offset])
|
27
|
+
id = "%0#{width}d" % id.to_i if(args[:width])
|
28
|
+
id = args[:prefix] + id.to_s if(args[:prefix])
|
29
|
+
|
30
|
+
destination = File.join(path, "#{id}#{base_name}")
|
31
|
+
puts "ACTION: #{action} #{name} #{destination}"
|
32
|
+
|
33
|
+
File.send( action, name, destination) if args[:commit]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
p = Product.seed( :name ) do |s|
|
2
|
+
s.name = <%= @name %>
|
3
|
+
s.available_on = '2009-09-01 09:00:00.0'
|
4
|
+
s.meta_keywords = ['training', 'training']
|
5
|
+
s.meta_description = ""
|
6
|
+
s.description = '<%= @description %>'
|
7
|
+
s.price = 0.00
|
8
|
+
s.sku = '<%= @sku %>'
|
9
|
+
|
10
|
+
s.is_physical = false
|
11
|
+
s.is_private = false
|
12
|
+
|
13
|
+
s.append_association :taxons, Taxon.find_by_name( 'Training' )
|
14
|
+
|
15
|
+
end
|