ar_loader 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|