datashift 0.10.2 → 0.11.0
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/VERSION +1 -1
- data/datashift.gemspec +2 -2
- data/lib/datashift/method_dictionary.rb +4 -6
- data/lib/datashift/method_mapper.rb +7 -6
- data/lib/datashift/populator.rb +1 -1
- data/lib/loaders/csv_loader.rb +2 -2
- data/lib/loaders/excel_loader.rb +4 -9
- data/lib/loaders/loader_base.rb +13 -12
- data/lib/loaders/paperclip/attachment_loader.rb +30 -21
- data/lib/loaders/paperclip/datashift_paperclip.rb +5 -3
- data/lib/loaders/paperclip/image_loading.rb +10 -11
- data/lib/thor/import.thor +2 -2
- data/lib/thor/paperclip.thor +28 -9
- data/spec/excel_loader_spec.rb +5 -3
- data/spec/fixtures/db/datashift_test_models_db.sqlite +0 -0
- data/spec/method_dictionary_spec.rb +48 -5
- data/spec/paperclip_loader_spec.rb +2 -2
- metadata +41 -46
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.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.
|
8
|
+
s.version = "0.11.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-10-
|
12
|
+
s.date = "2012-10-23"
|
13
13
|
s.description = "Comprehensive tools to import/export between Excel/CSV and ActiveRecord databases, Rails apps, and any Ruby project."
|
14
14
|
s.email = "rubygems@autotelik.co.uk"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -12,13 +12,11 @@ module DataShift
|
|
12
12
|
class MethodDictionary
|
13
13
|
|
14
14
|
include DataShift::Logging
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
# Has the dictionary been populated for klass
|
15
|
+
|
16
|
+
# Return true if dictionary has been populated for klass
|
20
17
|
def self.for?(klass)
|
21
|
-
|
18
|
+
any = has_many[klass] || belongs_to[klass] || has_one[klass] || assignments[klass]
|
19
|
+
return any != nil
|
22
20
|
end
|
23
21
|
|
24
22
|
# Create simple picture of all the operator names for assignment available on an AR model,
|
@@ -84,8 +84,7 @@ module DataShift
|
|
84
84
|
DataShift::MethodDictionary.build_method_details(klass)
|
85
85
|
end
|
86
86
|
|
87
|
-
forced = [*options[:force_inclusion]].compact
|
88
|
-
forced.collect! { |f| f.downcase }
|
87
|
+
forced = [*options[:force_inclusion]].compact.collect { |f| f.to_s.downcase }
|
89
88
|
|
90
89
|
@method_details, @missing_methods = [], []
|
91
90
|
|
@@ -94,7 +93,7 @@ module DataShift
|
|
94
93
|
raw_col_data = col_data.to_s
|
95
94
|
|
96
95
|
if(raw_col_data.nil? or raw_col_data.empty?)
|
97
|
-
logger.warn("Column list contains empty or null
|
96
|
+
logger.warn("Column list contains empty or null column at index #{col_index}")
|
98
97
|
@method_details << nil
|
99
98
|
next
|
100
99
|
end
|
@@ -103,10 +102,10 @@ module DataShift
|
|
103
102
|
|
104
103
|
md = MethodDictionary::find_method_detail( klass, raw_col_name )
|
105
104
|
|
106
|
-
# TODO be nice if we could
|
105
|
+
# TODO be nice if we could check that the assoc on klass responds to the specified
|
107
106
|
# lookup key now (nice n early)
|
108
107
|
# active_record_helper = "find_by_#{lookup}"
|
109
|
-
if(md.nil? && options[:include_all] || forced.include?(raw_col_name.downcase))
|
108
|
+
if(md.nil? && (options[:include_all] || forced.include?(raw_col_name.downcase)) )
|
110
109
|
md = MethodDictionary::add(klass, raw_col_name)
|
111
110
|
end
|
112
111
|
|
@@ -149,7 +148,9 @@ module DataShift
|
|
149
148
|
|
150
149
|
# Returns true if discovered methods contain every operator in mandatory_list
|
151
150
|
def contains_mandatory?( mandatory_list )
|
152
|
-
|
151
|
+
a = [*mandatory_list].collect { |f| f.downcase }
|
152
|
+
b = operator_names.collect { |f| f.downcase }
|
153
|
+
(a - b).empty?
|
153
154
|
end
|
154
155
|
|
155
156
|
def missing_mandatory( mandatory_list )
|
data/lib/datashift/populator.rb
CHANGED
@@ -31,7 +31,7 @@ module DataShift
|
|
31
31
|
record.send(op, value.send( f) )
|
32
32
|
break
|
33
33
|
rescue => e
|
34
|
-
|
34
|
+
puts "DEBUG: insistent_assignment: #{e.inspect}"
|
35
35
|
if f == Populator::insistent_method_list.last
|
36
36
|
puts "I'm sorry I have failed to assign [#{value}] to #{operator}"
|
37
37
|
raise "I'm sorry I have failed to assign [#{value}] to #{operator}" unless value.nil?
|
data/lib/loaders/csv_loader.rb
CHANGED
@@ -86,8 +86,8 @@ module DataShift
|
|
86
86
|
|
87
87
|
include DataShift::CsvLoading
|
88
88
|
|
89
|
-
def initialize(klass, object = nil, options = {})
|
90
|
-
super( klass, object, options )
|
89
|
+
def initialize(klass, find_operators = true, object = nil, options = {})
|
90
|
+
super( klass, find_operators, object, options )
|
91
91
|
raise "Cannot load - failed to create a #{klass}" unless @load_object
|
92
92
|
end
|
93
93
|
|
data/lib/loaders/excel_loader.rb
CHANGED
@@ -44,7 +44,6 @@ module DataShift
|
|
44
44
|
puts "\n\n\nLoading from Excel file: #{file_name}"
|
45
45
|
|
46
46
|
sheet_number = options[:sheet_number] || 0
|
47
|
-
|
48
47
|
|
49
48
|
@sheet = @excel.worksheet( sheet_number )
|
50
49
|
|
@@ -66,14 +65,11 @@ module DataShift
|
|
66
65
|
end
|
67
66
|
|
68
67
|
raise MissingHeadersError, "No headers found - Check Sheet #{@sheet} is complete and Row #{header_row_index} contains headers" if(@headers.empty?)
|
69
|
-
|
70
68
|
|
71
69
|
# Create a method_mapper which maps list of headers into suitable calls on the Active Record class
|
72
70
|
# For example if model has an attribute 'price' will map columns called Price, price, PRICE etc to this attribute
|
73
71
|
populate_method_mapper_from_headers( @headers, options )
|
74
72
|
|
75
|
-
@method_mapper
|
76
|
-
|
77
73
|
logger.info "Excel Loader processing #{@sheet.num_rows} rows"
|
78
74
|
|
79
75
|
loaded_objects.clear
|
@@ -105,10 +101,9 @@ module DataShift
|
|
105
101
|
|
106
102
|
# Iterate over method_details, working on data out of associated Excel column
|
107
103
|
@method_mapper.method_details.each do |method_detail|
|
108
|
-
|
109
|
-
|
104
|
+
|
110
105
|
next unless method_detail # TODO populate unmapped with a real MethodDetail that is 'null' and create is_nil
|
111
|
-
|
106
|
+
|
112
107
|
value = row[method_detail.column_index]
|
113
108
|
|
114
109
|
contains_data = true unless(value.nil? || value.to_s.empty?)
|
@@ -155,8 +150,8 @@ module DataShift
|
|
155
150
|
|
156
151
|
include ExcelLoading
|
157
152
|
|
158
|
-
def initialize(klass, object = nil, options = {})
|
159
|
-
super( klass, object, options )
|
153
|
+
def initialize(klass, find_operators = true, object = nil, options = {})
|
154
|
+
super( klass, find_operators, object, options )
|
160
155
|
raise "Cannot load - failed to create a #{klass}" unless @load_object
|
161
156
|
end
|
162
157
|
|
data/lib/loaders/loader_base.rb
CHANGED
@@ -39,25 +39,25 @@ module DataShift
|
|
39
39
|
#
|
40
40
|
# Options to drive building the method dictionary for a class, enabling headers to be mapped to operators on that class.
|
41
41
|
#
|
42
|
+
# find_operators [default = true] : Populate method dictionary with operators and method details
|
43
|
+
#
|
42
44
|
# Options
|
43
|
-
# :load [default = true] : Load the method dictionary for object_class
|
44
45
|
#
|
45
46
|
# :reload : Force load of the method dictionary for object_class even if already loaded
|
46
|
-
# :instance_methods : Include setter
|
47
|
-
|
48
|
-
|
49
|
-
def initialize(object_class, object = nil, options = {})
|
47
|
+
# :instance_methods : Include setter/delegate style instance methods for assignment, as well as AR columns
|
48
|
+
#
|
49
|
+
def initialize(object_class, find_operators = true, object = nil, options = {})
|
50
50
|
@load_object_class = object_class
|
51
|
-
|
51
|
+
|
52
52
|
# Gather names of all possible 'setter' methods on AR class (instance variables and associations)
|
53
|
-
|
54
|
-
|
53
|
+
if((find_operators && !MethodDictionary::for?(object_class)) || options[:reload])
|
54
|
+
puts "Building Method Dictionary for class #{object_class}"
|
55
55
|
DataShift::MethodDictionary.find_operators( @load_object_class, :reload => options[:reload], :instance_methods => options[:instance_methods] )
|
56
56
|
|
57
57
|
# Create dictionary of data on all possible 'setter' methods which can be used to
|
58
58
|
# populate or integrate an object of type @load_object_class
|
59
59
|
DataShift::MethodDictionary.build_method_details(@load_object_class)
|
60
|
-
end
|
60
|
+
end
|
61
61
|
|
62
62
|
@method_mapper = DataShift::MethodMapper.new
|
63
63
|
@config = options.dup # clone can cause issues like 'can't modify frozen hash'
|
@@ -152,10 +152,10 @@ module DataShift
|
|
152
152
|
raise MappingDefinitionError, "Missing mappings for columns : #{@method_mapper.missing_methods.join(",")}" if(strict)
|
153
153
|
end
|
154
154
|
|
155
|
-
unless(@method_mapper.contains_mandatory?(mandatory) )
|
156
|
-
@method_mapper.missing_mandatory(mandatory).each { |
|
155
|
+
unless(mandatory.empty? || @method_mapper.contains_mandatory?(mandatory) )
|
156
|
+
@method_mapper.missing_mandatory(mandatory).each { |er| puts "ERROR: Mandatory column missing - expected column '#{er}'" }
|
157
157
|
raise MissingMandatoryError, "Mandatory columns missing - please fix and retry."
|
158
|
-
end
|
158
|
+
end
|
159
159
|
|
160
160
|
@method_mapper
|
161
161
|
end
|
@@ -184,6 +184,7 @@ module DataShift
|
|
184
184
|
prepare_data(method_detail, data)
|
185
185
|
process()
|
186
186
|
else
|
187
|
+
puts "No matching method found for column #{column_name}"
|
187
188
|
@load_object.errors.add(:base, "No matching method found for column #{column_name}")
|
188
189
|
end
|
189
190
|
end
|
@@ -20,20 +20,16 @@ module DataShift
|
|
20
20
|
|
21
21
|
attr_writer :attach_to_field
|
22
22
|
attr_reader :attachment_path, :loading_files_cache
|
23
|
+
|
23
24
|
|
24
|
-
|
25
|
-
#
|
26
|
-
# return that otherwise return the raw format of :attach_to_find_by_field
|
27
|
-
|
28
|
-
def attach_to_field
|
29
|
-
@attach_to_method_detail || @attach_to_field
|
30
|
-
end
|
31
|
-
|
25
|
+
# Constructor
|
26
|
+
#
|
32
27
|
# Options
|
33
|
-
|
28
|
+
#
|
34
29
|
# => :attach_to_klass
|
35
|
-
# A class that has a relationship with
|
30
|
+
# A class that has a relationship with the attachment (has_many, has_one or belongs_to etc)
|
36
31
|
# The instance of :attach_to_klass can be searched for and the new attachment assigned.
|
32
|
+
#
|
37
33
|
# Examples
|
38
34
|
# Owner has_many pdfs and mp3 files as Digitals .... :attach_to_klass = Owner
|
39
35
|
# User has a single image used as an avatar ... :attach_to_klass = User
|
@@ -41,6 +37,7 @@ module DataShift
|
|
41
37
|
# => :attach_to_find_by_field
|
42
38
|
# For the :attach_to_klass, this is the field used to search for the parent
|
43
39
|
# object to assign the new attachment to.
|
40
|
+
#
|
44
41
|
# Examples
|
45
42
|
# Owner has a unique 'name' field ... :attach_to_find_by_field = :name
|
46
43
|
# User has a unique 'login' field ... :attach_to_klass = :login
|
@@ -48,27 +45,31 @@ module DataShift
|
|
48
45
|
# => :attach_to_field
|
49
46
|
# Attribute/association to assign attachment to on :attach_to_klass.
|
50
47
|
# Examples
|
48
|
+
#
|
51
49
|
# :attach_to_field => digitals : Owner.digitals = attachment
|
52
50
|
# :attach_to_field => avatar : User.avatar = attachment
|
53
51
|
#
|
54
52
|
#
|
55
|
-
def initialize(attachment_klazz, attachment = nil, options = {})
|
53
|
+
def initialize(attachment_klazz, find_operators = true, attachment = nil, options = {})
|
56
54
|
|
57
55
|
init_from_options( options )
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
super( attachment_klazz, attachment, opts )
|
56
|
+
|
57
|
+
super( attachment_klazz, find_operators, attachment, options.dup )
|
62
58
|
|
63
59
|
puts "Attachment Class is #{load_object_class}" if(@verbose)
|
64
60
|
|
65
61
|
raise "Failed to create Attachment for loading" unless @load_object
|
66
62
|
end
|
63
|
+
|
67
64
|
|
65
|
+
# Options
|
66
|
+
# :reload
|
67
|
+
# :attach_to_klass, :attach_to_field, :attach_to_find_by_field
|
68
|
+
#
|
68
69
|
def init_from_options( options )
|
69
|
-
|
70
|
-
@attach_to_klass = options[:attach_to_klass] || @attach_to_klass || nil
|
71
70
|
|
71
|
+
@attach_to_klass = options[:attach_to_klass] || @attach_to_klass || nil
|
72
|
+
|
72
73
|
unless(@attach_to_klass.nil? || (MethodDictionary::for?(@attach_to_klass) && options[:reload] == false))
|
73
74
|
#puts "Building Method Dictionary for class #{object_class}"
|
74
75
|
DataShift::MethodDictionary.find_operators( @attach_to_klass, :reload => options[:reload], :instance_methods => true )
|
@@ -86,6 +87,15 @@ module DataShift
|
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
90
|
+
|
91
|
+
# If we have instantiated a method detail based on the attach to class and fields
|
92
|
+
# return that otherwise return the raw format of :attach_to_find_by_field
|
93
|
+
|
94
|
+
def attach_to_field
|
95
|
+
@attach_to_method_detail || @attach_to_field
|
96
|
+
end
|
97
|
+
|
98
|
+
|
89
99
|
# This version creates attachments and also attaches them to instances of :attach_to_klazz
|
90
100
|
#
|
91
101
|
# Options
|
@@ -125,7 +135,7 @@ module DataShift
|
|
125
135
|
|
126
136
|
record = nil
|
127
137
|
|
128
|
-
puts "
|
138
|
+
puts "Attempting to find matching record where #{attach_to_find_by_field} ~= #{base_name}"
|
129
139
|
record = get_record_by(attach_to_klass, attach_to_find_by_field, base_name, split_on)
|
130
140
|
|
131
141
|
if(record)
|
@@ -139,12 +149,11 @@ module DataShift
|
|
139
149
|
|
140
150
|
# Check if attachment must have an associated record
|
141
151
|
if(record)
|
142
|
-
puts "now create attachment"
|
143
152
|
reset()
|
144
153
|
|
145
154
|
create_attachment(@load_object_class, file_name, record, attach_to_field, options)
|
146
155
|
|
147
|
-
puts "Added Attachment #{File.basename(file_name)} to #{record.send(attach_to_find_by_field)} : #{record.id}" if(@verbose)
|
156
|
+
puts "Added Attachment #{File.basename(file_name)} to #{record.send(attach_to_find_by_field)}(id : #{record.id})" if(@verbose)
|
148
157
|
end
|
149
158
|
|
150
159
|
end
|
@@ -153,7 +162,7 @@ module DataShift
|
|
153
162
|
FileUtils.mkdir_p('MissingAttachmentRecords') unless File.directory?('MissingAttachmentRecords')
|
154
163
|
|
155
164
|
puts "WARNING : #{missing_records.size} of #{loading_files_cache.size} files could not be attached to a #{@load_object_class}"
|
156
|
-
puts "For your convenience
|
165
|
+
puts "For your convenience files with MISSING #{attach_to_klass} have been copied to : MissingAttachmentRecords"
|
157
166
|
missing_records.each do |i|
|
158
167
|
puts "Copying #{i} to MissingAttachmentRecords folder" if(options[:verbose])
|
159
168
|
FileUtils.cp( i, 'MissingAttachmentRecords') unless(options[:dummy] == 'true')
|
@@ -56,7 +56,7 @@ module DataShift
|
|
56
56
|
#
|
57
57
|
# Pass through hash of attributes to klass initializer
|
58
58
|
#
|
59
|
-
# :
|
59
|
+
# :has_attached_file_name
|
60
60
|
#
|
61
61
|
# Paperclip attachment name defined with macro 'has_attached_file :name'
|
62
62
|
#
|
@@ -71,7 +71,7 @@ module DataShift
|
|
71
71
|
#
|
72
72
|
def create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
|
73
73
|
|
74
|
-
has_attached_file_attribute = options[
|
74
|
+
has_attached_file_attribute = options[:has_attached_file_name] ? options[:has_attached_file_name].to_sym : :attachment
|
75
75
|
|
76
76
|
attributes = { has_attached_file_attribute => get_file(attachment_path) }
|
77
77
|
|
@@ -81,6 +81,7 @@ module DataShift
|
|
81
81
|
|
82
82
|
#attributes.merge!(attach_to_record_field.to_sym => record) if(record && attach_to_record_field)
|
83
83
|
|
84
|
+
puts attributes.inspect
|
84
85
|
begin
|
85
86
|
|
86
87
|
@attachment = klass.new(attributes, :without_protection => true)
|
@@ -92,11 +93,12 @@ module DataShift
|
|
92
93
|
attach_to_record_field.assign(record, @attachment)
|
93
94
|
else
|
94
95
|
# assume its not a has_many and try basic send
|
95
|
-
record.send(attach_to_record_field
|
96
|
+
record.send("#{attach_to_record_field}=", @attachment)
|
96
97
|
end if(record && attach_to_record_field)
|
97
98
|
|
98
99
|
else
|
99
100
|
puts "ERROR : Problem saving to DB : #{@attachment.inspect}"
|
101
|
+
puts @attachment.errors.messages.inspect
|
100
102
|
end
|
101
103
|
|
102
104
|
@attachment
|
@@ -14,9 +14,6 @@ module DataShift
|
|
14
14
|
|
15
15
|
include DataShift::Paperclip
|
16
16
|
|
17
|
-
def initialize(attachment_klazz, attachment = nil, options = {})
|
18
|
-
super( attachment_klazz, attachment, options )
|
19
|
-
end
|
20
17
|
|
21
18
|
# Note the paperclip attachment model defines the storage path via something like :
|
22
19
|
#
|
@@ -33,15 +30,17 @@ module DataShift
|
|
33
30
|
# :viewable_record
|
34
31
|
#
|
35
32
|
def create_attachment(klass, attachment_path, record = nil, attach_to_record_field = nil, options = {})
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
|
34
|
+
image_attributes = { :attributes =>
|
35
|
+
{ :alt => (options[:alt] || ""),
|
36
|
+
:position => (!options[:position] && record and record.respond_to?(:images)) ? record.images.length : 0
|
37
|
+
}
|
38
|
+
}
|
42
39
|
|
43
|
-
attachment_options.merge
|
44
|
-
|
40
|
+
attachment_options = options.dup.merge(image_attributes)
|
41
|
+
|
42
|
+
#puts "DEBUG : create_attachment options : #{attachment_options.inspect}"
|
43
|
+
|
45
44
|
super(klass, attachment_path, record, attach_to_record_field, attachment_options)
|
46
45
|
end
|
47
46
|
|
data/lib/thor/import.thor
CHANGED
@@ -63,11 +63,11 @@ module Datashift
|
|
63
63
|
logger.info("INFO: Using loader : #{loader.class}")
|
64
64
|
rescue
|
65
65
|
logger.error("INFO: No specific #{model}Loader found - using generic ExcelLoader")
|
66
|
-
loader = DataShift::ExcelLoader.new(klass)
|
66
|
+
loader = DataShift::ExcelLoader.new(klass, true)
|
67
67
|
end
|
68
68
|
else
|
69
69
|
logger.info("No Loader specified - using generic ExcelLoader")
|
70
|
-
loader = DataShift::ExcelLoader.new(klass)
|
70
|
+
loader = DataShift::ExcelLoader.new(klass, true)
|
71
71
|
end
|
72
72
|
|
73
73
|
logger.info("ARGS #{options.inspect}")
|
data/lib/thor/paperclip.thor
CHANGED
@@ -22,11 +22,32 @@ module Datashift
|
|
22
22
|
|
23
23
|
include DataShift::Logging
|
24
24
|
|
25
|
-
desc "attach", "
|
26
|
-
|
25
|
+
desc "attach", "Create paperclip attachments and attach to a Model from files in a directory.
|
26
|
+
This is specifically for the use case where the paperclip attachments are stored in a class, such as Image, Icon, Asset,
|
27
|
+
and this class has a relationship, such as belongs_to, with another class, such as Product, User, Document.
|
28
|
+
|
29
|
+
Each matching file is used to create an instance of the paperclip attachment, given by :attachment_klass.
|
30
|
+
|
31
|
+
The class with the relationship, can be specified via :attach_to_klass
|
32
|
+
|
27
33
|
Examples
|
28
|
-
Owner has_many pdfs and mp3 files as Digitals .... :attach_to_klass = Owner
|
29
|
-
User has a single
|
34
|
+
Owner has_many pdfs and mp3 files as Digitals .... :attachment_klass = Digital and :attach_to_klass = Owner
|
35
|
+
User has a single Image used as an avatar ... attachment_klass = Image and :attach_to_klass = User
|
36
|
+
|
37
|
+
The file name is used to lookup the instance of :attach_to_klass to assign the new attachment to, via :attach_to_find_by_field
|
38
|
+
|
39
|
+
So say we have a file called smithj_avatar.gif, and we want to lookup Users by login
|
40
|
+
|
41
|
+
:attach_to_find_by_field = login => Run a loookup based on find_by_login == 'smithj'
|
42
|
+
|
43
|
+
Once instance of :attach_to_klass found, the new attachment is assigned.
|
44
|
+
|
45
|
+
The attribute to assign new attachment to is gtiven by :attach_to_field
|
46
|
+
|
47
|
+
Examples
|
48
|
+
:attach_to_field => digitals : Owner.digitals = attachment(Digital)
|
49
|
+
:attach_to_field => avatar : User.avatar = attachment(Image)"
|
50
|
+
|
30
51
|
|
31
52
|
# :dummy => dummy run without actual saving to DB
|
32
53
|
method_option :input, :aliases => '-i', :required => true, :desc => "The input path containing images "
|
@@ -35,13 +56,11 @@ module Datashift
|
|
35
56
|
method_option :recursive, :aliases => '-r', :type => :boolean, :desc => "Scan sub directories of input for images"
|
36
57
|
|
37
58
|
method_option :attachment_klass, :required => true, :aliases => '-a', :desc => "Ruby Class name of the Attachment e.g Image, Icon"
|
38
|
-
method_option :attach_to_klass, :required => true, :aliases => '-k', :desc => "A class that has a relationship with the attachment (has_many, has_one, belongs_to)"
|
39
|
-
|
40
|
-
|
41
|
-
method_option :attach_to_field, :required => true, :aliases => '-f', :desc => "Attachment belongs to field e.g Product.image, Blog.digital"
|
42
59
|
|
60
|
+
method_option :attach_to_klass, :required => true, :aliases => '-k', :desc => "A class that has a relationship with the attachment (has_many, has_one, belongs_to)"
|
43
61
|
method_option :attach_to_find_by_field, :required => true, :aliases => '-l', :desc => "The field to use to find the :attach_to_klass record"
|
44
|
-
|
62
|
+
method_option :attach_to_field, :required => true, :aliases => '-f', :desc => "Attachment belongs to field e.g Product.image, Blog.digital"
|
63
|
+
|
45
64
|
|
46
65
|
# => :attach_to_find_by_field
|
47
66
|
# For the :attach_to_klass, this is the field used to search for the parent
|
data/spec/excel_loader_spec.rb
CHANGED
@@ -31,9 +31,6 @@ describe 'Excel Loader' do
|
|
31
31
|
Category.find_or_create_by_reference(cat)
|
32
32
|
end
|
33
33
|
|
34
|
-
DataShift::MethodDictionary.find_operators( Category )
|
35
|
-
|
36
|
-
DataShift::MethodDictionary.build_method_details( Category )
|
37
34
|
|
38
35
|
end
|
39
36
|
|
@@ -57,6 +54,11 @@ describe 'Excel Loader' do
|
|
57
54
|
|
58
55
|
it "should process multiple associationss from single column" do
|
59
56
|
|
57
|
+
|
58
|
+
DataShift::MethodDictionary.find_operators( Category )
|
59
|
+
|
60
|
+
DataShift::MethodDictionary.build_method_details( Category )
|
61
|
+
|
60
62
|
Project.find_by_title('001').should be_nil
|
61
63
|
count = Project.count
|
62
64
|
|
Binary file
|
@@ -8,25 +8,34 @@
|
|
8
8
|
# and a classes different types of assignment operators
|
9
9
|
#
|
10
10
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
11
|
-
|
11
|
+
|
12
|
+
require 'method_dictionary'
|
13
|
+
|
12
14
|
describe 'Method Dictionary' do
|
13
15
|
|
16
|
+
|
14
17
|
include_context "ActiveRecordTestModelsConnected"
|
15
18
|
|
19
|
+
include DataShift
|
16
20
|
|
17
21
|
before(:each) do
|
18
22
|
MethodDictionary.clear
|
19
|
-
MethodDictionary.find_operators( Project )
|
20
|
-
MethodDictionary.find_operators( Milestone )
|
21
23
|
end
|
22
24
|
|
23
25
|
it "should store dictionary for multiple AR models" do
|
26
|
+
|
27
|
+
MethodDictionary.find_operators( Project )
|
28
|
+
MethodDictionary.find_operators( Milestone )
|
29
|
+
|
24
30
|
MethodDictionary.assignments.size.should == 2
|
25
31
|
MethodDictionary.has_many.size.should == 2
|
26
32
|
end
|
27
33
|
|
28
34
|
it "should populate method dictionary for a given AR model" do
|
29
35
|
|
36
|
+
MethodDictionary.find_operators( Project )
|
37
|
+
MethodDictionary.find_operators( Milestone )
|
38
|
+
|
30
39
|
MethodDictionary.has_many.should_not be_empty
|
31
40
|
MethodDictionary.has_many[Project].should include('milestones')
|
32
41
|
|
@@ -48,6 +57,8 @@ describe 'Method Dictionary' do
|
|
48
57
|
|
49
58
|
it "should populate assigment members without the equivalent association names" do
|
50
59
|
|
60
|
+
MethodDictionary.find_operators( Project )
|
61
|
+
|
51
62
|
# we should remove has-many & belongs_to from basic assignment set as they require a DB lookup
|
52
63
|
# or a Model.create call, not a simple assignment
|
53
64
|
|
@@ -58,6 +69,9 @@ describe 'Method Dictionary' do
|
|
58
69
|
|
59
70
|
it "should populate assignment operators for method details for different forms of a column name" do
|
60
71
|
|
72
|
+
MethodDictionary.find_operators( Project )
|
73
|
+
MethodDictionary.find_operators( Milestone )
|
74
|
+
|
61
75
|
MethodDictionary.build_method_details( Project )
|
62
76
|
|
63
77
|
[:value_as_string, 'value_as_string', "VALUE as_STRING", "value as string"].each do |format|
|
@@ -83,6 +97,8 @@ describe 'Method Dictionary' do
|
|
83
97
|
|
84
98
|
it "should populate column types for assignment operators in method details" do
|
85
99
|
|
100
|
+
MethodDictionary.find_operators( Project )
|
101
|
+
|
86
102
|
MethodDictionary.build_method_details( Project )
|
87
103
|
|
88
104
|
[:value_as_string, 'value_as_string', "VALUE as_STRING", "value as string"].each do |format|
|
@@ -101,6 +117,8 @@ describe 'Method Dictionary' do
|
|
101
117
|
|
102
118
|
it "should populate required Class for assignment operators based on column type" do
|
103
119
|
|
120
|
+
MethodDictionary.find_operators( Project )
|
121
|
+
|
104
122
|
MethodDictionary.build_method_details( Project )
|
105
123
|
|
106
124
|
[:value_as_string, 'value_as_string', "VALUE as_STRING", "value as string"].each do |format|
|
@@ -116,6 +134,9 @@ describe 'Method Dictionary' do
|
|
116
134
|
|
117
135
|
it "should populate belongs_to operator for method details for different forms of a column name" do
|
118
136
|
|
137
|
+
MethodDictionary.find_operators( Project )
|
138
|
+
MethodDictionary.find_operators( Milestone )
|
139
|
+
|
119
140
|
MethodDictionary.build_method_details( Project )
|
120
141
|
MethodDictionary.build_method_details( Milestone )
|
121
142
|
|
@@ -184,10 +205,24 @@ describe 'Method Dictionary' do
|
|
184
205
|
method_details.operator_class.should == LongAndComplexTableLinkedToVersion
|
185
206
|
end
|
186
207
|
end
|
208
|
+
|
209
|
+
it "should return false on for?(klass) if find_operators hasn't been called for klass" do
|
210
|
+
MethodDictionary.clear
|
211
|
+
MethodDictionary::for?(Project).should == false
|
212
|
+
MethodDictionary::find_operators(Project)
|
213
|
+
MethodDictionary::for?(Project).should == true
|
214
|
+
|
215
|
+
end
|
187
216
|
|
188
|
-
|
217
|
+
it "should return false on for?(klass) if find_operators hasn't been called for klass" do
|
218
|
+
MethodDictionary.clear
|
219
|
+
MethodDictionary::for?(Project).should == false
|
220
|
+
end
|
221
|
+
|
189
222
|
it "should find has_many operator for method details" do
|
190
|
-
|
223
|
+
|
224
|
+
MethodDictionary.find_operators( Project )
|
225
|
+
|
191
226
|
MethodDictionary.build_method_details( Project )
|
192
227
|
|
193
228
|
[:milestones, "Mile Stones", 'mileSTONES', 'MileStones'].each do |format|
|
@@ -208,6 +243,9 @@ describe 'Method Dictionary' do
|
|
208
243
|
|
209
244
|
|
210
245
|
it "should return nil when non existent column name" do
|
246
|
+
|
247
|
+
MethodDictionary.find_operators( Project )
|
248
|
+
|
211
249
|
["On sale", 'on_sale'].each do |format|
|
212
250
|
detail = MethodDictionary.find_method_detail( Project, format )
|
213
251
|
|
@@ -221,11 +259,16 @@ describe 'Method Dictionary' do
|
|
221
259
|
end
|
222
260
|
|
223
261
|
it "should not by default map setter methods" do
|
262
|
+
MethodDictionary.find_operators( Milestone )
|
263
|
+
|
224
264
|
MethodDictionary.assignments[Milestone].should_not include('title')
|
225
265
|
end
|
226
266
|
|
227
267
|
it "should support reload and inclusion of setter methods" do
|
228
268
|
|
269
|
+
MethodDictionary.find_operators( Project )
|
270
|
+
MethodDictionary.find_operators( Milestone )
|
271
|
+
|
229
272
|
MethodDictionary.assignments[Milestone].should_not include('title')
|
230
273
|
|
231
274
|
MethodDictionary.find_operators( Milestone, :reload => true, :instance_methods => true )
|
@@ -38,7 +38,7 @@ describe 'PaperClip Bulk Loader' do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should create a new paperclip loader and define attachment class" do
|
41
|
-
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, nil, @common_options)
|
41
|
+
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, true, nil, @common_options)
|
42
42
|
|
43
43
|
loader.load_object_class.should == Digital
|
44
44
|
loader.load_object.should be_a Digital
|
@@ -50,7 +50,7 @@ describe 'PaperClip Bulk Loader' do
|
|
50
50
|
|
51
51
|
opts = { :attach_to_klass => Owner }.merge(@common_options)
|
52
52
|
|
53
|
-
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, nil, opts)
|
53
|
+
loader = DataShift::Paperclip::AttachmentLoader.new(@attachment_klass, true, nil, opts)
|
54
54
|
|
55
55
|
loader.attach_to_klass.should == Owner
|
56
56
|
end
|
metadata
CHANGED
@@ -1,58 +1,50 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: datashift
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.2
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.11.0
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- Thomas Statter
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
date: 2012-10-23 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
15
16
|
name: spreadsheet
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version:
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements:
|
25
|
-
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
|
-
- !ruby/object:Gem::Dependency
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
31
27
|
name: rubyzip
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
33
29
|
none: false
|
34
|
-
requirements:
|
35
|
-
- -
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version:
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
|
-
version_requirements:
|
41
|
-
|
42
|
-
requirements:
|
43
|
-
- - ! '>='
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '0'
|
46
|
-
description: Comprehensive tools to import/export between Excel/CSV and ActiveRecord
|
47
|
-
databases, Rails apps, and any Ruby project.
|
36
|
+
version_requirements: *id002
|
37
|
+
description: Comprehensive tools to import/export between Excel/CSV and ActiveRecord databases, Rails apps, and any Ruby project.
|
48
38
|
email: rubygems@autotelik.co.uk
|
49
39
|
executables: []
|
40
|
+
|
50
41
|
extensions: []
|
51
|
-
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
52
44
|
- LICENSE.txt
|
53
45
|
- README.markdown
|
54
46
|
- README.rdoc
|
55
|
-
files:
|
47
|
+
files:
|
56
48
|
- .document
|
57
49
|
- LICENSE.txt
|
58
50
|
- README.markdown
|
@@ -229,28 +221,31 @@ files:
|
|
229
221
|
- tasks/file_tasks.rake
|
230
222
|
- tasks/word_to_seedfu.rake
|
231
223
|
homepage: http://github.com/autotelik/datashift
|
232
|
-
licenses:
|
224
|
+
licenses:
|
233
225
|
- MIT
|
234
226
|
post_install_message:
|
235
227
|
rdoc_options: []
|
236
|
-
|
228
|
+
|
229
|
+
require_paths:
|
237
230
|
- lib
|
238
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
231
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
239
232
|
none: false
|
240
|
-
requirements:
|
241
|
-
- -
|
242
|
-
- !ruby/object:Gem::Version
|
243
|
-
version:
|
244
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - ">="
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: "0"
|
237
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
245
238
|
none: false
|
246
|
-
requirements:
|
247
|
-
- -
|
248
|
-
- !ruby/object:Gem::Version
|
249
|
-
version:
|
239
|
+
requirements:
|
240
|
+
- - ">="
|
241
|
+
- !ruby/object:Gem::Version
|
242
|
+
version: "0"
|
250
243
|
requirements: []
|
244
|
+
|
251
245
|
rubyforge_project:
|
252
246
|
rubygems_version: 1.8.24
|
253
247
|
signing_key:
|
254
248
|
specification_version: 3
|
255
249
|
summary: Shift data betwen Excel/CSV and any Ruby app
|
256
250
|
test_files: []
|
251
|
+
|